TLS/HTTPS, shared secret и управление секретами
Аутентификация устанавливает «кто ты», авторизация — «что тебе можно». Но обе предполагают, что сам канал связи защищён. Если трафик между клиентом и кластером можно подслушать, пароли и токены аутентификации перехватываются, а результаты запросов читаются посторонними. Защита канала — это TLS.
Этот урок собирает воедино тему шифрования коммуникаций Trino: TLS между клиентом и кластером, внутренний TLS между нодами, shared secret для взаимной аутентификации нод и подстановку секретов в конфигурационные файлы, чтобы пароли не лежали в них открыто.
Зачем нужен TLS
TLS (Transport Layer Security) — протокол, шифрующий сетевое соединение. HTTP поверх TLS — это HTTPS. Без TLS весь обмен Trino идёт открытым текстом, и это создаёт два класса угроз.
Перехват секретов аутентификации. Как мы видели в уроке про аутентификацию, парольные и токеновые методы передают пароли и токены. По незашифрованному HTTP они летят открыто — любой в сетевом пути их прочитает и выдаст себя за пользователя.
Утечка данных запросов. Через соединение клиент-кластер идут не только креды, но и результаты запросов — реальные данные из источников. Без шифрования отчёт с чувствительными данными виден тому, кто слушает трафик.
Поэтому TLS — не опция, а фундамент. Большинство механизмов аутентификации требуют его как условие работы; OAUTH2, например, без TLS на координаторе попросту не включится.
TLS между клиентом и кластером
Первый и обязательный рубеж — TLS на координаторе, для соединений с клиентами. Координатор — точка входа: к нему подключаются Trino CLI, JDBC-приложения, BI-инструменты, к нему ходит Web UI. Этот трафик и шифруется в первую очередь.
Настройка сводится к тому, чтобы HTTP-сервер координатора слушал HTTPS и предъявлял TLS-сертификат. В etc/config.properties координатора это означает включить HTTPS-порт и указать сертификат:
# etc/config.properties — КООРДИНАТОР
http-server.https.enabled=true
http-server.https.port=8443
http-server.https.keystore.path=/etc/trino/tls/keystore.p12
http-server.https.keystore.key=<пароль keystore>
Сертификат — это удостоверение координатора. Клиент при подключении проверяет сертификат и убеждается, что соединяется именно с настоящим координатором, а не с подставным. Точные имена свойств TLS между версиями Trino могут отличаться — сверяй их со страницей безопасности своего релиза; здесь важен принцип: координатор слушает HTTPS и предъявляет сертификат.
Сертификат для продакшена должен быть выдан доверенным удостоверяющим центром (CA), чтобы клиенты доверяли ему без ручной настройки. Самоподписанный сертификат годится для тестов и экспериментов, но в продакшене клиентам придётся явно ему доверять, что хрупко и небезопасно.
Внутренний TLS между нодами
TLS клиент-координатор закрывает внешний периметр. Но внутри кластера тоже идёт трафик — между координатором и воркерами, между воркерами при exchange. Этот внутренний трафик можно (а в строгих окружениях нужно) шифровать тоже — это internal TLS.
Зачем, если кластер «внутри»? Потому что «внутри» — понятие относительное. В облаке или в общей сети внутрикластерный трафик идёт по инфраструктуре, которую кластер не контролирует полностью. Без internal TLS промежуточные данные запросов между воркерами и обмен координатор-воркер летят открыто внутри этой сети.
| Рубеж TLS | Что шифрует | Обязательность |
|---|---|---|
| TLS клиент-координатор (HTTPS) | трафик клиентов: креды, результаты запросов | обязателен, условие парольной/токеновой аутентификации |
| Internal TLS (между нодами) | внутренний обмен координатор-воркер и воркер-воркер | для строгих окружений; защищает внутренний трафик |
Shared secret: взаимная аутентификация нод
TLS шифрует канал, но шифрование само по себе не отвечает на вопрос «а с легитимной ли нодой я говорю». Эту задачу для внутрикластерного обмена решает shared secret — тот самый общий секрет из урока про аутентификацию.
internal-communication.shared-secret — длинная случайная строка, одинаковая на всех нодах кластера. Ноды используют её, чтобы аутентифицировать друг друга: координатор убеждается, что обращающийся «воркер» знает секрет и потому действительно часть кластера, и наоборот. Без shared secret посторонний, получивший доступ во внутреннюю сеть, мог бы прикинуться воркером или координатором.
# etc/config.properties — одинаковый на ВСЕХ нодах кластера
internal-communication.shared-secret=<длинная случайная строка>
TLS и shared secret дополняют друг друга: TLS даёт конфиденциальность (трафик зашифрован), shared secret даёт взаимную аутентификацию нод (ноды доверяют друг другу). Одно без другого неполно.
internal-communication.shared-secret должен быть достаточно длинной случайной строкой и строго одинаковым на всех нодах кластера. Большинство механизмов внешней аутентификации требуют, чтобы он был настроен. Нода с другим secret не сможет корректно участвовать в кластере — это, кстати, удобный признак при отладке: рассогласованный shared secret проявляется как «воркер не работает в кластере».
Управление секретами в конфигурации
Последняя тема — как сами секреты попадают в конфигурацию. Конфигурационные файлы Trino полны чувствительных значений: пароль keystore, shared secret, креды каталогов к источникам данных, пароли LDAP. Хранить их прямо в .properties-файлах открытым текстом — плохо: файлы попадают в систему контроля версий, в резервные копии, их видит каждый, у кого доступ к каталогу etc.
Trino поддерживает подстановку секретов из переменных окружения. Вместо литерального значения в конфигурационном файле пишут ссылку на переменную окружения, и Trino подставляет реальное значение при чтении конфигурации.
# etc/catalog/postgresql.properties
connector.name=postgresql
connection-url=jdbc:postgresql://db.internal:5432/analytics
connection-user=trino_ro
# Пароль НЕ в файле — берётся из переменной окружения
connection-password=${ENV:POSTGRES_PASSWORD}
Здесь ${ENV:POSTGRES_PASSWORD} — не литерал, а указание Trino взять значение из переменной окружения POSTGRES_PASSWORD. Сам пароль в файле не лежит — файл безопасно хранить в git. А реальное значение переменной окружения поставляется средой исполнения: secret-механизмом Kubernetes, менеджером секретов, защищённой системой деплоя.
Не коммить чувствительные значения в конфигурационные файлы открытым текстом. Пароли каталогов, shared secret, пароль keystore, попавшие в git, — это утечка, которую невозможно «отозвать» (история репозитория хранит их навсегда). Используй подстановку секретов из переменных окружения, а реальные значения держи в secret-механизме среды. Точный синтаксис подстановки сверяй с документацией своей версии Trino.
Полная картина защиты коммуникаций
Соберём все рубежи модуля по безопасности в единую картину. Защищённый кластер Trino выглядит так:
- TLS клиент-координатор — внешний трафик (креды, результаты запросов) зашифрован; обязательное условие парольной и токеновой аутентификации.
- Internal TLS — внутренний обмен координатор-воркер и воркер-воркер зашифрован (в строгих окружениях).
- Shared secret — ноды взаимно аутентифицируют друг друга; внутренний периметр закрыт.
- Аутентификация — личность внешнего клиента установлена (урок 3).
- Авторизация — действия клиента ограничены политикой доступа (урок 4).
- Управление секретами — пароли и ключи не лежат в конфигах открыто, а подставляются из защищённого окружения.
Шифрование, аутентификация, авторизация, гигиена секретов — слои дополняют друг друга. Пропуск любого слоя оставляет дыру: TLS без аутентификации пускает кого угодно в зашифрованный канал; аутентификация без TLS отдаёт креды по открытой сети; всё вместе без гигиены секретов утекает через закоммиченный пароль.
Попробуй сам
Настрой защиту коммуникаций на тестовом Trino.
- Включи HTTPS на координаторе: сгенерируй keystore с самоподписанным сертификатом, пропиши
http-server.https.enabled=true, HTTPS-порт и путь к keystore. Перезапусти и подключись Trino CLI по HTTPS. - Задай
internal-communication.shared-secretодинаковым на всех нодах. Проверь, что кластер собирается; затем поставь на воркере другой secret и убедись, что он выпадает из кластера. - Настрой каталог к внешней БД (например, PostgreSQL в соседнем контейнере) с паролем через подстановку из переменной окружения: в файле каталога — ссылка вида
${ENV:...}, реальный пароль — в переменной окружения контейнера. Убедись, что подключение работает, а пароль в файле отсутствует. - Поразмышляй: какие чувствительные значения в твоей конфигурации сейчас лежали бы открытым текстом и какие из них критично вынести в подстановку секретов в первую очередь.
Цель — собрать защищённый канал: TLS шифрует трафик, shared secret аутентифицирует ноды, секреты не хранятся в файлах открыто.