Модуль 1.4: Захист метаданих вузлів
Складність:
[СЕРЕДНЯ]— Критична навичка безпеки, специфічна для хмариЧас на виконання: 30-35 хвилин
Передумови: Модуль 1.1 (Network Policies), розуміння хмарних провайдерів
Що ви зможете робити
Розділ «Що ви зможете робити»Після завершення цього модуля ви зможете:
- Створити NetworkPolicies, що блокують доступ Pod до ендпоінтів хмарних метаданих
- Аудитувати робочі навантаження кластера на предмет ризиків відкриття сервісу метаданих
- Реалізувати примусове використання IMDS v2 та обмеження сервісу метаданих у хмарних провайдерів
- Простежити шляхи ескалації привілеїв від облікових даних метаданих до доступу до хмарних ресурсів
Чому цей модуль важливий
Розділ «Чому цей модуль важливий»Сервіси метаданих хмарних провайдерів (наприклад, 169.254.169.254 AWS) відкривають чутливу інформацію: облікові дані IAM, ідентифікатор інстансу та конфігураційні дані. Скомпрометований Pod може запитати цей ендпоінт і потенційно підвищити привілеї або отримати доступ до хмарних ресурсів.
Це улюблений вектор атаки. Порушення Capital One у 2019 році використовувало саме цю вразливість.
Атака через метадані
Розділ «Атака через метадані»┌─────────────────────────────────────────────────────────────┐│ ВЕКТОР АТАКИ ЧЕРЕЗ СЕРВІС МЕТАДАНИХ │├─────────────────────────────────────────────────────────────┤│ ││ ┌─────────────────┐ ││ │ Скомпромето- │ ││ │ ваний │ ││ │ застосунок Pod │ ││ └────────┬────────┘ ││ │ ││ │ curl http://169.254.169.254/latest/meta-data/ ││ │ ││ ▼ ││ ┌─────────────────────────────────────────────────────┐ ││ │ СЕРВІС МЕТАДАНИХ │ ││ │ │ ││ │ Повертає: │ ││ │ • ID інстансу │ ││ │ • Приватний IP │ ││ │ • Облікові дані ролі IAM │ ││ │ • User data (може містити секрети!) │ ││ │ • Конфігурація VPC │ ││ │ │ ││ └─────────────────────────────────────────────────────┘ ││ ││ Наслідки: ││ ⚠️ Зловмисник отримує тимчасові облікові дані AWS ││ ⚠️ Може отримати доступ до S3 бакетів, баз даних тощо ││ ⚠️ Латеральний рух через хмарні ресурси ││ │└─────────────────────────────────────────────────────────────┘Ендпоінти метаданих за провайдером
Розділ «Ендпоінти метаданих за провайдером»| Хмарний провайдер | Ендпоінт метаданих | Шлях до облікових даних |
|---|---|---|
| AWS | 169.254.169.254 | /latest/meta-data/iam/security-credentials/ |
| GCP | 169.254.169.254 | /computeMetadata/v1/ |
| Azure | 169.254.169.254 | /metadata/identity/oauth2/token |
| DigitalOcean | 169.254.169.254 | /metadata/v1/ |
Усі використовують одну IP-адресу: 169.254.169.254 (link-local адреса)
Метод захисту 1: NetworkPolicy
Розділ «Метод захисту 1: NetworkPolicy»Блокування вихідного трафіку до IP метаданих за допомогою NetworkPolicy:
# Block access to metadata serviceapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: block-metadata namespace: productionspec: podSelector: {} # All pods in namespace policyTypes: - Egress egress: # Allow all EXCEPT metadata - to: - ipBlock: cidr: 0.0.0.0/0 except: - 169.254.169.254/32Дозвіл DNS з блокуванням метаданих
Розділ «Дозвіл DNS з блокуванням метаданих»# More complete: block metadata but allow DNSapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: deny-metadata-allow-dns namespace: productionspec: podSelector: {} policyTypes: - Egress egress: # Allow DNS - to: - namespaceSelector: {} podSelector: matchLabels: k8s-app: kube-dns ports: - port: 53 protocol: UDP - port: 53 protocol: TCP # Allow all other traffic except metadata - to: - ipBlock: cidr: 0.0.0.0/0 except: - 169.254.169.254/32Метод захисту 2: iptables на вузлах
Розділ «Метод захисту 2: iptables на вузлах»Налаштування правил iptables на кожному вузлі для блокування доступу до метаданих:
# Block metadata access from pods (run on each node)iptables -A OUTPUT -d 169.254.169.254 -j DROP
# Or more specifically, block from pod networkiptables -I FORWARD -s 10.244.0.0/16 -d 169.254.169.254 -j DROP
# Make persistent (varies by OS)iptables-save > /etc/iptables/rules.v4DaemonSet для правил iptables
Розділ «DaemonSet для правил iptables»apiVersion: apps/v1kind: DaemonSetmetadata: name: metadata-blocker namespace: kube-systemspec: selector: matchLabels: app: metadata-blocker template: metadata: labels: app: metadata-blocker spec: hostNetwork: true hostPID: true containers: - name: blocker image: alpine command: - /bin/sh - -c - | apk add iptables iptables -C FORWARD -d 169.254.169.254 -j DROP 2>/dev/null || \ iptables -I FORWARD -d 169.254.169.254 -j DROP sleep infinity securityContext: privileged: true capabilities: add: ["NET_ADMIN"] tolerations: - operator: "Exists"Метод захисту 3: Функції хмарного провайдера
Розділ «Метод захисту 3: Функції хмарного провайдера»AWS IMDSv2 (Рекомендований)
Розділ «AWS IMDSv2 (Рекомендований)»AWS Instance Metadata Service v2 вимагає сесійний токен, що ускладнює прямий доступ з Pod:
# IMDSv2 requires PUT request first to get tokenTOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" \ -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
# Then use token in subsequent requestscurl -H "X-aws-ec2-metadata-token: $TOKEN" \ http://169.254.169.254/latest/meta-data/Налаштування вузлів для обов’язкового IMDSv2:
# AWS CLI to enforce IMDSv2 on instanceaws ec2 modify-instance-metadata-options \ --instance-id i-1234567890abcdef0 \ --http-tokens required \ --http-put-response-hop-limit 1Приховування метаданих GCP
Розділ «Приховування метаданих GCP»# Enable metadata concealment on GKE node poolgcloud container node-pools update POOL_NAME \ --cluster=CLUSTER_NAME \ --workload-metadata=GKE_METADATAAzure Instance Metadata Service (IMDS)
Розділ «Azure Instance Metadata Service (IMDS)»Azure вимагає спеціальні заголовки:
# Azure IMDS requires Metadata headercurl -H "Metadata:true" \ "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01"Тестування доступу до метаданих
Розділ «Тестування доступу до метаданих»Перевірка, що Pod не може отримати доступ до метаданих
Розділ «Перевірка, що Pod не може отримати доступ до метаданих»# Create test podkubectl run test-pod --image=curlimages/curl --rm -it --restart=Never -- \ curl -s --connect-timeout 2 http://169.254.169.254/latest/meta-data/
# Expected: Connection timeout or refused# If you see instance metadata, protection isn't working!Перевірка застосування NetworkPolicy
Розділ «Перевірка застосування NetworkPolicy»# List network policieskubectl get networkpolicies -n production
# Describe specific policykubectl describe networkpolicy block-metadata -n production
# Check if pod is selected by policykubectl get pod test-pod -n production --show-labelsПовний приклад захисту
Розділ «Повний приклад захисту»# Apply to every namespace that runs workloads---apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: default-deny-metadata namespace: defaultspec: podSelector: {} policyTypes: - Egress egress: # Allow DNS resolution - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: kube-system ports: - port: 53 protocol: UDP - port: 53 protocol: TCP # Allow cluster internal communication - to: - ipBlock: cidr: 10.0.0.0/8 # Allow external but block metadata - to: - ipBlock: cidr: 0.0.0.0/0 except: - 169.254.0.0/16 # Block entire link-local rangeРеальні сценарії іспиту
Розділ «Реальні сценарії іспиту»Сценарій 1: Блокування доступу до метаданих для простору імен
Розділ «Сценарій 1: Блокування доступу до метаданих для простору імен»# Create NetworkPolicy to block metadatacat <<EOF | kubectl apply -f -apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: block-cloud-metadata namespace: productionspec: podSelector: {} policyTypes: - Egress egress: - to: - ipBlock: cidr: 0.0.0.0/0 except: - 169.254.169.254/32EOF
# Verifykubectl get networkpolicy block-cloud-metadata -n productionСценарій 2: Тестування та перевірка блокування
Розділ «Сценарій 2: Тестування та перевірка блокування»# Create test podkubectl run metadata-test --image=curlimages/curl -n production --rm -it --restart=Never -- \ curl -s --connect-timeout 3 http://169.254.169.254/latest/meta-data/ || echo "BLOCKED (expected)"Сценарій 3: Дозвіл доступу для конкретного Pod
Розділ «Сценарій 3: Дозвіл доступу для конкретного Pod»# Most pods blocked, but monitoring pod needs metadataapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: allow-monitoring-metadata namespace: monitoringspec: podSelector: matchLabels: app: cloud-monitor policyTypes: - Egress egress: - to: - ipBlock: cidr: 0.0.0.0/0 # All traffic including metadataЕшелонована оборона
Розділ «Ешелонована оборона»┌─────────────────────────────────────────────────────────────┐│ РІВНІ ЗАХИСТУ МЕТАДАНИХ │├─────────────────────────────────────────────────────────────┤│ ││ Рівень 1: NetworkPolicy ││ └── Блокування вихідного трафіку до 169.254.169.254 ││ ││ Рівень 2: IMDSv2 хмарного провайдера ││ └── Обов'язкові сесійні токени ││ ││ Рівень 3: iptables рівня вузла ││ └── Блокування на мережевому рівні ││ ││ Рівень 4: Безпека Pod ││ └── Обмеження мережі хоста ││ ││ Рівень 5: Мінімальний IAM ││ └── Ролі вузлів з найменшими привілеями ││ ││ Найкраща практика: Використовуйте ДЕКІЛЬКА рівнів ││ │└─────────────────────────────────────────────────────────────┘Чи знали ви?
Розділ «Чи знали ви?»-
Порушення Capital One у 2019 році розкрило 100 мільйонів записів клієнтів через SSRF до сервісу метаданих. Зловмисник отримав облікові дані IAM та отримав доступ до S3 бакетів.
-
169.254.0.0/16 — це link-local. Ця підмережа зарезервована для локального мережевого зв’язку та ніколи не маршрутизується в інтернеті. Хмарні провайдери використовують її для метаданих, бо вона доступна з будь-якого інстансу без маршрутизації.
-
Kubernetes сам використовує метадані на хмарних провайдерах для інформації про вузли. Блокування системних компонентів від метаданих може порушити функціональність кластера.
-
AWS IMDSv2 з hop limit 1 запобігає контейнерам дістатися до метаданих, оскільки запит проходить через кілька мережевих стрибків (контейнер → вузол → сервіс метаданих).
Поширені помилки
Розділ «Поширені помилки»| Помилка | Чому це шкодить | Рішення |
|---|---|---|
| Забути DNS з політикою egress | Pod не можуть вирішувати імена | Завжди дозволяйте DNS egress |
| Блокування метаданих для kube-system | Ламає хмарні інтеграції | Обережно виключайте системні простори імен |
| Використання лише NetworkPolicy | Не всі CNI застосовують її | Використовуйте декілька рівнів захисту |
| Тестування з неправильного простору імен | Політика там не застосована | Тестуйте з простору імен з політикою |
| Блокування усього діапазону link-local | Може зламати інші сервіси | Почніть лише з 169.254.169.254/32 |
Тест
Розділ «Тест»-
Яку IP-адресу використовують хмарні сервіси метаданих?
Відповідь
169.254.169.254 — це link-local адреса, яку використовують AWS, GCP, Azure та інші хмарні провайдери для своїх сервісів метаданих інстансів. -
Чому блокування доступу до метаданих важливе для безпеки?
Відповідь
Сервіс метаданих відкриває чутливу інформацію, включаючи облікові дані IAM. Скомпрометований Pod може запитати цей ендпоінт для отримання облікових даних та доступу до хмарних ресурсів, уможливлюючи латеральний рух та підвищення привілеїв. -
Який ресурс Kubernetes використовується для блокування вихідного трафіку до IP метаданих?
Відповідь
NetworkPolicy з правилами egress, що використовує ipBlock з except для 169.254.169.254/32. -
Що таке AWS IMDSv2 і чому він допомагає?
Відповідь
Instance Metadata Service версії 2 вимагає сесійний токен, отриманий через PUT-запит, перед доступом до метаданих. З hop limit 1 контейнери не можуть отримати токени, оскільки їхні запити проходять через кілька стрибків.
Практична вправа
Розділ «Практична вправа»Завдання: Заблокуйте доступ до метаданих та перевірте захист.
# Setup namespacekubectl create namespace metadata-test
# Step 1: Verify metadata is accessible (before protection)kubectl run check-before --image=curlimages/curl -n metadata-test --rm -it --restart=Never -- \ curl -s --connect-timeout 3 http://169.254.169.254/ && echo "ACCESSIBLE" || echo "BLOCKED"
# Note: In non-cloud environments, you'll see "BLOCKED" already
# Step 2: Apply metadata blocking NetworkPolicycat <<EOF | kubectl apply -f -apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: block-metadata namespace: metadata-testspec: podSelector: {} policyTypes: - Egress egress: # Allow DNS - to: [] ports: - port: 53 protocol: UDP # Allow all except metadata - to: - ipBlock: cidr: 0.0.0.0/0 except: - 169.254.169.254/32EOF
# Step 3: Verify policy existskubectl get networkpolicy -n metadata-testkubectl describe networkpolicy block-metadata -n metadata-test
# Step 4: Test metadata is blockedkubectl run check-after --image=curlimages/curl -n metadata-test --rm -it --restart=Never -- \ curl -s --connect-timeout 3 http://169.254.169.254/ && echo "ACCESSIBLE" || echo "BLOCKED"
# Step 5: Verify other egress still workskubectl run check-external --image=curlimages/curl -n metadata-test --rm -it --restart=Never -- \ curl -s --connect-timeout 3 https://kubernetes.io -o /dev/null -w "%{http_code}" && echo " OK"
# Cleanupkubectl delete namespace metadata-testКритерії успіху: IP метаданих заблоковано, але зовнішній доступ працює.
Підсумок
Розділ «Підсумок»Ризик сервісу метаданих:
- Відкриває облікові дані IAM та дані інстансу
- Доступний з будь-якого Pod за замовчуванням
- Основний вектор атаки (порушення Capital One)
Методи захисту:
- NetworkPolicy, що блокує 169.254.169.254
- Примусове використання IMDSv2 хмарного провайдера
- Правила iptables на рівні вузла
- Безпека Pod (без hostNetwork)
Найкращі практики:
- Застосуйте захист до всіх просторів імен з робочими навантаженнями
- Пам’ятайте дозволити DNS egress
- Використовуйте декілька рівнів захисту
- Перевірте ефективність блокування
Поради для іспиту:
- Знайте, як написати NetworkPolicy з пам’яті
- Розумійте синтаксис ipBlock з except
- Пам’ятайте, що DNS — це UDP порт 53
Наступний модуль
Розділ «Наступний модуль»Модуль 1.5: Безпека GUI — Захист Kubernetes Dashboard та веб-інтерфейсів.