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
userAgentandpathto 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-FororX-Forwarded-Host. Add169.254.169.254tono_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
404on 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_instanceand 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
instanceOptionsset, 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-disabledis nottrue. - Alarm on v1 traffic per compartment using
InstanceMetadataRequestsgrouped bymetadataVersion, 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.
-
InstanceMetadataRequestsshows 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.