> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# OCI OKE Should Mount Secrets as Files Instead of Environment Variables

### More Info:

Secrets injected as environment variables show up in process listings, container metadata, and many crash dumps. Mount them as files (tmpfs volumes) so they remain inside the containers view of /proc and not in the wider environment.

### Risk Level

Medium

### Address

Compliance, Security

### Compliance Standards

* CIS OKE

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Using Console" defaultOpen="true">
        To remediate this in OCI OKE, you don’t change a “setting” in the OKE console; you change **how your Pods use secrets** (volume mounts instead of `env`). You’ll do it by:

        1. Ensuring your cluster is accessible.
        2. Creating/updating Kubernetes Secrets.
        3. Updating your Pod/Deployment manifests to use those secrets as files via volumes.

        Below are step‑by‑step instructions, starting from the OCI Console.

        ***

        ## 1. Get `kubeconfig` for your OKE cluster via OCI Console

        1. Sign in to OCI Console.
        2. Open the **Navigation menu** → **Developer Services** → **Kubernetes Clusters (OKE)**.
        3. Choose the correct **Compartment**.
        4. Click your **Cluster**.
        5. On the cluster details page, click **Access Cluster**.
        6. Choose your method:
           * If you’re on Cloud Shell: click **Cloud Shell Access** → follow on-screen instructions to set `KUBECONFIG`.
           * If you’re on your local machine:
             * Click **Local Access**.
             * Download the `kubeconfig` using the provided `oci ce cluster create-kubeconfig` command.
             * Export `KUBECONFIG` environment variable to point to that file.

        Example (local machine):

        ```bash theme={null}
        oci ce cluster create-kubeconfig \
          --cluster-id <your-cluster-ocid> \
          --file $HOME/.kube/config-oke \
          --region <your-region> \
          --token-version 2.0.0

        export KUBECONFIG=$HOME/.kube/config-oke
        kubectl get nodes
        ```

        ***

        ## 2. Create or verify the Kubernetes Secret

        If you currently inject secrets as env vars, you already have K8s Secrets. You can re-use them. If not, create one.

        Example: create a secret from literal values:

        ```bash theme={null}
        kubectl create secret generic my-app-secret \
          --from-literal=db-username=myuser \
          --from-literal=db-password=mypassword \
          -n my-namespace
        ```

        Or from a file:

        ```bash theme={null}
        kubectl create secret generic my-app-secret \
          --from-file=db-username=./db-username.txt \
          --from-file=db-password=./db-password.txt \
          -n my-namespace
        ```

        Verify:

        ```bash theme={null}
        kubectl get secrets -n my-namespace
        kubectl describe secret my-app-secret -n my-namespace
        ```

        ***

        ## 3. Update Deployment / Pod spec to mount secrets as files

        Identify the workloads currently using env vars like:

        ```yaml theme={null}
        env:
          - name: DB_USERNAME
            valueFrom:
              secretKeyRef:
                name: my-app-secret
                key: db-username
          - name: DB_PASSWORD
            valueFrom:
              secretKeyRef:
                name: my-app-secret
                key: db-password
        ```

        You’ll change them to a **volume** and **volumeMount**.

        ### 3.1. Edit the existing Deployment using `kubectl` (from OCI console access)

        From Cloud Shell or your local environment (after step 1):

        ```bash theme={null}
        kubectl -n my-namespace edit deployment my-app-deployment
        ```

        Replace the `env:` usage with a secret volume. Example:

        ```yaml theme={null}
        spec:
          template:
            spec:
              volumes:
                - name: secret-vol
                  secret:
                    secretName: my-app-secret   # the K8s Secret name

              containers:
                - name: my-app-container
                  image: <your-image>
                  volumeMounts:
                    - name: secret-vol
                      mountPath: "/etc/myapp/secrets"
                      readOnly: true
        ```

        Your application will see:

        * `/etc/myapp/secrets/db-username`
        * `/etc/myapp/secrets/db-password`

        Update your application configuration to **read from those files** instead of environment variables.

        Save and exit the editor; Kubernetes will roll out a new ReplicaSet with the change.

        ***

        ## 4. (Optional) Remove secret environment variables

        Once your application is confirmed working using files:

        1. Edit the Deployment again:

           ```bash theme={null}
           kubectl -n my-namespace edit deployment my-app-deployment
           ```

        2. Remove any `env:` entries that reference Secrets.

        This ensures secrets are **not present in the environment** anymore.

        ***

        ## 5. (Optional) Use OCI Vault via Secrets Store CSI Driver

        If you’re using OCI Vault and want **dynamic mount from Vault** instead of K8s Secret:

        1. In OCI Console: ensure Vault and Secrets are created:
           * **Identity & Security** → **Vault** → Create vault and secrets.
        2. Install and configure **Secrets Store CSI Driver** for OKE (follow OCI docs).
        3. Configure a `SecretProviderClass` and a Pod volume like:

        ```yaml theme={null}
        volumes:
          - name: vault-secrets
            csi:
              driver: secrets-store.csi.k8s.io
              readOnly: true
              volumeAttributes:
                secretProviderClass: "oci-vault-spc"
        ```

        This way secrets are **only mounted as files** from Vault.

        ***

        ## 6. Validate

        From a running Pod:

        ```bash theme={null}
        kubectl -n my-namespace exec -it <pod-name> -- ls /etc/myapp/secrets
        kubectl -n my-namespace exec -it <pod-name> -- cat /etc/myapp/secrets/db-username
        env | grep DB_USERNAME   # should be empty once you’ve removed env vars
        ```

        ***

        If you paste a current Deployment manifest, I can show an exact before/after YAML diff tailored to your setup.
      </Accordion>

      <Accordion title="Using CLI">
        Below is how to remediate this in OCI OKE using the OCI CLI plus `kubectl` (which is how you actually modify Kubernetes objects).

        Goal: stop injecting secrets as environment variables and instead mount them as files from Kubernetes Secrets.

        ***

        ## 1. Get kubeconfig for the OKE cluster (using OCI CLI)

        If you don’t already have `kubectl` access to the cluster, generate kubeconfig via OCI CLI:

        ```bash theme={null}
        # Variables
        REGION="<your-region-identifier>"          # e.g. us-ashburn-1
        CLUSTER_OCID="<your-oke-cluster-ocid>"
        KUBECONFIG_PATH="$HOME/.kube/config"

        # Create/merge kubeconfig
        oci ce cluster create-kubeconfig \
          --region $REGION \
          --cluster-id $CLUSTER_OCID \
          --file $KUBECONFIG_PATH \
          --token-version 2.0.0 \
          --kube-endpoint PUBLIC_ENDPOINT \
          --auth instance_principal \
          --overwrite
        ```

        Verify access:

        ```bash theme={null}
        kubectl get nodes
        ```

        ***

        ## 2. Identify workloads using Secrets as environment variables

        List all namespaces and check workloads:

        ```bash theme={null}
        kubectl get deploy,sts,ds -A
        ```

        For a particular Deployment (example):

        ```bash theme={null}
        NAMESPACE="my-namespace"
        DEPLOYMENT="my-app"

        kubectl get deploy $DEPLOYMENT -n $NAMESPACE -o yaml > deployment.yaml
        ```

        Look for patterns like:

        ```yaml theme={null}
        env:
          - name: DB_PASSWORD
            valueFrom:
              secretKeyRef:
                name: my-secret
                key: db_password
        # or
        envFrom:
          - secretRef:
              name: my-secret
        ```

        These must be removed and replaced with a volume/volumeMount.

        ***

        ## 3. Create or confirm the Secret

        If the Secret already exists, you can reuse it. To check:

        ```bash theme={null}
        kubectl get secret -n $NAMESPACE
        kubectl describe secret my-secret -n $NAMESPACE
        ```

        To create/update a Secret:

        ```bash theme={null}
        kubectl create secret generic my-secret \
          --from-literal=db_password='<actual-password>' \
          --namespace $NAMESPACE \
          --dry-run=client -o yaml | kubectl apply -f -
        ```

        ***

        ## 4. Modify the workload to mount the Secret as files

        Edit the YAML you exported (`deployment.yaml`) and change:

        1. **Remove env/envFrom entries referencing the Secret.**\
           Example to delete:

           ```yaml theme={null}
           env:
             - name: DB_PASSWORD
               valueFrom:
                 secretKeyRef:
                   name: my-secret
                   key: db_password
           ```

        2. **Add a volume that uses the Secret:**

           Under `spec.template.spec.volumes`:

           ```yaml theme={null}
           volumes:
             - name: my-secret-volume
               secret:
                 secretName: my-secret
           ```

        3. **Mount the volume in the container:**

           Under `spec.template.spec.containers[].volumeMounts`:

           ```yaml theme={null}
           containers:
             - name: my-container
               image: your-image
               volumeMounts:
                 - name: my-secret-volume
                   mountPath: "/etc/secrets"
                   readOnly: true
           ```

        After this, your application should read the secret from files like:

        ```text theme={null}
        /etc/secrets/db_password
        ```

        ***

        ## 5. Apply the updated manifest

        Apply the edited file back to the cluster:

        ```bash theme={null}
        kubectl apply -f deployment.yaml
        ```

        Verify:

        ```bash theme={null}
        kubectl get deploy $DEPLOYMENT -n $NAMESPACE -o yaml | grep -A5 "volumeMounts"
        kubectl get deploy $DEPLOYMENT -n $NAMESPACE -o yaml | grep -A5 "volumes"
        ```

        Confirm that `env` / `envFrom` entries referencing Secrets are gone and the Secret is only used as a volume.

        ***

        ## 6. Optional: Scripted remediation (using kubectl + yq)

        For many Deployments, you may script transformation, but conceptually it remains:

        1. Use OCI CLI to get kubeconfig for each OKE cluster.
        2. For each namespace/workload:
           * Export YAML
           * Remove `env/envFrom` with `secretKeyRef/secretRef`
           * Add `volumes[].secret` and `volumeMounts[]`
           * Reapply YAML

        ***

        **Summary:**\
        Using OCI CLI you obtain cluster credentials; the actual remediation is done via `kubectl` by replacing `env/secretKeyRef` or `envFrom/secretRef` with Secret-backed volumes and volumeMounts, so secrets are consumed as files instead of environment variables.
      </Accordion>

      <Accordion title="Using Python">
        Below are the key steps and a Python example using the Kubernetes Python client to ensure OCI OKE pods mount secrets as files instead of using environment variables.

        ***

        ## 1. Prerequisites

        1. Have `kubectl` working against your OKE cluster.
        2. Python 3.8+.
        3. Install Kubernetes Python client:

        ```bash theme={null}
        pip install kubernetes
        ```

        4. Ensure your kubeconfig is accessible (usually `~/.kube/config`) or you are running in a pod with in-cluster credentials.

        ***

        ## 2. Create / Ensure the Secret Exists

        If you already have a `Secret`, skip to step 3.

        Example: create a secret with two keys (`username`, `password`):

        ```bash theme={null}
        kubectl create secret generic my-app-secret \
          --from-literal=username=myuser \
          --from-literal=password=mypassword \
          -n my-namespace
        ```

        ***

        ## 3. Python: Create a Deployment That Mounts the Secret as Files

        This example shows a Deployment whose pods mount `my-app-secret` at `/etc/myapp-secrets` instead of using env vars.

        ```python theme={null}
        from kubernetes import client, config

        # 1. Load kubeconfig or in-cluster config
        try:
            config.load_kube_config()  # local
        except:
            config.load_incluster_config()  # if running inside OKE

        apps_v1 = client.AppsV1Api()

        namespace = "my-namespace"
        deployment_name = "my-app-deployment"

        # 2. Define the volume that uses the secret
        secret_volume = client.V1Volume(
            name="my-app-secret-volume",
            secret=client.V1SecretVolumeSource(
                secret_name="my-app-secret",
                # optional: items to control file names/keys
                # items=[client.V1KeyToPath(key="username", path="username"),
                #        client.V1KeyToPath(key="password", path="password")]
            )
        )

        # 3. Define the volume mount inside the container
        secret_volume_mount = client.V1VolumeMount(
            name="my-app-secret-volume",
            mount_path="/etc/myapp-secrets",  # path where files will appear
            read_only=True
        )

        # 4. Define the container (no secret-based env vars)
        container = client.V1Container(
            name="my-app-container",
            image="ghcr.io/myorg/my-app:latest",
            volume_mounts=[secret_volume_mount],
            # Example: your app reads from files like /etc/myapp-secrets/username
            # env=[]  # do NOT set secrets as env vars
        )

        # 5. Pod template
        pod_template = client.V1PodTemplateSpec(
            metadata=client.V1ObjectMeta(labels={"app": "my-app"}),
            spec=client.V1PodSpec(
                containers=[container],
                volumes=[secret_volume]
            )
        )

        # 6. Deployment spec
        deployment_spec = client.V1DeploymentSpec(
            replicas=2,
            selector=client.V1LabelSelector(match_labels={"app": "my-app"}),
            template=pod_template
        )

        deployment = client.V1Deployment(
            api_version="apps/v1",
            kind="Deployment",
            metadata=client.V1ObjectMeta(name=deployment_name),
            spec=deployment_spec
        )

        # 7. Create the Deployment
        apps_v1.create_namespaced_deployment(
            namespace=namespace,
            body=deployment
        )

        print(f"Deployment {deployment_name} created with secret mounted as files.")
        ```

        ***

        ## 4. Python: Migrating an Existing Deployment from Env Vars to Secret Files

        If your existing Deployment uses secret-based env vars like:

        ```yaml theme={null}
        env:
          - name: DB_USER
            valueFrom:
              secretKeyRef:
                name: my-app-secret
                key: username
        ```

        you should:

        1. Remove those `env` entries.
        2. Add a `volume` referencing the secret.
        3. Add `volumeMounts` in containers.
        4. Update your application to read the secret from files.

        Example patch in Python (simplified: assumes single container):

        ```python theme={null}
        from kubernetes import client, config

        config.load_kube_config()
        apps_v1 = client.AppsV1Api()

        namespace = "my-namespace"
        deployment_name = "my-app-deployment"

        # Get existing deployment
        dep = apps_v1.read_namespaced_deployment(deployment_name, namespace)

        container = dep.spec.template.spec.containers[0]

        # 1. Remove secret-based env vars (keep non-secret envs)
        new_env = []
        for e in container.env or []:
            if e.value_from and e.value_from.secret_key_ref:
                # skip secret-based env var
                continue
            new_env.append(e)
        container.env = new_env

        # 2. Add volume for the secret (if not already present)
        volumes = dep.spec.template.spec.volumes or []
        if not any(v.name == "my-app-secret-volume" for v in volumes):
            volumes.append(
                client.V1Volume(
                    name="my-app-secret-volume",
                    secret=client.V1SecretVolumeSource(secret_name="my-app-secret")
                )
            )
        dep.spec.template.spec.volumes = volumes

        # 3. Add volumeMount to the container (if not already present)
        volume_mounts = container.volume_mounts or []
        if not any(vm.name == "my-app-secret-volume" for vm in volume_mounts):
            volume_mounts.append(
                client.V1VolumeMount(
                    name="my-app-secret-volume",
                    mount_path="/etc/myapp-secrets",
                    read_only=True
                )
            )
        container.volume_mounts = volume_mounts

        # 4. Apply the patch/update
        apps_v1.patch_namespaced_deployment(
            name=deployment_name,
            namespace=namespace,
            body=dep
        )

        print(f"Deployment {deployment_name} updated to mount secrets as files.")
        ```

        ***

        ## 5. Application-Side Change

        Inside the container, read secrets from files, for example (Python app):

        ```python theme={null}
        from pathlib import Path

        username = Path("/etc/myapp-secrets/username").read_text().strip()
        password = Path("/etc/myapp-secrets/password").read_text().strip()
        ```

        This pattern satisfies “mount secrets as files instead of environment variables” for OCI OKE using Python-based automation.
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        # This finding cannot be remediated on the oci_containerengine_cluster
        # (OKE cluster) resource itself: how secrets are consumed (env vars vs
        # mounted files) is defined in Kubernetes Pod/Deployment specs, not on
        # the cluster.

        # You must change the Kubernetes workload manifests (or Helm charts)
        # that Terraform applies, so that:
        #
        # 1) Secrets are mounted as volumes (files) …
        #
        #    apiVersion: v1
        #    kind: Pod
        #    metadata:
        #      name: EXAMPLE_POD_NAME
        #    spec:
        #      containers:
        #        - name: EXAMPLE_CONTAINER_NAME
        #          image: EXAMPLE_IMAGE
        #          volumeMounts:
        #            - name: secret-volume
        #              mountPath: "/var/run/secrets/myapp"  # path inside container
        #              readOnly: true
        #      volumes:
        #        - name: secret-volume
        #          secret:
        #            secretName: EXAMPLE_K8S_SECRET_NAME   # substitute your Secret name
        #
        # 2) …instead of being injected as environment variables:
        #
        #    env:
        #      - name: DB_PASSWORD
        #        valueFrom:
        #          secretKeyRef:
        #            name: EXAMPLE_K8S_SECRET_NAME
        #            key: password

        # If you manage these manifests with Terraform, use the Kubernetes provider:

        provider "kubernetes" {
          host                   = var.OKE_CLUSTER_ENDPOINT          # substitute OKE endpoint
          cluster_ca_certificate = base64decode(var.OKE_CLUSTER_CA)  # substitute CA data
          token                  = var.OKE_CLUSTER_TOKEN             # or use exec/auth
        }

        resource "kubernetes_secret" "app" {
          metadata {
            name      = "EXAMPLE_K8S_SECRET_NAME"        # substitute
            namespace = "EXAMPLE_NAMESPACE"              # substitute
          }

          data = {
            "password" = var.APP_DB_PASSWORD             # substitute
          }

          type = "Opaque"
        }

        resource "kubernetes_deployment" "app" {
          metadata {
            name      = "EXAMPLE_DEPLOYMENT_NAME"        # substitute
            namespace = "EXAMPLE_NAMESPACE"
          }

          spec {
            replicas = 1

            selector {
              match_labels = {
                app = "EXAMPLE_APP_LABEL"                # substitute
              }
            }

            template {
              metadata {
                labels = {
                  app = "EXAMPLE_APP_LABEL"
                }
              }

              spec {
                container {
                  name  = "EXAMPLE_CONTAINER_NAME"
                  image = "EXAMPLE_IMAGE"

                  # Mount secret as files instead of env vars
                  volume_mount {
                    name       = "secret-volume"
                    mount_path = "/var/run/secrets/myapp"  # substitute
                    read_only  = true
                  }

                  # DO NOT define env from secretKeyRef for these secrets
                  # env { ... }  # remove any secretKeyRef-based vars
                }

                volume {
                  name = "secret-volume"

                  secret {
                    secret_name = kubernetes_secret.app.metadata[0].name
                  }
                }
              }
            }
          }
        }
        ```

        This change is applied at the workload level; the `oci_containerengine_cluster` resource does not expose any argument that can force “secrets as files” across the cluster. No cluster replacement is required; only affected Pods/Deployments are recreated. After updating, `terraform plan` should show changes only to the `kubernetes_*` workload resources that drop secret-based env vars and add secret volumes.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
