Home / OCI

OCIChecklistBeginner

Disabling legacy IMDSv1 on OCI instances - find v1 callers, switch to v2 only and keep it that way

Find which OCI instances and processes still call the v1 metadata endpoints, test IMDSv2, switch instances to "Version 2 only" and make new launches default to it.

Deepak Bhardwaj6 min read

Applies toOCI Compute VM and bare metal instances, Instance Metadata Service v1/v2, Oracle Cloud Agent, OCI CLI 3.x, Terraform OCI provider

Goal

Every OCI instance answers metadata requests only on /opc/v2/, which needs the Authorization: Bearer Oracle header. The legacy /opc/v1/ and /openstack paths return 404. This closes an easy path for SSRF bugs in web apps to read instance metadata, and it is a common finding in security posture tools.

The work is mostly finding out what still uses v1 before you switch it off.

Endpoint Header After v1 is disabled
http://169.254.169.254/opc/v2/... Authorization: Bearer Oracle required Works
http://169.254.169.254/opc/v1/... None 404 Not Found
http://169.254.169.254/openstack/... None 404 Not Found

Warning

If something on the instance still calls v1 (an old cloud-init, cloudbase-init on Windows, a bootstrap script, a monitoring agent), it breaks as soon as you disable v1. On a fresh launch with an unsupported image, you may not be able to log in at all. Test first.

1. Inventory: which instances still allow v1

  • List running instances where legacy endpoints are still enabled, per compartment:
C=ocid1.compartment.oc1..aaaa

oci compute instance list -c "$C" --lifecycle-state RUNNING --all \
  --query 'data[?"instance-options"."are-legacy-imds-endpoints-disabled"!=`true`].{name:"display-name",id:id,image:"image-id"}' \
  --output table

are-legacy-imds-endpoints-disabled is false when both v1 and v2 are allowed and true when only v2 is allowed.

  • Run the same loop across all compartments you own (or use your landing zone's compartment list), and save the output as the starting backlog.

2. Find who still calls v1

Compute publishes the InstanceMetadataRequests metric in the oci_compute namespace with no agent needed. Its dimensions include metadataVersion, path, userAgent, resourceId and status, so it tells you which instance, which path and which client made v1 calls.

  • Query the last 7 days, grouped by instance and version:
oci monitoring metric-data summarize-metrics-data \
  --namespace oci_compute -c "$C" --compartment-id-in-subtree true \
  --query-text 'InstanceMetadataRequests[1h].groupBy(resourceId, metadataVersion).sum()' \
  --start-time 2026-10-20T00:00:00Z --end-time 2026-10-27T00:00:00Z \
  --query 'data[].{dims:dimensions,points:length("aggregated-datapoints")}' --output json
  • For each instance with v1 traffic, group by userAgent and path to name the caller:
oci monitoring metric-data summarize-metrics-data \
  --namespace oci_compute -c "$C" \
  --query-text 'InstanceMetadataRequests[1h]{resourceId = "ocid1.instance.oc1..xxxx"}.groupBy(metadataVersion, userAgent, path).sum()' \
  --start-time 2026-10-20T00:00:00Z --end-time 2026-10-27T00:00:00Z

Check the exact metadataVersion values in the output (or in Metrics Explorer) before you build alarms on them; don't assume the label format.

  • On the instance, search for scripts and configs that hard-code v1:
sudo grep -rIl -e "opc/v1" -e "169.254.169.254/openstack" \
  /etc /opt /usr/local /home /root 2>/dev/null
# Windows
Get-ChildItem C:\ -Recurse -Include *.ps1,*.cmd,*.bat,*.py,*.conf,*.json -ErrorAction SilentlyContinue |
  Select-String -Pattern "opc/v1","169.254.169.254/openstack" -List | Select-Object Path

Common v1 callers:

Caller Fix
Old cloud-init (before 20.3) on custom Linux images Update cloud-init, rebuild the image
Oracle Cloud Agent older than 0.0.19 (Linux) / 1.0.0.0 (Windows) Update the agent
cloudbase-init on Windows It doesn't support IMDSv2; keep v1 on those instances or remove the dependency
Home-grown bootstrap and inventory scripts Change the URL to /opc/v2/ and add the header
Third-party agents and Marketplace images Check the vendor's notes; many older images don't support v2

Oracle's platform images of Oracle Linux 7/8 (July 2020 onward) and Oracle Autonomous Linux support IMDSv2. Most other platform, custom and Marketplace images need checking.

3. Fix the callers

  • Change scripts to use v2:
# before
curl -s http://169.254.169.254/opc/v1/instance/
# after
curl -s -H "Authorization: Bearer Oracle" -L http://169.254.169.254/opc/v2/instance/
import requests

r = requests.get(
    "http://169.254.169.254/opc/v2/instance/",
    headers={"Authorization": "Bearer Oracle"},
    timeout=5,
)
r.raise_for_status()
print(r.json()["displayName"])
  • Don't send requests through a proxy. IMDS rejects requests that carry Forwarded, X-Forwarded-For or X-Forwarded-Host. Add 169.254.169.254 to no_proxy / NO_PROXY.
  • Cache values you read often and retry with back-off. IMDS throttles requests and has short maintenance gaps. Note that once v1 is off, a 404 on a v1 path is permanent; don't retry it.
  • Update Oracle Cloud Agent and cloud-init on custom images, then rebuild the image.

4. Switch an instance to v2 only

  • Pick a non-production instance first. Confirm the metric shows no v1 calls for at least a full business cycle (including weekly jobs).
  • Disable v1:
oci compute instance update --instance-id ocid1.instance.oc1..xxxx \
  --instance-options '{"areLegacyImdsEndpointsDisabled": true}'

Console: Instance details > Instance metadata service > Edit > Version 2 only > Save changes.

  • Verify from the instance:
curl -s -o /dev/null -w "%{http_code}\n" http://169.254.169.254/opc/v1/instance/      # expect 404
curl -s -o /dev/null -w "%{http_code}\n" -H "Authorization: Bearer Oracle" \
  http://169.254.169.254/opc/v2/instance/                                              # expect 200
  • Check that the agent plugins still report (Oracle Cloud Agent tab, metrics in oci_computeagent), the application starts, and SSH or RDP still works after a reboot.

Caution

Rolling back needs a restart. To re-enable v1, choose Version 1 and version 2 (or set the flag back to false) and then restart the instance. Plan the rollout in waves so a bad wave doesn't mean restarting the whole fleet.

5. Make it the default for new instances

  • Terraform: set it on every oci_core_instance and in instance configurations used by instance pools.
resource "oci_core_instance" "app" {
  # ...
  instance_options {
    are_legacy_imds_endpoints_disabled = true
  }
}
  • CLI launches and scripts:
oci compute instance launch ... \
  --instance-options '{"areLegacyImdsEndpointsDisabled": true}'
  • Instance pools: the setting comes from the instance configuration. Create a new instance configuration with instanceOptions set, attach it to the pool, and let new instances pick it up. Existing pool members keep their old setting until replaced or updated.
  • OKE managed nodes: check your node pool images and the OKE documentation for your Kubernetes version before forcing v2 only; don't assume every node image supports it.

6. Keep it that way

  • Run the inventory query from section 1 on a schedule and report any instance where are-legacy-imds-endpoints-disabled is not true.
  • Alarm on v1 traffic per compartment using InstanceMetadataRequests grouped by metadataVersion, so a new image or agent that falls back to v1 shows up quickly.
  • Check whether your posture tool or Cloud Guard recipe has a rule for legacy metadata endpoints and turn it on; rule names vary, so search the recipe for "metadata".
  • Record exceptions (for example Windows images that rely on cloudbase-init) with an owner and a review date.

Done when

  • No running instance has legacy IMDS endpoints enabled, except documented exceptions.
  • InstanceMetadataRequests shows no v1 calls for a full week.
  • Terraform modules, instance configurations and launch scripts set v2 only by default.
  • The inventory check runs on a schedule and someone owns its output.