OCI OKE Hardening

Overview

This page targets OKE Enhanced Cluster as the baseline. Basic clusters silently lack three critical security capabilities — Workload Identity (oci-k8s-02), image verification policy (oci-k8s-05), and add-on lifecycle management (covered in oci-k8s-04). Auditors evaluating an OCI Kubernetes deployment against this guide should first confirm the cluster type is ENHANCED_CLUSTER (not BASIC_CLUSTER) before applying the remaining controls. See general/kubernetes.html for the cross-cutting threat model and cluster-baseline principles that apply to all providers.

Enhanced-only controls — Workload Identity (oci-k8s-02), image verification policy (oci-k8s-05), and the Enhanced-scoped variants of Vault CMK encryption (oci-k8s-03) and NSG-based segmentation (oci-k8s-06) — carry an inline <div class="callout-warning">Requires Enhanced OKE cluster</div> annotation so an auditor can flag them on a Basic deployment. Control oci-k8s-04 is the explicit Enhanced Cluster gate: if it is RED, every other Enhanced-only control on this page is implicitly RED regardless of any other configuration. Supporting IAM prerequisites are on oci/iam.html; VCN networking prerequisites are on oci/network.html; OCI Audit + Logging sink configuration is on oci/logging.html.

Terraform examples use oracle/oci ~> 6.0 as the page pin. The sealed v1.0 OCI pages use ~> 5.0 — do not edit those pages. No HashiCorp-namespaced OCI provider exists — the OCI provider has always been published under oracle/oci. (Authoring-time verification: the 6.x line of oracle/oci includes both oci_containerengine_cluster.type = "ENHANCED_CLUSTER" and the OKE Workload Identity annotation surface needed by oci-k8s-02; no upgrade to ~> 8.0 was required.)

oci-k8s-01 ! CRITICAL PREVENTIVE

Enhanced Cluster (default for this page): private API endpoint is enabled via endpoint_config.is_public_ip_enabled = false with an NSG attached to the API endpoint subnet. Basic Cluster: a private API endpoint is also supported, but the absence of Enhanced-only controls (Workload Identity, image verification) means the cluster fails the page baseline regardless of this control's status.

Disable the public IP on the Kubernetes API endpoint so the kube-apiserver is reachable only from authorized subnets via Network Security Group rules. Use private DNS for API resolution and bastion-mediated access for human operators. A public OKE control-plane endpoint is the number-one credential-leak blast amplifier — any leaked kubeconfig becomes immediately exploitable from the internet without network-level checks.

Remediation — Terraform

# Terraform OCI provider ~> 6.0
terraform {
  required_providers {
    oci = {
      source  = "oracle/oci"
      version = "~> 6.0"
    }
  }
}

resource "oci_containerengine_cluster" "hardened" {
  compartment_id     = var.compartment_id
  name               = "hardened-cluster"
  kubernetes_version = "v1.30.0"
  vcn_id             = oci_core_vcn.k8s.id
  type               = "ENHANCED_CLUSTER"

  endpoint_config {
    is_public_ip_enabled = false
    subnet_id            = oci_core_subnet.api.id
    nsg_ids              = [oci_core_network_security_group.api.id]
  }
}

Remediation — OCI CLI

oci ce cluster create \
  --compartment-id <COMPARTMENT-OCID> \
  --name hardened-cluster \
  --kubernetes-version v1.30.0 \
  --vcn-id <VCN-OCID> \
  --type ENHANCED_CLUSTER \
  --endpoint-public-ip-enabled false \
  --endpoint-subnet-id <PRIV-SUBNET-OCID> \
  --endpoint-nsg-ids '["<NSG-OCID>"]'

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-k8s-01-private-api-endpoint" \
  --display-name "oci-k8s-01-private-api-endpoint" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-k8s-01-private-api-endpoint" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Remediation — Pulumi (TypeScript)

import * as pulumi from "@pulumi/pulumi";
import * as oci from "@pulumi/oci";

// OKE cluster with PRIVATE API endpoint (no public Kubernetes API surface).
const cfg = new pulumi.Config();
const compartmentId = cfg.require("compartmentOcid");
const vcnOcid = cfg.require("vcnOcid");
const privateApiSubnetOcid = cfg.require("privateApiSubnetOcid");
const apiNsgOcid = cfg.require("apiNsgOcid");

const cluster = new oci.containerengine.Cluster("hardened-oke", {
  compartmentId: compartmentId,
  vcnId: vcnOcid,
  kubernetesVersion: "v1.30.1",
  name: "hardened-oke",
  type: "ENHANCED_CLUSTER",  // enables more controls (audit, addons)
  endpointConfig: {
    subnetId: privateApiSubnetOcid,
    isPublicIpEnabled: false,        // PRIVATE endpoint
    nsgIds: [apiNsgOcid],            // restrict to bastion/jumpbox NSG
  },
  options: {
    serviceLbSubnetIds: [privateApiSubnetOcid],
    admissionControllerOptions: {
      isPodSecurityPolicyEnabled: false,  // PSP deprecated — Pod Security Standards via OPA-Gatekeeper
    },
  },
});

export const clusterOcid = cluster.id;

Compliance mapping

Control Severity Type Provider CIS Kubernetes Benchmark v1.11.0 CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015 NIST SP 800-190 (Sep 2017) NSA/CISA Kubernetes Hardening Guide v1.2
oci-k8s-01 CRITICAL PREVENTIVE OCI OKE n/a (managed control plane) n/a (verify against CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 PDF) AC-17; SC-7 A.8.20; A.8.22 CLD.13.1.4 NIST SP 800-190 §4.4.1 NSA/CISA Kubernetes Hardening Guide v1.2 §2 (Network separation)

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and 'Service Name' = 'containerengine' with eventName = 'UpdateCluster' whose request payload flips endpointConfig.isPublicIpEnabled from false to true.
  • Cluster create events landing an OKE control-plane endpoint inside a subnet whose route table contains an Internet Gateway target.
  • OKE API-server kube-apiserver audit deltas indicating sustained authentication attempts originating from public IP ranges outside the documented bastion CIDR.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'containerengine'
          and eventName in ('CreateCluster', 'UpdateCluster')
          | eval public_api = data.request.payload.endpointConfig.isPublicIpEnabled
          | where public_api = 'true'
          | stats count by 'User Name', data.target.cluster.id, 'Compartment Name'

The OKE control-plane visibility surface is the single most expensive misconfiguration on the cluster — saving this search at 5-minute cadence is the recommended baseline.

Alert threshold

  • Any UpdateCluster turning the public API endpoint on — page; this is a regression of the cluster's network exposure posture.
  • Any new cluster created with isPublicIpEnabled = true outside a documented green-field engineering exception — page.

Initial response

  1. Re-apply the Terraform stack via Resource Manager to flip endpoint_config.is_public_ip_enabled back to false; OKE accepts the change online.
  2. Audit OKE API-server access from the period when the endpoint was public via the 'kube-apiserver' audit feed; identify any non-bastion principals.
  3. Rotate the cluster's kubeconfig admin tokens and any service-account tokens older than the public-exposure window per general/ir.html.

References

Equivalent controls in other providers: GKE private cluster + authorized networks, EKS private endpoint, AKS private cluster.

oci-k8s-02 ! HIGH PREVENTIVE
Requires Enhanced OKE cluster

Enhanced Cluster: OKE Workload Identity issues federated principals to pods via a ServiceAccount annotation, so each workload authenticates to OCI services with its own scoped identity. Basic Cluster: Workload Identity is NOT available — pods fall back to the node's instance principal (node-scoped IAM), which violates least-privilege because every pod on a node inherits the same permissions.

Enable OKE Workload Identity so a Kubernetes ServiceAccount authenticates to OCI services using a federated principal rather than sharing the node's instance principal. The cluster identity provider issues a short-lived token bound to the pod's ServiceAccount; OCI IAM policies can then grant permissions to the specific workload identity (cluster <cluster-ocid> + ServiceAccount) instead of to the entire node pool.

Remediation — Kubernetes ServiceAccount

apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-sa
  namespace: production
  annotations:
    oci.oraclecloud.com/workload-identity: "true"

Remediation — OCI IAM policy granting access to the workload identity

oci iam policy create \
  --compartment-id <COMPARTMENT-OCID> \
  --name oke-workload-app-policy \
  --statements '["allow any-user to read objects in compartment APP where all { request.principal.type='\''workload'\'', request.principal.cluster_id='\''<CLUSTER-OCID>'\'', request.principal.namespace='\''production'\'', request.principal.service_account='\''app-sa'\'' }"]' \
  --description "OKE Workload Identity scoped grant for production/app-sa"

Remediation — Terraform (IAM side)

# Terraform OCI provider ~> 6.0
terraform {
  required_providers {
    oci = {
      source  = "oracle/oci"
      version = "~> 6.0"
    }
  }
}

resource "oci_identity_policy" "workload_identity_app" {
  compartment_id = var.compartment_id
  name           = "oke-workload-app-policy"
  description    = "OKE Workload Identity scoped grant"
  statements = [
    "allow any-user to read objects in compartment APP where all { request.principal.type='workload', request.principal.cluster_id='${oci_containerengine_cluster.hardened.id}', request.principal.namespace='production', request.principal.service_account='app-sa' }"
  ]
}

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-k8s-02-workload-identity" \
  --display-name "oci-k8s-02-workload-identity" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-k8s-02-workload-identity" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Compliance mapping

Control Severity Type Provider CIS Kubernetes Benchmark v1.11.0 CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015 NIST SP 800-190 (Sep 2017) NSA/CISA Kubernetes Hardening Guide v1.2
oci-k8s-02 HIGH PREVENTIVE OCI OKE n/a (provider-specific identity federation) n/a (verify against CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 PDF) IA-2; AC-6; IA-5 A.5.15; A.5.18 n/a NIST SP 800-190 §4.4.2 NSA/CISA Kubernetes Hardening Guide v1.2 §4 (Authentication and authorization)

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and eventName = 'UpdateCluster' whose payload removes or nulls the workloadIdentityConfig block.
  • Pods inside OKE clusters carrying static OCI API signing keys mounted as Kubernetes Secrets — anti-pattern detectable via Cloud Guard OCIK8SExposedSecret detector.
  • Kubernetes ServiceAccount mutations adding the oci.oraclecloud.com/workload-identity annotation pointing to a dynamic group outside the cluster's documented allow-list.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'containerengine'
          and eventName = 'UpdateCluster'
          | eval wi_disabled = if(data.request.payload.workloadIdentityConfig.workloadIdentityEnabled = 'false', 'YES', 'NO')
          | where wi_disabled = 'YES'
          | stats count by 'User Name', data.target.cluster.id

Pair with a Cloud Guard custom detector recipe rule on the same JMESPath; the Cloud Guard finding fans out to the OKE platform team's Notifications topic.

Alert threshold

  • Any UpdateCluster turning workload-identity off on a cluster previously running with it on — page; this strips per-pod IAM scoping.
  • A ServiceAccount annotation that points to a dynamic group outside the documented OKE-bound allow-list — page.

Initial response

  1. Re-enable workload identity on the cluster via Resource Manager terraform apply; OKE accepts the toggle online without node-pool disruption.
  2. Audit the pods running during the disabled window for any that fell back to baked-in API keys; rotate those signing keys against OCI Vault.
  3. Restore the supported per-pod dynamic-group binding and reconcile any drifted ServiceAccount annotations from the cluster Git state.

References

Equivalent controls in other providers: GKE Workload Identity Federation, EKS Pod Identity, AKS Workload Identity.

oci-k8s-03 ! HIGH PREVENTIVE
Requires Enhanced OKE cluster

Enhanced Cluster: customer-managed key (CMK) envelope encryption for Kubernetes Secrets stored in OKE-managed etcd via OCI Vault. The cluster references the key OCID at create time through kms_key_id. Basic Cluster: CMK encryption is technically configurable but the full Enhanced-scoped key-rotation and audit integration depends on Enhanced Cluster features; treat as Enhanced-only for this guide.

Enable customer-managed key encryption for Kubernetes Secrets in OKE-managed etcd. Create a Vault and a Master Encryption Key (HSM or software protection mode); reference the key OCID on the cluster. Restrict access to the key with a vault-id-bound IAM condition so only the OKE cluster identity and break-glass administrators can use it.

Remediation — Terraform

# Terraform OCI provider ~> 6.0
terraform {
  required_providers {
    oci = {
      source  = "oracle/oci"
      version = "~> 6.0"
    }
  }
}

resource "oci_kms_vault" "oke" {
  compartment_id = var.compartment_id
  display_name   = "oke-secrets-vault"
  vault_type     = "DEFAULT"
}

resource "oci_kms_key" "oke_secrets" {
  compartment_id      = var.compartment_id
  display_name        = "oke-secrets-cmk"
  management_endpoint = oci_kms_vault.oke.management_endpoint
  key_shape {
    algorithm = "AES"
    length    = 32
  }
  protection_mode = "HSM"
}

resource "oci_containerengine_cluster" "hardened" {
  compartment_id     = var.compartment_id
  name               = "hardened-cluster"
  kubernetes_version = "v1.30.0"
  vcn_id             = oci_core_vcn.k8s.id
  type               = "ENHANCED_CLUSTER"
  kms_key_id         = oci_kms_key.oke_secrets.id
}

Remediation — OCI CLI

oci ce cluster create \
  --compartment-id <COMPARTMENT-OCID> \
  --name hardened-cluster \
  --kubernetes-version v1.30.0 \
  --vcn-id <VCN-OCID> \
  --type ENHANCED_CLUSTER \
  --kms-key-id <VAULT-KEY-OCID>

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-k8s-03-vault-secrets-encryption" \
  --display-name "oci-k8s-03-vault-secrets-encryption" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-k8s-03-vault-secrets-encryption" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Compliance mapping

Control Severity Type Provider CIS Kubernetes Benchmark v1.11.0 CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015 NIST SP 800-190 (Sep 2017) NSA/CISA Kubernetes Hardening Guide v1.2
oci-k8s-03 HIGH PREVENTIVE OCI OKE §1.2 (etcd encryption — managed control plane) n/a (verify against CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 PDF) SC-28; IA-5 A.8.24; A.8.10 n/a NIST SP 800-190 §4.3.2 NSA/CISA Kubernetes Hardening Guide v1.2 §5 (Secret management)

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and eventName = 'UpdateCluster' whose request payload removes or nulls kmsKeyId on the OKE etcd-encryption configuration.
  • Cluster create events where kmsKeyId is unset, indicating etcd at-rest encryption falls back to the Oracle-managed default rather than a customer Vault key.
  • Vault audit entries showing ScheduleKeyDeletion against a key actively bound to one or more OKE clusters.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'containerengine'
          and eventName in ('CreateCluster', 'UpdateCluster')
          | eval key = data.request.payload.kmsKeyId
          | where key is null
          | stats count by 'User Name', data.target.cluster.id, 'Compartment Name'

Correlate the result with the kms service event feed for ScheduleKeyDeletion against the cluster-bound key OCID inventory.

Alert threshold

  • Any new or updated cluster with kmsKeyId null — page; etcd at rest must hold a customer-controlled key for tenancy-wide BYOK posture.
  • Any ScheduleKeyDeletion on a key OCID present in the OKE cluster inventory — page within the Vault key's pending-deletion window.

Initial response

  1. Re-bind the cluster to its customer-controlled Vault key via Resource Manager; OKE re-wraps the etcd DEK on the next reconciliation cycle without data movement.
  2. Cancel any pending ScheduleKeyDeletion targeting bound keys using oci kms management key cancel-key-deletion.
  3. Confirm the cluster's etcd-encryption status reaches ACTIVE with the expected kmsKeyId via oci ce cluster get; document the rollback per general/ir.html.

References

Equivalent controls in other providers: GKE application-layer secrets encryption, EKS KMS envelope encryption, AKS KMS etcd encryption.

oci-k8s-04 ! CRITICAL PREVENTIVE

Enhanced Cluster (REQUIRED for this guide): create with type = "ENHANCED_CLUSTER". Enhanced unlocks Workload Identity (oci-k8s-02), image verification policy (oci-k8s-05), full NSG attachment surface (oci-k8s-06), and OKE-managed add-on lifecycle (CoreDNS, kube-proxy, OCI VCN-Native Pod Networking). Basic Cluster: silently lacks the controls above. Migration from Basic to Enhanced is supported via in-place upgrade.

This is the explicit Enhanced Cluster gate control for the page. Deploy OKE with type = "ENHANCED_CLUSTER" to access the full security capability surface. Enhanced also enables OKE-managed add-on lifecycle — CoreDNS, kube-proxy, and OCI VCN-Native Pod Networking can be installed and version-managed via the oci_containerengine_addon resource, addressing the add-on lifecycle requirement (REQ OKE-10). Node OS hardening is a related concern handled in oci-k8s-09; reference that control for Oracle Linux 8 minimal images and private node pool placement.

Remediation — Terraform

# Terraform OCI provider ~> 6.0
terraform {
  required_providers {
    oci = {
      source  = "oracle/oci"
      version = "~> 6.0"
    }
  }
}

resource "oci_containerengine_cluster" "hardened" {
  compartment_id     = var.compartment_id
  name               = "hardened-cluster"
  kubernetes_version = "v1.30.0"
  vcn_id             = oci_core_vcn.k8s.id
  type               = "ENHANCED_CLUSTER"   # Enhanced gate
  cluster_pod_network_options {
    cni_type = "OCI_VCN_IP_NATIVE"
  }
}

# Managed add-on lifecycle (Enhanced-only)
resource "oci_containerengine_addon" "coredns" {
  cluster_id                       = oci_containerengine_cluster.hardened.id
  addon_name                       = "CoreDNS"
  remove_addon_resources_on_delete = false
}

resource "oci_containerengine_addon" "kube_proxy" {
  cluster_id                       = oci_containerengine_cluster.hardened.id
  addon_name                       = "KubeProxy"
  remove_addon_resources_on_delete = false
}

Remediation — OCI CLI

oci ce cluster create \
  --compartment-id <COMPARTMENT-OCID> \
  --name hardened-cluster \
  --kubernetes-version v1.30.0 \
  --vcn-id <VCN-OCID> \
  --type ENHANCED_CLUSTER

oci ce cluster install-addon \
  --cluster-id <CLUSTER-OCID> \
  --addon-name CoreDNS

oci ce cluster install-addon \
  --cluster-id <CLUSTER-OCID> \
  --addon-name KubeProxy

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-k8s-04-enhanced-cluster" \
  --display-name "oci-k8s-04-enhanced-cluster" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-k8s-04-enhanced-cluster" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Remediation — Pulumi (TypeScript)

import * as pulumi from "@pulumi/pulumi";
import * as oci from "@pulumi/oci";

// ENHANCED_CLUSTER type — required for cluster add-ons (Calico, Kyverno, Audit-to-Logging).
const cfg = new pulumi.Config();
const compartmentId = cfg.require("compartmentOcid");
const vcnOcid = cfg.require("vcnOcid");
const apiSubnetOcid = cfg.require("apiSubnetOcid");

const enhancedCluster = new oci.containerengine.Cluster("oke-enhanced", {
  compartmentId: compartmentId,
  vcnId: vcnOcid,
  kubernetesVersion: "v1.30.1",
  name: "oke-enhanced",
  type: "ENHANCED_CLUSTER",   // BASIC_CLUSTER lacks add-on framework + per-cluster SLA
  endpointConfig: {
    subnetId: apiSubnetOcid,
    isPublicIpEnabled: false,
  },
  clusterPodNetworkOptions: [{
    cniType: "OCI_VCN_IP_NATIVE",  // native VCN CNI — required for NSG-per-pod
  }],
});

// Pin add-ons explicitly so version drift is impossible.
const calicoAddon = new oci.containerengine.Addon("calico", {
  clusterId: enhancedCluster.id,
  addonName: "Calico",
  removeAddonResourcesOnDelete: false,
  configurations: [{ key: "numOfReplicas", value: "1" }],
});

export const clusterOcid = enhancedCluster.id;

Compliance mapping

Control Severity Type Provider CIS Kubernetes Benchmark v1.11.0 CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015 NIST SP 800-190 (Sep 2017) NSA/CISA Kubernetes Hardening Guide v1.2
oci-k8s-04 CRITICAL PREVENTIVE OCI OKE n/a (provider-specific cluster tier) n/a (verify against CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 PDF) CM-6; SI-2 A.5.37; A.8.9 CLD.9.5.2 NIST SP 800-190 §4.1; §4.4 NSA/CISA Kubernetes Hardening Guide v1.2 §7 (Upgrading and application security practices)

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and eventName = 'UpdateCluster' whose payload sets type to BASIC on a cluster previously running as ENHANCED.
  • OKE Cluster Add-On disable events (DisableAddon) on Enhanced-only managed add-ons (CertManager, ClusterAutoscaler, Database operator), indicating loss of managed-lifecycle coverage.
  • OKE service-limit notifications signalling drift in node-pool count beyond the BASIC tier ceiling — a downgrade tells.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'containerengine'
          and eventName = 'UpdateCluster'
          | eval tier = data.request.payload.type
          | where tier = 'BASIC'
          | stats count by 'User Name', data.target.cluster.id, 'Compartment Name'

Run hourly; a tier downgrade is a planned, ticketed activity, so a non-zero count outside the maintenance calendar is anomalous.

Alert threshold

  • Any cluster type transition from ENHANCED to BASIC — page; this strips managed add-on lifecycle and per-node-pool customisation.
  • Disable events targeting any Oracle-managed add-on listed in the cluster's documented allow-list — open a ticket on the platform team.

Initial response

  1. Promote the cluster back to ENHANCED via oci ce cluster update --type ENHANCED; the upgrade is non-destructive and online.
  2. Re-enable any Oracle-managed add-ons that were disabled during the downgrade window via the Resource Manager state; reconcile any custom add-on configuration that lapsed.
  3. Document the rollback per general/ir.html and update the OKE tier inventory dashboard.

References

oci-k8s-05 ! HIGH PREVENTIVE
Requires Enhanced OKE cluster

Enhanced Cluster: configure image_policy_config with up to 5 KMS keys for signed-image enforcement at pod scheduling time. Pods whose images are not signed by a referenced key are rejected by admission. Basic Cluster: image verification policy is NOT available — any image the node can pull will run.

Enable sign-required image admission: pods are blocked from scheduling unless their container image is signed by an attested KMS key. Up to 5 KMS keys can be referenced per cluster, supporting trust hierarchies (per-team build pipelines plus a central security override key). Combine with OCIR repository policies that require signatures on push to close the supply-chain loop.

Remediation — Terraform

# Terraform OCI provider ~> 6.0
terraform {
  required_providers {
    oci = {
      source  = "oracle/oci"
      version = "~> 6.0"
    }
  }
}

resource "oci_kms_key" "image_signing" {
  compartment_id      = var.compartment_id
  display_name        = "oke-image-signing-key"
  management_endpoint = oci_kms_vault.oke.management_endpoint
  key_shape {
    algorithm = "RSA"
    length    = 512  # 4096-bit RSA
  }
  protection_mode = "HSM"
}

resource "oci_containerengine_cluster" "hardened" {
  compartment_id     = var.compartment_id
  name               = "hardened-cluster"
  kubernetes_version = "v1.30.0"
  vcn_id             = oci_core_vcn.k8s.id
  type               = "ENHANCED_CLUSTER"

  image_policy_config {
    is_policy_enabled = true
    key_details {
      kms_key_id = oci_kms_key.image_signing.id
    }
  }
}

Remediation — OCI CLI

oci ce cluster update \
  --cluster-id <CLUSTER-OCID> \
  --image-policy-config 'isPolicyEnabled=true,keyDetails=[{kmsKeyId=<KMS-KEY-OCID>}]'

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-k8s-05-image-verification-policy" \
  --display-name "oci-k8s-05-image-verification-policy" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-k8s-05-image-verification-policy" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Compliance mapping

Control Severity Type Provider CIS Kubernetes Benchmark v1.11.0 CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015 NIST SP 800-190 (Sep 2017) NSA/CISA Kubernetes Hardening Guide v1.2
oci-k8s-05 HIGH PREVENTIVE OCI OKE n/a (provider-specific admission) n/a (verify against CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 PDF) CM-14; SA-10; SI-7 A.8.9; A.8.29 CLD.9.5.2 NIST SP 800-190 §4.1 (Image risks) NSA/CISA Kubernetes Hardening Guide v1.2 §3 (Pod security)

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and 'Service Name' = 'containerengine' with eventName = 'UpdateImagePolicyConfig' turning verification off on an OKE cluster.
  • OCIR signing-verification failures emitted as Cloud Guard problems where image pull would have been blocked had the policy remained on.
  • Kubernetes pod admission events admitting an image whose digest is absent from the OCIR signature index — surface via Cloud Guard OCIK8SUnsignedImage finding type.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'containerengine'
          and eventName in ('UpdateImagePolicyConfig', 'CreateImagePolicyConfig')
          | eval enabled = data.request.payload.imagePolicyConfig.isPolicyEnabled
          | where enabled = 'false'
          | stats count by 'User Name', data.target.cluster.id, 'Compartment Name'

OKE image-policy configuration is a single boolean on the cluster resource; a flip to false is unambiguous.

Alert threshold

  • Any UpdateImagePolicyConfig with isPolicyEnabled = false on a cluster running production workloads — page.
  • More than two OCIR signature-verification failures per cluster per hour — open a workload-team ticket on the image build pipeline.

Initial response

  1. Re-enable image-policy verification on the cluster via Resource Manager and re-attach the signed-image key inventory.
  2. Review the pods scheduled during the disabled window; cordon any node running an unsigned image until the build pipeline can re-issue a signed digest.
  3. Brief the workload team on the image-signing pipeline expectations and capture the rollback per general/ir.html.

References

Equivalent controls in other providers: GKE Binary Authorization (closest parallel).

oci-k8s-06 ! HIGH PREVENTIVE
Requires Enhanced OKE cluster

Enhanced Cluster: attach NSGs at three layers — API endpoint subnet, node pool subnet, and pod subnet (when using OCI VCN-Native Pod Networking) — for granular east-west and egress control. Basic Cluster: NSG attachment at the pod-subnet layer is more limited; defense-in-depth is reduced.

Apply Network Security Groups at multiple layers — API endpoint subnet, node pool subnet, and pod subnet (VCN-Native Pod Networking) — for defense-in-depth network segmentation. Default-deny outbound to the public internet; allow only required egress, such as OCI service endpoints via a Service Gateway and the OKE management plane endpoints. NSGs complement (do not replace) Kubernetes NetworkPolicy (oci-k8s-09) — NSGs operate at the VCN layer; NetworkPolicy operates inside the cluster.

Remediation — Terraform

# Terraform OCI provider ~> 6.0
terraform {
  required_providers {
    oci = {
      source  = "oracle/oci"
      version = "~> 6.0"
    }
  }
}

resource "oci_core_network_security_group" "api" {
  compartment_id = var.compartment_id
  vcn_id         = oci_core_vcn.k8s.id
  display_name   = "oke-api-endpoint-nsg"
}

resource "oci_core_network_security_group_security_rule" "api_ingress_mgmt" {
  network_security_group_id = oci_core_network_security_group.api.id
  direction                 = "INGRESS"
  protocol                  = "6"   # TCP
  source                    = var.management_cidr
  source_type               = "CIDR_BLOCK"
  tcp_options {
    destination_port_range {
      min = 6443
      max = 6443
    }
  }
}

resource "oci_core_network_security_group" "nodes" {
  compartment_id = var.compartment_id
  vcn_id         = oci_core_vcn.k8s.id
  display_name   = "oke-node-pool-nsg"
}

resource "oci_containerengine_node_pool" "app" {
  cluster_id     = oci_containerengine_cluster.hardened.id
  compartment_id = var.compartment_id
  name           = "app-nodes"
  node_shape     = "VM.Standard.E4.Flex"
  node_config_details {
    placement_configs {
      availability_domain = var.ad_1
      subnet_id           = oci_core_subnet.nodes.id
    }
    nsg_ids = [oci_core_network_security_group.nodes.id]
    size    = 3
  }
}

Remediation — OCI CLI

oci network nsg create \
  --compartment-id <COMPARTMENT-OCID> \
  --vcn-id <VCN-OCID> \
  --display-name oke-node-pool-nsg

oci ce node-pool update \
  --node-pool-id <NODE-POOL-OCID> \
  --nsg-ids '["<NSG-OCID>"]'

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-k8s-06-network-security-groups" \
  --display-name "oci-k8s-06-network-security-groups" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-k8s-06-network-security-groups" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Compliance mapping

Control Severity Type Provider CIS Kubernetes Benchmark v1.11.0 CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015 NIST SP 800-190 (Sep 2017) NSA/CISA Kubernetes Hardening Guide v1.2
oci-k8s-06 HIGH PREVENTIVE OCI OKE §5.3 (Network policies — partial; VCN layer complements in-cluster policy) n/a (verify against CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 PDF) SC-7; AC-4 A.8.20; A.8.22 CLD.9.5.1 NIST SP 800-190 §4.4.1 NSA/CISA Kubernetes Hardening Guide v1.2 §2 (Network separation)

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and 'Service Name' = 'core' with eventName = 'UpdateNetworkSecurityGroupSecurityRules' targeting an NSG OCID present in the OKE node-pool NSG binding.
  • VCN Flow Logs records ('Log Source' = 'OCI VCN Flow Logs') showing East-West traffic between OKE node subnets that was previously denied by the NSG baseline.
  • OKE cluster update events that swap the bound NSG OCID list to one that no longer references the curated egress NSG.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'core'
          and eventName = 'UpdateNetworkSecurityGroupSecurityRules'
          | eval rules = data.request.payload.securityRules
          | where rules like '%0.0.0.0/0%'
          | stats count by 'User Name', data.target.networkSecurityGroup.id, 'Compartment Name'

Correlate the OCID list against the OKE node-pool NSG binding inventory so only OKE-relevant NSG widenings page.

Alert threshold

  • Any new rule inside an OKE-bound NSG with source equal to 0.0.0.0/0 on ports 22, 3389, 10250, or 6443 — page.
  • OKE cluster update that removes a previously bound egress NSG OCID — page; egress-NSG omission re-opens the worker-node DNS+API egress posture.

Initial response

  1. Revert the NSG rule set via Resource Manager to the last-known-good HCL; OCI applies the change without re-creating the NSG.
  2. Capture a 30-minute window of VCN Flow Logs around the widening for the affected NSG; export to Object Storage for forensic retention.
  3. Re-attach the OKE-bound NSG list to the cluster and node pools per the cluster's Resource Manager stack output.

References

Equivalent controls in other providers: GKE Dataplane V2 NetworkPolicy, EKS VPC security groups, AKS network policy.

oci-k8s-07 ! HIGH DETECTIVE

Enhanced Cluster / Basic Cluster: OCI Audit captures containerengine API operations by default. Kubernetes API audit log forwarding to OCI Logging is a separate stream — it requires a Log Group plus a Log object pointing at the cluster resource. It is NOT on by default.

OCI Audit captures all OCI-side operations on the cluster resource (create, update, scale node pool, update endpoint, install add-on). Kubernetes API audit logs are a separate stream — forward them to OCI Logging via a Log object on the cluster so in-cluster kubectl, controller, and webhook activity is durable and queryable. Configure log retention to match the organization's incident-investigation horizon.

Remediation — Terraform

# Terraform OCI provider ~> 6.0
terraform {
  required_providers {
    oci = {
      source  = "oracle/oci"
      version = "~> 6.0"
    }
  }
}

resource "oci_logging_log_group" "oke" {
  compartment_id = var.compartment_id
  display_name   = "oke-logs"
}

resource "oci_logging_log" "k8s_audit" {
  display_name = "k8s-audit"
  log_group_id = oci_logging_log_group.oke.id
  log_type     = "SERVICE"
  configuration {
    source {
      category    = "kubernetes-api-server-audit"
      resource    = oci_containerengine_cluster.hardened.id
      service     = "containerengine"
      source_type = "OCISERVICE"
    }
    compartment_id = var.compartment_id
  }
  is_enabled         = true
  retention_duration = 90
}

Remediation — OCI CLI

oci logging log-group create \
  --compartment-id <COMPARTMENT-OCID> \
  --display-name oke-logs

oci logging log create \
  --log-group-id <LOG-GROUP-OCID> \
  --display-name k8s-audit \
  --log-type SERVICE \
  --configuration '{"source":{"category":"kubernetes-api-server-audit","resource":"<CLUSTER-OCID>","service":"containerengine","sourceType":"OCISERVICE"},"compartmentId":"<COMPARTMENT-OCID>"}' \
  --is-enabled true \
  --retention-duration 90

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-k8s-07-oci-audit-logging" \
  --display-name "oci-k8s-07-oci-audit-logging" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-k8s-07-oci-audit-logging" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Compliance mapping

Control Severity Type Provider CIS Kubernetes Benchmark v1.11.0 CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015 NIST SP 800-190 (Sep 2017) NSA/CISA Kubernetes Hardening Guide v1.2
oci-k8s-07 HIGH DETECTIVE OCI OKE §1.2.22 (audit logging — managed control plane) n/a (verify against CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 PDF) AU-2; AU-12; SI-4 A.8.15; A.8.16 CLD.12.4.5 NIST SP 800-190 §4.4.3 NSA/CISA Kubernetes Hardening Guide v1.2 §6 (Audit logging and threat detection)

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and 'Service Name' = 'logging' with eventName in (DeleteLog, UpdateLog) targeting a log OCID that backs OKE cluster audit ingestion.
  • OKE cluster update events that flip options.addOns.isKubernetesDashboardEnabled or any audit-policy field — the latter changes the Kubernetes audit verbosity feeding the log group.
  • Logging Analytics ingestion gap on the OKE audit log source longer than five minutes during steady-state hours.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'logging'
          and eventName in ('DeleteLog', 'UpdateLog', 'DeleteLogGroup')
          | eval is_oke_log = if(data.target.log.displayName like '%kube-apiserver%' or data.target.log.displayName like '%oke-audit%', 'YES', 'NO')
          | where is_oke_log = 'YES'
          | stats count by 'User Name', data.target.log.displayName, eventName

Tag the OKE audit log OCIDs with a defined-tag OKEAuditPipeline = critical so the saved search can filter by tag rather than display-name pattern.

Alert threshold

  • Any DeleteLog on an OKE audit log OCID — page; loss of the log object breaks the detection pipeline for the cluster.
  • An ingestion gap exceeding 5 minutes for the OKE Kubernetes audit log source during business hours — page.

Initial response

  1. Re-create the deleted log via Resource Manager; OCI Logging recreates the log object idempotently and re-binds it to the OKE cluster's audit-policy reference.
  2. Pull the cluster's kube-apiserver audit feed directly via oci ce cluster get-kubeconfig and kubectl get events covering the gap; export to Object Storage.
  3. Confirm the Logging Analytics namespace resumes ingestion and update the OKE log inventory dashboard.

References

Equivalent controls in other providers: GKE Cloud Audit Logs, EKS control-plane logs, AKS diagnostic settings (control-plane audit logs).

oci-k8s-08 ! HIGH PREVENTIVE

Enhanced Cluster / Basic Cluster: the built-in PodSecurity admission controller (K8s 1.23+) enforces Pod Security Standards profiles via namespace labels. No add-on is required — the controller is built into kube-apiserver and is active on all current OKE Kubernetes versions.

Use the built-in PodSecurity admission controller to enforce the Restricted or Baseline Pod Security Standards (PSS) profile at the namespace level. Target Restricted for application namespaces; reserve Baseline for namespaces that legitimately need broader pod capabilities (e.g. CNI components, log forwarders) — and document the exemption. PSS replaces the removed legacy admission mechanism; do not look for a separate cluster-level policy object.

Remediation — kubectl

kubectl label namespace production \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/enforce-version=latest

kubectl label namespace production \
  pod-security.kubernetes.io/audit=restricted \
  pod-security.kubernetes.io/warn=restricted

Remediation — Terraform

# Terraform OCI provider ~> 6.0
terraform {
  required_providers {
    oci = {
      source  = "oracle/oci"
      version = "~> 6.0"
    }
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = "~> 2.30"
    }
  }
}

resource "kubernetes_namespace" "production" {
  metadata {
    name = "production"
    labels = {
      "pod-security.kubernetes.io/enforce"         = "restricted"
      "pod-security.kubernetes.io/enforce-version" = "latest"
      "pod-security.kubernetes.io/audit"           = "restricted"
      "pod-security.kubernetes.io/warn"            = "restricted"
    }
  }
}

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-k8s-08-pod-security-standards" \
  --display-name "oci-k8s-08-pod-security-standards" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-k8s-08-pod-security-standards" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Compliance mapping

Control Severity Type Provider CIS Kubernetes Benchmark v1.11.0 CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015 NIST SP 800-190 (Sep 2017) NSA/CISA Kubernetes Hardening Guide v1.2
oci-k8s-08 HIGH PREVENTIVE OCI OKE §5.2 (Pod Security Standards) n/a (verify against CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 PDF) CM-6; AC-6; SI-3 A.8.9; A.8.28 CLD.6.3.1 NIST SP 800-190 §4.2 NSA/CISA Kubernetes Hardening Guide v1.2 §3 (Pod security)

Log signals

  • OKE Kubernetes audit log entries forwarded to OCI Logging Analytics where 'Log Source' = 'OCI Audit Logs' filtered to annotations."pod-security.kubernetes.io/enforce" field deltas at the Namespace object scope.
  • Pod admission entries showing responseStatus.code = 403 with reason text containing violates PodSecurity — these are denials and themselves a healthy detection signal.
  • Namespace update events removing the Pod Security enforce label entirely, downgrading the namespace to admission-free state.

Query

'Log Source' = 'OCI Audit Logs'
          and data.kubernetesAudit.verb = 'update'
          and data.kubernetesAudit.objectRef.resource = 'namespaces'
          | eval enforce = data.kubernetesAudit.requestObject.metadata.labels."pod-security.kubernetes.io/enforce"
          | where enforce in ('privileged', '')
          | stats count by 'User Name', data.kubernetesAudit.objectRef.name, enforce

OKE forwards Kubernetes audit log records into Logging Analytics by default in Enhanced clusters; surface the kubernetesAudit JSONPath as a tagged field at parser time.

Alert threshold

  • Any namespace whose enforce label moves from restricted or baseline down to privileged or empty — page on first occurrence.
  • More than five admission denials per namespace per hour — open a workload-team ticket; either the workload is non-compliant or the policy needs tuning.

Initial response

  1. Re-apply the namespace baseline via the cluster's GitOps state (Flux or Argo CD reconciliation) — the namespace label is reverted on the next sync interval.
  2. Review the pods that were admitted during the downgraded window; evict any that would have been blocked under restricted enforcement.
  3. Brief the workload team on the Pod Security baseline contract per general/ir.html.

References

Equivalent controls in other providers: GKE PSS, EKS PSS, AKS Azure Policy add-on.

oci-k8s-09 ! HIGH PREVENTIVE

Enhanced Cluster / Basic Cluster: install Calico CNI for NetworkPolicy enforcement on OKE. OCI VCN-Native Pod Networking plus Calico for policy is the typical pairing. Node OS hardening: Oracle Linux 8 minimal is the default OKE node image; private node pools must have no public IPs on node VNICs.

Apply a default-deny NetworkPolicy in every namespace, then add explicit allow rules for required east-west and egress paths. Calico is the NetworkPolicy enforcer on OKE — install via the documented manifest or Helm chart after cluster create. This control also covers REQ OKE-09 node-hardening requirements: select Oracle Linux 8 minimal as the node image; place node pools in private subnets with no public IPs on node VNICs.

Remediation — default-deny NetworkPolicy

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

Remediation — Terraform (private node pool, Oracle Linux 8 minimal)

# Terraform OCI provider ~> 6.0
terraform {
  required_providers {
    oci = {
      source  = "oracle/oci"
      version = "~> 6.0"
    }
  }
}

data "oci_core_images" "ol8_minimal" {
  compartment_id           = var.compartment_id
  operating_system         = "Oracle Linux"
  operating_system_version = "8"
  shape                    = "VM.Standard.E4.Flex"
  sort_by                  = "TIMECREATED"
  sort_order               = "DESC"
}

resource "oci_containerengine_node_pool" "app" {
  cluster_id         = oci_containerengine_cluster.hardened.id
  compartment_id     = var.compartment_id
  name               = "app-nodes"
  kubernetes_version = "v1.30.0"
  node_shape         = "VM.Standard.E4.Flex"

  node_source_details {
    source_type = "IMAGE"
    image_id    = data.oci_core_images.ol8_minimal.images[0].id
  }

  node_config_details {
    placement_configs {
      availability_domain = var.ad_1
      subnet_id           = oci_core_subnet.nodes_private.id
    }
    nsg_ids                             = [oci_core_network_security_group.nodes.id]
    is_pv_encryption_in_transit_enabled = true
    size                                = 3
  }
}

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-k8s-09-network-policy-calico" \
  --display-name "oci-k8s-09-network-policy-calico" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-k8s-09-network-policy-calico" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Compliance mapping

Control Severity Type Provider CIS Kubernetes Benchmark v1.11.0 CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015 NIST SP 800-190 (Sep 2017) NSA/CISA Kubernetes Hardening Guide v1.2
oci-k8s-09 HIGH PREVENTIVE OCI OKE §5.3 (Network policies) n/a (verify against CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 PDF) SC-7; SC-5; AC-4 A.8.20; A.8.22 CLD.9.5.1 NIST SP 800-190 §4.4.1 NSA/CISA Kubernetes Hardening Guide v1.2 §2 (Network separation)

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' with eventName = 'UpdateClusterAddon' targeting the Calico add-on with payload flipping configurations.networkPolicyEnabled to false or disabling the add-on outright.
  • Kubernetes audit entries showing NetworkPolicy resource deletions across all namespaces within a short window — a deliberate teardown.
  • VCN Flow Logs showing pod-to-pod traffic between namespaces previously denied by a NetworkPolicy now succeeding.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'containerengine'
          and eventName in ('UpdateClusterAddon', 'DisableAddon')
          | eval addon = data.request.payload.addonName
          | where addon like '%Calico%' or addon = 'NetworkPolicy'
          | stats count by 'User Name', data.target.cluster.id, addon, eventName

OKE manages Calico via the add-on plane on Enhanced clusters — a configuration change is a single audit-level event.

Alert threshold

  • Any UpdateClusterAddon on the Calico add-on flipping policy enforcement off — page; cluster default becomes permit-all.
  • Bulk deletion of NetworkPolicy resources across two or more namespaces inside 10 minutes — page.

Initial response

  1. Re-enable Calico policy enforcement via Resource Manager; the add-on reconciles to ACTIVE within a few minutes and reinstates default-deny inside affected namespaces.
  2. Re-apply the cluster's NetworkPolicy manifests from the GitOps repo; verify Calico Felix logs show policy load success.
  3. Sample VCN Flow Logs across the gap window and document any pod-to-pod traffic that occurred outside policy per general/ir.html.

References

Equivalent controls in other providers: GKE Dataplane V2 NetworkPolicy, EKS default-deny NetworkPolicy, AKS network policy.

oci-k8s-10 ! HIGH PREVENTIVE

Enhanced Cluster / Basic Cluster: scope OCI IAM dynamic groups to compartments using least-privilege allow dynamic-group ... to manage ... in compartment ... rules. Avoid manage all-resources in tenancy for any cluster identity.

Use OCI IAM dynamic groups to grant the cluster control plane and node pool exactly the permissions required — never tenancy-wide. Scope each statement to a specific compartment (APP for application resources; NET for networking; SHARED for shared KMS keys with a vault-id-bound condition). Where a grant must reach beyond a single compartment, prefer a where condition that restricts the target by OCID rather than a broader compartment scope.

NEVER: allow dynamic-group ... to manage all-resources in tenancy — this collapses the entire compartment hierarchy into a single trust boundary.

Remediation — Terraform

# Terraform OCI provider ~> 6.0
terraform {
  required_providers {
    oci = {
      source  = "oracle/oci"
      version = "~> 6.0"
    }
  }
}

resource "oci_identity_dynamic_group" "oke_cluster" {
  compartment_id = var.tenancy_ocid
  name           = "oke-cluster-dyn-group"
  description    = "OKE cluster + node-pool identities"
  matching_rule  = "ANY {ALL {instance.compartment.id = '${var.compartment_id}', tag.oke.cluster.value = '${oci_containerengine_cluster.hardened.id}'}}"
}

resource "oci_identity_policy" "oke_cluster" {
  compartment_id = var.tenancy_ocid
  name           = "oke-cluster-policy"
  description    = "Least-privilege OKE cluster + node-pool grants"
  statements = [
    "allow dynamic-group oke-cluster-dyn-group to use cluster-family in compartment APP",
    "allow dynamic-group oke-cluster-dyn-group to manage instances in compartment APP",
    "allow dynamic-group oke-cluster-dyn-group to use vaults in compartment SHARED where target.vault.id = '${oci_kms_vault.oke.id}'",
  ]
}

Remediation — OCI CLI

oci iam dynamic-group create \
  --compartment-id <TENANCY-OCID> \
  --name oke-cluster-dyn-group \
  --description "OKE cluster + node-pool identities" \
  --matching-rule "ANY {ALL {instance.compartment.id = '<COMPARTMENT-OCID>', tag.oke.cluster.value = '<CLUSTER-OCID>'}}"

oci iam policy create \
  --compartment-id <TENANCY-OCID> \
  --name oke-cluster-policy \
  --description "Least-privilege OKE grants" \
  --statements '["allow dynamic-group oke-cluster-dyn-group to use cluster-family in compartment APP", "allow dynamic-group oke-cluster-dyn-group to manage instances in compartment APP", "allow dynamic-group oke-cluster-dyn-group to use vaults in compartment SHARED where target.vault.id = '\''<VAULT-OCID>'\''"]'

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-k8s-10-iam-least-privilege-cluster-access" \
  --display-name "oci-k8s-10-iam-least-privilege-cluster-access" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-k8s-10-iam-least-privilege-cluster-access" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Compliance mapping

Control Severity Type Provider CIS Kubernetes Benchmark v1.11.0 CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015 NIST SP 800-190 (Sep 2017) NSA/CISA Kubernetes Hardening Guide v1.2
oci-k8s-10 HIGH PREVENTIVE OCI OKE §5.1 (RBAC and service accounts) n/a (verify against CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0 PDF) AC-2; AC-3; AC-6 A.5.15; A.5.16; A.5.18 CLD.12.1.5 NIST SP 800-190 §4.4.2 NSA/CISA Kubernetes Hardening Guide v1.2 §4 (Authentication and authorization)

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and 'Service Name' = 'identity' with eventName = 'UpdatePolicy' whose statement body widens grants on cluster-family or cluster-content-read resource types.
  • Kubernetes RBAC ClusterRoleBinding creation events binding cluster-admin to subjects outside the documented OKE admin allow-list.
  • Kubeconfig generation events (CreateClusterKubeconfig) issued to OCI user OCIDs not present in the cluster operator inventory.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'identity'
          and eventName in ('CreatePolicy', 'UpdatePolicy')
          | eval stmt = data.request.payload.statements
          | where stmt like '%manage cluster-family%' or stmt like '%use cluster-content-read%'
          | stats count by 'User Name', data.target.policy.name, 'Compartment Name'

Run continuously; OKE policy statements are narrow by convention and any new manage cluster-family in tenancy grant is a control-fence break.

Alert threshold

  • Any new policy statement granting manage cluster-family in tenancy to a group outside OKEAdmins — page on first event.
  • Kubernetes RBAC cluster-admin binding outside the cluster's documented break-glass group — page; the in-cluster grant bypasses OCI IAM scoping.

Initial response

  1. Revert the OCI policy via the OCI Identity policy version history; the prior statement set was the last-known-good least-privilege scope.
  2. Remove the in-cluster ClusterRoleBinding via the cluster's GitOps reconciliation loop; any drift snaps back on the next sync.
  3. Rotate any kubeconfig tokens issued during the widened window and document the rollback per general/ir.html.

References

Equivalent controls in other providers: GKE IAM identity layer (Workload Identity Federation), EKS access entries / node IAM, AKS Entra ID + Kubernetes RBAC.

Sources

  • OKE Cluster types (Enhanced vs Basic): https://docs.oracle.com/iaas/Content/ContainerEngine/Concepts/contengworkingwithclusters.htm (accessed 2026-05)
  • OKE Workload Identity: https://docs.oracle.com/iaas/Content/ContainerEngine/Tasks/contenggrantingworkloadaccesstoresources.htm (accessed 2026-05)
  • OKE Secrets encryption with OCI Vault: https://docs.oracle.com/iaas/Content/ContainerEngine/Tasks/contengencryptingdata.htm (accessed 2026-05)
  • OKE Image verification policy: https://docs.oracle.com/iaas/Content/ContainerEngine/Tasks/contengsigningimages.htm (accessed 2026-05)
  • OKE Network Security Groups: https://docs.oracle.com/iaas/Content/ContainerEngine/Concepts/contengnetworkconfig.htm (accessed 2026-05)
  • OCI Audit service: https://docs.oracle.com/iaas/Content/Audit/Concepts/auditoverview.htm (accessed 2026-05)
  • OCI IAM dynamic groups: https://docs.oracle.com/iaas/Content/Identity/Tasks/managingdynamicgroups.htm (accessed 2026-05)
  • Terraform OCI provider (oracle/oci): https://registry.terraform.io/providers/oracle/oci/latest (accessed 2026-05)
  • CIS Oracle Container Engine for Kubernetes (OKE) Benchmark v1.8.0: https://www.cisecurity.org/benchmark/kubernetes (accessed 2026-05)
  • CIS Kubernetes Benchmark v1.11.0: https://www.cisecurity.org/benchmark/kubernetes (accessed 2026-05)
  • NSA/CISA Kubernetes Hardening Guide v1.2: https://media.defense.gov/2022/Aug/29/2003066362/-1/-1/0/CTR_KUBERNETES_HARDENING_GUIDANCE_1.2_20220829.PDF (accessed 2026-05)