Safeguard
Container Security

Hardening Oracle Kubernetes Engine (OKE) clusters

A practical, command-by-command guide to OKE security best practices: locking down the API endpoint, network security lists, pod security policies, IAM, and image signing.

Karan Patel
Cloud Security Engineer
8 min read

Oracle Kubernetes Engine (OKE) takes care of the control plane for you, but that only covers a slice of what "secure" actually means. The default OKE quickstart — a public API endpoint, permissive network security lists, and no pod-level restrictions — is fine for a demo and dangerous in production. Attackers don't need a zero-day to get into a misconfigured cluster; an open NodePort, an overly broad IAM policy, or a container running as root is usually enough.

This guide walks through the OKE security best practices we recommend to every team we work with, from network isolation and node hardening down to pod security policies and supply chain controls. By the end, you'll have a concrete checklist covering Oracle Kubernetes Engine hardening at the infrastructure layer, OKE network security lists configured correctly, and pod-level guardrails that stop compromised containers from becoming compromised clusters.

Step 1: Restrict the Kubernetes API Endpoint

The single highest-leverage change you can make is deciding who can even talk to the control plane. Public API endpoints are the default in the OKE quickstart flow, and they're the first thing an attacker scans for.

If your CI/CD and admins can reach OCI's Bastion service or a VPN, create the cluster with a private endpoint:

oci ce cluster create \
  --name prod-oke \
  --compartment-id $COMPARTMENT_ID \
  --vcn-id $VCN_ID \
  --kubernetes-version v1.29.1 \
  --endpoint-subnet-id $PRIVATE_SUBNET_ID \
  --endpoint-public-ip-enabled false \
  --service-lb-subnet-ids '["'$LB_SUBNET_ID'"]'

If you must keep a public endpoint (some CI runners require it), lock it down with an explicit allow-list of CIDR blocks instead of leaving it open to 0.0.0.0/0:

oci ce cluster update \
  --cluster-id $CLUSTER_ID \
  --endpoint-nsg-ids '["'$API_ENDPOINT_NSG_ID'"]'

Then attach an NSG rule that permits TCP/6443 only from your office range and CI egress IPs. Rotate the kubeconfig and confirm kubectl access still works from an approved network before you tear down the old rule.

Step 2: Harden OKE Network Security Lists and NSGs

OKE gives you two tools for network segmentation — security lists (attached to subnets) and network security groups (attached to individual resources). Most teams start with the security-list defaults Terraform or the console generates, which are broader than they need to be, then never revisit them.

Audit your worker node subnet's security list and remove any ingress rule that isn't explicitly required:

oci network security-list get --security-list-id $SL_ID \
  --query 'data.["ingress-security-rules"]'

At minimum, worker nodes need:

  • Ingress from the control plane CIDR on the kubelet port (10250)
  • Ingress from the load balancer subnet on your service NodePort range (30000–32767)
  • Ingress from other worker nodes for pod-to-pod traffic if you're not using a CNI with its own overlay

Everything else — SSH from the internet, broad VCN-wide "allow all," management ports open to 0.0.0.0/0 — should be removed. Prefer NSGs over security lists for anything workload-specific, since NSGs let you scope rules to a resource rather than an entire subnet, which matters once you're running multiple node pools with different trust levels in the same VCN. This is the core of OKE network security lists hygiene: least-privilege ingress, no wildcard CIDRs, and rules that map to a documented reason.

Step 3: Enforce OKE Pod Security Policies

PodSecurityPolicy is gone from upstream Kubernetes, but the equivalent controls in OKE are very much alive through Pod Security Admission (PSA) labels and, for finer-grained control, OPA Gatekeeper or Kyverno.

Start by labeling namespaces to enforce the restricted profile, which blocks privileged containers, host namespace sharing, and root by default:

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

For workloads that need exceptions, use a dedicated namespace with baseline rather than loosening restricted cluster-wide. If you need policy-as-code beyond what PSA labels cover — disallowing :latest tags, requiring resource limits, blocking hostPath mounts — install Kyverno and apply a starter policy:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-privileged-containers
spec:
  validationFailureAction: Enforce
  rules:
    - name: no-privileged
      match:
        resources:
          kinds: ["Pod"]
      validate:
        message: "Privileged containers are not allowed."
        pattern:
          spec:
            containers:
              - =(securityContext):
                  =(privileged): "false"

These OKE pod security policies-as-code should be part of your CI pipeline too, not just admission control — catching a violation before merge is cheaper than catching it at deploy time.

Step 4: Lock Down IAM and Node Pool Configuration

OKE integrates directly with OCI IAM, so cluster access control is only as strong as your compartment and policy design. Scope RBAC bindings to OCI groups rather than granting cluster-admin broadly:

oci iam policy create \
  --compartment-id $COMPARTMENT_ID \
  --name oke-developer-access \
  --statements '["Allow group OKE-Developers to manage cluster-node-pools in compartment prod", "Allow group OKE-Developers to use clusters in compartment prod"]'

On the node pool side, disable the Kubernetes dashboard, avoid injecting SSH keys into worker nodes unless you have an active debugging need, and use OCI's shielded instances for node pools where available to get Secure Boot and measured boot. Enable the instance metadata service v2 (IMDSv2) to close off a common SSRF-to-credential-theft path:

oci compute instance-configuration update \
  --instance-configuration-id $NODE_CONFIG_ID \
  --instance-details '{"launchDetails":{"instanceOptions":{"areLegacyImdsEndpointsDisabled":true}}}'

Step 5: Turn On Audit Logging and Runtime Monitoring

Hardening without visibility just means you fail quietly. Enable OCI Audit and Kubernetes audit logs, and ship them somewhere you actually query:

oci logging log-group create --compartment-id $COMPARTMENT_ID --display-name oke-audit-logs

oci ce cluster update \
  --cluster-id $CLUSTER_ID \
  --kubernetes-network-config '{"podsCidr":"10.244.0.0/16","servicesCidr":"10.96.0.0/16"}' \
  --options '{"admissionControllerOptions":{"isPodSecurityPolicyEnabled":false}}'

Pair this with OCI Cloud Guard for OKE, which flags public API endpoints, missing NSG restrictions, and unusually broad IAM grants automatically. At the workload layer, a runtime tool (Falco or an eBPF-based sensor) catches what static config review can't: a pod that's compliant at deploy time but starts spawning shells or reaching out to unexpected IPs at 2 a.m.

Step 6: Secure the Image Supply Chain

Hardened infrastructure doesn't help if the image running inside it is already compromised. Enforce signature verification on OCI Registry (OCIR) pulls and block unscanned images at admission:

kubectl apply -f - <<EOF
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signatures
spec:
  rules:
    - name: check-signature
      match:
        resources:
          kinds: ["Pod"]
      verifyImages:
        - imageReferences:
            - "iad.ocir.io/yourtenancy/*"
          attestors:
            - entries:
                - keys:
                    publicKeys: |-
                      -----BEGIN PUBLIC KEY-----
                      ...
                      -----END PUBLIC KEY-----
EOF

Combine this with SBOM generation in your build pipeline and vulnerability scanning gated on severity, not just presence — a cluster full of "scanned" images with 40 unpatched criticals isn't actually hardened.

Verification and Troubleshooting: An OKE Security Best Practices Checklist

Once the changes above are in place, verify them rather than assuming they took effect:

  • API endpoint exposure: oci ce cluster get --cluster-id $CLUSTER_ID --query 'data."endpoints"' — confirm the public endpoint is either disabled or restricted to your NSG.
  • Security list drift: re-run the security-list audit quarterly; Terraform state and console edits diverge more often than teams expect.
  • PSA enforcement: kubectl get ns -o jsonpath='{.items[*].metadata.labels}' to confirm every namespace carries the expected pod-security.kubernetes.io/enforce label — a namespace created outside your IaC pipeline is a common gap.
  • Policy false positives: if Kyverno starts blocking legitimate deploys, check kubectl get events -A --field-selector reason=PolicyViolation before loosening the policy — often it's a missing resources.limits block rather than a bad rule.
  • IMDSv2 rollback risk: test IMDSv2 enforcement in a staging node pool first; some older sidecar images still call the legacy metadata endpoint and will fail silently.
  • Audit log gaps: confirm log volume didn't drop to zero after enabling a new log group — a common misconfiguration is pointing the OKE audit log to a log group the cluster's dynamic group lacks permission to write to.

If you hit a "not authorized" error on any of the OCI CLI commands above, check that your dynamic group and IAM policy cover the specific resource type — OKE's granular policy model is powerful but a frequent source of hard-to-diagnose 403s during hardening work.

How Safeguard Helps

Manually working through OKE security best practices across every cluster, namespace, and node pool doesn't scale once you're running more than a handful of clusters — and configuration drift creeps back in the moment someone applies a manifest outside your reviewed IaC. Safeguard continuously scans your OKE clusters and OCI tenancy for the exact gaps covered here: public API endpoints, overly permissive network security lists, missing pod security policies, unsigned images reaching production, and IAM policies that grant more than a workload needs.

Instead of a point-in-time audit, Safeguard gives you continuous drift detection tied to your software supply chain — so a change that reopens an NSG rule or removes a PSA label gets flagged before it becomes an incident, and every image running in your cluster is traceable back to a signed, scanned build. If you're hardening OKE as part of a broader supply chain security program, Safeguard is built to keep that hardening intact over time, not just verify it once.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.