Безопасность¶
Безопасность¶
Безопасность распределённой системы — это не периметр, а сеть. В монолите доверие заканчивается на входной двери: прошёл аутентификацию — дальше внутри все свои. В микросервисах «внутри» нет. Десятки сервисов ходят друг к другу по сети, которая по умолчанию враждебна.
Главная идея: identity ездит с запросом до самого низа, и каждый сервис проверяет её сам.
AuthN vs AuthZ — не путай
Два разных вопроса, которые часто валят в кучу.
- AuthN (authentication) — кто ты. Доказательство личности.
- AuthZ (authorization) — что тебе можно. Проверка прав на конкретное действие.
AuthN делаешь один раз на входе. AuthZ — на каждом защищённом действии, в каждом сервисе. Сервис заказов не повторяет логин, но обязан сам решить, можно ли этому пользователю отменить чужой заказ.
OAuth2 и OpenID Connect
Главная путаница индустрии: OAuth2 — про authorization, не про authentication.
OAuth2(RFC 6749, IETF, 2012) — authorization framework для делегированного доступа. Third-party приложение получает limited access к ресурсу от имени владельца через access token, без передачи пароля.OpenID Connect(OIDC Core 1.0, OpenID Foundation, 2014) — identity-слой поверхOAuth2. Добавляет ID Token: доказательство, что пользователь аутентифицирован, и кто он.
Грабли:
- Брать access token и по нему «логинить» пользователя. Access token — для resource server, он про
«что можно трогать», а не «кто ты». Для identity бери ID Token из OIDC.
- Считать OAuth2 единым протоколом. Это extensible framework с набором grant types. Для server-side
и SPA сегодня берёшь Authorization Code + PKCE, не implicit (он deprecated).
Роли в контуре раздаются так. Есть Authorization Server (Identity Provider — Keycloak, Auth0,
свой) — он один на весь контур, выдаёт токены. Сервисы — это resource servers, они токены только
проверяют, сами не выдают.
JWT vs opaque tokens
Два способа представить access token. Выбор влияет на всю схему валидации.
- JWT — самодостаточный токен. Внутри подписанный JSON с claims (
sub,exp,scope, роли). Проверяешь подпись публичным ключом — и веришь содержимому, без сетевого вызова. - Opaque token — непрозрачная строка-идентификатор. Сам по себе ничего не значит. Чтобы узнать, валиден ли и чей он, дёргаешь introspection endpoint (RFC 7662) у Authorization Server.
Чем отличаются на практике:
| Свойство | JWT | Opaque |
|---|---|---|
| Валидация | локально, проверка подписи | сетевой вызов на introspection |
| Latency | ~ноль | +RTT до auth server на запрос |
| Отзыв (revocation) | трудно: живёт до exp |
мгновенно: auth server решает |
| Утечка | видно содержимое (не secret, но PII утекает) | строка бесполезна без introspection |
| Связь с auth server | нужна только для ключей | нужна на каждую проверку |
Главная боль JWT — его трудно отозвать. Токен подписан и валиден до exp. Уволил сотрудника — а его
токен ещё 15 минут живой. Поэтому:
- Делай access token коротким (5–15 минут), refresh token — долгим и отзываемым.
- На по-настоящему критичных действиях проверяй ещё и через introspection, не только подпись.
- Держи механизм отзыва: blacklist по
jti, или версия токена в claims против счётчика в БД.
Гибрид встречается часто: opaque token наружу для клиента (легко отозвать), JWT внутри контура между
сервисами (дёшево валидировать). Gateway меняет одно на другое на входе — паттерн Token Exchange /
Phantom Token.
RBAC vs ABAC
Две модели authorization. Грубо говоря, по ролям против по атрибутам.
- RBAC (Role-Based Access Control) — права привязаны к ролям, роли — к пользователю. «admin может удалять заказы». Просто, предсказуемо, легко аудировать. Плохо тянет контекст: «можно, но только свои заказы и только в рабочие часы».
- ABAC (Attribute-Based Access Control) — решение из атрибутов субъекта, ресурса и среды. «user может редактировать документ, если он его owner и документ в статусе draft». Гибко, но политики быстро превращаются в нечитаемый клубок.
Когда что: - RBAC — старт по умолчанию. Покрывает большинство случаев, дёшев в поддержке. - ABAC — когда правила реально зависят от данных (ownership, tenant, время, гео). - На практике часто смесь: роль грубо режет доступ, атрибуты доуточняют на уровне ресурса.
Отдельная тема — где живёт сама политика. Тренд — выносить authorization в отдельный policy engine
(OPA/Rego, Cedar): сервис спрашивает «можно ли?», движок отвечает. Логика прав не размазана по
коду каждого сервиса.
Где валидировать токен
Самый частый грабли всей секции: токен проверяют только на периметре, а внутри контур открыт.
Два уровня, и нужны оба:
- На
gateway(edge) — отсечь явный мусор как можно раньше. Нет токена или подпись битая — заворачивай на входе, не пускай нагрузку внутрь. Здесь же снимаешь identity и кладёшь в заголовок дальше. - В каждом сервисе — повторная проверка. Сервис не верит, что «раз дошло до меня, значит gateway всё проверил». Он валидирует токен (или подписанный gateway header) сам и делает свою AuthZ.
Почему нельзя только на edge:
- Любой, кто попал внутрь сети (скомпрометированный сервис, под в том же кластере, SSRF), ходит к
твоим сервисам напрямую, минуя gateway.
- AuthZ всё равно контекстная. Только сервис заказов знает, чей это заказ. Gateway этого не знает.
┌──────────────────────────────────────────────┐
client ──TLS──▶│ Gateway / edge │
(opaque/JWT) │ - проверка подписи / introspection │
│ - rate-limit, WAF, schema validation │
│ - кладёт identity в заголовок / mint JWT │
└───────────────────┬──────────────────────────┘
│ mTLS, JWT внутри
┌──────────────────────┼──────────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ orders │ │ payments │ │ users │
│ AuthZ тут│ │ AuthZ тут│ │ AuthZ тут│
└──────────┘ └──────────┘ └──────────┘
каждый сам валидирует identity и решает права — не доверяет соседу
Note
Не пробрасывай сырой клиентский токен бесконечно вглубь. Чем глубже, тем шире blast radius при
утечке. Часто gateway обменивает внешний токен на внутренний, узкий по scope и короткий по
времени (Token Exchange), и пробрасывает уже его.
mTLS внутри контура
Токен говорит, кто пользователь. mTLS говорит, какой сервис с каким сервисом разговаривает.
mTLS (mutual TLS) — это режим TLS, где обе стороны предъявляют и проверяют X.509-сертификаты, а не только клиент проверяет сервер.
- Обычный TLS: клиент убедился, что сервер — тот самый. Сервер про клиента ничего не знает.
mTLS: сервер тоже требует сертификат клиента. Взаимная аутентификация service-to-service.
Зачем в микросервисах:
- Шифрование трафика внутри кластера — сеть не доверенная даже за firewall.
- Сервис A криптографически уверен, что к нему пришёл именно сервис B, а не кто-то в той же подсети.
- Identity сервиса в самом сертификате (SPIFFE/SVID) — основа для service-level AuthZ.
mTLS — не отдельный протокол, а режим стандартного TLS (client-cert auth там с TLS 1.0, RFC 2246,
1999). И это authentication, не authorization: сертификат доказывает, кто звонит, но не что ему можно.
Грабли:
- Ротация сертификатов. Коротко живущие серты на сотнях подов руками не выдашь — нужна автоматика.
- Поэтому на практике mTLS почти всегда отдают service mesh (Istio, Linkerd): sidecar
терминирует TLS, выдаёт и крутит серты сам. Про mesh и sidecar — подробнее в Macro-Architecture.
Zero Trust
Идея простая: сеть не доверенная по умолчанию. Нет «защищённого периметра», внутри которого все свои.
- Старая модель: толстый firewall снаружи, мягкая середина. Пробил периметр — гуляй свободно.
- Zero Trust: каждый запрос аутентифицируется и авторизуется, независимо от того, откуда пришёл — снаружи или из соседнего пода.
Что это значит для архитектуры:
- Identity на каждом hop: и пользователь (токен), и сервис (mTLS-сертификат).
- «За VPN/firewall, значит свой» — больше не аргумент. Внутренний вызов проверяется как внешний.
- Least privilege: каждый сервис ходит только туда, куда ему явно разрешено (network policy + AuthZ).
Zero Trust — это не продукт, который покупаешь. Это принцип, который собирается из кусков этой
секции: mTLS плюс per-request AuthZ плюс короткие scoped-токены плюс network policies.
Секреты
Пароли БД, ключи подписи JWT, API-ключи, TLS-серты. В распределённой системе их много, и они не
должны лежать в коде или в .env-файлах в репозитории.
Грабли (повторяются в каждом втором проекте):
- Секрет в git. Закоммитил — считай утёк навсегда, история помнит. Ротируй ключ, а не надейся на
git rm.
- Секрет в plain-env в образе или манифесте. kubectl describe, дамп образа, логи — и он наружу.
Куда складывать:
- HashiCorp Vault — централизованное хранилище с динамическими секретами (генерит креды БД на
лету, с TTL), аудитом, шифрованием. Дорого в эксплуатации, но мощно.
- K8s Secrets — встроено в кластер, но по умолчанию это base64, не шифрование. Включай encryption at
rest для etcd и RBAC на доступ, иначе это «секрет» только на словах.
- SOPS — шифруешь секреты прямо в git, ключом из KMS/age. GitOps-friendly: зашифрованный файл
лежит в репо, расшифровывается только при деплое.
Что в любом случае: - Ротация. Секрет с бесконечным сроком жизни — это утечка, которая ещё не случилась. - Least privilege на доступ: сервис читает только свои секреты, не все подряд. - Не логируй секреты. Очевидно, но именно так они чаще всего и утекают — в трейс или лог.
Политики на gateway
Gateway — естественная точка для cross-cutting защиты, которую глупо дублировать в каждом сервисе.
Детали роутинга и протоколов — в Communication & API Style, здесь только про security-функции.
- Rate-limiting — режешь частоту запросов на клиента/токен/IP. Защита от abuse и от того, что один крикливый клиент положит всех. Это не backpressure — это жёсткая внешняя политика «больше N в окно».
- WAF (Web Application Firewall) — фильтр типовых атак на L7: SQL injection, XSS, известные сигнатуры. Первый грубый щит, не замена валидации в сервисе.
- Schema validation на входе и выходе —
gatewayпроверяет, что запрос соответствует контракту (тип, обязательные поля, формат). Кривой и враждебный ввод не доходит до сервисов.
Note
Gateway фильтрует и режет, но не заменяет AuthZ внутри. Он не знает бизнес-контекст — чей это
заказ, в каком статусе документ. Edge защищает от мусора и шума, сервис защищает бизнес-правила.
Если коротко:
- Нет токена или битая подпись на входе → заворачивай на gateway, не пускай нагрузку внутрь.
- Дошло до сервиса → всё равно валидируй сам и делай свою AuthZ, не доверяй соседу.
- Нужна личность пользователя → ID Token из OIDC, не access token.
- Нужно мгновенно отзывать → opaque token + introspection или короткий JWT + refresh.
- Сервис-сервис в кластере → mTLS, на проде через service mesh.
- Секрет → в Vault/SOPS/зашифрованный K8s Secret, никогда в git или plain-env.
- Хочешь Zero Trust → собери из кусков: mTLS + per-request AuthZ + scoped-токены + network policy.