Skip to main content

OCI OKE Should Limit Default Service Account Usage

More Info:

Workloads should not run under the namespaces default ServiceAccount. Mounting tokens from the default SA to every pod blurs blast-radius and breaks per-workload least privilege.

Risk Level

Medium

Address

Compliance, Security

Compliance Standards

  • CIS OKE

Triage and Remediation

Remediation

Using Console

In OKE this is a Kubernetes-level issue, so you remediate it by changing service accounts and RBAC inside the cluster. You can start from the OCI Console, but the actual fix is done via kubectl (e.g., from OCI Cloud Shell).

Below are the steps doing everything initiated from the OCI Console.


1. Open Cloud Shell and connect to the OKE cluster

  1. Sign in to the OCI Console.

  2. Open the navigation menuDeveloper ServicesKubernetes Clusters (OKE).

  3. Select the Compartment and click your cluster.

  4. On the cluster details page, click Access Cluster.

  5. In the panel that opens, click Cloud Shell Access (or open Cloud Shell from the top-right “>_” icon and follow the displayed oci ce cluster create-kubeconfig command).

  6. Run the suggested oci ce cluster create-kubeconfig command in Cloud Shell, for example:

    oci ce cluster create-kubeconfig \
    --cluster-id <OCID_OF_CLUSTER> \
    --file $HOME/.kube/config \
    --region <region> \
    --token-version 2.0.0 \
    --kube-endpoint PRIVATE_ENDPOINT
  7. Verify access:

    kubectl get nodes

2. Disable token auto-mount for the default service account

You need to patch the default service account in each namespace where workloads run. Commonly at least default and any custom namespaces.

  1. List namespaces:

    kubectl get ns
  2. For each relevant namespace (e.g. default, production, staging), run:

    kubectl patch serviceaccount default \
    -n <namespace> \
    -p '{"automountServiceAccountToken": false}'

    Example for the default namespace:

    kubectl patch serviceaccount default \
    -n default \
    -p '{"automountServiceAccountToken": false}'

This prevents pods in that namespace from automatically mounting the default token unless explicitly overridden.


3. Create dedicated, least-privilege service accounts

Instead of relying on the default service account, create service accounts per application with minimal RBAC.

  1. Create a service account:

    kubectl create serviceaccount my-app-sa -n <namespace>
  2. Define an RBAC role (example: read-only access to ConfigMaps in that namespace). Create a YAML file role.yaml in Cloud Shell:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
    name: my-app-readonly
    namespace: <namespace>
    rules:
    - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "list", "watch"]

    Apply it:

    kubectl apply -f role.yaml
  3. Bind the role to the new service account. Create rolebinding.yaml:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
    name: my-app-readonly-binding
    namespace: <namespace>
    subjects:
    - kind: ServiceAccount
    name: my-app-sa
    namespace: <namespace>
    roleRef:
    kind: Role
    name: my-app-readonly
    apiGroup: rbac.authorization.k8s.io

    Apply it:

    kubectl apply -f rolebinding.yaml

4. Update workloads to stop using the default service account

For each deployment/statefulset/daemonset, explicitly set a non-default service account and (optionally) ensure auto-mount is disabled if not needed.

  1. Edit the deployment (example):

    kubectl edit deployment my-app -n <namespace>
  2. Under spec.template.spec, add or change:

    spec:
    serviceAccountName: my-app-sa
    automountServiceAccountToken: false # set to true only if the pod truly needs the token
  3. Save and exit; Kubernetes will roll out the updated pods.

  4. Verify:

    kubectl get pods -n <namespace> -o jsonpath='{range .items[*]}{.metadata.name}{" => "}{.spec.serviceAccountName}{"\n"}{end}'

5. (Optional) Enforce this pattern for new namespaces

For each new namespace you create, immediately:

kubectl patch serviceaccount default \
-n <new-namespace> \
-p '{"automountServiceAccountToken": false}'

And always define explicit service accounts + RBAC for new applications.


If you share how your workloads are structured (namespaces, critical apps), I can give you concrete kubectl patches tailored to your setup.

Using CLI

You can’t directly change Kubernetes service accounts with oci itself; you use oci to get kubeconfig, then kubectl to do the remediation on the OKE cluster.

Below are the minimal CLI steps to limit default service account usage in OKE.


1. Get kubeconfig for your OKE cluster (with OCI CLI)

# Set variables
COMPARTMENT_OCID="<compartment_ocid>"
CLUSTER_OCID="<cluster_ocid>"
KUBECONFIG_PATH="$HOME/.kube/config"

# Generate kubeconfig
oci ce cluster create-kubeconfig \
--cluster-id "$CLUSTER_OCID" \
--file "$KUBECONFIG_PATH" \
--region "<region_identifier>" \
--token-version 2.0.0 \
--kube-endpoint PUBLIC_ENDPOINT

Verify:

kubectl get nodes

2. Disable token automount on the default service account

Run this for each namespace where you want to restrict the default SA (including default and any app namespaces):

NAMESPACE="default"

kubectl patch serviceaccount default \
-n "$NAMESPACE" \
-p '{"automountServiceAccountToken": false}'

Optional: verify

kubectl get sa default -n "$NAMESPACE" -o yaml | grep automountServiceAccountToken

3. Ensure workloads don’t use the default SA

For each Deployment/StatefulSet/DaemonSet, patch them to either:

Option A – Explicitly disable token mounting (if they don’t need K8s API access)

kubectl patch deployment <deployment_name> \
-n <namespace> \
--type merge \
-p '{"spec":{"template":{"spec":{"automountServiceAccountToken": false}}}}'

Option B – Use a dedicated least-privilege service account

  1. Create a new service account:

    kubectl create serviceaccount <sa_name> -n <namespace>
  2. Bind only needed permissions (example: read-only in namespace):

    cat <<EOF | kubectl apply -f -
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
    name: <role_name>
    namespace: <namespace>
    rules:
    - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list", "watch"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
    name: <rb_name>
    namespace: <namespace>
    subjects:
    - kind: ServiceAccount
    name: <sa_name>
    namespace: <namespace>
    roleRef:
    kind: Role
    name: <role_name>
    apiGroup: rbac.authorization.k8s.io
    EOF
  3. Patch the workload to use this SA:

    kubectl patch deployment <deployment_name> \
    -n <namespace> \
    --type merge \
    -p '{"spec":{"template":{"spec":{"serviceAccountName":"<sa_name>","automountServiceAccountToken": true}}}}'

4. (Optional) Restrict the default service account’s RBAC rights

If the default service account already has bindings, remove or tighten them:

# List all RoleBindings/ClusterRoleBindings referencing default SA
kubectl get rolebindings,clusterrolebindings --all-namespaces -o yaml | \
grep -B5 -A5 "name: default"

Then delete or replace those bindings as appropriate, for example:

kubectl delete rolebinding <rb_name> -n <namespace>
kubectl delete clusterrolebinding <crb_name>

These steps, driven via OCI CLI → kubeconfig → kubectl, will effectively limit use and privileges of the default service account in your OKE cluster.

Using Python

Here’s how to remediate “OCI OKE Should Limit Default Service Account Usage” using Python and the Kubernetes API.

Goal

  1. Stop the default ServiceAccount from automatically getting tokens.
  2. Detect workloads still using the default ServiceAccount so you can fix them.

1. Prerequisites

Install the Kubernetes Python client:

pip install kubernetes

Make sure your local kubectl can access the OKE cluster (e.g., via oci ce cluster create-kubeconfig ... and KUBECONFIG or ~/.kube/config).


2. Disable token auto-mounting for the default ServiceAccount in all namespaces

This script:

  • Lists all namespaces
  • Patches the default ServiceAccount in each namespace to set automountServiceAccountToken: false
from kubernetes import client, config
from kubernetes.client.rest import ApiException

def disable_default_sa_automount():
# Load kubeconfig (adjust if running inside a pod)
config.load_kube_config()

v1 = client.CoreV1Api()

# Get all namespaces
namespaces = [ns.metadata.name for ns in v1.list_namespace().items]

for ns in namespaces:
print(f"Processing namespace: {ns}")
try:
# Build patch body
patch_body = {
"automountServiceAccountToken": False
}

# Patch the default ServiceAccount in this namespace
v1.patch_namespaced_service_account(
name="default",
namespace=ns,
body=patch_body
)
print(f" Patched default ServiceAccount in {ns}")
except ApiException as e:
if e.status == 404:
print(f" No default ServiceAccount found in {ns} (unexpected)")
else:
print(f" Failed to patch default SA in {ns}: {e}")

if __name__ == "__main__":
disable_default_sa_automount()

This makes the default service account safer (no token auto-mount), but it does not prevent workloads from explicitly specifying serviceAccountName: default or inheriting it.


3. Detect pods using the default ServiceAccount

You should also identify existing pods and controllers using the default SA so you can fix manifests (Deployments, StatefulSets, Jobs, etc.) to use dedicated service accounts.

from kubernetes import client, config

def find_pods_using_default_sa():
config.load_kube_config()
v1 = client.CoreV1Api()

pods = v1.list_pod_for_all_namespaces().items

print("Pods using the 'default' ServiceAccount:")
for pod in pods:
sa_name = pod.spec.service_account_name or "default"
if sa_name == "default":
print(f"- Namespace: {pod.metadata.namespace}, Pod: {pod.metadata.name}")

if __name__ == "__main__":
find_pods_using_default_sa()

Use this output to:

  • Create dedicated service accounts with minimal RBAC.
  • Update Deployment/Job/StatefulSet specs to use those service accounts.
  • Redeploy workloads so new pods don’t run with default.

4. (Optional) Enforce at Namespace Level

You can also set automountServiceAccountToken: false at the namespace level as a default:

from kubernetes import client, config

def set_namespace_default_automount_false():
config.load_kube_config()
v1 = client.CoreV1Api()

namespaces = [ns.metadata.name for ns in v1.list_namespace().items]

for ns in namespaces:
print(f"Processing namespace: {ns}")
patch_body = {
"metadata": {
"annotations": {
"kubernetes.io/automount-service-account-token": "false"
}
}
}
try:
v1.patch_namespace(name=ns, body=patch_body)
print(f" Patched namespace {ns}")
except Exception as e:
print(f" Failed to patch namespace {ns}: {e}")

if __name__ == "__main__":
set_namespace_default_automount_false()

Minimal remediation steps in OKE:

  1. Run the script to set automountServiceAccountToken: false on all default ServiceAccounts.
  2. Identify pods using the default SA and update their controllers to use dedicated service accounts with least-privilege RBAC.
  3. (Optional) Annotate namespaces to default to no token auto-mount.
Using Terraform
# This cannot be remediated on oci_containerengine_cluster itself; it is a Kubernetes-level setting
# that must be applied inside the OKE cluster via the Kubernetes provider.

provider "kubernetes" {
# Configure this provider to talk to your OKE cluster (substitute as appropriate)
host = "https://OKE_API_ENDPOINT" # replace with your OKE API server endpoint
token = "KUBE_BEARER_TOKEN" # replace with a valid token
cluster_ca_certificate = file("PATH_TO_OKE_CLUSTER_CA_CERT.pem") # replace with the CA file path
}

# 1) Disable default ServiceAccount token mounting in a namespace
resource "kubernetes_service_account" "default_sa_patch" {
metadata {
name = "default"
namespace = "TARGET_NAMESPACE" # replace with the namespace you are hardening
}

automount_service_account_token = false
}

# 2) Create a dedicated ServiceAccount for a workload
resource "kubernetes_service_account" "app_sa" {
metadata {
name = "APP_SERVICE_ACCOUNT_NAME" # e.g., "payments-api-sa"
namespace = "TARGET_NAMESPACE"
}

automount_service_account_token = true
}

# 3) Example: ensure a deployment uses the dedicated ServiceAccount (not the default)
resource "kubernetes_deployment" "app" {
metadata {
name = "APP_DEPLOYMENT_NAME" # e.g., "payments-api"
namespace = "TARGET_NAMESPACE"
labels = {
app = "APP_LABEL"
}
}

spec {
replicas = 2

selector {
match_labels = {
app = "APP_LABEL"
}
}

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

spec {
service_account_name = kubernetes_service_account.app_sa.metadata[0].name

container {
name = "APP_CONTAINER_NAME"
image = "APP_IMAGE_REF"
}
}
}
}
}

# No replacement of the OKE cluster resource is required; these are in-cluster changes only.

# Verification: `terraform plan` should show:
# - an in-place update for the `kubernetes_service_account.default_sa_patch` (creating or modifying the "default" SA)
# - creation of `kubernetes_service_account.app_sa`
# - creation or update of `kubernetes_deployment.app` to reference the non-default ServiceAccount.