Перейти к содержимому
Hogin Hogin
Назад

Teleport вместо VPN: доступ к серверам, Kubernetes и БД с полным аудитом

7 мин чтения

Bastion-хост, статичные SSH-ключи, отдельный kubeconfig для каждого разработчика и ещё один пароль для psql — типичный набор доступа к продакшн-инфраструктуре, в котором никто не может быстро ответить на вопрос «кто именно и что делал на этом сервере во вторник в 3 часа ночи». Zero-trust VPN вроде NetBird или Cloudflare Tunnel эту проблему не решает — он даёт зашифрованный сетевой путь до периметра, но что происходит после подключения, остаётся на совести SSH-ключей и логов auth.log на самой машине. Teleport решает другую задачу: не «как попасть в сеть», а «как получить доступ к конкретному ресурсу — поду, базе, серверу — так, чтобы каждое действие было аудируемо и отзываемо».

Содержание

Открыть содержание

VPN и Teleport закрывают разные слои

NetBird и Cloudflare Tunnel — это сетевой уровень: они решают, кто может достучаться до приватной подсети или конкретного хоста по IP и порту. Внутри этой подсети всё по-старому: SSH по ключу, kubectl с локальным kubeconfig, psql с паролем из .pgpass. Zero-trust на уровне сети — необходимое, но не достаточное условие для privileged-доступа: SOC2 и ISO 27001 спрашивают не «кто мог физически достучаться до базы», а «кто именно и когда выполнял конкретные команды в production», и сетевой VPN на этот вопрос ответить не может в принципе — он не видит, что происходит выше транспортного уровня.

Teleport встаёт не вместо VPN, а поверх произвольной сети (в том числе поверх уже настроенного NetBird или Cloudflare Tunnel) и выдаёт доступ не к сети, а к ресурсу: конкретному серверу по имени, конкретному Kubernetes-кластеру, конкретной базе данных. Разница на практике: сетевой VPN не может дать «доступ только на чтение к поду checkout в namespace prod на 20 минут» — а Teleport может, потому что RBAC у него привязан к ресурсу и роли, а не к CIDR.

Zero-trust сеть и аудируемый доступ к ресурсу — разные уровни, разные вопросы

Проблема статичных SSH-ключей и kubeconfig

Классическая связка bastion + SSH-ключи ломается на масштабе предсказуемо:

Всё это — статичные секреты с неограниченным сроком жизни, которые утекают одинаково: через скомпрометированный ноутбук, забытый доступ у уволенного сотрудника или CI-раннер, чей workspace попал не в те руки. Teleport убирает статичные секреты как класс, заменяя их короткоживущими сертификатами.

Как работают короткоживущие сертификаты

Teleport Auth Service — это внутренний CA. Пользователь логинится один раз через tsh login (по SSO — Okta, Google Workspace, GitHub — или через существующий SPIFFE/SPIRE как источник identity), и в ответ получает не пароль, а X.509- и SSH-сертификат с TTL по умолчанию 8–12 часов. Дальше:

Когда сертификат истекает, доступ пропадает сам — без ручного отзыва ключей на десятках хостов. Утечка сертификата ограничена по времени и по объёму: он привязан к ролям пользователя на момент выдачи, а не к статичному ключу, который работает, пока кто-то не вспомнит его удалить.

Короткоживущий сертификат вместо ключа: TTL и привязка к ролям вместо вечного доступа

RBAC на уровне ресурса, а не сети

Роли в Teleport (teleport.yamlroles) описывают доступ в терминах меток на ресурсах: env: prod, app: checkout, конкретное имя базы данных или namespace. Это принципиально другой уровень гранулярности по сравнению с сетевым ACL:

Комбинация с существующим identity-провайдером — SSO или SPIFFE/SPIRE, если в кластере уже развёрнут SPIRE как источник workload identity — означает, что роли Teleport мапятся на группы SSO или на SPIFFE ID, а не заводятся вручную для каждого нового человека. Доступ выдаётся и отзывается там же, где заводится и увольняется сотрудник — в HR-системе или каталоге identity, а не в отдельном списке authorized_keys.

Session recording и разбор инцидента

Каждая SSH-сессия, kubectl exec или запрос к базе через Teleport проксируется через Auth/Proxy Service, и по умолчанию записывается как session recording — терминальный ввод-вывод с таймкодами. После инцидента разбор не требует восстановления состояния хоста из истории bash (которую пользователь мог подчистить) — достаточно:

tsh recordings ls --format=json | jq '.[] | select(.user=="alice")'
tsh play <session-id>

tsh play воспроизводит сессию как терминальную запись — посимвольно, в реальном темпе или ускоренно. Для аудита SOC2/ISO это не «мы верим, что доступ был ограничен» — это конкретная запись, кто, когда и что именно вводил в shell на проде.

Что нужно, чтобы поднять доступ к тестовому кластеру Kubernetes

Минимальный стенд: Teleport cluster (self-hosted teleport или managed Teleport Cloud) + tbot для получения identity в CI/автоматизации + один Kubernetes-кластер, зарегистрированный как ресурс.

Устанавливаем teleport и логинимся:

# на машине с Teleport Auth/Proxy Service
curl https://cdn.teleport.dev/install.sh | bash -s 17.0.0

# с рабочей станции
tsh login --proxy=teleport.example.com --user=alice

Регистрируем Kubernetes-кластер как ресурс, доступный через Teleport (агент teleport-kube-agent разворачивается прямо в кластере как Helm chart):

helm repo add teleport https://charts.releases.teleport.dev
helm install teleport-kube-agent teleport/teleport-kube-agent \
  --set roles=kube \
  --set proxyAddr=teleport.example.com:443 \
  --set kubeClusterName=prod-cluster \
  --create-namespace -n teleport

Роль, ограничивающая доступ до чтения в конкретном namespace (файл reader.yaml, применяется через tctl create -f):

kind: role
version: v7
metadata:
  name: kube-reader-checkout
spec:
  allow:
    kubernetes_labels:
      env: staging
    kubernetes_resources:
      - kind: pod
        namespace: checkout
        verbs: ["get", "list"]

Получаем доступ вместо статичного kubeconfig:

tsh kube login prod-cluster
kubectl get pods -n checkout

Никакого файла kubeconfig с бессрочным токеном на диске не остаётся — tsh kube login встраивает в локальный kubeconfig сертификат, привязанный к текущей сессии tsh, и он перестаёт работать вместе с истечением TTL логина.

Как проверить, что это работает

Смотрим, что kubectl exec в этом кластере попал в запись сессии, а не только в audit log Kubernetes API:

kubectl exec -it -n checkout deploy/checkout -- sh
# в другом терминале, с ролью аудитора
tsh recordings ls --format=json | jq '.[-1]'
tsh play $(tsh recordings ls --format=json | jq -r '.[-1].id')

Если сессия проигрывается посимвольно — proxy действительно терминирует и пишет каждое подключение, а не просто маршрутизирует трафик до API-сервера напрямую.

Итог

VPN (NetBird, Cloudflare Tunnel) отвечает на вопрос «кто может достучаться до сети» — это правильный и достаточный слой для доступа к внутренним сервисам, которые сами по себе не privileged. Teleport отвечает на другой вопрос — «кто, к какому именно ресурсу и с каким аудиторским следом» — и это тот слой, который обязателен, когда речь идёт о продакшн-базе, проде Kubernetes или любом доступе, который должен пройти SOC2/ISO-аудит. Разворачивать оба сразу не обязательно: если сетевой VPN уже есть и privileged-доступа как отдельной задачи ещё не стоит, можно подождать. Но в момент, когда впервые звучит вопрос «а кто заходил в prod вчера ночью», ответом должен быть tsh play, а не грепанье .bash_history.


Поделиться:

Следующая статья
Схема БД как код: Atlas поверх CloudNativePG вместо ручных миграций