OCI Workloads Hardening

Overview

This page covers Oracle Cloud Infrastructure workload hardening across the surfaces that decide whether an attacker who lands code execution on a single OCI workload can pivot to credentials, sibling workloads, or the OCI control plane. Scope is the commercial OCI realms (OC1); OCI Government Cloud and dedicated-region tenancies inherit the same controls but expose realm-specific endpoints, region availability, and Identity Domains (formerly IDCS) federation constraints — re-verify region availability and the relevant docs.oracle.com realm-endpoint documentation before applying any of the IaC below to a sovereign or dedicated-region deployment. Workload surfaces covered: Compute Instances (VM and bare-metal shapes), Container Registry (image hosting plus image signing via OCI Vault keys), Container Engine for Kubernetes (OKE) in both Basic and Enhanced cluster tiers (billing/SLA axis, not a control-surface axis), Functions (FaaS authenticating via resource principals), DevOps build pipelines, and OS Management Hub (patch lifecycle management). CIS sub-IDs and NIST / ISO mappings throughout this page reference the CIS Oracle Cloud Infrastructure Foundations Benchmark v2.0.0 (accessed 2026-05) unless explicitly annotated as a post-v2.0.0 feature or a best-practice recommendation that the v2.0.0 benchmark has not yet codified; CIS published v3.1.0 in 2026 but this site cites v2.0.0 throughout the corpus for consistency with the locked compliance-table contract. The crosswalk page at compliance frameworks describes how the seven pinned framework columns relate to each other.

One canonical-content cross-link to flag at the top, because authoring this page in isolation would otherwise duplicate ~1500 words of canonical material: secrets management for OCI Functions is documented on the General IAM — secrets management page, not here. The Phase 4 canonical-content rule (one canonical treatment per cross-cutting topic) lives this rule out in oci-work-05: the control covers Functions resource-principal authentication and Vault secret retrieval at runtime, and cross-links to general/iam.html for the secrets-handling reference architecture rather than re-authoring it. The same pattern is mirrored on the AWS, Azure, and GCP sibling pages (aws-work-05, azure-work-05, gcp-work-05). Compartment hierarchy and instance / resource principal mechanics are owned canonically by oci-iam-06-instance-principals and oci-iam-07-compartment-hierarchy on the OCI IAM page; this page cross-references and does not re-author the canonical IAM model.

Three anti-conflation callouts up front, because each pair gets conflated in audit reports and architecture reviews and the distinction matters for control design. First: instance principals vs resource principals. Instance principals authenticate Compute instances to OCI APIs via dynamic-group membership; the dynamic-group matching rule pins which instances may obtain the principal (typically by compartment OCID or by free-form tag). Resource principals extend the same model to non-Compute resources — Functions, API Gateway, OKE pods via instance principals on worker nodes, and a growing list of OCI managed services. Both are bound to dynamic groups; both eliminate user-API-keys-on-workloads. The canonical treatment lives at oci-iam-06-instance-principals; oci-work-05 on this page maps the model to Functions specifically. Second: OKE Basic vs OKE Enhanced clusters. Basic clusters lack the Oracle-managed control-plane SLA and the Cluster Add-on Management lifecycle features; Enhanced clusters add the SLA, add-on management, Virtual Nodes, and Workload Identity in its current GA shape. The control-surface — the hardening posture this page recommends (Workload Identity, private cluster endpoints, image signing, network policies, Bastion access) — is identical between tiers. Basic vs Enhanced is a billing and SLA axis, not a hardening axis. oci-work-06 is therefore authored as a single umbrella control covering both tiers, mirroring the umbrella decisions made for EKS / AKS / GKE on the AWS / Azure / GCP sibling pages.

Third: OS Management Hub and the Vulnerability Scanning Service are complementary, not alternative. OS Management Hub (oci-work-08) is the patch deployment surface — managed-instance enrolment, software-source pinning (custom mirrors or vendor-default), and scheduled patch jobs that actually install packages on instances. The Vulnerability Scanning Service (VSS) — host scope on oci-work-04 and container-image scope on oci-work-03 — is the CVE detection surface that reports unpatched CVEs and open ports against benchmark baselines. The two services close a feedback loop: VSS detects, OS Management Hub remediates, the next VSS scan confirms. Treating either as a substitute for the other leaves either detection blind or remediation manual.

Order matters. Controls 01–02 are foundational invariants for every Compute instance: IMDSv2 enforced (the SSRF-to-credentials kill-chain mitigation) and the OCI Bastion service as the remote-access plane (SSH-to-public-IP enumerated only to be ruled out). Controls 03–04 close the supply-chain and vulnerability-assessment loop: Container Registry image scanning, signing, and immutability at build time; VSS host scanning at runtime. Control 05 hardens Functions. Control 06 hardens OKE. Control 07 establishes signed-image provenance via the OCI DevOps build pipeline. Control 08 handles ongoing patch hygiene via OS Management Hub. The page is structured so a reader can skim 01–02 for the everyday Compute baseline, then dip into 03–08 by service area as needed. Equivalence callouts at the bottom of each control point at the matching control on the AWS, Azure, and GCP sibling pages.

oci-work-01-instance-metadata-v2 ! CRITICAL PREVENTIVE

Configure every Compute instance with IMDSv2 enforced by setting are_legacy_imds_endpoints_disabled = true in the instance's instance_options block — at both launch time and as a remediation on existing instances. The OCI Instance Metadata Service v2 requires a session token obtained via an authenticated PUT before any GET to 169.254.169.254/opc/v2/ will return credentials or metadata; the legacy v1 surface accepts unauthenticated GETs and is the SSRF-exploitable path (Oracle Cloud Infrastructure — Instance Metadata Service v2 (accessed 2026-05)). Disabling the legacy endpoints turns the metadata service into a token-required surface that an unauthenticated SSRF reflection cannot drive. PITFALL: a Compute instance that obtains an instance principal via dynamic-group membership inherits the same kill-chain risk as an AWS instance-role identity; SSRF reflection against IMDSv1 hands the attacker an instance-principal session token that the workload's compartment-scoped IAM policy may have generously authorised. Mirrors AWS aws-work-01-imdsv2-mandatory, Azure Trusted Launch at azure-work-01-trusted-launch, and GCP gcp-work-01-shielded-vm — all CRITICAL PREVENTIVE across providers because the SSRF-to-credentials path is the highest-leverage Compute misconfiguration. The principle traces back to General Workloads — runtime security: never let a workload's network position alone determine its identity strength.

Remediation — OCI CLI

# oci CLI (v3.x)
# Enforce IMDSv2-only on an existing instance.
oci compute instance update \
  --instance-id "$INSTANCE_OCID" \
  --instance-options '{"areLegacyImdsEndpointsDisabled": true}' \
  --force

# Launch a new instance with IMDSv2-only from the start.
oci compute instance launch \
  --compartment-id "$WORKLOAD_COMPARTMENT_OCID" \
  --availability-domain "$AD" \
  --shape "VM.Standard.E4.Flex" \
  --shape-config '{"ocpus": 2, "memoryInGBs": 16}' \
  --image-id "$IMAGE_OCID" \
  --subnet-id "$PRIVATE_SUBNET_OCID" \
  --assign-public-ip false \
  --instance-options '{"areLegacyImdsEndpointsDisabled": true}'

# Audit: list instances still allowing IMDSv1 (legacy endpoints enabled).
oci search resource structured-search \
  --query-text "query instance resources where (lifecycleState = 'RUNNING')" \
  --output json \
  | jq -r '.data.items[] | .identifier' \
  | while read -r oid; do
      legacy=$(oci compute instance get --instance-id "$oid" --query 'data."instance-options"."are-legacy-imds-endpoints-disabled"' --raw-output)
      [ "$legacy" = "false" ] && echo "LEGACY-IMDS: $oid"
    done

Remediation — Terraform

# Terraform OCI provider ~> 5.0
# Source: Oracle Cloud docs (accessed 2026-05)
resource "oci_core_instance" "app_prod" {
  compartment_id      = var.workload_compartment_id
  availability_domain = data.oci_identity_availability_domain.ad.name
  shape               = "VM.Standard.E4.Flex"
  display_name        = "vm-app-prod-01"

  shape_config {
    ocpus         = 2
    memory_in_gbs = 16
  }

  source_details {
    source_type = "image"
    source_id   = var.hardened_image_ocid
  }

  create_vnic_details {
    subnet_id        = oci_core_subnet.app_prod_private.id
    assign_public_ip = false
    nsg_ids          = [oci_core_network_security_group.app_tier.id]
  }

  # IMDSv2 enforced: legacy v1 endpoint is unreachable; SSRF reflection cannot
  # obtain a session token because most SSRF payloads can only emit GETs.
  instance_options {
    are_legacy_imds_endpoints_disabled = true
  }

  agent_config {
    is_management_disabled = false
    is_monitoring_disabled = false

    plugins_config {
      name          = "Vulnerability Scanning"
      desired_state = "ENABLED"
    }

    plugins_config {
      name          = "OS Management Hub Agent"
      desired_state = "ENABLED"
    }
  }
}

# Instance configuration default — every instance launched via this configuration
# inherits IMDSv2-only without each launch having to remember the toggle.
resource "oci_core_instance_configuration" "hardened_default" {
  compartment_id = var.workload_compartment_id
  display_name   = "ic-hardened-default"

  instance_details {
    instance_type = "compute"
    launch_details {
      compartment_id = var.workload_compartment_id
      shape          = "VM.Standard.E4.Flex"

      instance_options {
        are_legacy_imds_endpoints_disabled = true
      }
    }
  }
}

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-work-01-instance-metadata-v2" \
  --display-name "oci-work-01-instance-metadata-v2" \
  --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-work-01-instance-metadata-v2" \
  --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";

// Compute instance with IMDSv2-only metadata access (IMDSv1 disabled).
const cfg = new pulumi.Config();
const compartmentId = cfg.require("compartmentOcid");
const subnetOcid = cfg.require("subnetOcid");
const imageOcid = cfg.require("imageOcid");
const sshAuthorizedKeys = cfg.require("sshAuthorizedKeys");

const hardenedInstance = new oci.core.Instance("hardened-vm", {
  compartmentId: compartmentId,
  availabilityDomain: cfg.require("availabilityDomain"),
  shape: "VM.Standard.E5.Flex",
  shapeConfig: { ocpus: 2, memoryInGbs: 16 },
  displayName: "hardened-vm-imdsv2",
  sourceDetails: {
    sourceType: "image",
    sourceId: imageOcid,
  },
  createVnicDetails: {
    subnetId: subnetOcid,
    assignPublicIp: "false",
  },
  metadata: {
    ssh_authorized_keys: sshAuthorizedKeys,
  },
  instanceOptions: {
    areLegacyImdsEndpointsDisabled: true,  // IMDSv1 OFF — IMDSv2 only
  },
  // Trusted launch surrogate: Secure Boot + Measured Boot via plain shielded-instance config.
  platformConfig: {
    type: "AMD_VM",
    isSecureBootEnabled: true,
    isMeasuredBootEnabled: true,
    isTrustedPlatformModuleEnabled: true,
  },
});

export const instanceOcid = hardenedInstance.id;

Compliance mapping

CIS AWS Foundations v3.0.0 CIS Microsoft Azure Foundations v3.0.0 CIS GCP Foundation v4.0.0 CIS OCI Foundation v2.0.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015
n/an/an/a5.x (verify) AC-3; CM-7; SC-8A.8.20; A.8.25CLD.9.5.1

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and 'Service Name' = 'compute' with eventName = 'LaunchInstance' whose payload sets instanceOptions.areLegacyImdsEndpointsDisabled = false.
  • Existing-instance update events (UpdateInstance) flipping the legacy-IMDS option back on for an instance previously locked to v2-only.
  • Cloud Guard problems of type InstanceMetadataV1Enabled against the tenancy's Compute inventory.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'compute'
          and eventName in ('LaunchInstance', 'UpdateInstance')
          | eval legacy_enabled = if(data.request.payload.instanceOptions.areLegacyImdsEndpointsDisabled = 'false', 'YES', 'NO')
          | where legacy_enabled = 'YES'
          | stats count by 'User Name', data.target.instance.id, eventName

The IMDS-v2-only enforcement is a single boolean on each instance; v1 endpoint exposure is the OCI analogue of the EC2 IMDSv1 pivot risk.

Alert threshold

  • Any new or updated instance running with legacy IMDS endpoints enabled — page on first event.
  • Cloud Guard InstanceMetadataV1Enabled problem severity HIGH on a production-tagged instance — page.

Initial response

  1. Update the instance to disable legacy IMDS endpoints via oci compute instance update --instance-options '{"areLegacyImdsEndpointsDisabled":true}' — OCI applies the change without reboot.
  2. For new launches drifted from baseline, reconcile the launch template (Compute instance configuration) via Resource Manager so subsequent launches inherit v2-only enforcement.
  3. Audit instance-principal token issuance for the affected instances across the drift window; rotate any downstream secrets that may have been fetched via the legacy endpoint.

References

Equivalent on: AWS · Azure · GCP

oci-work-02-bastion-service ! HIGH PREVENTIVE

Operators reach workload Compute instances via the OCI Bastion service, which provisions per-session, time-limited SSH sessions (or managed port-forwarding sessions for non-SSH services such as Oracle Database 1521 or RDP 3389) to target instances that have no public IP; sessions are audited in the OCI Audit service, ephemeral SSH key pairs are bound to the session, and the session TTL is capped at 3 hours with explicit renewal required for longer access windows (Oracle Cloud Infrastructure — Bastion Service overview (accessed 2026-05)). The Bastion attaches to a target subnet in your VCN, applies a client-CIDR allow-list (so only operator-network IPs can initiate sessions), and uses Oracle-managed infrastructure to bridge to the target — no public IPs, no open SSH listener on the workload itself, no long-lived SSH key material on disk to compromise. SSH-to-public-IP is an anti-pattern, not an alternative. Putting a public IP on a workload instance and opening TCP 22 to 0.0.0.0/0 on a Security List or NSG exposes the instance to unauthenticated network scanning and SSH brute-force; even with public-key-only authentication and Fail2ban, the attack surface is the SSH daemon itself and the daemon's TLS / SSH-protocol implementation, not just the password layer. The Bastion service is the default; SSH-to-public-IP is documented here only to be ruled out — this control continues the same anti-pattern enumeration started on oci-net-02 (which denies 0.0.0.0/0 on TCP 22 at the firewall layer regardless of whether the workload has a public IP). HIGH PREVENTIVE because the Bastion is the structural fix for the entire class of "operator opens TCP 22 to debug something" misconfigurations; mirrors AWS Session Manager (aws-work-02), Azure JIT Bastion (azure-work-02), and GCP OS Login + IAP (gcp-work-02). The principle traces back to General Workloads — runtime security: never make the workload's SSH daemon the perimeter.

Remediation — OCI CLI

# oci CLI (v3.x)
# Step 1: create a Standard Bastion attached to a private workload subnet.
oci bastion bastion create \
  --compartment-id "$WORKLOAD_COMPARTMENT_OCID" \
  --bastion-type STANDARD \
  --target-subnet-id "$PRIVATE_SUBNET_OCID" \
  --client-cidr-block-allow-list '["10.0.0.0/8","192.0.2.0/24"]' \
  --max-session-ttl-in-seconds 10800 \
  --name bastion-app-prod

# Step 2: create a managed SSH session against a target Compute instance.
oci bastion session create \
  --bastion-id "$BASTION_OCID" \
  --display-name dev-session-2026-05-23 \
  --session-ttl-in-seconds 10800 \
  --key-details '{"publicKeyContent":"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... ops@bastion"}' \
  --target-resource-details '{
    "sessionType": "MANAGED_SSH",
    "targetResourceId": "'"$INSTANCE_OCID"'",
    "targetResourceOperatingSystemUserName": "opc",
    "targetResourcePort": 22
  }'

# Step 3: connect (the bastion returns an ssh ProxyCommand string at session-create time).
ssh -i ~/.ssh/ephemeral_ed25519 \
    -o ProxyCommand="ssh -W %h:%p -p 22 $SESSION_OCID@host.bastion.$REGION.oci.oraclecloud.com" \
    -p 22 opc@"$INSTANCE_PRIVATE_IP"

# Audit: any session activity is in the Audit service (eventName=session.create).
oci audit event list \
  --compartment-id "$WORKLOAD_COMPARTMENT_OCID" \
  --start-time "$(date -u -d '-7 days' '+%Y-%m-%dT%H:%M:%SZ')" \
  --end-time "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
  --query 'data[?contains("event-name", `Session`)].[("event-time"),"event-name",("user-name")]' \
  --output table

Remediation — Terraform

# Terraform OCI provider ~> 5.0
# Source: Oracle Cloud docs (accessed 2026-05)
resource "oci_bastion_bastion" "app_prod" {
  compartment_id               = var.workload_compartment_id
  bastion_type                 = "STANDARD"
  target_subnet_id             = oci_core_subnet.app_prod_private.id
  client_cidr_block_allow_list = ["10.0.0.0/8", "192.0.2.0/24"]
  max_session_ttl_in_seconds   = 10800 # 3 hours; the Bastion service hard cap
  name                         = "bastion-app-prod"

  freeform_tags = {
    "purpose" = "operator-access"
    "owner"   = "platform-security"
  }
}

# Per-session resource — typically driven by a just-in-time access tool, not
# baked into the static Terraform state. Shown here for completeness.
resource "oci_bastion_session" "dev_session" {
  bastion_id              = oci_bastion_bastion.app_prod.id
  display_name            = "dev-session-${formatdate("YYYY-MM-DD", timestamp())}"
  session_ttl_in_seconds  = 10800

  key_details {
    public_key_content = var.operator_ephemeral_ssh_pubkey
  }

  target_resource_details {
    session_type                               = "MANAGED_SSH"
    target_resource_id                         = oci_core_instance.app_prod.id
    target_resource_operating_system_user_name = "opc"
    target_resource_port                       = 22
  }
}

# NSG rule on the workload tier: SSH only from the Bastion service's managed
# infrastructure (the Bastion injects its source addresses at session create).
# The deny on 0.0.0.0/0 TCP 22 is enforced by absence — see oci-net-02.

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-work-02-bastion-service" \
  --display-name "oci-work-02-bastion-service" \
  --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-work-02-bastion-service" \
  --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

CIS AWS Foundations v3.0.0 CIS Microsoft Azure Foundations v3.0.0 CIS GCP Foundation v4.0.0 CIS OCI Foundation v2.0.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015
n/an/an/a(best-practices) AC-17; AC-17(3); AU-2A.8.5; A.8.15CLD.9.5.1

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and 'Service Name' = 'bastion' with eventName = 'CreateSession' whose targetResourcePrivateIpAddress falls outside the bastion's documented target-CIDR allow-list.
  • Bastion resource update events that widen clientCidrBlockAllowList beyond the corporate VPN CIDR.
  • Session-duration audit events where sessionTtlInSeconds exceeds the documented 3-hour cap.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'bastion'
          and eventName in ('CreateSession', 'UpdateBastion')
          | eval wide_client = if(data.request.payload.clientCidrBlockAllowList like '%0.0.0.0/0%', 'YES', 'NO')
          | eval long_ttl = if(data.request.payload.sessionTtlInSeconds > 10800, 'YES', 'NO')
          | where wide_client = 'YES' or long_ttl = 'YES'
          | stats count by 'User Name', data.target.bastion.id, eventName

Bastion session events name the requesting principal, the target IP, and the session TTL — all three are inputs to the policy decision.

Alert threshold

  • Any Bastion resource update extending clientCidrBlockAllowList to 0.0.0.0/0 — page.
  • Session TTL configured above 10 800 seconds (3 hours) on a production-tagged bastion — page.

Initial response

  1. Restore the bastion's client_cidr_block_allow_list to the corporate-VPN CIDR via Resource Manager; OCI applies the change online to new sessions.
  2. Terminate any active sessions originating outside the corporate VPN CIDR via oci bastion session delete.
  3. Audit the bastion's session log archive for any session that originated outside the documented client-CIDR during the widened-allow-list window and document per general/ir.html.

References

Equivalent on: AWS · Azure · GCP

oci-work-03-image-scanning ! HIGH DETECTIVE

Every Container Registry (OCIR) repository that backs a production workload must satisfy three independent requirements: (1) repository immutability via is_immutable = true so that a published tag cannot be overwritten with new bytes after publication (preserves the bytes-to-name binding the rest of the supply chain assumes), (2) vulnerability scanning via a Vulnerability Scanning Service container scan recipe that scans on push and on a recurring schedule, surfacing OS-package CVEs and language-runtime CVEs in the pushed image layers, and (3) image signing via an OCI Vault key, producing an attestation object that oci-work-06 (the OKE image policy) verifies at admission time (Oracle Cloud Infrastructure — Container Registry (accessed 2026-05)). Immutability without scanning lets vulnerable images through; scanning without immutability lets an attacker who compromises the build pipeline overwrite a signed-and-scanned tag with a backdoored layer; signing without scanning attests bytes that may carry critical CVEs. The three controls compose — none of the three on its own is sufficient. HIGH DETECTIVE because VSS scans surface unsafe state after publication; oci-work-07 (signed build pipeline) and oci-work-06 (admission policy that requires signed images) are the paired PREVENTIVE controls that turn detection into enforcement. Mirrors AWS ECR scan-on-push (aws-work-03), Azure Container Registry scanning (azure-work-03), and GCP Artifact Registry scanning (gcp-work-03). The principle traces back to General Workloads — supply chain: every artifact promoted to production must carry both a scan record and a signature whose root of trust the cluster verifies.

Remediation — OCI CLI

# oci CLI (v3.x)
# Step 1: create an immutable Container Registry repository.
oci artifacts container-repository create \
  --compartment-id "$WORKLOAD_COMPARTMENT_OCID" \
  --display-name prod-images-app \
  --is-immutable true \
  --is-public false

# Step 2: create a VSS container scan recipe with standard scan level (OS + lang CVEs).
oci vulnerability-scans container-scan-recipe create \
  --compartment-id "$SECURITY_COMPARTMENT_OCID" \
  --display-name csr-prod-images \
  --scan-settings '{"scanLevel":"STANDARD"}'

# Step 3: bind the recipe to the target repository (container scan target).
oci vulnerability-scans container-scan-target create \
  --compartment-id "$SECURITY_COMPARTMENT_OCID" \
  --display-name cst-prod-images \
  --container-scan-recipe-id "$CSR_OCID" \
  --target-registry '{
    "type":"OCIR",
    "compartmentId":"'"$WORKLOAD_COMPARTMENT_OCID"'",
    "repositoryId":"'"$REPO_OCID"'"
  }'

# Step 4: sign a pushed image with an OCI Vault key (post-push, pre-deploy).
oci artifacts container-image-signature sign-upload \
  --compartment-id "$WORKLOAD_COMPARTMENT_OCID" \
  --image-id "$IMAGE_OCID" \
  --kms-key-id "$VAULT_KEY_OCID" \
  --kms-key-version-id "$VAULT_KEY_VERSION_OCID" \
  --signing-algorithm SHA_512_RSA_PKCS_PSS \
  --description "release v1.2.3"

# Audit: list scan results above CRITICAL for the repository.
oci vulnerability-scans container-scan-result list \
  --compartment-id "$SECURITY_COMPARTMENT_OCID" \
  --container-scan-target-id "$CST_OCID" \
  --highest-problem-severity CRITICAL \
  --output table

Remediation — Terraform

# Terraform OCI provider ~> 5.0
# Source: Oracle Cloud docs (accessed 2026-05)
resource "oci_artifacts_container_repository" "prod_images" {
  compartment_id = var.workload_compartment_id
  display_name   = "prod-images-app"

  # Immutability: a published tag cannot be overwritten. Backdoor-swap defence.
  is_immutable = true
  is_public    = false

  readme {
    content = "Production application images. Immutable; scanned by VSS; signed via Vault key."
    format  = "text/markdown"
  }
}

# Vault key dedicated to image-signing — distinct from data-encryption keys
# so its policy can be tightly scoped (sign-only for the CI principal).
resource "oci_kms_key" "image_signing" {
  compartment_id      = var.security_compartment_id
  display_name        = "key-image-signing-app"
  management_endpoint = data.oci_kms_vault.security.management_endpoint
  protection_mode     = "HSM"

  key_shape {
    algorithm = "RSA"
    length    = 512 # 4096-bit RSA for signing
  }
}

# VSS container scan recipe — STANDARD scan level covers OS + language CVEs.
resource "oci_vulnerability_scans_container_scan_recipe" "prod" {
  compartment_id = var.security_compartment_id
  display_name   = "csr-prod-images"

  scan_settings {
    scan_level = "STANDARD"
  }
}

resource "oci_vulnerability_scans_container_scan_target" "prod" {
  compartment_id             = var.security_compartment_id
  display_name               = "cst-prod-images"
  container_scan_recipe_id   = oci_vulnerability_scans_container_scan_recipe.prod.id

  target_registry {
    type           = "OCIR"
    compartment_id = var.workload_compartment_id
    repository_id  = oci_artifacts_container_repository.prod_images.id
  }
}

# Image signature — typically created by the CI pipeline post-push, not in
# static Terraform state. Shape shown here for the build_spec to model.
resource "oci_artifacts_container_image_signature" "release_v123" {
  compartment_id        = var.workload_compartment_id
  image_id              = var.published_image_ocid
  kms_key_id            = oci_kms_key.image_signing.id
  kms_key_version_id    = var.image_signing_key_version_ocid
  signing_algorithm     = "SHA_512_RSA_PKCS_PSS"
  signature             = var.precomputed_signature_b64
  message               = var.signing_message_b64
  description           = "release v1.2.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-work-03-image-scanning" \
  --display-name "oci-work-03-image-scanning" \
  --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-work-03-image-scanning" \
  --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

CIS AWS Foundations v3.0.0 CIS Microsoft Azure Foundations v3.0.0 CIS GCP Foundation v4.0.0 CIS OCI Foundation v2.0.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015
n/an/an/a(best-practices) RA-5; SI-3; SA-11A.8.8; A.8.29CLD.12.4.5

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and 'Service Name' = 'artifacts' with eventName in (UpdateContainerRepository, UpdateContainerImageSignature) toggling repository-level vulnerability scanning off.
  • OCIR image-push events emitting a digest absent from the corresponding signed-digest inventory — surfaces unsigned-image regressions.
  • Vulnerability Scanning Service (VSS) report events showing CRITICAL CVE counts greater than zero on images currently referenced by OKE deployments.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' in ('artifacts', 'vulnerability-scanning')
          and eventName in ('UpdateContainerRepository', 'CreateContainerImageSignature', 'PutContainerImage')
          | eval scan_off = if(data.request.payload.isImmutable = 'false' or data.request.payload.scanningEnabled = 'false', 'YES', 'NO')
          | where scan_off = 'YES'
          | stats count by 'User Name', data.target.repository.name, eventName

Pair the audit-side gate with a periodic VSS report-list scan via oci vulnerability-scanning host-scan-target list for container-image targets.

Alert threshold

  • Any OCIR repository where scanning or signature requirement is disabled — page.
  • VSS report containing one or more CRITICAL CVEs against an image currently deployed in OKE — page within the SLA window for critical-severity vulnerabilities.

Initial response

  1. Re-enable repository scanning and signing requirement via Resource Manager; OCIR runs the next scan within minutes.
  2. For deployed images carrying CRITICAL CVEs, schedule a rolling redeploy onto a freshly built image with the patch applied; OKE supports surge updates that keep the workload available throughout.
  3. Brief the workload team on the scan policy and update the vulnerability-management dashboard per general/ir.html.

References

Equivalent on: AWS · Azure · GCP

oci-work-04-vulnerability-scanning ! HIGH DETECTIVE

Every Compute instance in a workload compartment is enrolled in a Vulnerability Scanning Service host scan recipe that reports OS-package CVEs, open ports relative to the workload's expected listening posture, and CIS-benchmark compliance against the instance's OS family. The recipe runs the Oracle-managed scanner agent inside the instance (enrolled via the Compute agent's Vulnerability Scanning plugin set to ENABLED — see the agent_config block in oci-work-01's Terraform) plus optional port-scan and benchmark sub-scans (Oracle Cloud Infrastructure — Vulnerability Scanning Service (accessed 2026-05)). Findings feed back into OS Management Hub (oci-work-08) which is the remediation surface that actually applies patches via scheduled jobs, closing the detect-then-remediate loop. Scope distinction (anti-conflation): this control is workload-level host scanning — the per-instance Compute hardening surface that pairs with the per-image container scanning surface on oci-work-03. Both surfaces are powered by the same Vulnerability Scanning Service but operate on different artefact types (running OS image vs published container image). HIGH DETECTIVE because absence of host scanning means CVE drift on long-running instances goes unnoticed until exploitation; the remediation pair is OS Management Hub. Mirrors AWS Inspector for EC2 (aws-work-04), Azure Defender for Servers (azure-work-04), and GCP VM Manager (gcp-work-04). The principle traces back to General Workloads — patch management: detection + remediation are paired controls, never substitutes.

Remediation — OCI CLI

# oci CLI (v3.x)
# Step 1: create the host scan recipe (STANDARD = OS CVE + open-port + benchmark).
oci vulnerability-scans host-scan-recipe create \
  --compartment-id "$SECURITY_COMPARTMENT_OCID" \
  --display-name hsr-prod-hosts \
  --port-settings '{"scanLevel":"STANDARD"}' \
  --agent-settings '{
    "scanLevel":"STANDARD",
    "agentConfiguration":{
      "vendor":"OCI",
      "cisBenchmarkSettings":{"scanLevel":"STRICT"}
    }
  }' \
  --schedule '{"type":"DAILY","dayOfWeek":null}'

# Step 2: attach the recipe to a target compartment subtree.
oci vulnerability-scans host-scan-target create \
  --compartment-id "$SECURITY_COMPARTMENT_OCID" \
  --display-name hst-prod-hosts \
  --host-scan-recipe-id "$HSR_OCID" \
  --target-compartment-id "$WORKLOAD_COMPARTMENT_OCID"

# Step 3: query latest scan results above HIGH severity in the workload compartment.
oci vulnerability-scans host-scan-result list \
  --compartment-id "$SECURITY_COMPARTMENT_OCID" \
  --highest-problem-severity HIGH \
  --output table

Remediation — Terraform

# Terraform OCI provider ~> 5.0
# Source: Oracle Cloud docs (accessed 2026-05)
resource "oci_vulnerability_scans_host_scan_recipe" "prod_hosts" {
  compartment_id = var.security_compartment_id
  display_name   = "hsr-prod-hosts"

  port_settings {
    scan_level = "STANDARD"
  }

  agent_settings {
    scan_level = "STANDARD"

    agent_configuration {
      vendor = "OCI"

      cis_benchmark_settings {
        scan_level = "STRICT"
      }
    }
  }

  schedule {
    type = "DAILY"
  }
}

resource "oci_vulnerability_scans_host_scan_target" "prod_hosts" {
  compartment_id        = var.security_compartment_id
  display_name          = "hst-prod-hosts"
  host_scan_recipe_id   = oci_vulnerability_scans_host_scan_recipe.prod_hosts.id
  target_compartment_id = var.workload_compartment_id

  # Empty instance_ids means: scan every instance in the target compartment subtree.
}

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-work-04-vulnerability-scanning" \
  --display-name "oci-work-04-vulnerability-scanning" \
  --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-work-04-vulnerability-scanning" \
  --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

CIS AWS Foundations v3.0.0 CIS Microsoft Azure Foundations v3.0.0 CIS GCP Foundation v4.0.0 CIS OCI Foundation v2.0.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015
n/an/an/a(best-practices) RA-5; SI-4A.8.8CLD.12.4.5

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and 'Service Name' = 'vulnerability-scanning' with eventName in (DeleteHostScanRecipe, DeleteHostScanTarget, UpdateHostScanRecipe) lowering scan-aggression or removing target coverage.
  • Compute-instance host agent events where the VSS Plugin reports STOPPED for longer than 24 hours.
  • Host-scan reports absent for a tagged production host for longer than 7 days — surfaces silent VSS-agent failure.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'vulnerability-scanning'
          and eventName in ('DeleteHostScanRecipe', 'DeleteHostScanTarget', 'UpdateHostScanRecipe')
          | eval lowered = if(data.request.payload.applicationScanSettings.applicationScanRecurrence in ('WEEKLY', 'MONTHLY'), 'YES', 'NO')
          | where eventName like 'Delete%' or lowered = 'YES'
          | stats count by 'User Name', data.target.id, eventName

VSS scan recipes are infrequently edited; correlate the audit-event count with the per-host scan-report cadence.

Alert threshold

  • Any DeleteHostScanTarget on a production-tagged target — page.
  • VSS scan recipe recurrence lowered from DAILY to WEEKLY or coarser — page.

Initial response

  1. Re-create the scan target and recipe via Resource Manager; VSS rebuilds the scan inventory on the next scheduled cycle.
  2. For hosts with stopped agents, re-deploy the Oracle Cloud Agent VSS plugin via Bastion-mediated SSH and restart the oracle-cloud-agent-vss systemd unit.
  3. Triage any backlog of unscanned production hosts and report coverage via the regular vulnerability-management cycle per general/ir.html.

References

Equivalent on: AWS · Azure · GCP

oci-work-05-function-resource-principal ! HIGH PREVENTIVE

OCI Functions authenticate to OCI APIs via resource principals — a dynamic-group-bound identity OCI injects into the function's runtime as a short-lived session token, so the function code calls the OCI SDK without ever holding a long-lived user API key (Oracle Cloud Infrastructure — Functions Service overview (accessed 2026-05)). The dynamic-group matching rule pins which functions obtain the principal — typically ALL {resource.type = 'fnfunc', resource.compartment.id = '$WORKLOAD_COMPARTMENT_OCID'} so only functions in the named compartment qualify. IAM policy on the dynamic group is least-privilege: name the specific OCI resource families the function actually needs ("read secret-family", "use keys", "manage object-family on bucket X"), never the catch-all manage all-resources in the tenancy. Secrets: the canonical treatment of secrets handling on OCI Functions lives at General IAM — secrets management; this control documents the OCI-specific shape (Functions configuration references the Vault secret OCID, not the secret value; the function reads the secret at invocation time using its resource principal). The same canonical-content rule was applied on AWS aws-work-05 (Secrets Manager), Azure azure-work-05 (Key Vault), and GCP gcp-work-05 (Secret Manager). HIGH PREVENTIVE because embedded API keys in function code or configuration is the single largest source of cloud-credential leaks in serverless workloads; the structural fix is to eliminate the credential entirely. Cross-link oci-iam-06-instance-principals for the canonical instance / resource principal model. Mirrors AWS Lambda execution role least-priv (aws-work-05), Azure Function managed identity (azure-work-05), GCP Cloud Run service account (gcp-work-05). Principle: General Workloads — serverless-specific concerns.

Remediation — OCI CLI

# oci CLI (v3.x)
# Step 1: create the dynamic group bound to Functions in the workload compartment.
oci iam dynamic-group create \
  --compartment-id "$TENANCY_OCID" \
  --name FunctionsResourcePrincipals \
  --description "All Functions in the workload compartment authenticate as this dynamic group." \
  --matching-rule "ALL {resource.type = 'fnfunc', resource.compartment.id = '$WORKLOAD_COMPARTMENT_OCID'}"

# Step 2: write a least-privilege policy — name specific resource families only.
oci iam policy create \
  --compartment-id "$WORKLOAD_COMPARTMENT_OCID" \
  --name policy-functions-resource-principals \
  --description "Least-privilege grants for Functions resource principals." \
  --statements '[
    "Allow dynamic-group FunctionsResourcePrincipals to read secret-family in compartment Prod",
    "Allow dynamic-group FunctionsResourcePrincipals to use keys in compartment Prod where target.key.id = '"'"'ocid1.key.oc1..exampleocid'"'"'",
    "Allow dynamic-group FunctionsResourcePrincipals to read objects in compartment Prod where target.bucket.name = '"'"'app-data-prod'"'"'"
  ]'

# Step 3: configure the Function with VAULT SECRET OCIDs (not secret values).
oci fn function update \
  --function-id "$FUNCTION_OCID" \
  --config '{
    "VAULT_SECRET_OCID":"'"$DB_PASSWORD_SECRET_OCID"'",
    "OCI_REGION":"eu-frankfurt-1"
  }'

# Audit: list any IAM user that is a member of an Administrators-equivalent group
# AND has Functions invoke rights — Functions should authenticate via resource principal only.
oci iam user list --compartment-id "$TENANCY_OCID" --output table

Remediation — Terraform

# Terraform OCI provider ~> 5.0
# Source: Oracle Cloud docs (accessed 2026-05)
resource "oci_identity_dynamic_group" "functions_rp" {
  compartment_id = var.tenancy_ocid
  name           = "FunctionsResourcePrincipals"
  description    = "All Functions in the workload compartment authenticate as this dynamic group."

  matching_rule = "ALL {resource.type = 'fnfunc', resource.compartment.id = '${var.workload_compartment_id}'}"
}

resource "oci_identity_policy" "functions_rp" {
  compartment_id = var.workload_compartment_id
  name           = "policy-functions-resource-principals"
  description    = "Least-privilege grants for Functions resource principals."

  statements = [
    "Allow dynamic-group FunctionsResourcePrincipals to read secret-family in compartment Prod",
    "Allow dynamic-group FunctionsResourcePrincipals to use keys in compartment Prod where target.key.id = '${oci_kms_key.app_data.id}'",
    "Allow dynamic-group FunctionsResourcePrincipals to read objects in compartment Prod where target.bucket.name = 'app-data-prod'",
  ]
}

resource "oci_functions_application" "app_prod" {
  compartment_id = var.workload_compartment_id
  display_name   = "fn-app-prod"
  subnet_ids     = [oci_core_subnet.app_prod_private.id]

  # NSG controls Functions ingress at the VNIC layer.
  network_security_group_ids = [oci_core_network_security_group.functions_tier.id]
}

resource "oci_functions_function" "process_event" {
  application_id = oci_functions_application.app_prod.id
  display_name   = "process-event"
  image          = "${var.region}.ocir.io/${var.tenancy_namespace}/prod-images-app:v1.2.3"
  memory_in_mbs  = "256"

  # Config references Vault secret OCIDs — NEVER secret values themselves.
  # The function reads the secret at invocation time via its resource principal.
  config = {
    "VAULT_SECRET_OCID" = oci_vault_secret.db_password.id
    "OCI_REGION"        = var.region
  }
}

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-work-05-function-resource-principal" \
  --display-name "oci-work-05-function-resource-principal" \
  --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-work-05-function-resource-principal" \
  --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

CIS AWS Foundations v3.0.0 CIS Microsoft Azure Foundations v3.0.0 CIS GCP Foundation v4.0.0 CIS OCI Foundation v2.0.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015
n/an/an/a(best-practices) AC-6; SC-12; SC-28A.5.15; A.8.24n/a

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and 'Service Name' = 'functions' with eventName = 'UpdateFunction' whose image path points outside the tenancy OCIR namespace.
  • Function-create events where the function's compartment-bound dynamic group grants manage all-resources rather than scoped resource-family rights.
  • Function invocation logs containing repeated calls to OCI services using a baked-in API signing key — indicates the function bypassed the resource-principal SDK auto-discovery.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'functions'
          and eventName in ('UpdateFunction', 'CreateFunction')
          | eval external_image = if(not data.request.payload.image like '%.ocir.io/%', 'YES', 'NO')
          | where external_image = 'YES'
          | stats count by 'User Name', data.target.function.id, eventName, data.request.payload.image

Function images must come from a tenancy-controlled OCIR; external registries are a supply-chain risk.

Alert threshold

  • Any function whose image source is outside *.ocir.io — page.
  • Function-tier dynamic group with manage all-resources in tenancy — page; resource principals must hold scoped resource-family rights.

Initial response

  1. Update the function image reference to a tenancy-OCIR-hosted digest via Resource Manager; Functions re-pulls the image on the next deployment.
  2. Rescope the function's dynamic group to the documented resource-family scope; OCI applies the policy delta within seconds.
  3. Audit function invocation logs across the drift window for any OCI-API call made under the over-broad scope and reconcile any downstream secrets per general/ir.html.

References

Equivalent on: AWS · Azure · GCP

oci-work-06-oke-workload-identity ! HIGH PREVENTIVE

Production OKE clusters satisfy a single umbrella of hardening properties simultaneously: (a) Workload Identity — per-pod OCI principal via instance principals on worker nodes plus dynamic-group OCID-based matching, so each Kubernetes service account maps to an OCI dynamic group that scopes the pod's OCI rights to least privilege; (b) private cluster endpoint via endpoint_config { is_public_ip_enabled = false }, so the Kubernetes API server has no public IP and the only access path is from inside the VCN (or via the Bastion service); (c) image signing via OCI Vault keys with the cluster image-policy enabled (image_policy_config { is_policy_enabled = true; key_details { kms_key_id = ... } }), so the kubelet rejects unsigned images at admission and the supply chain from oci-work-03 / oci-work-07 is actually enforced; (d) network policies via the Calico CNI add-on (default OKE networking) or the optional Cilium add-on, enforcing east-west pod isolation that is independent of NSG rules at the worker-node VNIC level; (e) Bastion service for worker-node SSH — workers carry no public IPs, and any node-level debugging happens via Bastion-managed SSH sessions per oci-work-02 (Oracle Cloud Infrastructure — Container Engine for Kubernetes (OKE) (accessed 2026-05)). Basic vs Enhanced clusters: Basic clusters lack the Oracle-managed control-plane SLA and the Cluster Add-on Management feature; Enhanced clusters add the SLA, Virtual Nodes, and Workload Identity in its current GA shape. The hardening posture (this umbrella) is identical between tiers — Basic vs Enhanced is a billing axis, not a control-surface axis. This is authored as one umbrella control because the surfaces compose: private endpoint without image-policy lets unsigned bytes run on internal-only API; image-policy without Workload Identity makes signed pods carry over-broad worker-node-inherited principals; network policies without private endpoint protect pod-to-pod but leave the API server exposed. Mirrors AWS EKS Pod Identity umbrella (aws-work-06), Azure AKS Workload Identity umbrella (azure-work-06), and GCP GKE Workload Identity Federation umbrella (gcp-work-06). HIGH PREVENTIVE because the cluster is the multi-tenant runtime where misconfigurations compound across workloads. Principle: General Workloads — container-specific concerns.

Remediation — OCI CLI

# oci CLI (v3.x)
# Step 1: create the OKE cluster as Enhanced, with a private endpoint and image-policy.
oci ce cluster create \
  --compartment-id "$WORKLOAD_COMPARTMENT_OCID" \
  --name oke-app-prod \
  --vcn-id "$VCN_OCID" \
  --kubernetes-version v1.30.1 \
  --type ENHANCED_CLUSTER \
  --endpoint-config '{
    "isPublicIpEnabled": false,
    "subnetId":"'"$ENDPOINT_SUBNET_OCID"'",
    "nsgIds":["'"$ENDPOINT_NSG_OCID"'"]
  }' \
  --options '{
    "addOns": {"isKubernetesDashboardEnabled": false},
    "kubernetesNetworkConfig": {"podsCidr":"10.244.0.0/16","servicesCidr":"10.96.0.0/16"}
  }' \
  --image-policy-config '{
    "isPolicyEnabled": true,
    "keyDetails":[{"kmsKeyId":"'"$VAULT_KEY_OCID"'"}]
  }'

# Step 2: create the worker node pool in a private subnet — no public IPs on workers.
oci ce node-pool create \
  --cluster-id "$CLUSTER_OCID" \
  --compartment-id "$WORKLOAD_COMPARTMENT_OCID" \
  --name np-app-prod \
  --kubernetes-version v1.30.1 \
  --node-shape "VM.Standard.E4.Flex" \
  --node-shape-config '{"ocpus":2,"memoryInGBs":16}' \
  --node-image-id "$HARDENED_NODE_IMAGE_OCID" \
  --node-config-details '{
    "placementConfigs":[{"availabilityDomain":"'"$AD"'","subnetId":"'"$WORKER_SUBNET_OCID"'"}],
    "size":3,
    "nsgIds":["'"$WORKER_NSG_OCID"'"]
  }'

# Step 3: dynamic group + policy for Workload Identity (per-pod resource principal).
oci iam dynamic-group create \
  --compartment-id "$TENANCY_OCID" \
  --name OkeAppProdWorkloadIdentity \
  --matching-rule "ALL {resource.type = 'workload', resource.compartment.id = '$WORKLOAD_COMPARTMENT_OCID', resource.workload.kind = 'pod', resource.workload.cluster.id = '$CLUSTER_OCID', resource.workload.namespace = 'app-prod', resource.workload.serviceaccount = 'app-sa'}"

oci iam policy create \
  --compartment-id "$WORKLOAD_COMPARTMENT_OCID" \
  --name policy-oke-app-prod-workload-identity \
  --statements '["Allow dynamic-group OkeAppProdWorkloadIdentity to read secret-family in compartment Prod"]'

Remediation — Terraform

# Terraform OCI provider ~> 5.0
# Source: Oracle Cloud docs (accessed 2026-05)
resource "oci_kms_key" "oke_image_signing" {
  compartment_id      = var.security_compartment_id
  display_name        = "key-oke-image-signing"
  management_endpoint = data.oci_kms_vault.security.management_endpoint
  protection_mode     = "HSM"

  key_shape {
    algorithm = "RSA"
    length    = 512
  }
}

resource "oci_containerengine_cluster" "app_prod" {
  compartment_id     = var.workload_compartment_id
  name               = "oke-app-prod"
  vcn_id             = oci_core_vcn.app_prod.id
  kubernetes_version = "v1.30.1"
  type               = "ENHANCED_CLUSTER" # Required for Workload Identity GA shape.

  endpoint_config {
    # Private cluster endpoint: API server has no public IP.
    is_public_ip_enabled = false
    subnet_id            = oci_core_subnet.app_prod_oke_endpoint.id
    nsg_ids              = [oci_core_network_security_group.oke_endpoint.id]
  }

  options {
    add_ons {
      is_kubernetes_dashboard_enabled = false
      is_tiller_enabled               = false
    }

    kubernetes_network_config {
      pods_cidr     = "10.244.0.0/16"
      services_cidr = "10.96.0.0/16"
    }

    admission_controller_options {
      is_pod_security_policy_enabled = false # PSPs are removed in v1.25+; use Pod Security Admission.
    }
  }

  # Image policy: kubelet verifies image signatures against the named Vault key
  # at admission time. Unsigned images are rejected.
  image_policy_config {
    is_policy_enabled = true

    key_details {
      kms_key_id = oci_kms_key.oke_image_signing.id
    }
  }
}

resource "oci_containerengine_node_pool" "app_prod" {
  cluster_id         = oci_containerengine_cluster.app_prod.id
  compartment_id     = var.workload_compartment_id
  name               = "np-app-prod"
  kubernetes_version = "v1.30.1"
  node_shape         = "VM.Standard.E4.Flex"

  node_shape_config {
    ocpus         = 2
    memory_in_gbs = 16
  }

  node_source_details {
    source_type = "IMAGE"
    image_id    = var.hardened_node_image_ocid
  }

  node_config_details {
    placement_configs {
      availability_domain = data.oci_identity_availability_domain.ad.name
      subnet_id           = oci_core_subnet.app_prod_oke_workers.id
    }
    size    = 3
    nsg_ids = [oci_core_network_security_group.oke_workers.id]
  }
}

# Workload Identity dynamic group — per-pod (per-namespace + per-service-account) OCID match.
resource "oci_identity_dynamic_group" "oke_workload_identity" {
  compartment_id = var.tenancy_ocid
  name           = "OkeAppProdWorkloadIdentity"
  description    = "Per-pod resource principal for app-prod namespace / app-sa service account."

  matching_rule = "ALL {resource.type = 'workload', resource.compartment.id = '${var.workload_compartment_id}', resource.workload.kind = 'pod', resource.workload.cluster.id = '${oci_containerengine_cluster.app_prod.id}', resource.workload.namespace = 'app-prod', resource.workload.serviceaccount = 'app-sa'}"
}

resource "oci_identity_policy" "oke_workload_identity" {
  compartment_id = var.workload_compartment_id
  name           = "policy-oke-app-prod-workload-identity"

  statements = [
    "Allow dynamic-group OkeAppProdWorkloadIdentity to read secret-family in compartment Prod",
  ]
}

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-work-06-oke-workload-identity" \
  --display-name "oci-work-06-oke-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-work-06-oke-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

CIS AWS Foundations v3.0.0 CIS Microsoft Azure Foundations v3.0.0 CIS GCP Foundation v4.0.0 CIS OCI Foundation v2.0.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015
n/an/an/a(best-practices) AC-3; AC-6; SC-7A.5.15; A.8.20CLD.9.5.1

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' with eventName = 'UpdatePolicy' whose statement body widens grants on dynamic groups bound to OKE workloads via the oci.oraclecloud.com/workload-identity annotation.
  • Kubernetes audit events where a ServiceAccount annotation is changed to map onto a dynamic group with broader IAM scope.
  • Pod admission events where workload-identity binding fails (missing annotation, dynamic-group lookup error) — surfaces drift from the in-cluster Git state.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'identity'
          and eventName in ('CreateDynamicGroup', 'UpdateDynamicGroup', 'UpdatePolicy')
          | eval workload_bound = if(data.target.id in_subset 'OKEWorkloadIdentityGroups', 'YES', 'NO')
          | where workload_bound = 'YES'
          | stats count by 'User Name', data.target.id, eventName

Maintain a managed list of dynamic-group OCIDs annotated by OKE ServiceAccounts so the gate can scope to workload-relevant identities only.

Alert threshold

  • Any policy widening grants for a dynamic group bound to an OKE ServiceAccount — page.
  • ServiceAccount annotation change re-pointing a pod identity to a dynamic group outside its documented workload-identity allow-list — page.

Initial response

  1. Revert the policy via the OCI Identity policy version history; the prior statement set was the last-known-good least-privilege grant for the workload identity.
  2. Re-reconcile the cluster's ServiceAccount manifests via GitOps; any drifted annotation snaps back on the next sync.
  3. Rotate any OCI-API credentials issued during the widened-policy window via the affected resource principals and document per general/ir.html.

References

Equivalent on: AWS · Azure · GCP

oci-work-07-image-build ! MEDIUM PREVENTIVE

Production container images are produced by an OCI DevOps build pipeline (or an external CI system that emits identical artefacts), pushed to an immutable Container Registry repository per oci-work-03, signed with the OCI Vault image-signing key, and only then promoted to the OKE deployment manifest that references them by digest. The build pipeline is the provenance origin — every byte in production must trace to a build job in a known compartment, with the build_spec.yaml stored in source-control and the build context limited to the source repository plus signed dependency manifests (Oracle Cloud Infrastructure — DevOps service (accessed 2026-05)). The signing step inside the pipeline uses the same oci artifacts container-image-signature sign-upload primitive as oci-work-03, but bound to the build pipeline's instance principal so signatures are attributable to specific build jobs. oci-work-06's image-policy verifies the signatures at admission time, closing the supply-chain loop. MEDIUM PREVENTIVE because the build pipeline is the provenance origin; absent a controlled build, every other supply-chain control downstream is verifying signatures whose subjects nobody can audit. Mirrors AWS EC2 Image Builder golden AMIs (aws-work-07), Azure VM Images / Azure DevOps signed builds (azure-work-07), and GCP confidential / signed images (gcp-work-07). Principle: General Workloads — supply chain.

Remediation — OCI CLI

# oci CLI (v3.x)
# Step 1: DevOps project (top-level container for pipelines).
oci devops project create \
  --compartment-id "$WORKLOAD_COMPARTMENT_OCID" \
  --name proj-app-prod \
  --notification-config '{"topicId":"'"$NOTIFICATION_TOPIC_OCID"'"}'

# Step 2: build pipeline.
oci devops build-pipeline create \
  --project-id "$PROJECT_OCID" \
  --display-name bp-app-prod-image-build \
  --description "Builds, scans, and signs the prod-images-app container image."

# Step 3: build stage that invokes build_spec.yaml from source-control.
oci devops build-pipeline-stage create \
  --build-pipeline-id "$BP_OCID" \
  --build-pipeline-stage-type BUILD \
  --display-name stage-docker-build \
  --build-spec-file build_spec.yaml \
  --image OL7_X86_64_STANDARD_10

# A representative build_spec.yaml fragment that does the sign step:
cat <<'EOF' > build_spec.yaml
version: 0.1
component: build
timeoutInSeconds: 1800
shell: bash

steps:
  - type: Command
    name: docker-build
    command: |
      docker build -t "$REGION.ocir.io/$NAMESPACE/prod-images-app:$BUILDRUN_HASH" .
      docker push "$REGION.ocir.io/$NAMESPACE/prod-images-app:$BUILDRUN_HASH"

  - type: Command
    name: sign-image
    command: |
      IMAGE_OCID=$(oci artifacts container-image list \
        --compartment-id "$WORKLOAD_COMPARTMENT_OCID" \
        --repository-name prod-images-app \
        --version "$BUILDRUN_HASH" \
        --query 'data.items[0].id' --raw-output)
      oci artifacts container-image-signature sign-upload \
        --compartment-id "$WORKLOAD_COMPARTMENT_OCID" \
        --image-id "$IMAGE_OCID" \
        --kms-key-id "$VAULT_KEY_OCID" \
        --kms-key-version-id "$VAULT_KEY_VERSION_OCID" \
        --signing-algorithm SHA_512_RSA_PKCS_PSS \
        --description "build $BUILDRUN_HASH"
EOF

Remediation — Terraform

# Terraform OCI provider ~> 5.0
# Source: Oracle Cloud docs (accessed 2026-05)
resource "oci_devops_project" "app_prod" {
  compartment_id = var.workload_compartment_id
  name           = "proj-app-prod"

  notification_config {
    topic_id = oci_ons_notification_topic.devops.id
  }
}

resource "oci_devops_build_pipeline" "image_build" {
  project_id   = oci_devops_project.app_prod.id
  display_name = "bp-app-prod-image-build"
  description  = "Builds, scans, and signs the prod-images-app container image."
}

resource "oci_devops_build_pipeline_stage" "docker_build" {
  build_pipeline_id            = oci_devops_build_pipeline.image_build.id
  build_pipeline_stage_type    = "BUILD"
  display_name                 = "stage-docker-build"
  build_spec_file              = "build_spec.yaml"
  image                        = "OL7_X86_64_STANDARD_10"

  build_source_collection {
    items {
      connection_type = "DEVOPS_CODE_REPOSITORY"
      branch          = "main"
      name            = "source"
      repository_id   = oci_devops_repository.app_prod.id
      repository_url  = oci_devops_repository.app_prod.http_url
    }
  }

  build_pipeline_stage_predecessor_collection {
    items {
      id = oci_devops_build_pipeline.image_build.id
    }
  }
}

# Dynamic group + policy so the build pipeline's instance principal can sign images.
resource "oci_identity_dynamic_group" "devops_build_signers" {
  compartment_id = var.tenancy_ocid
  name           = "DevOpsBuildSigners"

  matching_rule = "ALL {resource.type = 'devopsbuildpipeline', resource.compartment.id = '${var.workload_compartment_id}'}"
}

resource "oci_identity_policy" "devops_build_signers" {
  compartment_id = var.workload_compartment_id
  name           = "policy-devops-build-signers"

  statements = [
    "Allow dynamic-group DevOpsBuildSigners to use keys in compartment Security where target.key.id = '${oci_kms_key.image_signing.id}'",
    "Allow dynamic-group DevOpsBuildSigners to manage repos in compartment Prod where target.repo.name = 'prod-images-app'",
  ]
}

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-work-07-image-build" \
  --display-name "oci-work-07-image-build" \
  --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-work-07-image-build" \
  --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

CIS AWS Foundations v3.0.0 CIS Microsoft Azure Foundations v3.0.0 CIS GCP Foundation v4.0.0 CIS OCI Foundation v2.0.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015
n/an/an/a(best-practices) CM-2; SI-2; SA-10A.8.9; A.8.32CLD.12.4.5

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and 'Service Name' = 'compute' with eventName = 'LaunchInstance' referencing a custom image OCID absent from the documented golden-image inventory.
  • Image-import events (CreateImage) sourcing from an external Object Storage bucket OCID outside the tenancy's image-build pipeline.
  • OCI DevOps pipeline events showing the golden-image build failed on the latest run — surfaces silent supply-chain regressions.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'compute'
          and eventName in ('LaunchInstance', 'CreateImage')
          | eval external_image = if(eventName = 'CreateImage' and not data.request.payload.imageSourceDetails.bucketName like '%golden-image-pipeline%', 'YES', 'NO')
          | where external_image = 'YES'
          | stats count by 'User Name', data.target.id, eventName

Tag the golden-image inventory with ImageRole = golden so the saved search filters by tag membership rather than OCID enumeration.

Alert threshold

  • Any instance launched against a custom image OCID outside the golden-image allow-list — page.
  • Image-import event sourcing from an external Object Storage bucket — page.

Initial response

  1. Re-launch the affected instance from the latest golden image via Resource Manager; OCI Compute supports rolling re-creation through instance configurations and pools.
  2. Decommission the externally-imported image OCID via oci compute image delete after confirming no other instance references it.
  3. Rebuild the golden-image pipeline run, verify image attestation against the build-pipeline OIDC token, and document per general/ir.html.

References

Equivalent on: AWS · Azure · GCP

oci-work-08-os-management-hub ! MEDIUM DETECTIVE

Every Compute instance is enrolled as a managed instance in OS Management Hub, attached to one or more software sources (custom mirrors or vendor-default Oracle Linux / Windows software sources), with a scheduled job that installs available package updates on a defined cadence (typical baseline: monthly on a recurring rule such as FREQ=MONTHLY;BYMONTHDAY=15). Patch-compliance reports flow from OS Management Hub into Cloud Guard, where they show up as configuration-detector findings that pair with the Vulnerability Scanning Service CVE detections from oci-work-04 to close the detect-then-remediate loop (Oracle Cloud Infrastructure — OS Management Hub (accessed 2026-05)). Scope distinction: OS Management Hub is the remediation surface (installs patches); VSS is the detection surface (reports unpatched CVEs). Neither replaces the other — VSS without OS Management Hub leaves remediation manual; OS Management Hub without VSS leaves compliance unverified. MEDIUM DETECTIVE because the compliance reports surface unsafe state (instances out of patch SLA); the active remediation is the scheduled-job that pairs with the maintenance window. Mirrors AWS Systems Manager Patch Manager (aws-work-08), Azure Update Manager (azure-work-08), and GCP VM Manager Patch (gcp-work-08) — all MEDIUM DETECTIVE across providers because patch-management reports compliance and pairs with the remediation schedule, not because it prevents single-step exploitation. Principle: General Workloads — patch management.

Remediation — OCI CLI

# oci CLI (v3.x)
# Step 1: attach a Compute instance as an OS Management Hub managed instance.
oci os-management-hub managed-instance attach \
  --managed-instance-id "$INSTANCE_OCID" \
  --compartment-id "$WORKLOAD_COMPARTMENT_OCID"

# Step 2: create a vendor-default software source for Oracle Linux 9 x86_64.
oci os-management-hub software-source create \
  --compartment-id "$WORKLOAD_COMPARTMENT_OCID" \
  --display-name ss-prod-ol9-x86 \
  --vendor-name ORACLE \
  --os-family ORACLE_LINUX_9 \
  --architecture-type X86_64 \
  --software-source-type VENDOR

# Step 3: attach the software source to the managed instance.
oci os-management-hub managed-instance attach-software-source \
  --managed-instance-id "$INSTANCE_OCID" \
  --software-source-id "$SS_OCID"

# Step 4: scheduled monthly patch job — install all available package updates on the 15th.
oci os-management-hub scheduled-job create \
  --display-name sj-prod-monthly-patch \
  --compartment-id "$WORKLOAD_COMPARTMENT_OCID" \
  --schedule-type CUSTOM \
  --time-next-execution "$(date -u -d 'next month 15' '+%Y-%m-%dT02:00:00Z')" \
  --recurring-rule "FREQ=MONTHLY;BYMONTHDAY=15" \
  --operations '[{"operationType":"INSTALL_PACKAGES","manageModuleStreamsDetails":null}]' \
  --managed-instance-ids '["'"$INSTANCE_OCID"'"]'

# Audit: list managed instances out of patch compliance.
oci os-management-hub managed-instance list \
  --compartment-id "$WORKLOAD_COMPARTMENT_OCID" \
  --status NEEDS_ATTENTION \
  --output table

Remediation — Terraform

# Terraform OCI provider ~> 5.0
# Source: Oracle Cloud docs (accessed 2026-05)
resource "oci_os_management_hub_managed_instance" "app_prod" {
  managed_instance_id = oci_core_instance.app_prod.id

  # The agent_config in oci-work-01's instance Terraform must have the
  # 'OS Management Hub Agent' plugin set to ENABLED for this enrolment to take.
}

resource "oci_os_management_hub_software_source" "prod_ol9" {
  compartment_id       = var.workload_compartment_id
  display_name         = "ss-prod-ol9-x86"
  vendor_name          = "ORACLE"
  os_family            = "ORACLE_LINUX_9"
  arch_type            = "X86_64"
  software_source_type = "VENDOR"
}

resource "oci_os_management_hub_managed_instance_attach_software_sources_management" "app_prod" {
  managed_instance_id = oci_os_management_hub_managed_instance.app_prod.id
  software_sources    = [oci_os_management_hub_software_source.prod_ol9.id]
}

resource "oci_os_management_hub_scheduled_job" "monthly_patch" {
  display_name         = "sj-prod-monthly-patch"
  compartment_id       = var.workload_compartment_id
  schedule_type        = "CUSTOM"
  time_next_execution  = "2026-06-15T02:00:00Z"
  recurring_rule       = "FREQ=MONTHLY;BYMONTHDAY=15"
  managed_instance_ids = [oci_core_instance.app_prod.id]

  operations {
    operation_type = "INSTALL_PACKAGES"
  }
}

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-work-08-os-management-hub" \
  --display-name "oci-work-08-os-management-hub" \
  --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-work-08-os-management-hub" \
  --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

CIS AWS Foundations v3.0.0 CIS Microsoft Azure Foundations v3.0.0 CIS GCP Foundation v4.0.0 CIS OCI Foundation v2.0.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015
n/an/an/a(best-practices) SI-2; SI-2(2); CM-3A.8.8; A.8.9CLD.12.4.5

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and 'Service Name' = 'osmh' with eventName in (UpdateProfile, DeleteProfile) disabling patch policy or stripping a security-update channel.
  • Managed-instance association events that detach a Compute instance from the OS Management Hub patch group.
  • OSMH compliance-report events where the patch-state for a managed instance has been OUT_OF_SYNC for longer than 14 days on a production-tagged host.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'osmh'
          and eventName in ('UpdateProfile', 'DeleteProfile', 'DisableManagementForManagedInstance')
          | eval channels_dropped = if(data.request.payload.softwareSources = '[]', 'YES', 'NO')
          | where eventName like 'Delete%' or eventName like 'Disable%' or channels_dropped = 'YES'
          | stats count by 'User Name', data.target.id, eventName

OSMH profiles enumerate the software sources (channels) a managed instance subscribes to; emptying the list disables future patching.

Alert threshold

  • Any OSMH profile delete or channel-list emptying on a production-tagged managed group — page.
  • OSMH compliance-report OUT_OF_SYNC longer than 14 days on a host carrying the PatchClass = critical tag — page.

Initial response

  1. Re-attach the OSMH profile and channel list via Resource Manager; OSMH reconciles managed-instance subscriptions within the next cycle.
  2. Force a patch run on the affected managed group via oci os-management-hub managed-instance install-windows-updates / install-package-updates-on-managed-instance.
  3. Verify dnf check-update on the managed instance returns empty and document the remediation per general/ir.html.

References

Equivalent on: AWS · Azure · GCP

Sources