Configuring Read-only Root Filesystem
Container images deployed in OpenShift can be configured to use the readOnlyRootFilesystem security setting to enhance runtime security.
- Define volumes: Create PersistentVolumeClaims (PVCs) and declare the required volumes in the pod specification.
- Configure pod security context: Apply the necessary pod-level security settings.
- Add an init container: Configure the populate-volumes init container with appropriate volume mounts.
- Update the main container: Apply the required security context and configure volume mounts for the main container.
The yaml samples include a shared volume mounted at /var/shared for inter-container communication. While the use of the Snapshot Manager container or the CONFIG_SERVICE_URL environment variable is recommended for sharing snapshots, a shared volume is included in these examples to demonstrate how it can be used alongside read-only root filesystem mounts.
- Writable paths within the container image are replaced with volume mounts.
- An init container is used to populate the required volumes under
/mnt. - The main container mounts the corresponding paths without the
/mntprefix. - Persistent data is managed using PersistentVolumeClaims (PVCs) configured with
subPath. - Temporary runtime directories, such as
/tmpand/runare provisioned usingemptyDirvolumes.
Deployment
The following section illustrates how to deploy Verify Identity Access containers with read-only root filesystem security.
Configuration Container
Instructions on how to create the Verify Identity Access configuration container are provided in the following steps:
- Create a
PersistentVolumeClaim.apiVersion: v1 kind: PersistentVolumeClaim metadata: name: ivia-config-data namespace: <your-namespace> spec: accessModes: - ReadWriteOnce # Customise based on your storage requirements resources: requests: storage: 2Gi # Customise based on your storage requirements storageClassName: <your-storage-class> volumeMode: Filesystem - Configure a deployment job using the following command
apiVersion: apps/v1 kind: Deployment metadata: name: ivia-config namespace: <your-namespace> spec: replicas: 1 selector: matchLabels: app: ivia-config template: metadata: labels: app: ivia-config spec: # Pod-level security context securityContext: fsGroupChangePolicy: "OnRootMismatch" runAsNonRoot: true # Define volumes volumes: # Persistent storage for Config container data - name: config-data persistentVolumeClaim: claimName: ivia-config-data # Ephemeral storage for temporary files - name: tmp-volume emptyDir: {} # Shared volume for inter-service communication - name: shared-volume persistentVolumeClaim: claimName: ivia-shared-volume # Init container to populate volumes initContainers: - name: populate-volumes image: icr.io/ivia/ivia-config:latest imagePullPolicy: Always securityContext: readOnlyRootFilesystem: true allowPrivilegeEscalation: false runAsNonRoot: true capabilities: drop: ["ALL"] env: - name: INIT_VOLUMES value: "true" volumeMounts: # Mount persistent volumes at /mnt/* paths - name: config-data mountPath: /mnt/var subPath: var - name: config-data mountPath: /mnt/etc subPath: etc - name: config-data mountPath: /mnt/opt/ibm/wlp subPath: opt/ibm/wlp - name: config-data mountPath: /mnt/opt/rtss/wlp subPath: opt/rtss/wlp - name: config-data mountPath: /mnt/opt/java/jre/PolicyDirector subPath: opt/java/jre/PolicyDirector - name: config-data mountPath: /mnt/opt/pdjrte/log subPath: opt/pdjrte/log - name: config-data mountPath: /mnt/opt/pdjrte/etc subPath: opt/pdjrte/etc - name: config-data mountPath: /mnt/opt/PolicyDirector/etc subPath: opt/PolicyDirector/etc - name: config-data mountPath: /mnt/usr/lib64/iss-pam subPath: usr/lib64/iss-pam # Main container containers: - name: ivia-config image: icr.io/ivia/ivia-config:latest imagePullPolicy: Always securityContext: readOnlyRootFilesystem: true allowPrivilegeEscalation: false runAsNonRoot: true capabilities: drop: ["ALL"] volumeMounts: # Persistent volumes at actual paths - name: config-data mountPath: /var subPath: var - name: config-data mountPath: /etc subPath: etc - name: config-data mountPath: /opt/ibm/wlp subPath: opt/ibm/wlp - name: config-data mountPath: /opt/rtss/wlp subPath: opt/rtss/wlp - name: config-data mountPath: /opt/java/jre/PolicyDirector subPath: opt/java/jre/PolicyDirector - name: config-data mountPath: /opt/pdjrte/log subPath: opt/pdjrte/log - name: config-data mountPath: /opt/pdjrte/etc subPath: opt/pdjrte/etc - name: config-data mountPath: /opt/PolicyDirector/etc subPath: opt/PolicyDirector/etc - name: config-data mountPath: /usr/lib64/iss-pam subPath: usr/lib64/iss-pam # Ephemeral volumes - name: tmp-volume mountPath: /tmp subPath: tmp - name: tmp-volume mountPath: /run subPath: run # Shared volume - name: shared-volume mountPath: /var/shared # Add your existing environment variables, ports, etc.
Runtime Container
The Verify Identity Access Runtime Container (called ivia-runtime provides the advanced authentication, context-based access, and federation services.
The following steps illustrate how to create a runtime container with additional security of readOnlyRootFilesystem:
- Create a
PersistentVolumeClaim.apiVersion: v1 kind: PersistentVolumeClaim metadata: name: ivia-runtime-data namespace: <your-namespace> spec: accessModes: - ReadWriteOnce # Customise based on your storage requirements resources: requests: storage: 2Gi # Customise based on your storage requirements storageClassName: <your-storage-class> volumeMode: Filesystem - Configure a deployment job using the following c
apiVersion: apps/v1 kind: Deployment metadata: name: ivia-runtime namespace: <your-namespace> spec: replicas: 1 selector: matchLabels: app: ivia-runtime template: metadata: labels: app: ivia-runtime spec: # Pod-level security context securityContext: fsGroupChangePolicy: "OnRootMismatch" runAsNonRoot: true # Define volumes volumes: # Persistent storage for Runtime container data - name: runtime-data persistentVolumeClaim: claimName: ivia-runtime-data # Ephemeral storage for temporary files - name: tmp-volume emptyDir: {} # Shared volume for inter-service communication - name: shared-volume persistentVolumeClaim: claimName: ivia-shared-volume # Init container to populate volumes initContainers: - name: populate-volumes image: icr.io/ivia/ivia-runtime:latest imagePullPolicy: Always securityContext: readOnlyRootFilesystem: true allowPrivilegeEscalation: false runAsNonRoot: true capabilities: drop: ["ALL"] env: - name: INIT_VOLUMES value: "true" volumeMounts: # Mount persistent volumes at /mnt/* paths - name: runtime-data mountPath: /mnt/var subPath: var - name: runtime-data mountPath: /mnt/etc subPath: etc - name: runtime-data mountPath: /mnt/opt/ibm/wlp subPath: opt/ibm/wlp - name: runtime-data mountPath: /mnt/opt/java/jre/PolicyDirector subPath: opt/java/jre/PolicyDirector # Main container containers: - name: ivia-runtime image: icr.io/ivia/ivia-runtime:latest imagePullPolicy: Always securityContext: readOnlyRootFilesystem: true allowPrivilegeEscalation: false runAsNonRoot: true capabilities: drop: ["ALL"] volumeMounts: # Persistent volumes at actual paths - name: runtime-data mountPath: /var subPath: var - name: runtime-data mountPath: /etc subPath: etc - name: runtime-data mountPath: /opt/ibm/wlp subPath: opt/ibm/wlp - name: runtime-data mountPath: /opt/java/jre/PolicyDirector subPath: opt/java/jre/PolicyDirector # Ephemeral volumes - name: tmp-volume mountPath: /tmp subPath: tmp - name: tmp-volume mountPath: /run subPath: run # Shared volume - name: shared-volume mountPath: /var/shared # Add your existing environment variables, ports, etc.
Web Reverse Proxy Container
The following steps illustrate how to create a Web Reverse Proxy container with additional security of readOnlyRootFilesystem:
- Create a
PersistentVolumeClaim.apiVersion: v1 kind: PersistentVolumeClaim metadata: name: ivia-wrp-data namespace: <your-namespace> spec: accessModes: - ReadWriteOnce # Customise based on your storage requirements resources: requests: storage: 1Gi # Customise based on your storage requirements storageClassName: <your-storage-class> volumeMode: Filesystem - Configure a deployment job using the following command:
apiVersion: apps/v1 kind: Deployment metadata: name: ivia-wrp namespace: <your-namespace> spec: replicas: 1 selector: matchLabels: app: ivia-wrp template: metadata: labels: app: ivia-wrp spec: # Pod-level security context securityContext: fsGroupChangePolicy: "OnRootMismatch" runAsNonRoot: true # Define volumes volumes: # Persistent storage for WRP container data - name: wrp-data persistentVolumeClaim: claimName: ivia-wrp-data # Ephemeral storage for temporary files - name: tmp-volume emptyDir: {} # Shared volume for inter-service communication - name: shared-volume persistentVolumeClaim: claimName: ivia-shared-volume # Init container to populate volumes initContainers: - name: populate-volumes image: icr.io/ivia/ivia-wrp:latest imagePullPolicy: Always securityContext: readOnlyRootFilesystem: true allowPrivilegeEscalation: false runAsNonRoot: true capabilities: drop: ["ALL"] env: - name: INIT_VOLUMES value: "true" volumeMounts: # Mount persistent volumes at /mnt/* paths - name: wrp-data mountPath: /mnt/var subPath: var - name: wrp-data mountPath: /mnt/etc subPath: etc - name: wrp-data mountPath: /mnt/usr/lib64/iss-pam subPath: usr/lib64/iss-pam # Main container containers: - name: ivia-wrp image: icr.io/ivia/ivia-wrp:latest imagePullPolicy: Always securityContext: readOnlyRootFilesystem: true allowPrivilegeEscalation: false runAsNonRoot: true capabilities: drop: ["ALL"] volumeMounts: # Persistent volumes at actual paths - name: wrp-data mountPath: /var subPath: var - name: wrp-data mountPath: /etc subPath: etc - name: wrp-data mountPath: /usr/lib64/iss-pam subPath: usr/lib64/iss-pam # Ephemeral volumes - name: tmp-volume mountPath: /tmp subPath: tmp - name: tmp-volume mountPath: /run subPath: run # Shared volume - name: shared-volume mountPath: /var/shared # Add your existing environment variables, ports, etc.
Distributed Session Cache
The Verify Identity Access Distributed Session Cache Container (called ivia-dsc) can be used by the Web Reverse Proxy and Runtime to share sessions between multiple containers.
readOnlyRootFilesystem:
- Create a
PersistentVolumeClaim.apiVersion: v1 kind: PersistentVolumeClaim metadata: name: ivia-dsc-data namespace: <your-namespace> spec: accessModes: - ReadWriteOnce # Customise based on your storage requirements resources: requests: storage: 1Gi # Customise based on your storage requirements storageClassName: <your-storage-class> volumeMode: Filesystem - Configure a deployment job using the following command:
apiVersion: apps/v1 kind: Deployment metadata: name: ivia-dsc namespace: <your-namespace> spec: replicas: 1 selector: matchLabels: app: ivia-dsc template: metadata: labels: app: ivia-dsc spec: # Pod-level security context securityContext: fsGroupChangePolicy: "OnRootMismatch" runAsNonRoot: true # Define volumes volumes: # Persistent storage for DSC container data - name: dsc-data persistentVolumeClaim: claimName: ivia-dsc-data # Ephemeral storage for temporary files - name: tmp-volume emptyDir: {} # Shared volume for inter-service communication - name: shared-volume persistentVolumeClaim: claimName: ivia-shared-volume # Init container to populate volumes initContainers: - name: populate-volumes image: icr.io/ivia/ivia-dsc:latest imagePullPolicy: Always securityContext: readOnlyRootFilesystem: true allowPrivilegeEscalation: false runAsNonRoot: true capabilities: drop: ["ALL"] env: - name: INIT_VOLUMES value: "true" volumeMounts: # Mount persistent volumes at /mnt/* paths - name: dsc-data mountPath: /mnt/var subPath: var - name: dsc-data mountPath: /mnt/etc subPath: etc # Main container containers: - name: ivia-dsc image: icr.io/ivia/ivia-dsc:latest imagePullPolicy: Always securityContext: readOnlyRootFilesystem: true allowPrivilegeEscalation: false runAsNonRoot: true capabilities: drop: ["ALL"] volumeMounts: # Persistent volumes at actual paths - name: dsc-data mountPath: /var subPath: var - name: dsc-data mountPath: /etc subPath: etc # Ephemeral volumes - name: tmp-volume mountPath: /tmp subPath: tmp - name: tmp-volume mountPath: /run subPath: run # Shared volume - name: shared-volume mountPath: /var/shared # Add your existing environment variables, ports, etc.
PostgreSQL Container
readOnlyRootFilesystem:
- Create a
PersistentVolumeClaim.apiVersion: v1 kind: PersistentVolumeClaim metadata: name: ivia-postgresql namespace: <your-namespace> spec: accessModes: - ReadWriteOnce # Customise based on your storage requirements resources: requests: storage: 1Gi # Customise based on your storage requirements storageClassName: <your-storage-class> volumeMode: Filesystem - Configure a deployment job using the following command:
apiVersion: apps/v1 kind: Deployment metadata: name: ivia-postgresql namespace: <your-namespace> spec: replicas: 1 selector: matchLabels: app: ivia-postgresql template: metadata: labels: app: ivia-postgresql spec: # Pod-level security context securityContext: fsGroupChangePolicy: "OnRootMismatch" runAsNonRoot: true # Define volumes volumes: # Persistent storage for PostgreSQL data - name: ivia-postgresql persistentVolumeClaim: claimName: ivia-postgresql # Ephemeral storage for temporary files - name: tmp-volume emptyDir: {} # Ephemeral storage for runtime files - name: run-volume emptyDir: {} # Init container to populate volumes initContainers: - name: populate-volumes image: icr.io/ivia/ivia-postgresql:latest imagePullPolicy: Always securityContext: readOnlyRootFilesystem: true allowPrivilegeEscalation: false runAsNonRoot: true capabilities: drop: ["ALL"] env: - name: INIT_VOLUMES value: "true" volumeMounts: # Mount persistent volume at /mnt/var (no subPath) - name: ivia-postgresql mountPath: /mnt/var # Mount ephemeral volume at /mnt/run (no subPath) - name: run-volume mountPath: /mnt/run # Main container containers: - name: ivia-postgresql image: icr.io/ivia/ivia-postgresql:latest imagePullPolicy: Always securityContext: readOnlyRootFilesystem: true allowPrivilegeEscalation: false runAsNonRoot: true capabilities: drop: ["ALL"] volumeMounts: # Persistent volume at actual path (no subPath) - name: ivia-postgresql mountPath: /var # Ephemeral volumes - name: run-volume mountPath: /run - name: run-volume mountPath: /var/run/postgresql # Dual mount for PostgreSQL socket - name: tmp-volume mountPath: /tmp # Add your existing environment variables, ports, etc.
Snapshot Manager Container
The Snapshot Manager container, when deployed with a persistent volume for storing snapshot data, is already compatible with a read-only root filesystem. The only change required is to apply the appropriate security context configuration to enable the readOnlyRootFilesystem setting.
readOnlyRootFilesystem:
- Update the existing deployment with following command:
apiVersion: apps/v1 kind: Deployment metadata: name: ivia-snapshot-manager namespace: <your-namespace> spec: replicas: 1 selector: matchLabels: app: ivia-snapshot-manager template: metadata: labels: app: ivia-snapshot-manager spec: # Pod-level security context securityContext: fsGroupChangePolicy: "OnRootMismatch" runAsNonRoot: true # Use your existing volume configuration volumes: - name: snapshot-data persistentVolumeClaim: claimName: ivia-snapshot-data containers: - name: ivia-snapshot-manager image: icr.io/ivia/ivia-snapshotmgr:latest imagePullPolicy: Always # Add read-only security context securityContext: readOnlyRootFilesystem: true allowPrivilegeEscalation: false runAsNonRoot: true capabilities: drop: ["ALL"] # Use your existing volume mount volumeMounts: - name: snapshot-data mountPath: /data # Add your existing environment variables, ports, etc.