11 KiB
Секреты (sops-nix)
Кратко: как всё устроено, как добавить новый секрет и как поднять рабочий sops на новом устройстве.
Как устроено
.sops.yamlв корне репозитория — список age-реципиентов и правила выбора ключа по пути файла.- Секреты делятся на два уровня:
- host (системные, читает root): зашифрованные файлы в
host/<Host>/secrets/<файл>, метаданные вhost/<Host>/secrets/secrets.nix, подключается изhost/<Host>/configuration.nixчерезimports. При активации расшифровываются в/run/secrets/<имя>. - user (пользовательские, читает home-manager): один файл
user/<username>/secrets.yaml, метаданные вmodules/user/secrets.nix, подключается изmodules/user.nixчерезimports. При активации расшифровываются в~/.config/sops-nix/secrets/<имя>.
- host (системные, читает root): зашифрованные файлы в
- Расшифровка идёт в runtime, в
/nix/storeсекреты не попадают. - age-ключи выводятся из существующих SSH-ключей:
- host —
/root/age.key(читает root при активации), - user —
~/.config/sops/age/keys.txt(читает пользователь при активации home-manager).
- host —
Текущие правила в .sops.yaml:
keys:
- &key age1tx6jdr6535xxeygpwkkgvdwc24tulr420cm7gk56xzfp0slzeqks2nwpyg
creation_rules:
- path_regex: host/[^/]+/secrets/.+$
key_groups:
- age:
- *key
- path_regex: user/[^/]+/secrets\.yaml$
key_groups:
- age:
- *key
Правило выбирается по пути файла. Важно: path_regex проверяется против пути входного файла относительно корня репозитория, поэтому открытое значение нужно создавать по целевому пути (см. примеры ниже).
Host-секрет (системный)
Добавить новый
-
Придумай имя, дальше для примера —
mysecret. -
Положи открытое значение во временный файл внутри
host/<Host>/secrets/(путь важен — см.path_regex):printf '%s' 'СЕКРЕТНОЕ_ЗНАЧЕНИЕ' > host/ThinkPad/secrets/mysecret.plain -
Зашифруй (приватный ключ не нужен — sops берёт публичного реципиента из
.sops.yaml). Флаги указываются до позиционных аргументов:nix-shell -p sops --run \ "sops --input-type binary --output-type binary \ --encrypt --output host/ThinkPad/secrets/mysecret.enc \ host/ThinkPad/secrets/mysecret.plain" rm host/ThinkPad/secrets/mysecret.plain--input-type/--output-typeобязательны для бинарных данных, иначе sops пытается определить тип по расширению. -
Опиши секрет в
host/<Host>/secrets/secrets.nix:{ sops = { age.keyFile = "/root/age.key"; secrets = { mysecret = { format = "binary"; # yaml | json | binary | dotenv | ini sopsFile = ./mysecret.enc; owner = "kirill"; # владелец расшифрованного файла mode = "0400"; }; }; }; }format = "binary"— для произвольных байтов (.pem, токены). Для структурированных данных используйyaml/json.owner/modeнужны, если секрет читает пользовательский процесс (а не root-сервис). -
git add— Nix видит только файлы, добавленные в git — и ребилд:git add host/ThinkPad/secrets/ sudo nixos-rebuild switch --flake .#ThinkPad -
Проверка:
sudo ls -l /run/secrets/mysecret
Изменить или посмотреть существующий секрет
Используется приватный ключ (/root/age.key), поэтому нужен root:
sudo SOPS_AGE_KEY_FILE=/root/age.key nix-shell -p sops --run \
"sops --input-type binary --output-type binary host/ThinkPad/secrets/mysecret.enc"
sops сам не ищет /root/age.key, путь передаётся через SOPS_AGE_KEY_FILE.
Для бинарных файлов нужны --input-type/--output-type, иначе sops не сможет определить формат.
User-секрет (home-manager)
Все пользовательские секреты лежат в одном файле user/<username>/secrets.yaml. Значения — вложенные ключи YAML, а имя секрета в nix-коде совпадает с путём этого ключа (/ = вложенность). Например, секрет tokens/apis/wallhaven читается из:
tokens:
apis:
wallhaven: ЗНАЧЕНИЕ
Путь расшифрованного файла: ~/.config/sops-nix/secrets/tokens/apis/wallhaven.
Добавить новый
-
Придумай имя, дальше для примера —
tokens/apis/wallhaven. -
Добавь значение в общий файл секретов (sops откроет расшифрованное содержимое в
$EDITORи зашифрует при сохранении; путь совпадает сpath_regex, поэтому правило применится автоматически):nix-shell -p sops --run "sops user/kirill/secrets.yaml"Если файла ещё нет, создай его сразу по целевому пути и зашифруй на месте:
printf 'tokens:\n apis:\n wallhaven: ЗНАЧЕНИЕ\n' > user/kirill/secrets.yaml nix-shell -p sops --run "sops --encrypt --in-place user/kirill/secrets.yaml" -
Опиши секрет в
modules/user/secrets.nix:{ config, ... }: { sops = { defaultSopsFile = ../../user/${config.home.username}/secrets.yaml; age.keyFile = "${config.home.homeDirectory}/.config/sops/age/keys.txt"; secrets."tokens/apis/wallhaven" = {}; }; } -
Используй в home-manager конфиге через путь:
config.sops.secrets."tokens/apis/wallhaven".path -
git add— Nix видит только файлы, добавленные в git — и ребилд:git add user/kirill/secrets.yaml modules/user/secrets.nix modules/user.nix sudo nixos-rebuild switch --flake .#ThinkPad -
Проверка:
ls -l ~/.config/sops-nix/secrets/tokens/apis/wallhaven
Изменить или посмотреть существующий секрет
nix-shell -p sops --run "sops user/kirill/secrets.yaml"
sops подхватывает ~/.config/sops/age/keys.txt автоматически. Если путь другой — задай SOPS_AGE_KEY_FILE.
Новое устройство
-
Убедись, что на устройстве есть приватный SSH-ключ, из которого выводится age-ключ. Ключ должен быть без пароля — иначе расшифровка при активации будет требовать ввода. На текущей машине это
~/.ssh/id_ed25519_sops. -
Создай age-ключ хоста (root читает его при активации):
sudo nix-shell -p ssh-to-age --run \ "ssh-to-age -private-key -i /home/kirill/.ssh/id_ed25519_sops -o /root/age.key" sudo chmod 600 /root/age.key -
Создай age-ключ пользователя (home-manager читает его при активации):
mkdir -p ~/.config/sops/age nix-shell -p ssh-to-age --run \ "ssh-to-age -private-key -i ~/.ssh/id_ed25519_sops -o ~/.config/sops/age/keys.txt" chmod 700 ~/.config/sops/age chmod 600 ~/.config/sops/age/keys.txt -
Проверь, что публичный age-реципиент устройства совпадает с тем, что в
.sops.yaml:nix-shell -p ssh-to-age --run \ "ssh-to-age -i ~/.ssh/id_ed25519_sops.pub"Должно вывести:
age1tx6jdr6535xxeygpwkkgvdwc24tulr420cm7gk56xzfp0slzeqks2nwpygЕсли вывод другой — добавь новый реципиент (см. следующий раздел).
-
Создай структуру секретов хоста:
host/<Host>/secrets/secrets.nix host/<Host>/secrets/<файлы секретов>Минимальный
secrets.nix:{ sops = { age.keyFile = "/root/age.key"; secrets = { }; }; }И добавь
./secrets/secrets.nixвimportsвhost/<Host>/configuration.nix. -
Для пользовательских секретов создай
user/<username>/secrets.yaml(см. раздел «User-секрет») и убедись, чтоmodules/user.nixподключает./user/secrets.nix. -
git add+ ребилд:git add host/<Host>/secrets/ user/<username>/secrets.yaml modules/user/secrets.nix sudo nixos-rebuild switch --flake .#<Host>
Добавить устройство к существующим секретам
-
Получи age-реципиент устройства (шаг 4 выше).
-
Добавь его в
.sops.yamlв оба правила:keys: - &key age1tx6jdr6535xxeygpwkkgvdwc24tulr420cm7gk56xzfp0slzeqks2nwpyg - &newdevice age1...новый_реципиент... # замени на реальный creation_rules: - path_regex: host/[^/]+/secrets/.+$ key_groups: - age: - *key - *newdevice - path_regex: user/[^/]+/secrets\.yaml$ key_groups: - age: - *key - *newdevice -
Перешифруй существующие секреты под новый список реципиентов (нужен приватный ключ). Для host-секретов — root и
/root/age.key:sudo SOPS_AGE_KEY_FILE=/root/age.key nix-shell -p sops --run \ "sops updatekeys host/ThinkPad/secrets/mysecret.enc"Для user-секретов — пользовательский ключ:
SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt nix-shell -p sops --run \ "sops updatekeys user/kirill/secrets.yaml" -
git add+ ребилд на этом устройстве.