backlog: задача no-secrets-in-playbook-output ужата до защиты от дрейфа
- сплошная простановка no_log сделана, инвентарь рендеров заменён на историю - осталась проверка в lefthook/CI и перенос miniflux_postgres_user и miniflux_postgres_database из vault в открытые group_vars - возврат читаемого диффа отдан серии secrets-file-*, приоритет понижен до среднего
This commit is contained in:
@@ -19,10 +19,10 @@ _(sec-ревью)_ — три независимых security-ревью на м
|
|||||||
|
|
||||||
- [Изолировать/запаролить сессионный redis Authelia](authelia-redis-isolation.md) — стор сессий SSO в общей сети без пароля; запись в него = hijack SSO всех сервисов _(sec-ревью)_
|
- [Изолировать/запаролить сессионный redis Authelia](authelia-redis-isolation.md) — стор сессий SSO в общей сети без пароля; запись в него = hijack SSO всех сервисов _(sec-ревью)_
|
||||||
- [Алерты на проблемные контейнеры](container-alerts.md) — wakapi крутился в restart-loop несколько дней незамеченным; healthcheck в compose + алерты Netdata
|
- [Алерты на проблемные контейнеры](container-alerts.md) — wakapi крутился в restart-loop несколько дней незамеченным; healthcheck в compose + алерты Netdata
|
||||||
- [Никаких секретов в выводе плейбуков](no-secrets-in-playbook-output.md) — `inv pl` всегда с `--diff`, и каждая задача, рендерящая файл с секретом, печатает его в терминал; `no_log` стоит ровно в одном месте из четырнадцати
|
|
||||||
|
|
||||||
## Средний
|
## Средний
|
||||||
|
|
||||||
|
- [Никаких секретов в выводе плейбуков](no-secrets-in-playbook-output.md) — сплошной `no_log` проставлен, но правило держится на внимании: нужна проверка в lefthook/CI, иначе следующее приложение заведут по образцу без него
|
||||||
- [Дампы бэкапов без группы `docker`](backup-dump-without-docker-group.md) — у miniflux/gitea/outline группа осталась: их backup.sh дампит через `docker compose exec` из-под самого приложения
|
- [Дампы бэкапов без группы `docker`](backup-dump-without-docker-group.md) — у miniflux/gitea/outline группа осталась: их backup.sh дампит через `docker compose exec` из-под самого приложения
|
||||||
- [Захардить мониторинг-стек (netdata / dozzle / goaccess)](harden-monitoring-stack.md) — netdata/dozzle: лишние привилегии + docker.sock + доверие Remote-User в общей сети; socket-proxy + monitoring_network _(sec-ревью)_
|
- [Захардить мониторинг-стек (netdata / dozzle / goaccess)](harden-monitoring-stack.md) — netdata/dozzle: лишние привилегии + docker.sock + доверие Remote-User в общей сети; socket-proxy + monitoring_network _(sec-ревью)_
|
||||||
- [Закрыть calibre-web forward-auth и проверить дефолтные креды](calibre-forward-auth.md) — домен без forward_auth, защита на встроенной авторизации с дефолтом `admin/admin123` _(sec-ревью)_
|
- [Закрыть calibre-web forward-auth и проверить дефолтные креды](calibre-forward-auth.md) — домен без forward_auth, защита на встроенной авторизации с дефолтом `admin/admin123` _(sec-ревью)_
|
||||||
|
|||||||
@@ -1,93 +1,42 @@
|
|||||||
# Никаких секретов в выводе плейбуков
|
# Никаких секретов в выводе плейбуков: защита от дрейфа
|
||||||
|
|
||||||
**Приоритет:** Высокий
|
**Приоритет:** средний
|
||||||
|
|
||||||
`inv pl` всегда запускает `ansible-playbook --diff`, поэтому любая задача
|
`inv pl` всегда запускает `ansible-playbook --diff`, поэтому любая задача
|
||||||
`template`/`copy`, чей целевой файл содержит переменную vault, печатает значение
|
`template`/`copy`, чей целевой файл содержит переменную vault, печатает значение
|
||||||
в терминал — целой строкой, при каждом изменении файла. `no_log: true` стоит
|
в терминал. Скроллбек терминала, мультиплексор, лог CI, транскрипт агента — всё
|
||||||
ровно в одном месте на весь репозиторий (`roles/secrets`), и именно поэтому
|
это места, куда расшифрованному значению попадать не должно, и куда оно попадает
|
||||||
miniflux деплоится молча, а остальные — нет.
|
бесплатно.
|
||||||
|
|
||||||
Это тот же класс, что инцидент 24 июля: секрет попадает не в vault-файл, а в
|
Сплошная простановка `no_log: true` сделана (коммит `b7b1f49`, 2026-07-25): все
|
||||||
поток вывода, у которого нет никакой модели угроз. Скроллбек терминала,
|
задачи-рендеры файлов с секретами закрыты, сегодня `no_log` стоит в четырнадцати
|
||||||
мультиплексор, лог CI, транскрипт агента — всё это места, куда расшифрованному
|
плейбуках и в `roles/secrets`. Осталось то, ради чего задача и заводилась в
|
||||||
значению попадать не должно, и куда оно попадает бесплатно.
|
категории «держится проверкой, а не вниманием»: сейчас правило живёт только во
|
||||||
|
внимании, и следующее приложение заведут по образцу без `no_log`.
|
||||||
|
|
||||||
## Инвентарь (сверено 2026-07-25)
|
## Что осталось
|
||||||
|
|
||||||
Задачи без `no_log`, рендерящие файл с секретом:
|
1. **Проверка в lefthook или CI**: если шаблон ссылается на переменную, которой
|
||||||
|
нет в открытых `group_vars`/`vars`, задача, которая его рендерит, обязана
|
||||||
- **backups** — `config.template.toml`, `restic-shell.template.sh`,
|
иметь `no_log`.
|
||||||
`rclone.template.conf`: пароли репозиториев restic, ключи S3, доступ rclone
|
2. **Убрать единственное исключение из правила.** Compose miniflux сознательно
|
||||||
- **authelia** — `configuration.template.yml`: jwt-секрет, секрет сессий, ключ
|
оставлен без `no_log` — он тянет из vault только `miniflux_postgres_user` и
|
||||||
шифрования хранилища, hmac и jwks OIDC, четыре client secret, SMTP; плюс
|
`miniflux_postgres_database`, а это имя пользователя и имя базы, не секреты.
|
||||||
`users.secrets.yml` — vault-файл копируется как есть, дифф покажет его
|
Чище не заводить исключение в проверке, а перенести обе переменные из vault в
|
||||||
расшифрованным
|
`group_vars/all/main.yml`. Правка в зашифрованном файле, поэтому за
|
||||||
- **transcriber** — `config.secrets.toml`, тоже vault-файл копией
|
пользователем.
|
||||||
- **bifrost** — `config.template.json`: ключ DeepSeek, ключ шифрования
|
|
||||||
- **apprise** — `backups.template.cfg`: SMTP, пароль matrix-бота
|
|
||||||
- **remembos** — `config.template.toml`: токены memos и Telegram
|
|
||||||
- **outline, tududi, wakapi, wanderer, gramps, gitea** — секреты в
|
|
||||||
`environment:` docker-compose, см. серию задач `secrets-file-*`
|
|
||||||
|
|
||||||
Чисто: miniflux (образец), memos, calibre, tuwunel, dashboard, caddy, netdata,
|
|
||||||
dozzle, goaccess, rssbridge, homepage.
|
|
||||||
|
|
||||||
`docker_login` в homepage, transcriber и remembos с `yc_oauth_token` в аргументах
|
|
||||||
модуля — снят: приложения собирают образы локально ролью `app_image`, реестра и
|
|
||||||
токена в плейбуках больше нет.
|
|
||||||
|
|
||||||
## Рычаги и их цена
|
|
||||||
|
|
||||||
- **`no_log: true`** на задачу — прячет результат целиком, включая дифф. Так уже
|
|
||||||
сделано в `roles/secrets`. Цена: не видно, что именно изменилось; на отладке
|
|
||||||
снимать руками.
|
|
||||||
- **`diff: false`** на задачу — прячет только содержимое диффа, статус `changed`
|
|
||||||
остаётся виден. Хирургичнее, но не закрывает вывод при падении задачи, поэтому
|
|
||||||
для файлов, состоящих из одних секретов, слабее.
|
|
||||||
- **Структурный вынос** — секрет живёт в отдельном файле, который рендерит роль
|
|
||||||
`secrets` под `no_log`, а основной файл ссылается на путь. Тогда дифф основного
|
|
||||||
файла остаётся полезным. Это серия `secrets-file-*`, но она про `environment:`
|
|
||||||
и не покрывает конфиги.
|
|
||||||
- **Убрать `--diff` из `inv pl`** или выставить глобальный `ANSIBLE_NO_LOG` —
|
|
||||||
режет обзор на всех задачах разом ради десятка проблемных. Не годится.
|
|
||||||
|
|
||||||
## Порядок
|
|
||||||
|
|
||||||
1. ~~`no_log: true` на весь инвентарь выше~~ — **сделано** (коммит `b7b1f49`,
|
|
||||||
2026-07-25): пятнадцать задач-рендеров и три `docker_login`, всего 13
|
|
||||||
плейбуков. Не под `no_log` осталась одна задача — compose miniflux: он тянет
|
|
||||||
из vault только `miniflux_postgres_user` и `miniflux_postgres_database`, а это
|
|
||||||
имя пользователя и имя базы, не секреты (в `secrets_vars` роли `secrets` их
|
|
||||||
нет). Дифф образцового файла оставлен читаемым сознательно.
|
|
||||||
2. Где файл смешанный (compose, конфиг authelia) — вернуться и вынести секреты в
|
|
||||||
отдельные файлы, сняв `no_log` с основного: он нужен ради диффа, который сейчас
|
|
||||||
и теряется. Что именно умеет каждое приложение — матрица в
|
|
||||||
[справке](../drafts/secrets-file-support.md), сверенная по исходникам, и
|
|
||||||
задачи `secrets-file-*` по одной на приложение. Для
|
|
||||||
authelia (конфиг на тысячу строк, дифф там самый ценный) ответ такой: пять
|
|
||||||
полей закрываются `AUTHELIA_..._FILE`, оставшиеся два — только
|
|
||||||
template-фильтром, а четыре `client_secret` штатно хранятся хэшем и тогда
|
|
||||||
вообще перестают быть секретами. Для tududi и wanderer ответа нет: они не
|
|
||||||
умеют читать секреты из файлов, и `no_log` для них — не первый шаг, а
|
|
||||||
единственный.
|
|
||||||
3. Защита от дрейфа: проверка в lefthook или CI — если шаблон ссылается на
|
|
||||||
переменную, которой нет в открытых `group_vars`/`vars`, задача, которая его
|
|
||||||
рендерит, обязана иметь `no_log`. Иначе следующее приложение заведут по
|
|
||||||
образцу без него. Единственное исключение на сегодня — miniflux (см. шаг 1);
|
|
||||||
чище не заводить исключение в правиле, а перенести `miniflux_postgres_user` и
|
|
||||||
`miniflux_postgres_database` из vault в `group_vars/all/main.yml` — секретами
|
|
||||||
они не являются. Правка в зашифрованном файле, поэтому за пользователем.
|
|
||||||
|
|
||||||
Готово, когда первый (создающий все файлы) прогон любого плейбука не печатает ни
|
|
||||||
одного значения из vault, и это держится проверкой, а не вниманием.
|
|
||||||
|
|
||||||
Попутно стоит выяснить, почему молчит правило `no-log-password` в ansible-lint:
|
Попутно стоит выяснить, почему молчит правило `no-log-password` в ansible-lint:
|
||||||
похоже, оно смотрит на список модулей с параметром `password`, а не на содержимое
|
похоже, оно смотрит на список модулей с параметром `password`, а не на содержимое
|
||||||
шаблонов — тогда на него в этой задаче полагаться нельзя.
|
шаблонов — тогда на него в этой задаче полагаться нельзя.
|
||||||
|
|
||||||
Связано: tasks.py (`inv pl`), roles/secrets (образец), files/backups,
|
## Что сюда больше не входит
|
||||||
files/authelia, files/bifrost, files/apprise, files/remembos,
|
|
||||||
files/transcriber/config.secrets.toml,
|
Возврат читаемого диффа там, где файл смешанный (compose, конфиг authelia) —
|
||||||
[vault-password-storage](vault-password-storage.md),
|
это вынос секретов в отдельные файлы, и он живёт отдельной серией задач
|
||||||
|
`secrets-file-*`, по одной на приложение. Матрица возможностей приложений —
|
||||||
|
в [справке](../drafts/secrets-file-support.md).
|
||||||
|
|
||||||
|
Связано: lefthook.yml, tasks.py (`inv pl`), roles/secrets (образец),
|
||||||
|
playbook-miniflux.yml, [vault-password-storage](vault-password-storage.md),
|
||||||
[narrow-secret-scopes](narrow-secret-scopes.md).
|
[narrow-secret-scopes](narrow-secret-scopes.md).
|
||||||
|
|||||||
Reference in New Issue
Block a user