Files
clickstream-data-platform/docs/adr/0007-clickhouse-access.md
T
ddadminandClaude Opus 5 5cc0ede75a fix(clickhouse): починена проверка словаря, выметены хвосты переноса
Зачем: ревью в три линии нашло настоящий дефект. Девятая проверка кластера
звала dictGet('dds.products', …) — объекта после переноса не существует, и
make check-clickhouse падал. Промах вырос из недосчёта: упоминания dds.products
я искал с фильтром по расширениям, .sh туда не попал, и в ADR уехало «четыре
упоминания» вместо семи.

Что: проверка переведена на dic.products и сверяет цену, умножив её обратно на
сто, — так утверждается ещё и точность приведения к Decimal. Раздел 3
мастер-спеки и список пользователей README догнали перенос: оба описывали
отменённое устройство. В ADR 0012 добавлено условие пересмотра для дома — для
него его не было, хотя ради дома тикет и заводился; недосчёт записан в
«Что проверено» как урок. Разнобой обозначений сведён: карта таблиц в доке
хранилища перешла на имена баз строчными, таблица мастер-спеки — на заголовок
«Где», зону всюду зовут зоной, а не слоем. Термин «Справочник» переписан без
метафоры и уложен в формат глоссария. Вычтено лишнее: комментарии DDL
сократились вдвое, из ADR 0006 и 0007 убраны самооправдание и дублирующие
абзацы, отраслевой образец получил честную оговорку о непроверяемости.

Проверка: make config-test, make lint, make smoke и make check-clickhouse —
все зелёные, десять проверок кластера из десяти.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 12:10:03 +03:00

14 KiB
Raw Blame History

ADR 0007. Доступ к ClickHouse: пользователи файлом, доверие нод секретом

Дата: 9 августа 2026 года. Статус: принято. Реализация — отдельным тикетом.

Решение

У кластера пять пользователей и четыре роли.

  • default — с паролем, только служебный: проверки здоровья нод и работа изнутри контейнеров. Приложения им не ходят.
  • etl с ролью etl_writer — чтение и запись во всех слоях. Им ходит Airflow и применяется DDL при подъёме стенда.
  • bi с ролью bi_reader — чтение витрин DM, слоя DDS и справочников dic. Им ходит Superset.
  • analyst с ролью analyst_reader — чтение всех слоёв и справочников. Им человек подключается снаружи, из своего клиента.
  • dict с ролью dict_reader — чтение одной базы dic, пароля нет. Им словарь читает свою подложку, и больше он никем не используется (ADR 0012).

Роль здесь шире прав на слои: образ ClickHouse требует отдельного разрешения на запросы ON CLUSTER, на чтение системных таблиц и на каждый движок внешнего источника. Без права на системные таблицы клиент вовсе не открывает соединение. Состав прав каждой роли — работа тикета реализации.

Пользователи, роли и права объявлены файлами настройки сервера, а не запросами SQL. Файл монтируется на обе ноды и лежит в git. Пароли живут в .env и попадают в настройку подстановкой по имени переменной; значения собраны по конвенции «слово-число-слово», потому что пароль analyst человек набирает руками.

Ноды доверяют друг другу по общему секрету, объявленному в описании кластера. Учётные данные по репликам не расписываются.

Сетевых ограничений на default нет: порты стенда и так открыты только на 127.0.0.1, а внутри сети Compose дверь заперта паролем.

Устройство модели — кто каким пользователем ходит и где живут пароли — опишет раздел README «Состав и доступ»; здесь только решение и доводы. Отдельного справочника по доступу в docs/architecture/ не заводим: своего содержания сверх этих двух документов у него сегодня нет, а дом ему понадобится, когда появятся слои DDS и DM и гранты размножатся. Абзац README «У локального учебного кластера нет пароля» этим решением отменяется.

Почему

Стенд учит не администрированию доступа, а чтению отказа в правах: человек должен узнать в бою картину, которую видел здесь, и понимать, чего просить. Отсюда критерий отбора — модель должна читаться из текста ошибки, а не из описания. Поэтому пользователей мало, имена у них говорящие, а границы проходят там, где их обычно проводят в компаниях: bi видит витрины и слой DDS под ними, потому что так чаще всего и бывает; заодно расхождение витрины со слоем ниже остаётся проверяемым одним и тем же пользователем. Справочники видны всем читателям и никому на запись: зона входит в хранилище сбоку, и её содержимым владеет файл репозитория, а не слой.

Беспарольный dict. Пароль пришлось бы вписать в текст DDL словаря, то есть положить секрет в git. Ходить этой учёткой некуда: право у неё одно — SELECT на dic, и хост источника локальный.

Секрет, а не учётные данные по репликам. У каждой реплики в описании кластера своя учётка — вписанная явно или подразумеваемая, и тогда это беспарольный default. Подключение к соседней ноде идёт от неё, а не от того, кто задал вопрос. Значит, стоит default получить пароль — и без правки описания кластера межнодовые запросы встают. Починить это можно двумя способами, и они различаются не удобством. С учётными данными по репликам вся межшардовая работа идёт от одной общей учётки: на своей ноде права применяются, на соседней — нет. Ролевая модель тогда наполовину декорация, а это хуже её отсутствия: она утверждает разграничение, которого не существует. Общий секрет передаёт на соседа личность инициатора, стоит одной строки в описании кластера и не размножает пароль по репликам. Шифрование тут ни при чём; в соседнем clickhouse-learning-cluster выбран как раз путь с учётными данными по репликам, и там пароль вписан в каждую реплику каждого из трёх кластеров.

Довод подтверждён замером 13 августа 2026 года: на соседнем шарде сохранился пользователь-инициатор, поэтому там применились его права. Результат замера — в разделе «Что проверено».

Файлы, а не SQL. Объявленное файлом не оставляет состояния в томах: повторный make up на живом стенде ничего не сдвигает, правка пароля применяется перезапуском, источник истины — файл в git. Оба варианта с SQL — локальное хранилище доступа с ON CLUSTER и реплицированное в keeper — делают пользователей состоянием тома: .env на них больше не влияет, и появляется расхождение того же сорта, которое README уже описывает для Postgres и Grafana. Цена выбора известна: объявленных файлом пользователей нельзя менять запросами. Она принята как упрощение — сломать модель случайно труднее.

Что ещё отвергнуто. Оставить всё на беспарольном default — цена нулевая, но и рассказать по итогам нечего. Раздать всем пароль без ролей — дёшево и показывает ровно тот антипаттерн, за который ругают в бою: все под одним админом. Вынести доступ в лекцию, как репликационную эксплуатацию, — довод «отдельный операционный домен» здесь не работает: у репликации это живые процессы, учения и runbook, а тут несколько десятков строк настройки и ноль эксплуатации. Запереть default ещё и по сети — второй замок на ту же дверь.

Отложено явно: TLS и защищённые межсерверные соединения, настоящее хранилище секретов, внешние поставщики учётных записей, квоты и профили под пользователя, политики строк, аудит запросов.

Следствия

До появления слоёв DDS и DM читать bi почти нечего, и это намеренно. Дашборды рисуются поверх модели данных, а не поверх типизированных событий, поэтому доступ Superset к ODS не планировался и правами не выдаётся. Единственное, что ему доступно сегодня, — справочники: с ними зона dic пришла раньше своих потребителей.

Владелец матвью приёма определяется тем, кто применил 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 на чтение после появления базы.