- Зачем: - менти должен увидеть разделение доступа без состояния в томах. - Что: - пользователи и роли объявлены файлом с паролями из окружения. - межшардовые запросы передают пользователя через общий секрет. - четыре допущения реализации подтверждены в ADR живыми замерами. - Проверка: - make config-test, make smoke, make check-clickhouse, make check-services.
13 KiB
ADR 0007. Доступ к ClickHouse: пользователи файлом, доверие нод секретом
Дата: 9 августа 2026 года. Статус: принято. Реализация — отдельным тикетом.
Решение
У кластера четыре пользователя и три роли.
default— с паролем, только служебный: проверки здоровья нод и работа изнутри контейнеров. Приложения им не ходят.etlс рольюetl_writer— чтение и запись во всех слоях. Им ходит Airflow и применяется DDL при подъёме стенда.biс рольюbi_reader— чтение витрин DM и слоя DDS. Им ходит Superset.analystс рольюanalyst_reader— чтение всех слоёв. Им человек подключается снаружи, из своего клиента.
Роль здесь шире прав на слои: образ ClickHouse требует отдельного разрешения
на запросы ON CLUSTER и на чтение системных таблиц, а без второго клиент
не открывает соединение вовсе. Состав прав каждой роли — работа тикета
реализации.
Пользователи, роли и права объявлены файлами настройки сервера, а не
запросами SQL. Файл монтируется на обе ноды и лежит в git. Пароли живут
в .env и попадают в настройку подстановкой по имени переменной; значения
собраны по конвенции «слово-число-слово», потому что пароль analyst
человек набирает руками.
Ноды доверяют друг другу по общему секрету, объявленному в описании кластера. Учётные данные по репликам не расписываются.
Сетевых ограничений на default нет: порты стенда и так открыты только на
127.0.0.1, а внутри сети Compose дверь заперта паролем.
Устройство модели — кто каким пользователем ходит и где живут пароли —
опишет раздел README «Состав и доступ»; здесь только решение и доводы.
Отдельного справочника по доступу в docs/architecture/ не заводим: своего
содержания сверх этих двух документов у него сегодня нет, а дом ему
понадобится, когда появятся слои DDS и DM и гранты размножатся. Абзац README
«У локального учебного кластера нет пароля» этим решением отменяется.
Почему
Стенд учит не администрированию доступа, а чтению отказа в правах: человек
должен узнать в бою картину, которую видел здесь, и понимать, чего просить.
Отсюда критерий отбора — модель должна читаться из текста ошибки, а не из
описания. Поэтому пользователей мало, имена у них говорящие, а границы
проходят там, где их обычно проводят в компаниях: bi видит витрины и слой
DDS под ними, потому что так чаще всего и бывает; заодно расхождение витрины
со слоем ниже остаётся проверяемым одним и тем же пользователем.
Секрет, а не учётные данные по репликам. У каждой реплики в описании
кластера своя учётка — вписанная явно или подразумеваемая, и тогда это
беспарольный default. Подключение к соседней ноде идёт от неё, а не от
того, кто задал вопрос. Значит, стоит default получить пароль — и без
правки описания кластера межнодовые запросы встают. Починить это можно двумя
способами, и они различаются не удобством. С учётными данными по репликам вся
межшардовая работа идёт от одной общей учётки: на своей ноде права
применяются, на соседней — нет. Ролевая модель тогда наполовину декорация, а
это хуже её отсутствия: она утверждает разграничение, которого не существует.
Общий секрет передаёт на соседа личность инициатора, стоит одной строки в
описании кластера и не размножает пароль по репликам. Шифрование тут ни при
чём; в соседнем clickhouse-learning-cluster выбран как раз путь с учётными
данными по репликам, и там пароль вписан в каждую реплику каждого из трёх
кластеров.
Довод опирается на измеренное лишь наполовину: колонку user видно на живом
стенде, а вот что права bi на соседнем шарде подменяются правами общей
учётки — вывод из неё, и он ждёт измерения (см. конец документа). Окажись
вывод неверным, выбор секрета устоит по остальным основаниям, но перестанет
быть единственно возможным.
Файлы, а не SQL. Объявленное файлом не оставляет состояния в томах:
повторный make up на живом стенде ничего не сдвигает, правка пароля
применяется перезапуском, источник истины — файл в git. Оба варианта с SQL —
локальное хранилище доступа с ON CLUSTER и реплицированное в keeper —
делают пользователей состоянием тома: .env на них больше не влияет, и
появляется расхождение того же сорта, которое README уже описывает для
Postgres и Grafana. Цена выбора известна: объявленных файлом пользователей
нельзя менять запросами. Она принята как упрощение — сломать модель случайно
труднее.
Что ещё отвергнуто. Оставить всё на беспарольном default
— цена нулевая, но и рассказать по итогам нечего. Раздать всем пароль без
ролей — дёшево и показывает ровно тот антипаттерн, за который ругают в бою:
все под одним админом. Вынести доступ в лекцию, как репликационную
эксплуатацию, — довод «отдельный операционный домен» здесь не работает:
у репликации это живые процессы, учения и runbook, а тут несколько десятков
строк настройки и ноль эксплуатации. Запереть default ещё и по сети —
второй замок на ту же дверь.
Отложено явно: TLS и защищённые межсерверные соединения, настоящее хранилище секретов, внешние поставщики учётных записей, квоты и профили под пользователя, политики строк, аудит запросов.
Следствия
До появления слоёв DDS и DM читать bi нечего, и это намеренно. Дашборды
рисуются поверх модели данных, а не поверх типизированных событий, поэтому
доступ Superset к ODS не планировался и правами не выдаётся. Пока витрин нет,
подключение Superset проверяется связью, а не запросом к таблице.
Владелец матвью приёма определяется тем, кто применил DDL. Весь DDL стенда
идёт через IF NOT EXISTS, поэтому на существующих томах три матвью
сохранят нынешнего владельца, а на собранном заново стенде им станет etl.
Работают оба — права есть у обоих, — но два стенда в этом расходятся.
Лаба «сломать права» вариантом с файлами не закрывается: создать через SQL
новую роль и нового пользователя ничто не мешает, и новую роль разрешено
выдавать даже пользователю, объявленному файлом. Недоступно обратное —
править объявленных файлом. Всё, что лаба создаст, живёт в томе и убирается
make clean.
Что проверено
Сверено 9 августа 2026 года по документации ClickHouse через MCP Context7 и на живом стенде 26.3.17.56.
- В
system.clustersу каждой реплики своя колонкаuser; на стенде тамdefault. Отсюда весь довод про общий секрет. - Секрет кластера аутентифицирует серверы хешем от соли, нонса, секрета и запроса и позволяет назначить пользователя, от которого запрос выполняется на соседней ноде.
- Роли и права объявляются в файле настройки пользователей наравне с самими пользователями; SQL-хранилище доступа для этого не требуется.
- Хранилище пользователей из файла — только для чтения: попытка изменить
такого пользователя запросом даёт
ACCESS_STORAGE_READONLY. - Роль, созданную через SQL, разрешено выдавать пользователю из файла — отдельное улучшение ClickHouse 2025 года, в 26.3 оно есть.
- Настройку пользователей можно перечитать на живом стенде, в том числе на всём кластере сразу.
- Подстановка значений из переменных окружения — общий механизм файлов настройки ClickHouse.
- Весь DDL репозитория идёт через
IF NOT EXISTS(файлыsql/ddl/).
Проверено на живом стенде 13 августа 2026 года, ClickHouse 26.3.17.56.
- Секрет кластера принимается внутри
remote_serversв виде<secret from_env="CLICKHOUSE_CLUSTER_SECRET"/>. Запрос отetlчерез обе ноды выполнился на каждой под пользователемetl. from_envработает у элементаpassword:etl,biиanalystвошли с паролями из окружения и получили свои роли.- При запросе от
biчерез Distributed на второй нодеsystem.query_logпоказалuser = biиinitial_user = bi. Значит, на соседнем шарде применяются права инициатора, а не общей учётной записи. - Права на отсутствующие базы
ddsиdmприняты. До создания баз они видны вSHOW GRANTS FOR etl_writer; временная таблица вdmподтвердила правоbiна чтение после появления базы.