Instance metadata (IMDS – Instance Metadata Service) – дані про EC2 інстанс, такі як інформація про AMI, IP, ім’я хосту, і т.д.
Також до Instance Metadata можна додати User Data для зберігання якихось параметрів, які потім можна буде отримати всередині інстансу.
Див. Instance metadata and user data та Instance metadata categories.
Від початку, в AWS була реалізована request/response модель доступу до IMDS, тобто для отримання доступу достатньо було зробити HTTP-запит з хосту. Пізніше, була реалізована система session-oriented, коли для доступу вже треба було отримати токен, і ця система отримала індекс v2. Див. Add defense in depth against open firewalls, reverse proxies, and SSRF vulnerabilities with enhancements to the EC2 Instance Metadata Service та Use IMDSv2.
Зміст
Приклад роботи з IMDSv1
Наприклад, запустимо EC2 та в User Data додамо якесь значення:
Та пробуємо curl
на адресу 169.254.169.254:
[simterm]
$ curl http://169.254.169.254/latest/user-data somedata: somevalue
[/simterm]
Або отримаємо саме метадату інстансу:
[simterm]
$ curl http://169.254.169.254/latest/meta-data ami-id ami-launch-index ami-manifest-path block-device-mapping/ events/ hostname identity-credentials/ instance-action instance-id instance-life-cycle instance-type local-hostname local-ipv4 mac metrics/ network/ placement/ profile public-hostname public-ipv4 public-keys/ reservation-id security-groups services/
[/simterm]
Або теж саме з Docker-контейнеру:
[simterm]
root@ip-172-31-30-97:/home/ubuntu# docker run -ti alpine/curl curl http://169.254.169.254/latest/meta-data/ Unable to find image 'alpine/curl:latest' locally latest: Pulling from alpine/curl 59bf1c3509f3: Pull complete da353f38084f: Pull complete 05df90dbd213: Pull complete Digest: sha256:81372de8c566f2d731bde924bed45230018e6d7c21d051c15e283eb8e06dfa2d Status: Downloaded newer image for alpine/curl:latest ami-id ami-launch-index ami-manifest-path block-device-mapping/ events/ hostname ...
[/simterm]
Доступ до IMDS із Kubernetes Pod
Тобто, і у випадку Kubernetes будь-який под має змогу отримати ці дані.
Для перевірки створимо под:
apiVersion: v1 kind: Pod metadata: name: test-imds namespace: default spec: containers: - name: test-imds image: alpine/curl:latest command: - sleep - "3600" imagePullPolicy: IfNotPresent restartPolicy: Always
Запускаємо його:
[simterm]
$ kk apply -f pod-imds.yaml
[/simterm]
І з нього виконуємо той самий запит з curl
:
[simterm]
$ kk exec -ti test-imds -- curl http://169.254.169.254/latest/meta-data/ ami-id ami-launch-index ami-manifest-path autoscaling/ block-device-mapping/ events/ hostname iam/ ...
[/simterm]
IMDS Security Credentials
Крім інших даних, IMDS може повернути Access/Secret ключі та токен, які використовуються для отримання доступу до Instance IAM Role.
Отримаємо security credentials:
[simterm]
$ curl http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance { "Code" : "Success", "LastUpdated" : "2023-03-28T12:48:33Z", "Type" : "AWS-HMAC", "AccessKeyId" : "ASI***BND", "SecretAccessKey" : "4Yz***8Bx", "Token" : "IQo***GaQ=", "Expiration" : "2023-03-28T18:51:13Z" }
[/simterm]
А використовуючи ці Access/Secret ключі, ми можемо робити все, що дозволено інстансу, і, наприклад, якщо до інстансу підключено ІAМ роль з AdminAccess – ми зможемо отримати ці права.
Перевіримо – додамо до інстансу роль з доступом до S3-бакетів:
Перевіряємо роль в метаданих:
[simterm]
root@ip-172-31-30-97:/home/ubuntu# curl http://169.254.169.254/latest/meta-data/iam/info { "Code" : "Success", "LastUpdated" : "2023-03-28T13:34:51Z", "InstanceProfileArn" : "arn:aws:iam::514***799:instance-profile/IMDSTestS3ReadOnlyAccess", "InstanceProfileId" : "AIPAXPNJUS3H7XEB7UT24" }
[/simterm]
Та отримаємо ключі та токен:
[simterm]
root@ip-172-31-30-97:/home/ubuntu# curl http://169.254.169.254/latest/meta-data/iam/security-credentials/IMDSTestS3ReadOnlyAccess { "Code" : "Success", "LastUpdated" : "2023-03-28T13:35:16Z", "Type" : "AWS-HMAC", "AccessKeyId" : "ASI***3PJ", "SecretAccessKey" : "IfC***t2n", "Token" : "IQo***o4a", "Expiration" : "2023-03-28T20:09:51Z" }
[/simterm]
Додаємо їх собі на робочу машину в ~/.aws/credentials
:
[testiam] aws_access_key_id = ASI***3PJ aws_secret_access_key = IfC***t2n aws_session_token = IQo***o4a
Та створимо профіль у ~/.aws/config
:
[profile testiam] region = eu-central-1 output = json
І отримаємо доступ:
[simterm]
$ aws --profile testiam s3 ls 2022-12-19 16:43:06 cronjob-test 2023-02-14 15:02:28 gitlab-s3-cache-test ...
[/simterm]
Жах! :scream:
Права доступу до IMDS
Щоб запобігти такому, можна просто відключити IMDS взагалі, або використовувати IMDS v2.
Відключення IMDS
Для ЕС2 робиться через AWS CLI:
[simterm]
$ aws --profile internal ec2 modify-instance-metadata-options --instance-id i-0b0c0e351255ba78c --http-endpoint disabled { "InstanceId": "i-0b0c0e351255ba78c", "InstanceMetadataOptions": { "State": "pending", "HttpTokens": "optional", "HttpPutResponseHopLimit": 1, "HttpEndpoint": "disabled", "HttpProtocolIpv6": "disabled", "InstanceMetadataTags": "disabled" } }
[/simterm]
Або через AWS Console – EC2 > Instance Settings > Modify instance metadata options:
І тепер при запиті до meta-data маємо помилку 403 – Forbidden:
[simterm]
root@ip-172-31-30-97:/home/ubuntu# curl http://169.254.169.254/latest/meta-data/iam/info <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>403 - Forbidden</title> ...
[/simterm]
Переключення на IMDS v2
Якщо доступ все ж треба, то можемо відключити IMDS v1 та використовувати тільки IMDS v2 додавши обов’язкове використання токена за допомогою параметру --http-tokens
:
[simterm]
$ aws --profile internal ec2 modify-instance-metadata-options --instance-id i-0b0c0e351255ba78c --http-endpoint enabled --http-tokens required { "InstanceId": "i-0b0c0e351255ba78c", "InstanceMetadataOptions": { "State": "pending", "HttpTokens": "required", "HttpPutResponseHopLimit": 1, "HttpEndpoint": "enabled", "HttpProtocolIpv6": "disabled", "InstanceMetadataTags": "disabled" } }
[/simterm]
Тепер при запиті маємо 401 – Unauthorized:
[simterm]
root@ip-172-31-30-97:/home/ubuntu# curl http://169.254.169.254/latest/meta-data/iam/info <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>401 - Unauthorized</title> ...
[/simterm]
Але якщо додамо токен – то все працюватиме.
Отримаємо токен:
[simterm]
root@ip-172-31-30-97:/home/ubuntu# TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
[/simterm]
Глянемо його:
[simterm]
root@ip-172-31-30-97:/home/ubuntu# echo $TOKEN AQA***Vyg==
[/simterm]
І тепер знову curl
з хедером X-aws-ec2-metadata-token
:
[simterm]
root@ip-172-31-30-97:/home/ubuntu# curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/info { "Code" : "Success", "LastUpdated" : "2023-03-28T13:34:51Z", "InstanceProfileArn" : "arn:aws:iam::514***799:instance-profile/IMDSTestS3ReadOnlyAccess", "InstanceProfileId" : "AIPAXPNJUS3H7XEB7UT24" }
[/simterm]
При використані Terraform модулів для створення Node Groups, звертайте увагу на опції. Наприклад, у cloudposse/terraform-aws-eks-node-group по дефолту включена IMDSv2, див. Behavior changes.
IMDS v2 та Docker
У випадку, коли використовуються контейнери, з включеним IMDSv2 можуть бути проблеми при отримані токену, наприклад:
[simterm]
root@64cbbd918977:/# curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" root@64cbbd918977:/# echo $? 56
[/simterm]
Щоб запобігти цьому, додаємо параметр http-put-response-hop-limit
зі значенням більше 1, так як виклик з контейнеру додає ще один хоп при проходженні запросу від клієнта до IMDS: перший, це запит з самого хосту, а другий – із контейнера на цьому хості:
[simterm]
$ aws --profile internal ec2 modify-instance-metadata-options --http-endpoint enabled --http-tokens required --http-put-response-hop-limit 2 --instance-id i-0b0c0e351255ba78c { "InstanceId": "i-0b0c0e351255ba78c", "InstanceMetadataOptions": { "State": "pending", "HttpTokens": "required", "HttpPutResponseHopLimit": 2, "HttpEndpoint": "enabled", ...
[/simterm]
І пробуємо зараз:
[simterm]
root@64cbbd918977:/# TOKEN=`curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` root@64cbbd918977:/# curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/info { "Code" : "Success", "LastUpdated" : "2023-04-10T10:46:45Z", "InstanceProfileArn" : "arn:aws:iam::514***799:instance-profile/IMDSTestS3ReadOnlyAccess", "InstanceProfileId" : "AIP***T24" }
[/simterm]
Готово.