Configuring Read-only Root Filesystem

Container images deployed in OpenShift can be configured to use the readOnlyRootFilesystem security setting to enhance runtime security.

Most containers require a common set of configuration changes to support a read-only root filesystem:
  1. Define volumes: Create PersistentVolumeClaims (PVCs) and declare the required volumes in the pod specification.
  2. Configure pod security context: Apply the necessary pod-level security settings.
  3. Add an init container: Configure the populate-volumes init container with appropriate volume mounts.
  4. Update the main container: Apply the required security context and configure volume mounts for the main container.
Note: The PVC definitions provided are for reference only. It is recommended to adjust the size and access requirements of your PVC to meet the needs of your environment.

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.

All containers follow a similar configuration model to support a read-only root filesystem:
  • 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 /mnt prefix.
  • Persistent data is managed using PersistentVolumeClaims (PVCs) configured with subPath.
  • Temporary runtime directories, such as /tmp and /run are provisioned using emptyDir volumes.
Note: This configuration pattern must be applied consistently. Missing or incorrectly configured volume mounts can result in runtime failures.

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:

  1. 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
  2. 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:

  1. 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
  2. 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:

  1. 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
  2. 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.

The following steps illustrate how to create a DSC container with additional security of readOnlyRootFilesystem:
  1. 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
  2. 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

The following steps illustrate how to create a PostgreSQL container with additional security of readOnlyRootFilesystem:
  1. 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
  2. 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.

The following steps illustrate how to update the Snapshot Manager container with additional security of readOnlyRootFilesystem:
  1. 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.