backlog: secrets-env-to-file разложена по приложениям
- Шесть задач `secrets-file-*` вместо одной: outline, wakapi, authelia, gramps (средний), gitea, tududi (низкий, выигрыш частичный). Wanderer отпал — файловых секретов не умеет ни meilisearch, ни pocketbase. - Матрица механизмов со ссылками на код уехала в docs/drafts/secrets-file-support.md, родитель — на кладбище.
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
# Кладбище беклога
|
||||||
|
- 2026-07-25 `secrets-env-to-file` — Секреты приложений через `*_FILE`, а не inline `environment:`. Причина: разложена по приложениям на secrets-file-outline/-wakapi/-authelia/-gramps/-gitea/-tududi; матрица поддержки уехала в docs/drafts/secrets-file-support.md, wanderer отпал — файловых секретов не умеет. Был приоритет: средний.
|
||||||
@@ -25,7 +25,6 @@ _(sec-ревью)_ — три независимых security-ревью на м
|
|||||||
|
|
||||||
## Средний
|
## Средний
|
||||||
|
|
||||||
- [Секреты приложений через `*_FILE`, а не inline `environment:`](secrets-env-to-file.md) — outline/wakapi/tududi/gitea/gramps/tuwunel держат секреты в env; видны в `docker inspect` и `/proc` _(sec-ревью)_
|
|
||||||
- [Захардить мониторинг-стек (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-ревью)_
|
||||||
- [Ограничить эндпоинт `/metrics` Miniflux](miniflux-metrics-restrict.md) — `METRICS_ALLOWED_NETWORKS=0.0.0.0/0` + прокси без ограничения пути отдаёт метрики публично _(sec-ревью)_
|
- [Ограничить эндпоинт `/metrics` Miniflux](miniflux-metrics-restrict.md) — `METRICS_ALLOWED_NETWORKS=0.0.0.0/0` + прокси без ограничения пути отдаёт метрики публично _(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-ревью)_
|
||||||
@@ -39,6 +38,10 @@ _(sec-ревью)_ — три независимых security-ревью на м
|
|||||||
- [Composable-роль `backup`](ansible-backup-role.md) — бэкап — самый чистый шов для extraction (одинаков у всех, различается только список targets) _(ансибл-ревью)_
|
- [Composable-роль `backup`](ansible-backup-role.md) — бэкап — самый чистый шов для extraction (одинаков у всех, различается только список targets) _(ансибл-ревью)_
|
||||||
- [Локальная сборка образов вместо Yandex Container Registry](local-image-build.md) — homepage/transcriber/remembos тянут образы из cr.yandex, ради `docker login` в vault лежит OAuth-токен всего аккаунта; роль `app_image` уже обкатана в umbar
|
- [Локальная сборка образов вместо Yandex Container Registry](local-image-build.md) — homepage/transcriber/remembos тянут образы из cr.yandex, ради `docker login` в vault лежит OAuth-токен всего аккаунта; роль `app_image` уже обкатана в umbar
|
||||||
- [Вывести из оборота секреты с широкими правами](narrow-secret-scopes.md) — один `yc_oauth_token` открывает весь аккаунт Yandex Cloud; после инцидента 24 июля цена утечки одной переменной должна быть ограничена одним сервисом
|
- [Вывести из оборота секреты с широкими правами](narrow-secret-scopes.md) — один `yc_oauth_token` открывает весь аккаунт Yandex Cloud; после инцидента 24 июля цена утечки одной переменной должна быть ограничена одним сервисом
|
||||||
|
- [outline: секреты из файлов](secrets-file-outline.md) — умеет generic `<NAME>_FILE` для всех пяти секретов; единственное приложение, где выносится вообще всё, включая пароль БД
|
||||||
|
- [wakapi: секреты из файлов](secrets-file-wakapi.md) — salt, SMTP и OIDC выносятся либо через generic `_FILE`, либо через `config.yml`; для OIDC второй путь надёжнее из-за бага с `=` в значении
|
||||||
|
- [authelia: секреты из файлов](secrets-file-authelia.md) — пять полей закрывает `_FILE`, jwks-ключ и client secret — только template-фильтр; заодно четыре client secret можно перевести в хэш и перестать считать секретами
|
||||||
|
- [gramps: секретный ключ из файла](secrets-file-gramps.md) — entrypoint читает `/app/secret/secret`, если переменная не задана, и этот путь уже смонтирован — самая дешёвая правка из всей серии
|
||||||
|
|
||||||
## Низкий
|
## Низкий
|
||||||
|
|
||||||
@@ -50,3 +53,5 @@ _(sec-ревью)_ — три независимых security-ревью на м
|
|||||||
- [Фоновая зачистка стиля и конфигурации Ansible](ansible-style-nits.md) — sudoers.d, профиль ansible-lint, `ansible.cfg`, кавычки, `cache_valid_time` _(ансибл-ревью)_
|
- [Фоновая зачистка стиля и конфигурации Ansible](ansible-style-nits.md) — sudoers.d, профиль ansible-lint, `ansible.cfg`, кавычки, `cache_valid_time` _(ансибл-ревью)_
|
||||||
- [Logrotate для логов бэкапов](backup-logs-logrotate.md) — логи `backup-all.py`/restic копятся без ротации _(tududi)_
|
- [Logrotate для логов бэкапов](backup-logs-logrotate.md) — логи `backup-all.py`/restic копятся без ротации _(tududi)_
|
||||||
- [[idea] Где хранить пароль ansible-vault](vault-password-storage.md) — пароль открытым текстом рядом с репо, `ansible.cfg` подставляет его в любую ansible-команду; вариант — доставать из KeePassXC
|
- [[idea] Где хранить пароль ansible-vault](vault-password-storage.md) — пароль открытым текстом рядом с репо, `ansible.cfg` подставляет его в любую ansible-команду; вариант — доставать из KeePassXC
|
||||||
|
- [gitea: пароль SMTP из файла](secrets-file-gitea.md) — умеет `GITEA__mailer__PASSWD__FILE`, но пишет значение открытым текстом в `app.ini` — выигрыш только против `docker inspect`
|
||||||
|
- [tududi: секреты в смонтированный `.env`](secrets-file-tududi.md) — файловых секретов не умеет вовсе; единственный путь — смонтировать `.env` под `dotenv`, чтобы значения не попадали в `Config.Env`
|
||||||
|
|||||||
@@ -39,4 +39,4 @@ transcriber и remembos. Радиус прав несопоставим с за
|
|||||||
Связано: group_vars/all/secrets.yml, playbook-homepage.yml,
|
Связано: group_vars/all/secrets.yml, playbook-homepage.yml,
|
||||||
playbook-transcriber.yml, playbook-remembos.yml,
|
playbook-transcriber.yml, playbook-remembos.yml,
|
||||||
[vault-password-storage](vault-password-storage.md),
|
[vault-password-storage](vault-password-storage.md),
|
||||||
[secrets-env-to-file](secrets-env-to-file.md).
|
[справка по файловым секретам](../drafts/secrets-file-support.md).
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ miniflux деплоится молча, а остальные — нет.
|
|||||||
- **apprise** — `server.template.cfg`: токен бота Telegram, SMTP
|
- **apprise** — `server.template.cfg`: токен бота Telegram, SMTP
|
||||||
- **remembos** — `config.template.toml`: токены memos и Telegram
|
- **remembos** — `config.template.toml`: токены memos и Telegram
|
||||||
- **outline, tududi, wakapi, wanderer, gramps, gitea** — секреты в
|
- **outline, tududi, wakapi, wanderer, gramps, gitea** — секреты в
|
||||||
`environment:` docker-compose, см. [secrets-env-to-file](secrets-env-to-file.md)
|
`environment:` docker-compose, см. серию задач `secrets-file-*`
|
||||||
|
|
||||||
Чисто: miniflux (образец), memos, calibre, tuwunel, dashboard, caddy, netdata,
|
Чисто: miniflux (образец), memos, calibre, tuwunel, dashboard, caddy, netdata,
|
||||||
dozzle, goaccess, rssbridge, homepage.
|
dozzle, goaccess, rssbridge, homepage.
|
||||||
@@ -48,8 +48,8 @@ dozzle, goaccess, rssbridge, homepage.
|
|||||||
для файлов, состоящих из одних секретов, слабее.
|
для файлов, состоящих из одних секретов, слабее.
|
||||||
- **Структурный вынос** — секрет живёт в отдельном файле, который рендерит роль
|
- **Структурный вынос** — секрет живёт в отдельном файле, который рендерит роль
|
||||||
`secrets` под `no_log`, а основной файл ссылается на путь. Тогда дифф основного
|
`secrets` под `no_log`, а основной файл ссылается на путь. Тогда дифф основного
|
||||||
файла остаётся полезным. Это [secrets-env-to-file](secrets-env-to-file.md), но
|
файла остаётся полезным. Это серия `secrets-file-*`, но она про `environment:`
|
||||||
она про `environment:` и не покрывает конфиги.
|
и не покрывает конфиги.
|
||||||
- **Убрать `--diff` из `inv pl`** или выставить глобальный `ANSIBLE_NO_LOG` —
|
- **Убрать `--diff` из `inv pl`** или выставить глобальный `ANSIBLE_NO_LOG` —
|
||||||
режет обзор на всех задачах разом ради десятка проблемных. Не годится.
|
режет обзор на всех задачах разом ради десятка проблемных. Не годится.
|
||||||
|
|
||||||
@@ -60,7 +60,8 @@ dozzle, goaccess, rssbridge, homepage.
|
|||||||
2. Где файл смешанный (compose, конфиг authelia) — вернуться и вынести секреты в
|
2. Где файл смешанный (compose, конфиг authelia) — вернуться и вынести секреты в
|
||||||
отдельные файлы, сняв `no_log` с основного: он нужен ради диффа, который сейчас
|
отдельные файлы, сняв `no_log` с основного: он нужен ради диффа, который сейчас
|
||||||
и теряется. Что именно умеет каждое приложение — матрица в
|
и теряется. Что именно умеет каждое приложение — матрица в
|
||||||
[secrets-env-to-file](secrets-env-to-file.md), сверенная по исходникам. Для
|
[справке](../drafts/secrets-file-support.md), сверенная по исходникам, и
|
||||||
|
задачи `secrets-file-*` по одной на приложение. Для
|
||||||
authelia (конфиг на тысячу строк, дифф там самый ценный) ответ такой: пять
|
authelia (конфиг на тысячу строк, дифф там самый ценный) ответ такой: пять
|
||||||
полей закрываются `AUTHELIA_..._FILE`, оставшиеся два — только
|
полей закрываются `AUTHELIA_..._FILE`, оставшиеся два — только
|
||||||
template-фильтром, а четыре `client_secret` штатно хранятся хэшем и тогда
|
template-фильтром, а четыре `client_secret` штатно хранятся хэшем и тогда
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
# Секреты приложений через `*_FILE`, а не inline `environment:`
|
|
||||||
|
|
||||||
**Приоритет:** средний
|
|
||||||
|
|
||||||
miniflux сделан правильно — роль `secrets` кладёт каждую переменную vault в
|
|
||||||
отдельный файл, каталог монтируется `:ro`, а compose ссылается на `*_FILE`
|
|
||||||
(`files/miniflux/docker-compose.template.yml`). Остальные держат секреты прямыми
|
|
||||||
значениями в `environment:`: их читает любой член группы `docker`, любой процесс
|
|
||||||
с доступом к `/proc/<pid>/environ`, и они попадают в `docker inspect`.
|
|
||||||
|
|
||||||
tuwunel из списка убран: в его compose секретов нет (сверено 2026-07-25). memos и
|
|
||||||
calibre тоже чистые.
|
|
||||||
|
|
||||||
## Матрица поддержки (проверено по исходникам, 2026-07-25)
|
|
||||||
|
|
||||||
Проверялись **закреплённые в compose версии**, не `main`.
|
|
||||||
|
|
||||||
| Приложение | Секрет | Файл | Механизм |
|
|
||||||
|---|---|---|---|
|
|
||||||
| **outline** `1.9.2` | `SECRET_KEY`, `UTILS_SECRET`, OIDC, SMTP | да | generic: `environment.ts` оборачивает `process.env` в Proxy, пустая переменная читается из `<NAME>_FILE`. С v1.7.0 |
|
|
||||||
| | пароль postgres | да, с оговоркой | `DATABASE_URL_FILE` выносит URL целиком; чтобы вынести только пароль — раздельные `DATABASE_HOST/PORT/NAME/USER` + `DATABASE_PASSWORD_FILE` (взаимоисключимы с `DATABASE_URL`) |
|
|
||||||
| | пароль сайдкара postgres | да | штатный `POSTGRES_PASSWORD_FILE` официального образа |
|
|
||||||
| **wakapi** `2.17.5` | salt, SMTP, OIDC | да | два пути: generic `_FILE` в `loadSecretFiles()` (с 2.17.2, в README список устарел) либо `config.yml` в `/app/config.yml` |
|
|
||||||
| **gitea** `1.27.0` | SMTP | да, частично | `GITEA__mailer__PASSWD__FILE`, работает для любого ключа `app.ini`, с 1.20 |
|
|
||||||
| **gramps** `26.7.0` | `GRAMPSWEB_SECRET_KEY` | да | entrypoint читает `/app/secret/secret`, если переменная не задана. **Путь уже смонтирован** в нашем compose |
|
|
||||||
| | SMTP | нет | обходной путь есть, но недокументированный: `config.cfg` исполняется Flask как Python, значит `EMAIL_HOST_PASSWORD = open(...).read()` сработает |
|
|
||||||
| **authelia** `4.39.20` | jwt-секрет, session, ключ хранилища, hmac OIDC, SMTP | да | `AUTHELIA_..._FILE` |
|
|
||||||
| | jwks-ключ, `client_secret` × 4 | нет через `_FILE` | `IsSecretKey()` отвергает ключи с `[]`. Покрывается template-фильтром: `X_AUTHELIA_CONFIG_FILTERS=template` + `{{ secret "путь" }}` (с 4.38) |
|
|
||||||
| **tududi** `1.2.4` | все четыре | **нет** | всё через `process.env`; ни `_FILE`, ни конфига, ни CLI |
|
|
||||||
| **wanderer** `0.18.3` | мастер-ключ meilisearch | **нет** | только CLI/env/инлайн в `config.toml`; апстрим отклонил запрос в 2023 |
|
|
||||||
| | ключ шифрования pocketbase | **нет** | собственная переменная wanderer, не штатный `--encryptionEnv` pocketbase; ENTRYPOINT в exec-форме, подстановку не сделать |
|
|
||||||
|
|
||||||
## Что из этого следует
|
|
||||||
|
|
||||||
Самое дешёвое — **gramps**: каталог `/app/secret` уже смонтирован, достаточно
|
|
||||||
записать туда значение и убрать переменную из `environment:`. Оба сервиса
|
|
||||||
(`gramps_app` и `gramps_celery`) читают тот же файл через общий entrypoint.
|
|
||||||
|
|
||||||
**Authelia** выгоднее переводить не на `_FILE`, а целиком на template-фильтр:
|
|
||||||
`_FILE` покрывает пять полей из семи, фильтр — все разом, и смешивать их в одном
|
|
||||||
поле нельзя (`errFmtSecretAlreadyDefined`). Отдельная находка: четыре
|
|
||||||
`client_secret` штатно хранятся **хэшем** `$pbkdf2-sha512$...` (у нас уже есть
|
|
||||||
`inv authelia-gen-secret-and-hash`), плейнтекст с 4.38 вызывает warning
|
|
||||||
валидатора. То есть эти поля можно вообще перестать считать секретами.
|
|
||||||
|
|
||||||
**Gitea** даёт частичный выигрыш: секрет уходит из `environment:`, `docker
|
|
||||||
inspect` и окружения процесса, но `environment-to-ini` пишет его открытым
|
|
||||||
текстом в `app.ini` на диске — открытая проблема апстрима (#35316).
|
|
||||||
|
|
||||||
**tududi и wanderer не умеют ничего.** Для них остаётся либо принять env, либо
|
|
||||||
перенести переменные в файл на диске: у tududi `backend/app.js` зовёт `dotenv`
|
|
||||||
при рабочей директории `/app/backend`, поэтому смонтированный туда `.env` уберёт
|
|
||||||
секреты хотя бы из `docker inspect` — в отличие от `env_file:`, где они попадают
|
|
||||||
в `Config.Env`. У wanderer доступен только `env_file:`, то есть косметика.
|
|
||||||
|
|
||||||
Попутная находка в закомментированном OIDC-блоке wakapi (ловушка на будущее, не
|
|
||||||
живой баг): имена `WAKAPI_OIDC_PROVIDER_*` устарели, в 2.17.5 разбирается
|
|
||||||
`WAKAPI_OIDC_PROVIDERS_(\d+)_(...)`; кроме того `renameEnvVars()` режет строку по
|
|
||||||
первому `=`, так что секрет с base64-паддингом через env поедет битым. Оба
|
|
||||||
довода — за `config.yml`.
|
|
||||||
|
|
||||||
## Docker secrets — разобрано, покрытия не расширяет
|
|
||||||
|
|
||||||
`secrets:` в compose вне swarm — это bind-mount файла в `/run/secrets/<name>`,
|
|
||||||
то есть **тот же способ доставки**, что и `*_FILE`. Приложение, умеющее только
|
|
||||||
переменную окружения, из `/run/secrets` не прочитает — ограничение в приложении,
|
|
||||||
а не в доставке. На таблицу выше не влияет.
|
|
||||||
|
|
||||||
Что даёт: секрет объявляется на сервис, а не монтируется каталогом. У miniflux
|
|
||||||
сейчас `{{ secrets_dir }}:/secrets:ro` висит на обоих сервисах, и postgres видит
|
|
||||||
все шесть файлов, включая секреты OIDC, хотя нужен ему один пароль. Улучшение
|
|
||||||
поверх образца, а не замена ему. `uid`/`gid`/`mode` вне swarm игнорируются —
|
|
||||||
права остаются с хост-файла, их ставит роль `secrets`.
|
|
||||||
|
|
||||||
## Границы задачи
|
|
||||||
|
|
||||||
Она про то, где секрет **лежит в рантайме** (env против файла). Про то, чтобы он
|
|
||||||
не печатался в терминал при деплое, — [no-secrets-in-playbook-output](no-secrets-in-playbook-output.md),
|
|
||||||
и она идёт первой: `no_log` дёшев, закрывает все приложения разом и не зависит от
|
|
||||||
этой матрицы. Для tududi и wanderer он вообще единственный ответ.
|
|
||||||
|
|
||||||
Матрица показала, что задача переросла в эпик: каждая строка — отдельное
|
|
||||||
приложение со своим механизмом, и каждое мерджится независимо и само по себе
|
|
||||||
даёт пользу. Договорились разбить по приложениям отдельным заходом (2026-07-25);
|
|
||||||
до этого задачу в работу не брать целиком.
|
|
||||||
|
|
||||||
Из sec-ревью (fable, 2026-07-13). Связка с
|
|
||||||
[service-users-docker-group](service-users-docker-group.md).
|
|
||||||
|
|
||||||
Связано: files/outline, files/wakapi, files/tududi, files/wanderer, files/gramps,
|
|
||||||
files/gitea, files/authelia, files/miniflux (образец), roles/secrets.
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# authelia: секреты из файлов
|
||||||
|
|
||||||
|
**Приоритет:** Средний
|
||||||
|
|
||||||
|
В `configuration.template.yml` прямыми значениями стоят семь позиций:
|
||||||
|
jwt-секрет валидации, секрет сессий, ключ шифрования хранилища, hmac-секрет OIDC,
|
||||||
|
приватный jwks-ключ, четыре `client_secret` и пароль SMTP. Файл на тысячу строк —
|
||||||
|
дифф по нему самый ценный в репозитории, и именно его сейчас приходится глушить
|
||||||
|
целиком.
|
||||||
|
|
||||||
|
Пять полей закрываются переменными `AUTHELIA_..._FILE`. Оставшиеся два —
|
||||||
|
`jwks[].key` и `client_secret` клиентов — через `_FILE` невозможны в принципе:
|
||||||
|
валидатор отвергает ключи, содержащие `[]`. Их покрывает template-фильтр
|
||||||
|
(`X_AUTHELIA_CONFIG_FILTERS=template` + `{{ secret "путь" }}`). Механизмы
|
||||||
|
несовместимы в одном поле, поэтому проще выбрать фильтр — он берёт все семь
|
||||||
|
позиций разом, `_FILE` только пять.
|
||||||
|
|
||||||
|
Отдельная и более дешёвая половина задачи: четыре `client_secret` штатно хранятся
|
||||||
|
**хэшем** `$pbkdf2-sha512$...` — плейнтекст с 4.38 вызывает warning валидатора и
|
||||||
|
нужен только при `client_secret_jwt`, чего у наших клиентов нет. После перевода
|
||||||
|
эти поля перестают быть секретами вообще. Генератор уже есть —
|
||||||
|
`inv authelia-gen-secret-and-hash`, проверка — `inv authelia-validate-config`.
|
||||||
|
|
||||||
|
Готово, когда в отрендеренном конфиге не осталось секретов открытым текстом и
|
||||||
|
`no_log` с задачи снят — дифф конфига снова читаем.
|
||||||
|
|
||||||
|
Разбор по каждому полю, версии и ссылки на код:
|
||||||
|
[справка по файловым секретам](../drafts/secrets-file-support.md).
|
||||||
|
Из декомпозиции `secrets-env-to-file` (2026-07-25).
|
||||||
|
|
||||||
|
Связано: files/authelia, tasks.py (`authelia-*`), roles/secrets,
|
||||||
|
[no-secrets-in-playbook-output](no-secrets-in-playbook-output.md).
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# gitea: пароль SMTP из файла
|
||||||
|
|
||||||
|
**Приоритет:** Низкий
|
||||||
|
|
||||||
|
Единственный секрет gitea в compose — пароль SMTP. Механизм есть:
|
||||||
|
`GITEA__mailer__PASSWD__FILE` работает для любого ключа `app.ini` начиная с 1.20,
|
||||||
|
у нас 1.27.0.
|
||||||
|
|
||||||
|
Приоритет низкий из-за оговорки, которая съедает половину пользы:
|
||||||
|
`environment-to-ini` **записывает содержимое файла в `app.ini` открытым текстом**,
|
||||||
|
то есть пароль всё равно оседает на диске в `<data_dir>/gitea/conf/app.ini`. Это
|
||||||
|
открытая проблема апстрима (#35316), исправления нет. Выигрыш реальный, но
|
||||||
|
частичный — секрет уходит из `environment:`, `docker inspect` и окружения
|
||||||
|
процесса, а с диска не уходит.
|
||||||
|
|
||||||
|
Готово, когда `docker inspect` контейнера gitea не показывает пароль SMTP. Что он
|
||||||
|
остался в `app.ini` — известное ограничение, а не незакрытая часть задачи.
|
||||||
|
|
||||||
|
Механизм и ссылки на код:
|
||||||
|
[справка по файловым секретам](../drafts/secrets-file-support.md).
|
||||||
|
Из декомпозиции `secrets-env-to-file` (2026-07-25).
|
||||||
|
|
||||||
|
Связано: files/gitea, roles/secrets,
|
||||||
|
[no-secrets-in-playbook-output](no-secrets-in-playbook-output.md).
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# gramps: секретный ключ из файла
|
||||||
|
|
||||||
|
**Приоритет:** Средний
|
||||||
|
|
||||||
|
`GRAMPSWEB_SECRET_KEY` стоит прямым значением в `environment:`. Если переменную
|
||||||
|
не задавать, entrypoint образа читает ключ из файла `/app/secret/secret` — и этот
|
||||||
|
путь **уже смонтирован** в нашем compose как `gramps_secret`. То есть правка
|
||||||
|
сводится к «записать значение в файл и убрать переменную»: самая дешёвая из всей
|
||||||
|
серии. Entrypoint общий у `gramps_app` и `gramps_celery`, оба читают тот же файл,
|
||||||
|
расхождения не будет.
|
||||||
|
|
||||||
|
Пароль SMTP так не выносится: конфиг читается через `from_prefixed_env`, никакой
|
||||||
|
обработки `_FILE` нет. Обходной путь существует (Flask исполняет `config.cfg` как
|
||||||
|
Python, значит значение можно прочитать из файла прямо в нём), но он держится на
|
||||||
|
детали реализации и нигде не обещан — брать только если решим, что оно того
|
||||||
|
стоит. Пока пароль SMTP остаётся в env и закрывается `no_log`.
|
||||||
|
|
||||||
|
Готово, когда `docker inspect` контейнеров gramps не показывает секретный ключ
|
||||||
|
приложения.
|
||||||
|
|
||||||
|
Механизм и ссылки на код:
|
||||||
|
[справка по файловым секретам](../drafts/secrets-file-support.md).
|
||||||
|
Из декомпозиции `secrets-env-to-file` (2026-07-25).
|
||||||
|
|
||||||
|
Связано: files/gramps, roles/secrets,
|
||||||
|
[no-secrets-in-playbook-output](no-secrets-in-playbook-output.md).
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# outline: секреты из файлов
|
||||||
|
|
||||||
|
**Приоритет:** Средний
|
||||||
|
|
||||||
|
Сейчас в `environment:` стоят прямыми значениями пароль postgres (внутри
|
||||||
|
`DATABASE_URL`), `SECRET_KEY`, `UTILS_SECRET`, секрет OIDC и пароль SMTP. Outline
|
||||||
|
`1.9.2` умеет generic-механизм: пустая переменная читается из `<NAME>_FILE`,
|
||||||
|
поэтому все пять закрываются по образцу miniflux — роль `secrets` + монтирование
|
||||||
|
каталога `:ro`.
|
||||||
|
|
||||||
|
Отдельный шаг внутри задачи — пароль БД: `DATABASE_URL_FILE` выносит **весь** URL
|
||||||
|
одним куском, и чтобы вынести именно пароль, нужен переход на раздельные
|
||||||
|
`DATABASE_HOST/PORT/NAME/USER` (они взаимоисключимы с `DATABASE_URL`). Заодно
|
||||||
|
сайдкар postgres переводится на штатный `POSTGRES_PASSWORD_FILE`.
|
||||||
|
|
||||||
|
Готово, когда `docker inspect` контейнеров outline и его postgres не показывает
|
||||||
|
ни одного секрета, а в compose-шаблоне не осталось jinja-переменных с секретами —
|
||||||
|
и, как следствие, дифф этого шаблона снова можно смотреть без `no_log`.
|
||||||
|
|
||||||
|
Механизм и ссылки на код: [справка по файловым секретам](../drafts/secrets-file-support.md).
|
||||||
|
Из декомпозиции `secrets-env-to-file` (2026-07-25).
|
||||||
|
|
||||||
|
Связано: files/outline, files/miniflux (образец), roles/secrets,
|
||||||
|
[no-secrets-in-playbook-output](no-secrets-in-playbook-output.md).
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# tududi: секреты в смонтированный `.env`
|
||||||
|
|
||||||
|
**Приоритет:** Низкий
|
||||||
|
|
||||||
|
Tududi `1.2.4` не умеет файловых секретов вообще: session secret, секрет OIDC,
|
||||||
|
ключ LLM и пароль SMTP читаются напрямую из `process.env`, конфиг-файла и
|
||||||
|
CLI-опций нет. Проверено грепом по репозиторию на теге, не по докам.
|
||||||
|
|
||||||
|
Единственный доступный путь — не `env_file:` (при нём значения всё равно попадают
|
||||||
|
в `Config.Env`), а **смонтированный файл**: `backend/app.js` вызывает `dotenv`, а
|
||||||
|
рабочая директория контейнера — `/app/backend`, значит `.env` можно положить туда
|
||||||
|
томом. Тогда секреты не видны в `docker inspect`, но остаются в окружении
|
||||||
|
процесса и в `/proc/<pid>/environ`.
|
||||||
|
|
||||||
|
Приоритет низкий именно поэтому: механизм нештатный (держится на том, что
|
||||||
|
`dotenv` остаётся в prod-зависимостях образа), а закрывает только один вектор из
|
||||||
|
трёх. Если апстрим когда-нибудь добавит `_FILE` — задача переписывается на него.
|
||||||
|
|
||||||
|
Готово, когда `docker inspect` контейнера tududi не показывает ни одного из
|
||||||
|
четырёх секретов.
|
||||||
|
|
||||||
|
Разбор и ссылки на код:
|
||||||
|
[справка по файловым секретам](../drafts/secrets-file-support.md).
|
||||||
|
Из декомпозиции `secrets-env-to-file` (2026-07-25).
|
||||||
|
|
||||||
|
Связано: files/tududi,
|
||||||
|
[no-secrets-in-playbook-output](no-secrets-in-playbook-output.md).
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# wakapi: секреты из файлов
|
||||||
|
|
||||||
|
**Приоритет:** Средний
|
||||||
|
|
||||||
|
Сейчас в `environment:` прямыми значениями стоят salt паролей и пароль SMTP,
|
||||||
|
а в закомментированном блоке — секрет OIDC. Wakapi `2.17.5` даёт два пути, и
|
||||||
|
выбрать надо один:
|
||||||
|
|
||||||
|
- **generic `_FILE`** — `loadSecretFiles()` разворачивает любую переменную с
|
||||||
|
таким суффиксом. Ближе к образцу miniflux, меньше движений.
|
||||||
|
- **`config.yml`**, смонтированный в `/app/config.yml` — покрывает те же три
|
||||||
|
поля декларативно.
|
||||||
|
|
||||||
|
Для OIDC-секрета второй путь надёжнее: `renameEnvVars()` режет значение по
|
||||||
|
первому `=`, то есть секрет с base64-паддингом через env приедет битым. Там же
|
||||||
|
при включении OIDC надо поправить имена переменных — в 2.17.5 разбирается
|
||||||
|
`WAKAPI_OIDC_PROVIDERS_0_*`, а у нас закомментирован устаревший вариант, который
|
||||||
|
просто молча игнорируется.
|
||||||
|
|
||||||
|
Готово, когда `docker inspect` контейнера wakapi не показывает ни salt, ни пароль
|
||||||
|
SMTP, а дифф шаблона можно смотреть без `no_log`.
|
||||||
|
|
||||||
|
Механизм, версии и ссылки на код:
|
||||||
|
[справка по файловым секретам](../drafts/secrets-file-support.md).
|
||||||
|
Из декомпозиции `secrets-env-to-file` (2026-07-25).
|
||||||
|
|
||||||
|
Связано: files/wakapi, files/miniflux (образец), roles/secrets,
|
||||||
|
[no-secrets-in-playbook-output](no-secrets-in-playbook-output.md).
|
||||||
@@ -13,6 +13,8 @@ gitea и т.д.) фактически все имеют root — изоляци
|
|||||||
тем — лучше через socket-proxy). За `primary_user` группу оставить для ручных
|
тем — лучше через socket-proxy). За `primary_user` группу оставить для ручных
|
||||||
операций (`inv ssh`).
|
операций (`inv ssh`).
|
||||||
|
|
||||||
Из sec-ревью (fable, 2026-07-13). Усиливает риск из [secrets-env-to-file](secrets-env-to-file.md).
|
Из sec-ревью (fable, 2026-07-13). Усиливает риск из серии `secrets-file-*`
|
||||||
|
(секреты в `environment:` контейнеров): членство в группе `docker` — как раз тот
|
||||||
|
доступ, которым такие переменные и вычитываются.
|
||||||
|
|
||||||
Связано: все playbook-*.yml (owner_extra_groups), roles/owner, playbook-docker.yml.
|
Связано: все playbook-*.yml (owner_extra_groups), roles/owner, playbook-docker.yml.
|
||||||
|
|||||||
@@ -39,8 +39,9 @@ enforcement-слой применяет снаружи. Что смена хра
|
|||||||
случайного дампа, от копии `~` в чужих руках, или от агента. Ответы ведут к разным
|
случайного дампа, от копии `~` в чужих руках, или от агента. Ответы ведут к разным
|
||||||
вариантам, а третий не решается на этом уровне вообще.
|
вариантам, а третий не решается на этом уровне вообще.
|
||||||
|
|
||||||
Смежное: [secrets-env-to-file](secrets-env-to-file.md) — тот же вопрос слоем ниже
|
Смежное: серия `secrets-file-*` — тот же вопрос слоем ниже (секреты в
|
||||||
(секреты в `environment:` контейнеров). Отдельно не заведена, но обсуждалась,
|
`environment:` контейнеров), разбор механизмов в
|
||||||
|
[справке](../drafts/secrets-file-support.md). Отдельно не заведена, но обсуждалась,
|
||||||
идея скоупинга: вернуть `group_vars/all/secrets.yml` в `vars_files`, оставив
|
идея скоупинга: вернуть `group_vars/all/secrets.yml` в `vars_files`, оставив
|
||||||
`main.yml` в `group_vars` — это режет потолок утечки с 53 переменных до ~11, ценой
|
`main.yml` в `group_vars` — это режет потолок утечки с 53 переменных до ~11, ценой
|
||||||
возврата boilerplate в плейбуки.
|
возврата boilerplate в плейбуки.
|
||||||
|
|||||||
@@ -0,0 +1,203 @@
|
|||||||
|
# Файловые секреты: что умеет каждое приложение
|
||||||
|
|
||||||
|
Дата: 2026-07-25. Статус: справка по итогам проверки исходников (не план работ).
|
||||||
|
|
||||||
|
Проверялись **закреплённые в наших compose-файлах версии**, не `main`. Источник —
|
||||||
|
код парсинга конфига в репозиториях приложений; доки использовались как
|
||||||
|
подтверждение, а не как основание. Из этой справки выросли задачи
|
||||||
|
`secrets-file-*` в беклоге, по одной на приложение.
|
||||||
|
|
||||||
|
## Зачем
|
||||||
|
|
||||||
|
Секреты стоят прямыми значениями в `environment:` docker-compose. Такие
|
||||||
|
переменные читает любой член группы `docker`, любой процесс с доступом к
|
||||||
|
`/proc/<pid>/environ`, и они попадают в `docker inspect`. Цель — там, где
|
||||||
|
приложение умеет, брать секрет из файла.
|
||||||
|
|
||||||
|
Образец — **miniflux**: роль `secrets` кладёт каждую переменную vault в отдельный
|
||||||
|
файл, каталог монтируется `:ro`, compose ссылается на `*_FILE`
|
||||||
|
(`files/miniflux/docker-compose.template.yml`).
|
||||||
|
|
||||||
|
Отдельная и более важная цель — чтобы секреты не печатались в терминал при
|
||||||
|
деплое — этой справкой **не** решается: там работает `no_log`, см. задачу
|
||||||
|
`no-secrets-in-playbook-output`. Для tududi и wanderer `no_log` — единственный
|
||||||
|
доступный ответ.
|
||||||
|
|
||||||
|
## Матрица
|
||||||
|
|
||||||
|
| Приложение | Секрет | Файл | Механизм |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **outline** `1.9.2` | `SECRET_KEY`, `UTILS_SECRET`, OIDC, SMTP | да | generic `<NAME>_FILE` |
|
||||||
|
| | пароль postgres | да, с оговоркой | только вместе с переходом на раздельные `DATABASE_*` |
|
||||||
|
| | пароль сайдкара postgres | да | штатный `POSTGRES_PASSWORD_FILE` |
|
||||||
|
| **wakapi** `2.17.5` | salt, SMTP, OIDC | да | generic `_FILE` либо `config.yml` |
|
||||||
|
| **gitea** `1.27.0` | SMTP | да, частично | `GITEA__mailer__PASSWD__FILE`, но значение оседает в `app.ini` |
|
||||||
|
| **gramps** `26.7.0` | `GRAMPSWEB_SECRET_KEY` | да | файл `/app/secret/secret`, путь уже смонтирован |
|
||||||
|
| | SMTP | нет | только недокументированный трюк с `config.cfg` |
|
||||||
|
| **authelia** `4.39.20` | jwt, session, ключ хранилища, hmac OIDC, SMTP | да | `AUTHELIA_..._FILE` |
|
||||||
|
| | jwks-ключ, `client_secret` × 4 | нет через `_FILE` | только template-фильтр; секреты клиентов лучше хэшировать |
|
||||||
|
| **tududi** `1.2.4` | все четыре | **нет** | обходной путь — смонтированный `.env` |
|
||||||
|
| **wanderer** `0.18.3` | ключ meilisearch, ключ pocketbase | **нет** | ничего |
|
||||||
|
|
||||||
|
## Подробности по приложениям
|
||||||
|
|
||||||
|
### outline `1.9.2`
|
||||||
|
|
||||||
|
`server/utils/environment.ts` оборачивает `process.env` в Proxy: если переменная
|
||||||
|
пуста, читается `<NAME>_FILE` как путь к файлу, содержимое обрезается по
|
||||||
|
пробелам. Весь `server/env.ts` и плагины читают именно через него, поэтому
|
||||||
|
механизм покрывает **любую** переменную, включая OIDC-плагин.
|
||||||
|
|
||||||
|
- Появилось в v1.7.0 (PR [#11906](https://github.com/outline/outline/pull/11906),
|
||||||
|
merged 2026-03-30), в v1.9.0 переписано на ленивый Proxy (PR
|
||||||
|
[#12889](https://github.com/outline/outline/pull/12889)). Проверено бисекцией:
|
||||||
|
в v1.6.1 механизма нет, в v1.7.0 есть.
|
||||||
|
- Конвенция описана в
|
||||||
|
[.env.sample](https://github.com/outline/outline/blob/v1.9.2/.env.sample)
|
||||||
|
строки 4–18, включая правило приоритета: заданы обе — побеждает прямая
|
||||||
|
переменная.
|
||||||
|
- Пароль БД: `DATABASE_URL_FILE` выносит **весь** URL. Чтобы вынести только
|
||||||
|
пароль, нужны раздельные `DATABASE_HOST/PORT/NAME/USER` + `DATABASE_PASSWORD`,
|
||||||
|
они взаимоисключимы с `DATABASE_URL` через `@CannotUseWith`
|
||||||
|
([env.ts L95–L143](https://github.com/outline/outline/blob/v1.9.2/server/env.ts)).
|
||||||
|
- Сайдкар `postgres:16.3` — штатный `POSTGRES_PASSWORD_FILE`.
|
||||||
|
|
||||||
|
### wakapi `2.17.5`
|
||||||
|
|
||||||
|
Два независимых пути.
|
||||||
|
|
||||||
|
- `loadSecretFiles()`
|
||||||
|
([config.go L829](https://github.com/muety/wakapi/blob/2.17.5/config/config.go))
|
||||||
|
— generic: проходит по всему окружению, для любой переменной с суффиксом
|
||||||
|
`_FILE` читает файл, `TrimSpace`, кладёт в базовое имя, файловую снимает.
|
||||||
|
Заданы обе — процесс падает с «both environment variables are set».
|
||||||
|
- Порядок в `Load()`: `loadSecretFiles()` → `renameEnvVars()` → `configor.Load`,
|
||||||
|
поэтому `_FILE` работает и для OIDC-переменных в configor-формате.
|
||||||
|
- История: PR [#679](https://github.com/muety/wakapi/pull/679) (релиз 2.12.1)
|
||||||
|
сделал это в `entrypoint.sh` для пяти переменных; коммит `a45732b2` (релиз
|
||||||
|
2.17.2, переход на distroless) перенёс логику в Go и сделал универсальной.
|
||||||
|
README (L92) перечисляет только три переменные — **список устарел**
|
||||||
|
относительно кода.
|
||||||
|
- `config.yml` покрывает все три поля: `security.password_salt`,
|
||||||
|
`security.oidc[]`, `mail.smtp.password`. Образ кладёт `config.default.yml` в
|
||||||
|
`/app/config.yml`, туда же монтируется свой. Env перекрывает значения из yaml.
|
||||||
|
|
||||||
|
Две ловушки в нашем **закомментированном** OIDC-блоке (не живой баг, но при
|
||||||
|
включении выстрелит):
|
||||||
|
|
||||||
|
1. Имена `WAKAPI_OIDC_PROVIDER_CLIENT_ID` устарели — в 2.17.5 разбирается
|
||||||
|
`WAKAPI_OIDC_PROVIDERS_(\d+)_([A-Z_]+)`, старые молча игнорируются.
|
||||||
|
2. `renameEnvVars()` разбирает строку как `strings.Split(e, "=")` и берёт
|
||||||
|
`parts[1]` — значение обрезается по первому `=`, то есть секрет с
|
||||||
|
base64-паддингом через env поедет битым.
|
||||||
|
|
||||||
|
Оба довода — за `config.yml` для OIDC-секрета.
|
||||||
|
|
||||||
|
### gitea `1.27.0`
|
||||||
|
|
||||||
|
`GITEA__mailer__PASSWD__FILE=/path` — механизм универсальный, работает для любого
|
||||||
|
ключа `app.ini`, хвостовой перевод строки обрезается
|
||||||
|
([config_env.go L100–141](https://github.com/go-gitea/gitea/blob/v1.27.0/modules/setting/config_env.go),
|
||||||
|
PR [#24832](https://github.com/go-gitea/gitea/pull/24832), с 1.20).
|
||||||
|
|
||||||
|
Оговорка, которая режет пользу: `environment-to-ini` **записывает содержимое
|
||||||
|
файла в `app.ini` открытым текстом**, то есть секрет всё равно оседает на
|
||||||
|
хостовом диске в `<data_dir>/gitea/conf/app.ini`. Открытая проблема апстрима
|
||||||
|
[#35316](https://github.com/go-gitea/gitea/issues/35316) (ранее #25316), PR нет.
|
||||||
|
Выигрыш реальный, но частичный: уходит из `environment:`, `docker inspect` и
|
||||||
|
окружения процесса — не с диска.
|
||||||
|
|
||||||
|
### gramps `26.7.0` (gramps-web-api v3.18.0)
|
||||||
|
|
||||||
|
Если `GRAMPSWEB_SECRET_KEY` не задана, entrypoint образа читает ключ из файла
|
||||||
|
`/app/secret/secret` (и генерирует, если файла нет) —
|
||||||
|
[docker-entrypoint.sh](https://github.com/gramps-project/gramps-web-api/blob/v3.18.0/docker-entrypoint.sh).
|
||||||
|
**Этот путь у нас уже смонтирован** как `gramps_secret`. Entrypoint общий у
|
||||||
|
`gramps_app` и `gramps_celery` через YAML merge key, оба читают тот же файл.
|
||||||
|
|
||||||
|
SMTP-пароль файлового источника не имеет: конфиг читается через
|
||||||
|
`app.config.from_prefixed_env(prefix="GRAMPSWEB")`, никакой обработки `_FILE`
|
||||||
|
([app.py L99–121](https://github.com/gramps-project/gramps-web-api/blob/v3.18.0/gramps_webapi/app.py)).
|
||||||
|
Обходной путь: `Dockerfile` задаёт `GRAMPS_API_CONFIG=/app/config/config.cfg`, а
|
||||||
|
`from_envvar()` во Flask — это `from_pyfile()`, то есть **config.cfg исполняется
|
||||||
|
как Python**, и `EMAIL_HOST_PASSWORD = open("/run/secrets/smtp").read().strip()`
|
||||||
|
сработает. Механизм рабочий, но держится на детали реализации Flask и нигде не
|
||||||
|
обещан.
|
||||||
|
|
||||||
|
### authelia `4.39.20`
|
||||||
|
|
||||||
|
`IsSecretKey()` возвращает `false` для любого ключа, содержащего `[]` (элемент
|
||||||
|
списка), и требует, чтобы ключ оканчивался на `key`/`secret`/`password`/`token`/
|
||||||
|
`certificate_chain`
|
||||||
|
([helpers.go](https://github.com/authelia/authelia/blob/v4.39.20/internal/configuration/helpers.go)).
|
||||||
|
Отсюда деление:
|
||||||
|
|
||||||
|
- **Умеют `_FILE`** (5 полей): `identity_validation.reset_password.jwt_secret`,
|
||||||
|
`session.secret`, `storage.encryption_key`,
|
||||||
|
`identity_providers.oidc.hmac_secret`, `notifier.smtp.password`.
|
||||||
|
- **Не умеют** (2 позиции): `identity_providers.oidc.jwks[].key` и
|
||||||
|
`client_secret` четырёх клиентов — оба содержат `[]`.
|
||||||
|
|
||||||
|
Для них есть [template-фильтр](https://www.authelia.com/configuration/methods/files/)
|
||||||
|
(`X_AUTHELIA_CONFIG_FILTERS=template`, с 4.38): `{{ secret "/path" | nindent 10 }}`.
|
||||||
|
Механизмы **несовместимы в одном поле** — если задано и `_FILE`, и значение в
|
||||||
|
конфиге, Authelia падает с `errFmtSecretAlreadyDefined`. Поэтому проще выбрать
|
||||||
|
один: фильтр покрывает все семь позиций, `_FILE` — только пять.
|
||||||
|
|
||||||
|
Отдельно про `client_secret`: штатная форма хранения — PHC-дайджест
|
||||||
|
`$pbkdf2-sha512$310000$...`, плейнтекст с 4.38 вызывает warning валидатора
|
||||||
|
(`errFmtOIDCClientInvalidSecretPlainText`). Плейнтекст обязателен только при
|
||||||
|
`client_secret_jwt` или симметричном шифровании JWT — у наших четырёх клиентов
|
||||||
|
(miniflux, wakapi, tududi — дефолт; outline — `access_token_signed_response_alg:
|
||||||
|
none`) ничего такого нет. То есть эти поля можно **перестать считать секретами**:
|
||||||
|
в конфиге хэш, сам секрет живёт в vault и в конфиге клиентского приложения.
|
||||||
|
Генератор уже есть — `inv authelia-gen-secret-and-hash`.
|
||||||
|
|
||||||
|
### tududi `1.2.4` — не умеет
|
||||||
|
|
||||||
|
Все четыре секрета читаются напрямую из `process.env`
|
||||||
|
([config.js L94, L40](https://github.com/chrisvel/tududi/blob/v1.2.4/backend/config/config.js),
|
||||||
|
[providerConfig.js L82](https://github.com/chrisvel/tududi/blob/v1.2.4/backend/modules/oidc/providerConfig.js),
|
||||||
|
[service.js L20](https://github.com/chrisvel/tududi/blob/v1.2.4/backend/modules/ai-assistant/service.js)).
|
||||||
|
Греп по репозиторию на теге даёт только `DB_FILE` и `FILE_UPLOAD_LIMIT_MB`; ни
|
||||||
|
`readFileSync` для секретов, ни конфиг-файла, ни CLI-опции нет. Собственная дока
|
||||||
|
по OIDC советует «consider Docker secrets» — это совет про хранение, читать файлы
|
||||||
|
приложение не умеет.
|
||||||
|
|
||||||
|
Обходной путь: `backend/app.js` вызывает `require('dotenv').config()`, рабочая
|
||||||
|
директория контейнера — `/app/backend`, значит смонтированный туда `.env`
|
||||||
|
подхватится. Это не per-secret файлы, но значения не попадают в `Config.Env` —
|
||||||
|
в отличие от `env_file:`, где попадают. `dotenv` не перезаписывает уже
|
||||||
|
установленные переменные окружения.
|
||||||
|
|
||||||
|
### wanderer `0.18.3` — не умеет ничего
|
||||||
|
|
||||||
|
- `MEILI_MASTER_KEY` (meilisearch v1.20.0): только CLI `--master-key`, env или
|
||||||
|
инлайн в `config.toml`; `_FILE` нет
|
||||||
|
([option.rs](https://github.com/meilisearch/meilisearch/blob/v1.20.0/crates/meilisearch/src/option.rs)).
|
||||||
|
Апстрим отклонил запрос —
|
||||||
|
[discussion #201](https://github.com/orgs/meilisearch/discussions/201), 2023.
|
||||||
|
Клиентская сторона (`wanderer_db`, `wanderer_web`) тоже читает `os.Getenv`.
|
||||||
|
- `POCKETBASE_ENCRYPTION_KEY` — это **не** штатный механизм pocketbase
|
||||||
|
(`--encryptionEnv`), а собственная переменная wanderer, читается `os.Getenv` в
|
||||||
|
четырёх местах
|
||||||
|
([db/main.go](https://github.com/Flomp/wanderer/blob/v0.18.3/db/main.go)).
|
||||||
|
ENTRYPOINT в exec-форме, подстановку через шелл не сделать. Найденный в поиске
|
||||||
|
`POCKETBASE_ENCRYPTION_KEY_FILE` — фича стороннего образа
|
||||||
|
`adrianmusante/pocketbase`, к `flomp/wanderer-db` отношения не имеет.
|
||||||
|
|
||||||
|
Доступен только `env_file:`, где значения всё равно попадают в `Config.Env`, то
|
||||||
|
есть косметика. Задачи по wanderer не заведено.
|
||||||
|
|
||||||
|
## Docker secrets — покрытия не расширяет
|
||||||
|
|
||||||
|
`secrets:` в compose вне swarm — это bind-mount файла в `/run/secrets/<name>`, то
|
||||||
|
есть **тот же способ доставки**, что и `*_FILE`. Приложение, умеющее только
|
||||||
|
переменную окружения, из `/run/secrets` не прочитает: ограничение в приложении, а
|
||||||
|
не в доставке. На матрицу выше не влияет.
|
||||||
|
|
||||||
|
Что даёт: секрет объявляется на сервис, а не монтируется каталогом. У miniflux
|
||||||
|
сейчас `{{ secrets_dir }}:/secrets:ro` висит на обоих сервисах, и postgres видит
|
||||||
|
все шесть файлов, включая секреты OIDC, хотя нужен ему один пароль. Улучшение
|
||||||
|
поверх образца, а не замена ему. `uid`/`gid`/`mode` вне swarm игнорируются —
|
||||||
|
права остаются с хост-файла, их ставит роль `secrets`.
|
||||||
Reference in New Issue
Block a user