> ## 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 Access to Secrets Should Be Restricted

### More Info:

Roles that allow get, list, or watch on secrets effectively expose every credential they reference. Limit secret access to controllers and service accounts that strictly need it and audit changes to those bindings.

### Risk Level

High

### Address

Compliance, Security

### Compliance Standards

* CIS OKE

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Using Console" defaultOpen="true">
        Below are concrete, console-based steps to restrict access to OCI Vault secrets for OKE (Oracle Kubernetes Engine).

        Assumptions:

        * OKE worker nodes use **Dynamic Groups** + **IAM policies** to access Vault.
        * The misconfiguration is that these policies are too broad (e.g., `read secret-bundles` on entire compartment/tenancy).

        ***

        ## 1. Identify who currently has access to secrets

        1. Sign in to OCI Console.
        2. Open the navigation menu → **Identity & Security** → **Policies**.
        3. In the **Compartment** dropdown, select the compartment where your OKE policies live.
        4. Find policies that contain actions like:
           * `read secret-bundles`
           * `manage secret-family`
           * `manage vaults` or `read vaults`
        5. Look for statements that reference:
           * `dynamic-group <your-oke-nodes-dg>`
           * or groups used by your platform/team for OKE access.

        Examples of risky policies:

        ```text theme={null}
        Allow dynamic-group oke-nodes to read secret-bundles in compartment app-compartment
        Allow dynamic-group oke-nodes to manage secret-family in compartment app-compartment
        Allow group devs to manage secret-family in tenancy
        ```

        Write down:

        * The **dynamic group names** (e.g., `oke-nodes`).
        * The **compartments** and **vaults** they can access.

        ***

        ## 2. Confirm which dynamic groups are tied to your OKE clusters

        1. Go to **Identity & Security** → **Dynamic Groups**.
        2. Open each suspected dynamic group (e.g., `oke-nodes`).
        3. Check **Matching Rules**:
           * Typical OKE node rule looks like:
             ```text theme={null}
             ALL {instance.compartment.id = 'ocid1.compartment.oc1..xxxx'}
             ```
             or:
             ```text theme={null}
             ANY {instance.ocid = 'ocid1.instance.oc1..xxxx'}
             ```
        4. Confirm these are indeed the worker node instances (used by your OKE node pools).

        ***

        ## 3. Scope Vault access to the smallest necessary compartment/vault

        If workers currently have wide access (e.g., whole compartment), narrow that scope.

        ### A. Optionally move secrets into a dedicated “app-secrets” compartment

        1. Navigation menu → **Identity & Security** → **Compartments**.
        2. Create a new compartment (e.g., `app-secrets`).
        3. Go to **Security** → **Vault** → select your Vault.
        4. For each secret that should be tightly controlled:
           * Click the secret → **Move Resource** (if available) → move it into `app-secrets`.
           * If “Move” is not available, create a new secret in `app-secrets` and start using that in your apps, then delete the old one when no longer needed.

        This allows you to apply stricter policies only on `app-secrets`.

        ***

        ## 4. Replace broad IAM policies with least-privilege ones

        ### A. Edit or create a new policy with narrow permissions

        1. Go to **Identity & Security** → **Policies**.

        2. Either:
           * Edit the existing broad policy (preferred), or
           * Create a new policy (e.g., `oke-app-secrets-policy`) in the root or relevant parent compartment.

        3. Use **narrow** statements such as:

        **Only allow OKE nodes to read secrets in one compartment:**

        ```text theme={null}
        Allow dynamic-group oke-nodes to read secret-bundles in compartment app-secrets
        ```

        **If you want to restrict to a single vault only (where conditions supported):**

        ```text theme={null}
        Allow dynamic-group oke-nodes to read secret-bundles in compartment app-secrets
        where target.vault.id = 'ocid1.vault.oc1..xxxx'
        ```

        **If only some users need to manage secrets (not all devs):**

        ```text theme={null}
        Allow group secret-admins to manage secret-family in compartment app-secrets
        ```

        4. Remove or comment out (delete) existing **overly broad** rules, for example:
           * `Allow dynamic-group oke-nodes to read secret-bundles in tenancy`
           * `Allow group devs to manage secret-family in tenancy`
           * `Allow dynamic-group oke-nodes to manage vaults in tenancy`

        Save the policy.

        ***

        ## 5. Restrict who can manage Vaults and Keys

        1. Go to **Security** → **Vault**.
        2. For each vault:
           * Note its **compartment**.
        3. Return to **Identity & Security** → **Policies** and ensure:
           * Only a small admin group (e.g., `security-admins`) can **manage vaults** and **keys**:
             ```text theme={null}
             Allow group security-admins to manage vaults in compartment security
             Allow group security-admins to manage keys in compartment security
             ```
           * Regular users or OKE node dynamic groups should **not** have `manage vaults` or `manage keys`.

        ***

        ## 6. Validate OKE access from a sample pod

        1. Apply a simple test pod/deployment in the OKE cluster that uses the OCI SDK or Secrets Store CSI Driver to read a specific secret.
        2. Ensure:
           * The pod can only read the secrets it is supposed to.
           * Attempts to read secrets outside `app-secrets` (or outside allowed vault) fail with `NotAuthorizedOrNotFound`.

        If unauthorized access still works, revisit which policies grant access and further tighten them.

        ***

        ## 7. Ongoing hardening

        * Keep secrets in a **separate secrets-only compartment** per application or environment.
        * Avoid `manage secret-family` for dynamic groups; usually they only need `read secret-bundles`.
        * Periodically review:
          * **Policies** under Identity & Security.
          * **Dynamic Groups** associated with OKE nodes.
          * **Audit logs** (Identity & Security → **Audit**) for who is reading secrets.

        ***

        If you can share the current policy text you’re using for OKE to read secrets, I can give you an exact minimal replacement policy statement.
      </Accordion>

      <Accordion title="Using CLI">
        Below is a practical, CLI-focused way to tighten access to **OCI Vault secrets** and **Kubernetes Secrets in OKE**.

        ***

        ## 1. Restrict OCI Vault Secret Access for OKE Nodes

        ### 1.1. Identify dynamic groups used by OKE

        OKE worker nodes usually belong to a dynamic group. List dynamic groups:

        ```bash theme={null}
        oci iam dynamic-group list --compartment-id <TENANCY_OCID>
        ```

        Inspect each:

        ```bash theme={null}
        oci iam dynamic-group get --dynamic-group-id <DYNAMIC_GROUP_OCID>
        ```

        You’re looking for rules like:

        ```text theme={null}
        ALL {resource.type = 'instance', tag.oke-cluster.id = '<cluster-id>'}
        ```

        That’s likely your OKE worker node dynamic group.

        ***

        ### 1.2. List policies that grant vault/secret access

        List policies (tenant‑wide or per compartment):

        ```bash theme={null}
        oci iam policy list --compartment-id <COMPARTMENT_OCID>
        ```

        For each policy:

        ```bash theme={null}
        oci iam policy get --policy-id <POLICY_OCID>
        ```

        Look for broad statements involving `secret-bundles` or `vaults`, e.g.:

        ```text theme={null}
        Allow dynamic-group <dg-name> to read secret-bundles in compartment <compartment-name>
        Allow dynamic-group <dg-name> to manage vaults in tenancy
        ```

        These are what you will tighten.

        ***

        ### 1.3. Replace broad policies with least‑privilege ones

        #### a) Narrow to specific compartment

        If currently:

        ```text theme={null}
        Allow dynamic-group <dg-name> to read secret-bundles in tenancy
        ```

        Replace with:

        ```text theme={null}
        Allow dynamic-group <dg-name> to read secret-bundles in compartment <compartment-name>
        ```

        Update with CLI:

        1. Get existing statements into a JSON file:

        ```bash theme={null}
        oci iam policy get --policy-id <POLICY_OCID> \
          --query 'data."statements"' --raw-output > statements.json
        ```

        2. Edit `statements.json` locally:
           * Remove overly broad lines
           * Add the narrowed lines you want.

        3. Update the policy:

        ```bash theme={null}
        oci iam policy update \
          --policy-id <POLICY_OCID> \
          --statements file://statements.json \
          --description "Tightened OKE secret access"
        ```

        #### b) Limit to specific vault or secret (optional, more strict)

        Use a **policy with conditions** (if you want only specific secret(s)):

        Example (per secret):

        ```text theme={null}
        Allow dynamic-group <dg-name> to read secret-bundles in compartment <compartment-name>
          where target.secret.id = '<SECRET_OCID>'
        ```

        Or per vault:

        ```text theme={null}
        Allow dynamic-group <dg-name> to read secret-bundles in compartment <compartment-name>
          where target.vault.id = '<VAULT_OCID>'
        ```

        You still use the same `oci iam policy update` procedure, but with the conditioned statements.

        ***

        ### 1.4. Remove unneeded manage permissions

        If you see:

        ```text theme={null}
        Allow dynamic-group <dg-name> to manage secret-family in compartment <compartment-name>
        Allow dynamic-group <dg-name> to manage vaults in compartment <compartment-name>
        ```

        Change `manage` to `read` for worker nodes:

        ```text theme={null}
        Allow dynamic-group <dg-name> to read secret-bundles in compartment <compartment-name>
        ```

        Update via the same `statements.json` + `oci iam policy update` method.

        ***

        ## 2. Restrict Kubernetes Secret Access Inside OKE

        OCI CLI is used to configure kubeconfig, then `kubectl` enforces RBAC.

        ### 2.1. Get kubeconfig via OCI CLI

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

        Now `kubectl` uses that config.

        ***

        ### 2.2. Audit who can read secrets

        List ClusterRoles and RoleBindings:

        ```bash theme={null}
        kubectl get clusterroles
        kubectl get clusterrolebindings
        kubectl get roles -A
        kubectl get rolebindings -A
        ```

        Find any with `secrets` and broad verbs:

        ```bash theme={null}
        kubectl get clusterrole -o yaml | grep -A5 "secrets"
        kubectl get role -A -o yaml | grep -A5 "secrets"
        ```

        Look for things like:

        ```yaml theme={null}
        resources:
          - secrets
        verbs:
          - get
          - list
          - watch
          - update
          - delete
          - create
          - '*'
        ```

        Especially on `system:authenticated`, `system:serviceaccounts`, etc.

        ***

        ### 2.3. Remove or tighten overly broad bindings

        Example: if a ClusterRoleBinding gives all service accounts cluster‑wide access to secrets:

        ```bash theme={null}
        kubectl get clusterrolebinding secret-full-access -o yaml
        ```

        If it is not needed, delete:

        ```bash theme={null}
        kubectl delete clusterrolebinding secret-full-access
        ```

        Or rebind it only to specific service account(s):

        1. Create a YAML file (e.g. `restricted-sa-binding.yaml`):

        ```yaml theme={null}
        apiVersion: rbac.authorization.k8s.io/v1
        kind: Role
        metadata:
          name: namespace-secret-reader
          namespace: target-namespace
        rules:
          - apiGroups: [""]
            resources: ["secrets"]
            verbs: ["get"]
        ---
        apiVersion: rbac.authorization.k8s.io/v1
        kind: RoleBinding
        metadata:
          name: namespace-secret-reader-binding
          namespace: target-namespace
        subjects:
          - kind: ServiceAccount
            name: app-sa
            namespace: target-namespace
        roleRef:
          kind: Role
          name: namespace-secret-reader
          apiGroup: rbac.authorization.k8s.io
        ```

        2. Apply:

        ```bash theme={null}
        kubectl apply -f restricted-sa-binding.yaml
        ```

        3. Remove any old RoleBindings/ClusterRoleBindings that granted wider access than necessary.

        ***

        ### 2.4. Avoid mounting all secrets as environment variables

        Check Deployments:

        ```bash theme={null}
        kubectl get deploy -A -o yaml | grep -A10 "envFrom"
        ```

        Prefer specific secret keys instead of whole-secret or whole-namespace access.

        ***

        ## 3. Summary of CLI Commands Used

        * IAM / Vault via **OCI CLI**:
          * `oci iam dynamic-group list/get`
          * `oci iam policy list/get/update`
        * OKE / Kubernetes:
          * `oci ce cluster create-kubeconfig`
          * Then `kubectl` for RBAC and secret usage.

        If you share an example of your current IAM policy and dynamic group for OKE, I can give you an exact `statements.json` you can paste into `oci iam policy update`.
      </Accordion>

      <Accordion title="Using Python">
        Below is a practical, step‑by‑step way to **restrict access to Kubernetes Secrets in OCI OKE** using Python.\
        This focuses on tightening **Kubernetes RBAC** (what workloads and users can `get/list/watch` secrets).

        ***

        ## 1. Prerequisites

        1. An OKE cluster is already created.
        2. `kubeconfig` for the cluster is available (e.g. `~/.kube/config`).
        3. Python packages installed:
           ```bash theme={null}
           pip install kubernetes oci
           ```
        4. Your local environment can talk to the OKE API (e.g., `kubectl get pods` works).

        ***

        ## 2. Principle: Least-Privilege RBAC on Secrets

        You want:

        * Only specific service accounts / users / groups to:
          * `get`, `list`, `watch`, `create`, `update`, `delete` **Secrets**.
        * All others should have **no secret permissions** (or only what is strictly required).

        We’ll do this by:

        1. Auditing current bindings.
        2. Creating minimal `Role` / `ClusterRole` definitions.
        3. Adjusting `RoleBinding` / `ClusterRoleBinding` objects via Python.

        ***

        ## 3. Configure Kubernetes Python Client

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

        # Use default kubeconfig
        config.load_kube_config()  # or config.load_incluster_config() if running inside the cluster

        rbac_api = client.RbacAuthorizationV1Api()
        core_api = client.CoreV1Api()
        ```

        ***

        ## 4. Audit Current Secret Access

        List all `ClusterRole`s and `Role`s that touch `secrets`:

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

        config.load_kube_config()
        rbac_api = client.RbacAuthorizationV1Api()

        def roles_with_secret_access():
            roles_with_access = []

            # ClusterRoles
            cr_list = rbac_api.list_cluster_role()
            for cr in cr_list.items:
                for rule in cr.rules or []:
                    if 'secrets' in (rule.resources or []):
                        roles_with_access.append(("ClusterRole", cr.metadata.name, rule))

            # Namespaced Roles
            ns_list = client.CoreV1Api().list_namespace()
            for ns in ns_list.items:
                role_list = rbac_api.list_namespaced_role(namespace=ns.metadata.name)
                for role in role_list.items:
                    for rule in role.rules or []:
                        if 'secrets' in (rule.resources or []):
                            roles_with_access.append(("Role", f"{ns.metadata.name}/{role.metadata.name}", rule))

            return roles_with_access

        for kind, name, rule in roles_with_secret_access():
            print(f"{kind} {name} -> verbs={rule.verbs}, resources={rule.resources}, apiGroups={rule.api_groups}")
        ```

        Use this to identify overly permissive roles (e.g., `*` verbs on `secrets`, or broad `ClusterRole` bindings).

        ***

        ## 5. Create a Least‑Privilege Role for Secrets (Namespace Scoped)

        Example: allow only a specific service account to read secrets in namespace `prod`.

        ### 5.1 Create Role

        ```python theme={null}
        from kubernetes.client import V1ObjectMeta, V1PolicyRule, V1Role

        namespace = "prod"
        role_name = "secret-reader"

        role_body = V1Role(
            metadata=V1ObjectMeta(name=role_name, namespace=namespace),
            rules=[
                V1PolicyRule(
                    api_groups=[""],
                    resources=["secrets"],
                    verbs=["get", "list"]  # minimal; add 'watch' if truly needed
                )
            ]
        )

        rbac_api = client.RbacAuthorizationV1Api()
        created_role = rbac_api.create_namespaced_role(namespace=namespace, body=role_body)
        print("Created Role:", created_role.metadata.name)
        ```

        ### 5.2 Bind Role to a Service Account

        ```python theme={null}
        from kubernetes.client import V1RoleBinding, V1RoleRef, V1Subject

        sa_name = "app-sa"  # service account allowed to read secrets

        role_binding_body = V1RoleBinding(
            metadata=V1ObjectMeta(name="secret-reader-binding", namespace=namespace),
            subjects=[
                V1Subject(
                    kind="ServiceAccount",
                    name=sa_name,
                    namespace=namespace
                )
            ],
            role_ref=V1RoleRef(
                api_group="rbac.authorization.k8s.io",
                kind="Role",
                name=role_name
            )
        )

        created_rb = rbac_api.create_namespaced_role_binding(namespace=namespace, body=role_binding_body)
        print("Created RoleBinding:", created_rb.metadata.name)
        ```

        Now only `app-sa` in `prod` has `get`/`list` for secrets in that namespace.

        ***

        ## 6. Remove / Restrict Broad Secret Permissions

        Typical bad pattern: `ClusterRoleBinding` to `system:authenticated` or `system:serviceaccounts` with a role that grants `secrets` access.

        1. Identify dangerous bindings:

        ```python theme={null}
        crb_list = rbac_api.list_cluster_role_binding()
        for crb in crb_list.items:
            for subj in crb.subjects or []:
                if subj.kind in ["User", "Group", "ServiceAccount"]:
                    # heuristic: check widely-scoped subjects
                    if subj.name in ["system:authenticated", "system:serviceaccounts"]:
                        print("Potentially dangerous CRB:", crb.metadata.name)
        ```

        2. Inspect the `ClusterRole` they refer to. If it has `secrets` permissions and is too broad, either:
           * Create new, narrower `ClusterRole`s and rebind.
           * Or patch/remove the binding if not needed.

        ### Example: Patch a ClusterRole to Remove Secrets Access

        ```python theme={null}
        from kubernetes.client import V1ClusterRole

        cr_name = "example-cluster-role"
        cr = rbac_api.read_cluster_role(name=cr_name)

        new_rules = []
        for rule in cr.rules:
            # Remove 'secrets' from resources list
            if "secrets" in (rule.resources or []):
                filtered_resources = [r for r in rule.resources if r != "secrets"]
                if filtered_resources:
                    rule.resources = filtered_resources
                    new_rules.append(rule)  # keep rule, just without secrets
                # else drop the rule entirely
            else:
                new_rules.append(rule)

        cr.rules = new_rules

        updated_cr = rbac_api.replace_cluster_role(name=cr_name, body=cr)
        print("Updated ClusterRole:", updated_cr.metadata.name)
        ```

        Be careful: do this only after verifying no critical workloads rely on that access.

        ***

        ## 7. Use Dedicated Service Accounts per App

        For each deployment needing secret access:

        1. Create a dedicated service account.
        2. Bind only the minimal secret permissions to that SA.

        Example: ensure a Deployment uses the `app-sa`:

        ```yaml theme={null}
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: my-app
          namespace: prod
        spec:
          replicas: 1
          selector:
            matchLabels:
              app: my-app
          template:
            metadata:
              labels:
                app: my-app
            spec:
              serviceAccountName: app-sa
              containers:
              - name: app
                image: my-image:latest
        ```

        You can apply this YAML via Python using the Kubernetes client if desired, but usually `kubectl apply -f` is simpler.

        ***

        ## 8. (Optional) Move Sensitive Data to OCI Vault and Narrow Access

        For especially sensitive secrets:

        1. Store them in **OCI Vault** (KMS).
        2. Grant only needed OKE node pool instances / dynamic groups permissions to read those secrets via IAM policies.
        3. App accesses Vault with the OCI SDK from inside the pod instead of Kubernetes Secrets.

        Python snippet (OCI Vault read example – high level):

        ```python theme={null}
        import oci

        config = oci.config.from_file()  # or instance principal config
        secrets_client = oci.secrets.SecretsClient(config)

        secret_ocid = "ocid1.vaultsecret.oc1..xxxx"
        response = secrets_client.get_secret_bundle(secret_id=secret_ocid)
        secret_content = response.data.secret_bundle_content.content
        # base64 decode if needed depending on contentType
        ```

        Then your pod uses this method instead of relying on Kubernetes Secret objects.

        ***

        ## 9. Verification

        1. Attempt to list secrets with a service account or user that **should not** have access; verify it fails:
           ```bash theme={null}
           kubectl auth can-i list secrets --as=system:serviceaccount:prod:unauthorized-sa -n prod
           # should print "no"
           ```
        2. Verify permitted SA works:
           ```bash theme={null}
           kubectl auth can-i get secrets --as=system:serviceaccount:prod:app-sa -n prod
           # should print "yes"
           ```

        ***

        If you share your current RBAC definitions (or an example of a failing security check), I can give a concrete Python script tailored to that configuration.
      </Accordion>

      <Accordion title="Using Terraform">
        ```hcl theme={null}
        # OKE cluster (for context)
        resource "oci_containerengine_cluster" "OKE_CLUSTER" {
          name           = "OKE_CLUSTER_NAME"
          compartment_id = "COMPARTMENT_OCID"
          vcn_id         = "VCN_OCID"

          kubernetes_version = "K8S_VERSION"

          endpoint_config {
            is_public_ip_enabled = true
            subnet_id            = "ENDPOINT_SUBNET_OCID"
          }
        }

        # Kubernetes provider configured to talk to the OKE cluster
        # Substitute the placeholders with values from the OKE cluster's kubeconfig.
        provider "kubernetes" {
          host                   = "https://OKE_API_ENDPOINT_URL"
          cluster_ca_certificate = file("PATH_TO_OKE_CA_CERT_PEM")
          token                  = "OKE_BEARER_TOKEN"
        }

        # Restrict secret access to only the specific service account that needs it
        # (instead of broad roles/bindings like system:authenticated, system:serviceaccounts, etc.)

        resource "kubernetes_cluster_role" "restricted_secrets_reader" {
          metadata {
            name = "restricted-secrets-reader"
          }

          rule {
            api_groups = [""]
            resources  = ["secrets"]
            verbs      = ["get", "list", "watch"]
          }
        }

        resource "kubernetes_cluster_role_binding" "restricted_secrets_reader_binding" {
          metadata {
            name = "restricted-secrets-reader-binding"
          }

          role_ref {
            api_group = "rbac.authorization.k8s.io"
            kind      = "ClusterRole"
            name      = kubernetes_cluster_role.restricted_secrets_reader.metadata[0].name
          }

          subject {
            kind      = "ServiceAccount"
            name      = "SERVICE_ACCOUNT_NAME"   # replace with the service account that needs secret access
            namespace = "SERVICE_ACCOUNT_NS"     # replace with the namespace of that service account
          }
        }

        # (Optional) Example of a namespace‑scoped Role instead of a ClusterRole
        resource "kubernetes_role" "namespace_secrets_reader" {
          metadata {
            name      = "namespace-secrets-reader"
            namespace = "TARGET_NAMESPACE"       # replace with namespace where secrets live
          }

          rule {
            api_groups = [""]
            resources  = ["secrets"]
            verbs      = ["get", "list", "watch"]
          }
        }

        resource "kubernetes_role_binding" "namespace_secrets_reader_binding" {
          metadata {
            name      = "namespace-secrets-reader-binding"
            namespace = "TARGET_NAMESPACE"       # same as above
          }

          role_ref {
            api_group = "rbac.authorization.k8s.io"
            kind      = "Role"
            name      = kubernetes_role.namespace_secrets_reader.metadata[0].name
          }

          subject {
            kind      = "ServiceAccount"
            name      = "SERVICE_ACCOUNT_NAME"   # same service account as above or another strictly‑scoped one
            namespace = "SERVICE_ACCOUNT_NS"
          }
        }
        ```

        This change does **not** replace the `oci_containerengine_cluster` itself; it only adds/updates Kubernetes RBAC objects on the existing OKE cluster.

        To verify, `terraform plan` should show the `kubernetes_cluster_role`, `kubernetes_cluster_role_binding`, and (if used) `kubernetes_role`/`kubernetes_role_binding` being created or updated, with no `destroy`/`replace` on `oci_containerengine_cluster.OKE_CLUSTER`.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
