mirror of
https://github.com/oqyude/nixos.git
synced 2026-10-10 05:55:24 +03:00
sops secrets path changed
This commit is contained in:
@@ -42,6 +42,7 @@
|
|||||||
| 4 | `100.64.0.0` = Tailscale sapphira | Назначен вручную; в 4 файлах | AGENTS.md §5 |
|
| 4 | `100.64.0.0` = Tailscale sapphira | Назначен вручную; в 4 файлах | AGENTS.md §5 |
|
||||||
| 5 | 3x-ui заморожен | Панель на latest; ядро Xray на 26.7.x; миграция 26.9 провалена | todo C1–C5 |
|
| 5 | 3x-ui заморожен | Панель на latest; ядро Xray на 26.7.x; миграция 26.9 провалена | todo C1–C5 |
|
||||||
| 6 | nftables на VDS — явная финальная политика | Сейчас ruleset без финального правила + конфликт с `firewall.*` | todo A3 |
|
| 6 | nftables на VDS — явная финальная политика | Сейчас ruleset без финального правила + конфликт с `firewall.*` | todo A3 |
|
||||||
|
| 7 | sops-пути — через `config.sops.secrets.<name>.path` | Любой `path =` override на sops-блоке делает хардкод-потребителя молча сломанным: rebuild зелёный, сервис стартует, контент пустой | этот коммит, см. §S1 |
|
||||||
|
|
||||||
## Сводка по ловушкам
|
## Сводка по ловушкам
|
||||||
|
|
||||||
@@ -193,3 +194,52 @@ git-истории файла (последний коммит, где Q&A бы
|
|||||||
7. все файлы в `secrets/` матчат `path_regex` из `.sops.yaml`.
|
7. все файлы в `secrets/` матчат `path_regex` из `.sops.yaml`.
|
||||||
|
|
||||||
**Вопрос:** какие из этих проверок ты хочешь, а какие — лишний CI?
|
**Вопрос:** какие из этих проверок ты хочешь, а какие — лишний CI?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаблон инварианта
|
||||||
|
|
||||||
|
Этот шаблон — для добавления новых инвариантов в этот документ
|
||||||
|
(и для зеркалирования в `AGENTS.md`). Та же 4-осевая структура
|
||||||
|
используется, чтобы вытащить «невидимое знание владельца» из
|
||||||
|
существующего кода в явное утверждение.
|
||||||
|
|
||||||
|
1. **Утверждение** — что именно верно и нельзя менять без осознанного
|
||||||
|
решения. Один-два абзаца, никаких «может быть».
|
||||||
|
2. **Где** — конкретные файлы и строки. Агент не должен угадывать.
|
||||||
|
3. **Почему** — что происходит при нарушении. Лучше всего — сценарий
|
||||||
|
(rebuild / рантайм), а не абстрактный риск.
|
||||||
|
4. **Действие** — `todo X.Y`, ссылка на коммит, или явное
|
||||||
|
«закреплено автоматической проверкой (см. §11.2)».
|
||||||
|
|
||||||
|
Дополнительные поля по необходимости: «ловушка» (выглядит сломанным,
|
||||||
|
намеренно), «обратное» (где это уже было сломано раньше),
|
||||||
|
«как проверить» (grep / CI).
|
||||||
|
|
||||||
|
### S1 — sops-пути: `config.sops.secrets.<name>.path`
|
||||||
|
|
||||||
|
- **Утверждение.** Любой потребитель sops-секрета в `modules/` ссылается
|
||||||
|
на путь через `${config.sops.secrets.<attr>.path}`, а не через
|
||||||
|
литерал `"/run/secrets/<attr>"`. Атрибут sops-блока — единственный
|
||||||
|
источник истины для on-disk пути.
|
||||||
|
- **Где.** `modules/server/authelia.nix:107-108`,
|
||||||
|
`modules/containers/open-webui.nix:101`,
|
||||||
|
`modules/containers/tape-rotation.nix:63` — потребители
|
||||||
|
sops-материализации. Отдельный случай — композитный env-файл,
|
||||||
|
**не** sops: `modules/containers/remnawave.nix:16, 70, 138, 145`
|
||||||
|
— там `envFile` в `let`-биндинге, чтобы две копии пути не
|
||||||
|
разъехались.
|
||||||
|
- **Почему.** sops-nix материализует секреты на `/run/secrets/<attr>`
|
||||||
|
по умолчанию, но `sops.secrets.<attr>.path` это переопределяет.
|
||||||
|
Любой такой override в будущей правке делает хардкод-потребителя
|
||||||
|
**молча** сломанным: `nixos-rebuild` проходит, сервис стартует,
|
||||||
|
файл читается — но контент от прошлой версии или пустой. Симптом
|
||||||
|
приходит из рантайма, не из CI. В `remnawave.nix` тот же риск
|
||||||
|
был двойной: путь хардкожен и в генераторе, и в контейнере, и
|
||||||
|
расхождение двух копий → silent breakage.
|
||||||
|
- **Действие.** Закреплено в коммите этой правки. Автоматической
|
||||||
|
проверки пока нет (см. §11.2 — список потенциальных CI-проверок).
|
||||||
|
- **Обратное.** До правки: `authelia.nix:51,108,109` — через хелпер
|
||||||
|
`sopsPath = name: "/run/secrets/${name}"`; `open-webui.nix:95`,
|
||||||
|
`tape-rotation.nix:61`, `remnawave.nix:61, 129, 136` — литеральный
|
||||||
|
хардкод.
|
||||||
@@ -92,7 +92,15 @@ in
|
|||||||
# is the only source — and the container will refuse to start with
|
# is the only source — and the container will refuse to start with
|
||||||
# WEBUI_SECRET_KEY="" (env.py:762 — SystemExit). The error message is
|
# WEBUI_SECRET_KEY="" (env.py:762 — SystemExit). The error message is
|
||||||
# the clear signal that the secret needs to be created.
|
# the clear signal that the secret needs to be created.
|
||||||
environmentFiles = lib.optional (builtins.pathExists ./secrets/open-webui.env) "/run/secrets/open-webui-env";
|
# Path comes from the sops block below (`config.sops.secrets.<attr>.path`)
|
||||||
|
# rather than a hardcoded "/run/secrets/<attr>" — see invariant S1
|
||||||
|
# in docs/arch/invariants.md. Guards the sopsFile existence so the
|
||||||
|
# block is optional; the same predicate is what `sops.secrets` uses
|
||||||
|
# to decide whether to declare the attr at all, so `.path` is only
|
||||||
|
# read when the secret actually exists.
|
||||||
|
environmentFiles =
|
||||||
|
lib.optional (builtins.pathExists ./secrets/open-webui.env)
|
||||||
|
config.sops.secrets."open-webui-env".path;
|
||||||
volumes = [
|
volumes = [
|
||||||
"${panel}/data:/app/backend/data:rw"
|
"${panel}/data:/app/backend/data:rw"
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -6,6 +6,15 @@
|
|||||||
xlib,
|
xlib,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
|
let
|
||||||
|
# Composite env file path shared by the generator
|
||||||
|
# (remnawave-env.service below) and the container's `environmentFiles`.
|
||||||
|
# Lifting to a single binding prevents the two copies from drifting
|
||||||
|
# apart in future edits — see invariant S1 in docs/arch/invariants.md.
|
||||||
|
# Note: this is NOT a sops materialization (it's written by a oneshot),
|
||||||
|
# so `config.sops.secrets.<...>.path` is not the right primitive here.
|
||||||
|
envFile = "/run/secrets/remnawave-env";
|
||||||
|
in
|
||||||
{
|
{
|
||||||
# Runtime
|
# Runtime
|
||||||
virtualisation.podman = {
|
virtualisation.podman = {
|
||||||
@@ -58,7 +67,7 @@
|
|||||||
# "WEBHOOK_URL" = "https://your-webhook-url.com/endpoint";
|
# "WEBHOOK_URL" = "https://your-webhook-url.com/endpoint";
|
||||||
};
|
};
|
||||||
environmentFiles = [
|
environmentFiles = [
|
||||||
"/run/secrets/remnawave-env"
|
envFile
|
||||||
];
|
];
|
||||||
ports = [
|
ports = [
|
||||||
"3003:3003/tcp"
|
"3003:3003/tcp"
|
||||||
@@ -126,14 +135,14 @@
|
|||||||
User = "root";
|
User = "root";
|
||||||
};
|
};
|
||||||
script = ''
|
script = ''
|
||||||
cat > /run/secrets/remnawave-env <<EOF
|
cat > ${envFile} <<EOF
|
||||||
DATABASE_URL=$(cat ${config.sops.secrets.DATABASE_URL.path})
|
DATABASE_URL=$(cat ${config.sops.secrets.DATABASE_URL.path})
|
||||||
DATABASE_PASSWORD=$(cat ${config.sops.secrets.DATABASE_PASSWORD.path})
|
DATABASE_PASSWORD=$(cat ${config.sops.secrets.DATABASE_PASSWORD.path})
|
||||||
JWT_AUTH_SECRET=$(cat ${config.sops.secrets.JWT_AUTH_SECRET.path})
|
JWT_AUTH_SECRET=$(cat ${config.sops.secrets.JWT_AUTH_SECRET.path})
|
||||||
JWT_API_TOKENS_SECRET=$(cat ${config.sops.secrets.JWT_API_TOKENS_SECRET.path})
|
JWT_API_TOKENS_SECRET=$(cat ${config.sops.secrets.JWT_API_TOKENS_SECRET.path})
|
||||||
WEBHOOK_SECRET_HEADER=$(cat ${config.sops.secrets.WEBHOOK_SECRET_HEADER.path})
|
WEBHOOK_SECRET_HEADER=$(cat ${config.sops.secrets.WEBHOOK_SECRET_HEADER.path})
|
||||||
EOF
|
EOF
|
||||||
chmod 600 /run/secrets/remnawave-env
|
chmod 600 ${envFile}
|
||||||
'';
|
'';
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -58,7 +58,9 @@ in
|
|||||||
"NOTIFY_DAYS_BEFORE" = "7";
|
"NOTIFY_DAYS_BEFORE" = "7";
|
||||||
"TZ" = "Europe/Moscow";
|
"TZ" = "Europe/Moscow";
|
||||||
};
|
};
|
||||||
environmentFiles = [ "/run/secrets/tape-rotation-env" ];
|
# Path resolved from the sops block at the bottom of this file —
|
||||||
|
# see invariant S1 in docs/arch/invariants.md.
|
||||||
|
environmentFiles = [ config.sops.secrets."tape-rotation-env".path ];
|
||||||
volumes = [
|
volumes = [
|
||||||
"${panel}/db:/data:rw"
|
"${panel}/db:/data:rw"
|
||||||
"${panel}/uploads:/app/uploads:rw"
|
"${panel}/uploads:/app/uploads:rw"
|
||||||
|
|||||||
@@ -44,11 +44,6 @@
|
|||||||
let
|
let
|
||||||
cfg = config.host.authelia;
|
cfg = config.host.authelia;
|
||||||
sopsReady = builtins.pathExists ./secrets/authelia.yaml;
|
sopsReady = builtins.pathExists ./secrets/authelia.yaml;
|
||||||
# sops-nix materialises each `sops.secrets.<attr-name>` at
|
|
||||||
# /run/secrets/<attr-name> by default. Hardcoding the path here keeps
|
|
||||||
# the module independent of how the secret attr is named; rename only
|
|
||||||
# the sops block below if a different path is needed.
|
|
||||||
sopsPath = name: "/run/secrets/${name}";
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.host.authelia = {
|
options.host.authelia = {
|
||||||
@@ -105,8 +100,12 @@ in
|
|||||||
package = pkgs.authelia;
|
package = pkgs.authelia;
|
||||||
|
|
||||||
secrets = lib.mkIf sopsReady {
|
secrets = lib.mkIf sopsReady {
|
||||||
jwtSecretFile = sopsPath "authelia-jwt-secret";
|
# Read paths through `config.sops.secrets.<attr>.path` (not via a
|
||||||
storageEncryptionKeyFile = sopsPath "authelia-storage-encryption-key";
|
# hardcoded "/run/secrets/<attr>") so that any future `path =`
|
||||||
|
# override on the sops block below is picked up automatically —
|
||||||
|
# see invariant S1 in docs/arch/invariants.md.
|
||||||
|
jwtSecretFile = config.sops.secrets."authelia-jwt-secret".path;
|
||||||
|
storageEncryptionKeyFile = config.sops.secrets."authelia-storage-encryption-key".path;
|
||||||
};
|
};
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
|
|||||||
Reference in New Issue
Block a user