From a3d852cad9fe42ef941d91c2dc7ecab990cc08dc Mon Sep 17 00:00:00 2001 From: Dmitry Dementiev Date: Sun, 9 Aug 2026 23:16:41 +0300 Subject: [PATCH] =?UTF-8?q?docs(adr):=20=D1=80=D0=B5=D1=88=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D1=8C=20=D0=B4=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D1=83=D0=BF=D0=B0=20=D0=BA=20ClickHouse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Зачем: - до появления ETL и витрин надо выбрать границу боевого реализма в доступе: один беспарольный default учит нулю, а полноценная защита стоит эксплуатации, которой стенду не потянуть (#66). - Что: - принято четыре пользователя и три роли, объявленные файлами настройки, без состояния в томах и без SQL-хранилища доступа. - межнодовое доверие переведено на общий секрет кластера: учётные данные по репликам подменяют права спросившего правами общей учётки. - записаны отвергнутые варианты, отложенные меры защиты и четыре проверки, которые закрывает тикет реализации. - устройство модели отдано разделу README «Состав и доступ»: отдельный справочник по доступу своего содержания сверх ADR сегодня не имеет. - оговорено, что роль шире прав на слои — образ требует отдельных разрешений на ON CLUSTER и на чтение системных таблиц (холодное ревью #78). - Проверка: - реализации в этом коммите нет, менять нечему: make config-test. Co-Authored-By: Claude Opus 5 --- docs/adr/0007-clickhouse-access.md | 143 +++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 docs/adr/0007-clickhouse-access.md diff --git a/docs/adr/0007-clickhouse-access.md b/docs/adr/0007-clickhouse-access.md new file mode 100644 index 0000000..d768b1c --- /dev/null +++ b/docs/adr/0007-clickhouse-access.md @@ -0,0 +1,143 @@ +# 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/`). + +Осталось проверить при исполнении, и это работа тикета реализации: точная +форма объявления секрета в описании кластера (готового примера в +документации не нашлось); работает ли подстановка из окружения именно для +пароля пользователя, и если нет — пароли лягут в файл настройки прямым +текстом, а README назовёт это вслух; применяются ли на соседнем шарде права +`bi`, а не общей учётки (вывод из колонки `user`, живьём не измерен — +проверка требует создать пользователя); принимает ли ClickHouse права, +выданные на ещё не созданную базу — на момент решения `dds` и `dm` не +существуют.