Общие секреты
Одно значение на несколько проектов — без копий, которые расходятся.
Зачем
Доступы к репозиторию, ключ платёжного шлюза, настройки почты одинаковы во всех проектах. Если завести их в каждом отдельно, через месяц у вас пять «одинаковых» значений, из которых два уже разошлись, а какое из них верное — выяснится в тот день, когда пароль поменяют.
Общий секрет живёт в одном экземпляре. Меняете в одном месте — меняется у всех.
Как устроено
Окружение проекта может дополнительно читать ключи окружения другого проекта той же организации. Это ссылка, а не копия: значение остаётся в источнике и физически никуда не переносится.
billing/prod ← common/prod
DB_PASSWORD SMTP_PASSWORD
(свой) GIT_TOKEN
Проект billing в окружении prod отдаёт три ключа: свой DB_PASSWORD и два
пришедших из common. SDK и CLI получают их одинаково — по ссылке запись
приезжает неотличимо от собственной.
Что происходит при чтении
Значения зашифрованы ключом данных своего окружения. Сервер расшифровывает общий секрет ключом источника и зашифровывает заново ключом окружения, куда он приезжает. Поэтому:
- в ответе по-прежнему один ключ данных — тот, что у потребителя;
- ваш SDK не нужно обновлять: формат ответа не изменился;
- отключение не требует перешифрования — в окружение потребителя ничего не записывалось.
Как подключить
Заведите проект с общими значениями — например common — и положите туда
ключи, как в обычный проект. Затем в разделе Окружения проекта-потребителя,
в блоке «Общие секреты», нажмите «Подключить» и выберите:
| Поле | Что значит |
|---|---|
| Окружение этого проекта | куда придут чужие ключи |
| Проект-источник | где лежат общие значения |
| Окружение источника | какое именно окружение источника читать |
Подключение делается на каждое окружение отдельно. Это не лишняя работа, а
то, ради чего модель и устроена так: prod вашего сервиса читает common/prod,
а dev — common/dev, с другими значениями тех же ключей.
Связывать окружения может владелец организации. Причина не в иерархии, а в том, что подключение — операция с двумя сторонами. Разрешить её только получателю значило бы: администратор своего проекта подключается к чужому окружению и читает его. Разрешить только источнику — человек без роли в проекте молча меняет его конфигурацию. Владелец заведомо имеет права с обеих сторон.
Через API та же операция — один запрос (источник указывается slug'ом):
curl -s $BASE/projects/$PROJECT/links \
-H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
-d '{"environment":"production","fromProject":"common","fromEnvironment":"production"}'
Отключение — DELETE /projects/:id/links/:linkId, список подключений —
GET /projects/:id/links.
Своё всегда сильнее общего
Если в окружении есть свой ключ с тем же именем, он выигрывает. Общий в этот момент не отдаётся вовсе — ни SDK, ни в таблице.
Это способ сделать исключение: подключить общий SMTP везде, а в одном проекте переопределить его своим. Ничего отключать не нужно.
Переопределение обязано совпасть с источником по типу и видимости. Секрет нельзя перекрыть переменной: иначе значение, которое источник объявил секретом, стало бы открытым текстом для всех, кто читает это окружение.
Правка общего ключа в проекте-потребителе не меняет его в источнике — она создаёт местное переопределение. Кабинет предупреждает об этом до сохранения, но знать стоит заранее: «поменял пароль всем» и «поменял пароль себе» здесь выглядят одинаково.
Два источника с одним ключом
Отклоняются при подключении. Не выбирается победитель, не берётся «последний подключённый» — операция просто не проходит, с указанием, какой проект уже приносит этот ключ.
Молчаливый приоритет по порядку подключения — это конфигурация, которую нельзя отладить в два часа ночи, когда сервис читает не то значение.
Отключение
Удаляет ссылку. Со следующего запроса ключи не приходят; перешифровывать нечего.
Отключение не вынимает значения из памяти уже запущенных процессов — ровно как удаление записи или отзыв identity. Отзыв прекращает выдачу, а не стирает прошлое. Если секрет скомпрометирован, его нужно менять, а не отключать, — и в этом главный выигрыш модели: менять придётся один раз.
Вебхуки
Изменение общего секрета доходит и до подключённых проектов: событие
entry.updated приходит на вебхуки каждого потребителя, а не только источника,
с полем fromProjectId в метаданных.
Если ключ перекрыт своим значением, потребителю не сообщают — для него ничего не изменилось.
Как перенести то, что уже разложено по проектам
Раздел Проекты показывает владельцу организации, какие ключи держат одинаковое значение в разных проектах. Совпадение находится по отпечатку значения, без расшифровки: у одинаковых значений одинаковый HMAC.
Отчёт видит только владелец, и по той же причине, по какой только он связывает окружения. Отпечаток не солится — иначе отчёт был бы невозможен, — а значит фраза «здесь то же значение, что там» отвечает на вопрос о секрете, к которому у спрашивающего может не быть доступа. Владельцу это ничего не добавляет: он и так выдаёт себе любую роль. Построение отчёта попадает в журнал.
Переносить можно на работающем сервисе, и порядок важен:
- Завести ключ в общем проекте с тем же значением.
- Подключить общий проект к окружениям-потребителям. Эффективная конфигурация не меняется: копии ещё на месте и перекрывают ссылку.
- Удалить локальные копии. Значение переключается на общее — то же самое, отпечаток тот же, потребители разницы не видят.
Ключи с одинаковым именем, но разными значениями отчёт помечает отдельно. Это не дубль, а расхождение: какое из значений правильное, машина решить не может.
Ограничения
- Ссылки не транзитивны: если A читает B, а B читает C, то A не получит ключи C. Кольцо из ссылок отклоняется при создании — не потому, что зациклит сервер, а потому что означало бы ожидание транзитивности, которой нет.
- Ссылки не выходят за организацию.
- Удаление проекта или окружения снимает все ссылки, которые в него упирались.