Kloak: перехват TLS в Kubernetes через eBPF без сайдкаров

Kloak прозрачно перехватывает исходящий TLS-трафик в Kubernetes с помощью eBPF uprobes, заменяя хэшированные заглушки на реальные секреты на уровне ядра — ещё до шифрования. Приложения никогда не видят настоящих учётных данных, и никаких сайдкаров или изменений кода не требуется.

Возможности

  • Без изменений кода — никаких SDK, библиотек или правок приложения. Просто смонтируйте секрет, выполняйте HTTPS-запросы — всё остальное Kloak берёт на себя.

  • Изоляция секретов — приложения видят только хэшированные теневые значения (kl::<UUID>). Реальные секреты хранятся исключительно в eBPF-картах и подставляются в ядре в момент TLS-записи.

  • Нулевые накладные расходы — eBPF uprobes работают в пространстве ядра с минимальным влиянием на задержки. Никакого прокси в пространстве пользователя и никаких сайдкаров на пути данных.

  • Нативная интеграция с Kubernetes — работает со стандартными Kubernetes Secrets. Достаточно добавить одну метку (label).

  • Фильтрация по хостам и IP — секреты с аннотацией getkloak.io/hosts отправляются только на указанные хосты или IP-адреса, что исключает утечку на неавторизованные серверы.

  • Фильтрация по порту — секреты с аннотацией getkloak.io/port ограничены соединениями на конкретный порт назначения.

  • Широкая поддержка сред выполнения — подключается к OpenSSL, BoringSSL и встроенному пакету Go crypto/tls. Работает с Python, Node.js, Go, Rust, Ruby, PHP, curl и любой средой выполнения, слинкованной с OpenSSL.

Быстрый старт

Предварительные требования

  • Кластер Kubernetes (1.28+) с ядром Linux 5.17+

  • Helm 3.12+

  • kubectl, настроенный для доступа к кластеру

Установка с помощью Helm

helm repo add kloak https://chart.getkloak.io
helm repo update

helm install kloak kloak/kloak -n kloak-system --create-namespace

kubectl get pods -n kloak-system

Метки и аннотации

Ключ Тип Область применения Описание

getkloak.io/enabled=true

Метка

Secret, Namespace, Pod

Включает Kloak для целевого ресурса. На неймспейсах и подах управляет областью вебхука. На секретах запускает создание теневого секрета.

getkloak.io/hosts=host

Аннотация

Secret

Ограничивает, на какой хост или IP-адрес может быть отправлен секрет (только одно значение)

getkloak.io/port=443

Аннотация

Secret

Ограничивает, на какой порт назначения может быть отправлен секрет

getkloak.io/managed=true

Метка

Secret

Помечает теневые секреты, созданные Kloak (не устанавливайте вручную)

Демо

# Полное демо: создаёт Lima VM с K3s, разворачивает Kloak и примеры приложений
./examples/setup-demo.sh

export KUBECONFIG=/tmp/kloak-k3s.yaml

# Просмотр логов (должны отображаться реальные секреты в ответах httpbin.org)
kubectl logs -f -l app=demo-python -n kloak-demo -c demo-app

# Удаление
./examples/destroy-demo.sh

Архитектура

Kloak состоит из плоскости управления (контроллер и вебхук) и плоскости данных на основе eBPF, полностью работающей в пространстве ядра.

Компоненты

Компонент Описание

Контроллер (DaemonSet)

Отслеживает Secrets с меткой getkloak.io/enabled=true, создаёт теневые секреты с заглушками kl::<UUID>, совпадающими по длине с исходными значениями, синхронизирует реальные значения в eBPF-карты и подключает TLS uprobes к процессам контейнеров через обнаружение cgroup.

Вебхук (Deployment)

Мутирующий вебхук (mutating admission webhook), перехватывающий создание подов. Переписывает монтирование томов (volume mounts) с секретами, направляя их на теневые секреты. Оценивает включение через метки пода или неймспейса. Отклоняет поды, если теневой секрет ещё не создан (fail-closed). Два обработчика вебхука гарантируют, что затрагиваются только неймспейсы и поды с поддержкой Kloak; остальные рабочие нагрузки не затрагиваются никогда, даже когда вебхук недоступен.

TLS Uprobes

Подключаются к SSL_write / SSL_write_ex (OpenSSL/BoringSSL) и crypto/tls.(*Conn).Write (Go). Перехватывают исходящие TLS-записи, сканируют на наличие префиксов kl::. Два пути перезаписи: фаза 2 — перезапись открытого текста (до шифрования) и XOR-путь — патч шифртекста (после шифрования).

XOR-путь + TC Egress

Для нативного TLS Go: вычисляет XOR-разность в uprobe, передаёт через kprobe tcp_sendmsg в TC egress, который патчит зашифрованный пакет на лету и пересчитывает тег аутентификации GHASH через tail call в tc_ghash_update.

DNS Kprobe

Kprobe/kretprobe на udp_recvmsg перехватывает DNS-ответы на уровне узла. Разбирает A/AAAA-записи для отслеживаемых имён хостов и заполняет dns_ip_map (IP → имя хоста) с отслеживанием TTL.

Трассировочные точки Connect/Close

Хуки sys_enter/exit_connect отслеживают TCP-соединения (fd → IP назначения в conn_ip_map). Когда назначение соответствует DNS-верифицированному имени хоста, кэшируют fd в last_verified_fd. Хук sys_enter_close очищает устаревшие записи.

Трассировочные точки процессов

Хуки sched_process_exec и sched_process_exit отслеживают жизненный цикл процессов контейнеров для подключения uprobes и их очистки.

Поддерживаемые среды выполнения

Среда выполнения TLS-библиотека Точка подключения

Python, Rust, Ruby, PHP, curl

OpenSSL (libssl.so)

uprobe SSL_write / SSL_write_ex

Node.js

BoringSSL (статически слинкована)

uprobe SSL_write

Go

crypto/tls (нативный)

uprobe crypto/tls.(*Conn).Write

DNS-верифицированная цепочка доверия

Секреты с аннотациями getkloak.io/hosts перезаписываются только при условии, что назначение верифицировано через полную цепочку DNS-разрешения. Это предотвращает утечку секретов на неавторизованные серверы, даже если приложение скомпрометировано.

Как работает верификация хостов

  1. Перехват DNS — kprobe на udp_recvmsg перехватывает DNS-ответы на узле. Для имён хостов, указанных в getkloak.io/hosts, разрешённые IP-адреса сохраняются в dns_ip_map с отслеживанием TTL.

  2. Отслеживание соединений — трассировочные точки на sys_enter/exit_connect записывают сопоставление fd → IP назначения для каждого TCP-соединения в conn_ip_map. Если IP назначения присутствует в dns_ip_map, fd помечается как верифицированный.

  3. Разрешение хоста — в момент вызова SSL_write функция resolve_host() выстраивает цепочку через верифицированный fd, conn_ip_map и dns_ip_map, чтобы определить имя хоста текущего TLS-соединения.

  4. Фильтрация секретов — фаза 2 сравнивает разрешённое имя хоста с allowed_host секрета. При совпадении — перезапись. При несовпадении — заглушка отправляется как есть, секрет остаётся в безопасности.

  5. Соблюдение TTL — записи DNS содержат TTL. Устаревшие записи пропускаются при поиске, что требует повторной верификации через свежие DNS-ответы.

Как это работает

1. Пометьте и аннотируйте свои секреты

Добавьте метку getkloak.io/enabled=true, чтобы включить Kloak. Используйте аннотации для фильтрации по хосту и порту. Kloak генерирует теневой секрет с заглушками kl::<UUID>, длина которых совпадает с исходными значениями.

apiVersion: v1
kind: Secret
metadata:
  name: api-credentials
  labels:
    getkloak.io/enabled: "true"
  annotations:
    getkloak.io/hosts: "api.stripe.com"
    getkloak.io/port: "443"
data:
  api-key: c2stbGl2ZS14eXoxMjM=  # sk-live-xyz123

2. Разверните приложение

Вебхук автоматически переписывает монтирование томов, направляя его на теневой секрет. Приложение видит только заглушки kl::<UUID> и никогда не получает доступа к реальным учётным данным.

# Что приложение читает из смонтированного секрета:
kl::a1b2c3d4-e5f6-7890

3. Автоматическая перезапись на уровне ядра

Когда приложение выполняет исходящий HTTPS-запрос, eBPF uprobe перехватывает TLS-запись, верифицирует назначение через DNS-цепочку доверия и подставляет реальный секрет вместо заглушки — до шифрования.

# Что пишет приложение:
Authorization: Bearer kl::a1b2c3d4-e5f6-7890

# Что уходит с узла (после перезаписи eBPF):
Authorization: Bearer sk-live-xyz123

Разработка

make build          # Собрать бинарник kloak
make test           # Запустить все тесты
make docker-build   # Собрать Docker-образ

Разработка eBPF требует Linux. На macOS Kloak использует Lima VM:

make lima-start      # Запустить Lima VM
make generate-ebpf   # Сгенерировать Go-привязки eBPF
make test-linux      # Запустить тесты в Linux VM
make lima-shell      # Открыть shell внутри VM

Лицензия

AGPL-3.0

© 2026 meganuke