1. Introduction

Specific blocks of IP addresses have a designated purpose as listed under the IANA special-purpose address registry. Any networking infrastructure needs to adhere to these standards. Hence, cloud services use an appropriate block according to their use case. 169.254.169.254 is one such IP address accessible in cloud machines.

In this tutorial, we’ll study the meaning of 169.254.169.254 within a virtual computer on the cloud. Firstly, we’ll examine the address block that this IP address belongs to. Then, we’ll explore how cloud service providers use it. Lastly, we’ll review related security issues and steps to defend our cloud machines against them.

All scripts in this tutorial were run on a Debian 11 (Bullseye) OS and Bash shell.

A link-local IP address represents a host accessible within the same physical or logical network link.

This means that packets exchanged with such a host contain an unmodified IP header and payload. As per RFC 6890, IETF has reserved the block from 169.254.0.0 to 169.254.255.255 (both inclusive) for this purpose. The CIDR notation 169.254.0.0/16 denotes this block. Amongst these addresses, IETF has reserved the first and last 256 addresses for future use. Thus, there are 65024 usable link-local IP addresses.

This block exhibits certain features as described in RFC 3927:

  • This block is for auto-configuration of an interface, such as when neither a DHCP server nor a manually configured IP address is available.
  • Networking infrastructure mustn’t create subnets within this block.
  • Any packet containing a link-local IP address as its destination isn’t forwardable.
  • Once an interface acquires a routable IP address, i.e., through DHCP or other means, then it’ll use that for future connections.
  • DNS mustn’t store link-local IP addresses.

A host uses a pseudo-random number generator to select an IP address within this block for an interface. Then, it undergoes a process to claim this address as its own.

ARP is used in the claiming process. This process is also called Automatic Private IP Address (APIPA) configuration.

A host follows these steps:

  1. The host broadcasts an ARP probe request. This contains the source MAC address. The selected IP address is used as the target IP. Source IP and target hardware address fields contain zeroes.
  2. The host repeats step 1 up to a fixed number of probes. It waits for a random time interval between two consecutive requests.
  3. Once the host has sent the last probe request, it waits for a short period.
  4. Until the host is done waiting, if it receives an ARP probe request with the same chosen target IP or an ARP reply at any time during this claiming process, it’ll assume there’s a conflict. Then, it’ll choose a new IP address using the pseudo-random number generator and start afresh from step 1. However, if it doesn’t receive any such ARP request or reply after waiting, it’ll send an ARP announcement and successfully configure its interface.

Now, the host can start using that interface for new connections.

2.2. Presence in IP Routing Table

A routing table comprises a list of entries for communicating with other devices within the same network. Thus, the presence of 169.254.169.254 in the routing table indicates that a link-local device is connected.

Let’s check the IP routing table of a cloud instance on Azure:

$ ip route
default via 10.2.0.1 dev eth0
10.2.0.0/24 dev eth0 proto kernel scope link src 10.2.0.4
168.63.129.16 via 10.2.0.1 dev eth0
169.254.169.254 via 10.2.0.1 dev eth0

In this output, we can see that 169.254.169.254 is accessible over the interface eth0.

2.3. Significance in IPv6

So far, we’ve talked about link-local IPv4 addresses. However, the corresponding link-local IPv6 block, denoted by fe80::0/10, plays a prominent role in IPv6.

Every interface configured to use IPv6 must possess a link-local IPv6 address. This address is used for scenarios like IPv6 address auto-configuration and neighbor discovery as defined in RFC 4862.

Cloud service providers control physical infrastructure on the cloud. Thus, they can configure devices having local access to any instance.

Link-local IP addresses are suitable for auto-configuration services due to many reasons:

  • This is the only block of special non-forwardable IP addresses that works both as the source and the destination.
  • There’s no danger of any malicious device connecting to the same local network segment, as the cloud servers are guarded within data centers.
  • Cloud users can’t subnet this space.

Due to these reasons, services required for virtual computers on the cloud, such as Network Time Protocol (NTP) and Instance Metadata Service (IMDS), use link-local IP addresses.

4. Cloud Instance Metadata Service (IMDS)

There may arise a need to access the metadata of virtual computers running on the cloud programmatically. For instance, this could be metadata like its network configuration, availability zone, device type, and other information. This information is available through Instance Metadata Service (IMDS).

IMDS runs outside the cloud instance due to these reasons:

  • consumption of some system resources, such as disk storage, CPU cycles, or RAM
  • resilience to remain unaffected by a system shutdown, system crash, or other instance issues
  • reflecting latest changes on instance modification
  • flexibility in external configuration, such as deploying the same virtual machine image but with a different configuration

IMDS is accessible via 169.254.169.254 in most cloud platforms. We can make HTTP requests from within the instance to access IMDS.

4.1. IMDS on Amazon Web Services (AWS)

AWS offers Elastic Cloud Compute (EC2) as a virtual computer on the cloud. When creating an EC2 instance, we can configure metadata settings under the “Advanced details” section.

AWS IMDS comes in two versions: IMDSv1 and IMDSv2. The latter is more secure than the former. By default, both are enabled for EC2. We can access them via 169.254.169.254.

AWS provides a complete list of instance metadata categories. For now, let’s make an HTTP request to the root endpoint. We’ll specify the API version in the request path. We can access the latest API version using the value “latest”. Let’s use curl for making this request:

$ curl -s "http://169.254.169.254/latest/meta-data"
ami-id
...
public-hostname
public-ipv4
public-keys/
...
system

The output is in text format, analogous to directory listing. Let’s extract the public IPv4 address by providing the respective path:

$ curl -s "http://169.254.169.254/latest/meta-data/public-ipv4"
54.57.132.214

Thus, we obtained the public IPv4 address of the EC2 instance using AWS IMDSv1. Let’s try the same using IMDSv2:

$ TOKEN=`curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 3600"` && \
  curl -s -H "X-aws-ec2-metadata-token: $TOKEN" "http://169.254.169.254/latest/meta-data/public-ipv4"
54.57.132.214

Here, we first obtained a token for invoking the API. Then, we used that token in the subsequent request.

AWS recommends enabling only IMDSv2 to enhance security.

4.2. IMDS on Azure

Virtual Machine (VM) is a cloud instance service on Microsoft Azure. VMs and scale set instances support Azure IMDS. Apart from instance metadata, this IMDS allows fetching metadata for scheduled events, load balancers, and other details related to the instance.

Azure IMDS requires an API version as a query parameter. It also needs a header Metadata: true. Furthermore, we’ll use the –noproxy option because IMDS doesn’t support proxied requests. Lastly, we’ll use jq to format the JSON output. Let’s make a query to the root endpoint:

$ curl -s -H "Metadata:true" --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2021-02-01" | jq
{
  "compute": {
    ...
  },
  "network": {
    "interface": [
      {
        "ipv4": {
          "ipAddress": [
            {
              "privateIpAddress": "10.2.0.4",
              "publicIpAddress": "20.185.37.134"
            }
          ],
          ...
        },
        ...
      }
    ]
  }
}

Above, we highlight the IPv4 metadata within a JSON. Alternately, we can obtain an output similar to AWS using a query parameter format=text:

$ curl -s -H "Metadata:true" --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2021-02-01&format=text"
compute/
network/

Here, we can see the root-level keys from the previous JSON output.

Let’s extract the public IPv4 address using its path:

$ curl -s -H "Metadata:true" --noproxy "*" "http://169.254.169.254/metadata/instance/network/interface/0/ipv4/ipAddress/0/publicIpAddress?api-version=2021-02-01&format=text"
20.185.37.134

Thus, we used the JSON path to obtain the public IPv4 address. This command works for instances associated with a basic SKU public IP address. On the other hand, it doesn’t work for the ones associated with a standard SKU. For such VMs, we can use the load balancer metadata instead:

$ curl -s -H "Metadata:true" --noproxy "*" "http://169.254.169.254:80/metadata/loadbalancer?api-version=2020-10-01" | jq '.loadbalancer.publicIpAddresses[0].frontendIpAddress' -j
20.185.37.134

We accessed the value using jq by specifying the path to the key. Here, the -j option returns a raw string without a new line.

4.3. IMDS on Google Cloud Platform (GCP)

Compute Engine is a virtual computer service on GCP. Although the metadata IP address is still 169.254.169.254, we can access it using the host metadata.google.internal. Additionally, GCP IMDS allows access to the project metadata to which the instance belongs.

GCP IMDS requires a header Metadata-Flavor: Google. Let’s try the root endpoint:

$ curl -s -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/" 
instance/
oslogin/
project/

The above output is similar to the one on AWS. Let’s extract the public IPv4 address by specifying the path in kebab case:

$ curl -s -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip" 
34.171.124.2

Thus, we fetched the public IPv4 address using GCP IMDS.

4.4. IMDS on Other Clouds

IBM Cloud offers IMDS as part of its VPC Virtual Server solution. IMDS is off by default. Moreover, it requires an access token.

Oracle Cloud also offers IMDS as part of its Compute instance. They recommend upgrading to IMDSv2 and turning off IMDSv1.

5. Security Issues

IMDS is accessible from within a cloud instance via HTTP requests. Thus, any service or application running on that instance or logged-in user can access IMDS.

Moreover, IMDS access requires no additional authentication on most of these clouds. However, metadata may contain sensitive information like secret keys or tokens. Threat actors can steal this information using attacks like Server-Side Request Forgery (SSRF).

When a server makes HTTP requests based on user input without proper validation, SSRF vulnerability arises. An external user could manipulate the running server to make requests to IMDS. If the IMDS response body becomes available to the user, sensitive information may get exposed.

6. Defensive Measures

The first line of defense is to ensure that services or applications inside the cloud instance aren’t running with root privileges. Then, we’ll allow only the root user of the cloud instance to access IMDS. Let’s create an iptables firewall rule to enforce this restriction:

$ sudo iptables --append OUTPUT --proto tcp --destination 169.254.169.254 --match owner ! --uid-owner root --jump REJECT

Hereafter, any non-root service or application running within the instance can’t access IMDS. The same goes for any user logged in to the device without root privileges.

There are a few additional defensive measures we can take:

  • allocating minimal permissions to the cloud instance itself
  • upgrading to the latest version of IMDS, such as migrating from IMDSv1 to IMDSv2 in AWS
  • avoiding storage of sensitive information in metadata
  • disabling IMDS when not in use

By applying such defensive measures, we can secure our cloud services from security issues posed by IMDS.

7. Conclusion

In this article, we studied the meaning of 169.254.169.254 on the cloud. Initially, we learned about the link-local IP address block 169.254.0.0/16 and its importance in IP address auto-configuration.

Then, we explored how multiple cloud service providers use 169.254.169.254 as IMDS for programmatic access to metadata within cloud instances.

Lastly, we examined security issues posed by IMDS and the best practices to defend against them by restricting access to specific users.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.