Compare commits
2 commits
8ebf16d069
...
fc1d31244a
| Author | SHA1 | Date | |
|---|---|---|---|
| fc1d31244a | |||
| 7af3bce544 |
9 changed files with 465 additions and 1 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -13,3 +13,11 @@ result-*
|
||||||
.#*
|
.#*
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
|
|
||||||
|
# Secrets — track only sops-encrypted .yaml files + the README;
|
||||||
|
# block plaintext keys and any other content under secrets/
|
||||||
|
*.key
|
||||||
|
*.pem
|
||||||
|
secrets/*
|
||||||
|
!secrets/*.yaml
|
||||||
|
!secrets/README.md
|
||||||
|
|
|
||||||
21
.sops.yaml
Normal file
21
.sops.yaml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# sops recipient declarations.
|
||||||
|
#
|
||||||
|
# Replace the placeholder below with YOUR age public key before
|
||||||
|
# encrypting any files. One-time setup on this machine:
|
||||||
|
# pragma: allowlist secret
|
||||||
|
# age-keygen -o ~/.config/sops/age/keys.txt # creates the private key
|
||||||
|
# age-keygen -y ~/.config/sops/age/keys.txt # prints the public key
|
||||||
|
#
|
||||||
|
# Paste the printed `age1...` string in place of the placeholder.
|
||||||
|
# See docs/secrets-management.md for the full walkthrough.
|
||||||
|
|
||||||
|
keys:
|
||||||
|
# pragma: allowlist secret
|
||||||
|
# PLACEHOLDER — overwrite with your real age public key.
|
||||||
|
- &admin age1REPLACEME_run_age_keygen_y_then_paste_the_real_key_here
|
||||||
|
|
||||||
|
creation_rules:
|
||||||
|
- path_regex: secrets/.*\.yaml$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin
|
||||||
|
|
@ -291,6 +291,12 @@ top of the human-facing docs, not a replacement for them.
|
||||||
Vue/Quasar UMD traps in lnbits page templates: no self-closing
|
Vue/Quasar UMD traps in lnbits page templates: no self-closing
|
||||||
tags, CSS specificity vs Quasar's `!important` utilities, cache
|
tags, CSS specificity vs Quasar's `!important` utilities, cache
|
||||||
busting via `?v={server_startup_time}`, dark-mode color discipline.
|
busting via `?v={server_startup_time}`, dark-mode color discipline.
|
||||||
|
- [`docs/secrets-management.md`](docs/secrets-management.md) —
|
||||||
|
beginner-friendly walkthrough for getting secrets out of `.env`
|
||||||
|
and into sops-encrypted YAML files: generating an age key, adding
|
||||||
|
recipients, declaring secrets in NixOS, rotating, multi-host
|
||||||
|
server setups, and common pitfalls. The scaffold ships the sops-nix
|
||||||
|
wiring already (inert until you create your first encrypted file).
|
||||||
|
|
||||||
## Contributing to this scaffold
|
## Contributing to this scaffold
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,9 @@
|
||||||
# Option schema (lnbits-sensei.*).
|
# Option schema (lnbits-sensei.*).
|
||||||
./modules/core.nix
|
./modules/core.nix
|
||||||
|
|
||||||
|
# sops-nix wiring. Inert until secrets/<hostName>.yaml exists.
|
||||||
|
./modules/secrets.nix
|
||||||
|
|
||||||
# Git remote topology — upstream / fork / extras.
|
# Git remote topology — upstream / fork / extras.
|
||||||
./modules/git/remotes.nix
|
./modules/git/remotes.nix
|
||||||
|
|
||||||
|
|
|
||||||
313
docs/secrets-management.md
Normal file
313
docs/secrets-management.md
Normal file
|
|
@ -0,0 +1,313 @@
|
||||||
|
# Secrets management with sops-nix
|
||||||
|
|
||||||
|
A beginner-friendly walkthrough of getting secrets out of your
|
||||||
|
`.env` files and into `git` safely, using
|
||||||
|
[sops-nix](https://github.com/Mic92/sops-nix). Assumes no prior
|
||||||
|
familiarity with sops, age, or NixOS secret management.
|
||||||
|
|
||||||
|
## Why bother
|
||||||
|
|
||||||
|
Most LNbits dev setups start with secrets in `.env`:
|
||||||
|
|
||||||
|
```env
|
||||||
|
LNBITS_ADMIN_KEY=changeme-real-key-goes-here
|
||||||
|
POSTGRES_PASSWORD=changeme-real-password-goes-here
|
||||||
|
```
|
||||||
|
|
||||||
|
This works, but `.env` files have two failure modes:
|
||||||
|
|
||||||
|
1. **They land in git accidentally.** A `git add -A` in the wrong
|
||||||
|
moment, a forgotten `.gitignore` line, and the key history-leaks
|
||||||
|
into a public repo. Rotating after the fact is painful — there's
|
||||||
|
no fixing what's already in `git log`.
|
||||||
|
2. **They live as plaintext on disk.** A backup of your dev box, a
|
||||||
|
shared filesystem snapshot, a forgotten copy in `~/Downloads/` —
|
||||||
|
all paths to the same compromise.
|
||||||
|
|
||||||
|
sops + age fixes both. Secret values get encrypted into a YAML file
|
||||||
|
checked into your repo. The encrypted file is safe to commit, push,
|
||||||
|
and back up. Decryption happens transparently at NixOS activation
|
||||||
|
time on the host that has the matching private key. Each secret
|
||||||
|
becomes a file under `/run/secrets/<name>` that your services read.
|
||||||
|
|
||||||
|
## What's in the box (lnbits-sensei scaffold)
|
||||||
|
|
||||||
|
This repo already wires sops-nix:
|
||||||
|
|
||||||
|
- **`flake.nix`** declares `sops-nix` as a flake input.
|
||||||
|
- **`modules/secrets.nix`** imports the NixOS module and points it at
|
||||||
|
`secrets/${hostName}.yaml` + your private key at
|
||||||
|
`~/.config/sops/age/keys.txt`.
|
||||||
|
- **`.sops.yaml`** declares the recipients (which public keys can
|
||||||
|
decrypt files under `secrets/`).
|
||||||
|
- **`secrets/`** is gitignored except for `*.yaml` (which is
|
||||||
|
encrypted) and `README.md`.
|
||||||
|
- **`configuration.nix`** imports `modules/secrets.nix`.
|
||||||
|
|
||||||
|
All of this is **inert until you create your first encrypted file**.
|
||||||
|
`nix flake check` stays green meanwhile because the module gates on
|
||||||
|
`builtins.pathExists`.
|
||||||
|
|
||||||
|
## Step-by-step
|
||||||
|
|
||||||
|
### 1. Install the tools
|
||||||
|
|
||||||
|
`sops` and `age` ship in nixpkgs:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# in modules/packages.nix or home.nix
|
||||||
|
environment.systemPackages = with pkgs; [ sops age ];
|
||||||
|
```
|
||||||
|
|
||||||
|
After `nixos-rebuild switch`, `sops` and `age-keygen` are on PATH.
|
||||||
|
|
||||||
|
For an ad-hoc install without rebuilding:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nix-shell -p sops age
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Generate your age key
|
||||||
|
|
||||||
|
The age private key is the one piece of state that lives outside git
|
||||||
|
— it's how this machine decrypts secrets at activation time. Generate
|
||||||
|
it once per host:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir -p ~/.config/sops/age
|
||||||
|
age-keygen -o ~/.config/sops/age/keys.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
The file looks like:
|
||||||
|
|
||||||
|
```
|
||||||
|
# created: 2026-05-26T...
|
||||||
|
# public key: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
AGE-SECRET-KEY-1YYYY...
|
||||||
|
```
|
||||||
|
|
||||||
|
The line starting with `AGE-SECRET-KEY-` is your **private key**.
|
||||||
|
**Back it up somewhere safe** (a password manager, a hardware key, a
|
||||||
|
trusted offline drive). If you lose it, you lose access to every
|
||||||
|
secret encrypted to it — there is no recovery.
|
||||||
|
|
||||||
|
Print just the **public key** (you'll paste this into `.sops.yaml`):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
age-keygen -y ~/.config/sops/age/keys.txt
|
||||||
|
# age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Add your public key to `.sops.yaml`
|
||||||
|
|
||||||
|
Open `.sops.yaml` at the repo root. Replace the placeholder
|
||||||
|
`age1REPLACEME...` with your real public key from the previous step:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
keys:
|
||||||
|
- &admin age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
|
||||||
|
creation_rules:
|
||||||
|
- path_regex: secrets/.*\.yaml$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin
|
||||||
|
```
|
||||||
|
|
||||||
|
What this says: any file matching `secrets/*.yaml` should be
|
||||||
|
encrypted for the recipients listed under `key_groups.age`. Right
|
||||||
|
now that's just `*admin` (a YAML anchor pointing at your key). When
|
||||||
|
you have collaborators, you'll add their public keys as additional
|
||||||
|
recipients here.
|
||||||
|
|
||||||
|
### 4. Create your first encrypted secrets file
|
||||||
|
|
||||||
|
`sops` is the editor wrapper. It opens your `$EDITOR` on a plaintext
|
||||||
|
view of the file; on save it encrypts in place.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sops secrets/<hostName>.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
(Use the same `hostName` you put in `settings.nix` — that's what
|
||||||
|
`modules/secrets.nix` looks for.)
|
||||||
|
|
||||||
|
Your editor opens an empty buffer. Write your secrets as plain YAML:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
lnbits-admin-key: changeme-real-key-goes-here
|
||||||
|
postgres:
|
||||||
|
lnbits-password: changeme-real-password-goes-here
|
||||||
|
```
|
||||||
|
|
||||||
|
Save and exit. sops encrypts in place. Cat the file to see what
|
||||||
|
landed on disk:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cat secrets/<hostName>.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll see the keys are now opaque base64 blobs, and a `sops:`
|
||||||
|
metadata block at the bottom describes which recipients can decrypt.
|
||||||
|
This file is **safe to commit and push**.
|
||||||
|
|
||||||
|
### 5. Declare each secret in your NixOS config
|
||||||
|
|
||||||
|
For each secret you want exposed to a service, add a
|
||||||
|
`sops.secrets.<name>` declaration in the consuming module:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# in the module that consumes the secret
|
||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
sops.secrets.lnbits-admin-key = {
|
||||||
|
mode = "0400";
|
||||||
|
owner = config.lnbits-sensei.user;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `<name>` matches the YAML key in your encrypted file. Dots in
|
||||||
|
the key become nested paths under `/run/secrets/`:
|
||||||
|
|
||||||
|
| YAML key | Runtime file |
|
||||||
|
|---|---|
|
||||||
|
| `lnbits-admin-key` | `/run/secrets/lnbits-admin-key` |
|
||||||
|
| `postgres/lnbits-password` (nested) | `/run/secrets/postgres/lnbits-password` |
|
||||||
|
|
||||||
|
### 6. Reference the secret from a service
|
||||||
|
|
||||||
|
Read the runtime path via `config.sops.secrets.<name>.path`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# example: passing the LNbits admin key file to a service
|
||||||
|
services.someThing.adminKeyFile =
|
||||||
|
config.sops.secrets.lnbits-admin-key.path;
|
||||||
|
```
|
||||||
|
|
||||||
|
Use a file-path reference (not the value directly) wherever the
|
||||||
|
service supports one — that way the secret never ends up in the
|
||||||
|
Nix store, just at `/run/secrets/<name>` on the running host.
|
||||||
|
|
||||||
|
### 7. Activate
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo nixos-rebuild switch --flake .#<hostName>
|
||||||
|
```
|
||||||
|
|
||||||
|
At activation, sops-nix decrypts `secrets/<hostName>.yaml` using the
|
||||||
|
private key at `~/.config/sops/age/keys.txt`, writes each declared
|
||||||
|
secret as a file under `/run/secrets/`, and your service reads from
|
||||||
|
the file path. Done.
|
||||||
|
|
||||||
|
## Common operations
|
||||||
|
|
||||||
|
**Edit a secret:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sops secrets/<hostName>.yaml
|
||||||
|
# editor opens with plaintext view; sops re-encrypts on save
|
||||||
|
```
|
||||||
|
|
||||||
|
**View a secret without editing:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sops -d secrets/<hostName>.yaml | grep lnbits-admin-key
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rotate to new recipients (e.g. add a collaborator):**
|
||||||
|
|
||||||
|
1. Add their public key to `.sops.yaml`:
|
||||||
|
```yaml
|
||||||
|
keys:
|
||||||
|
- &admin age1xxx...
|
||||||
|
- &alice age1zzz...
|
||||||
|
creation_rules:
|
||||||
|
- path_regex: secrets/.*\.yaml$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin
|
||||||
|
- *alice
|
||||||
|
```
|
||||||
|
2. Re-encrypt every file to the new recipient set:
|
||||||
|
```sh
|
||||||
|
sops updatekeys secrets/<hostName>.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Remove a recipient:** edit `.sops.yaml`, then `sops updatekeys`.
|
||||||
|
**Also rotate the underlying secret values** — anything the removed
|
||||||
|
party could previously decrypt is now compromised.
|
||||||
|
|
||||||
|
## Multi-host / server deployment
|
||||||
|
|
||||||
|
When you have one repo deploying multiple machines, each machine
|
||||||
|
needs its own age private key, and each machine's secrets file
|
||||||
|
needs to be encrypted to that machine's public key in addition to
|
||||||
|
the operator's.
|
||||||
|
|
||||||
|
**On each NixOS host (one-time):**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo mkdir -p /var/lib/sops-nix
|
||||||
|
sudo age-keygen -o /var/lib/sops-nix/key.txt
|
||||||
|
sudo chmod 600 /var/lib/sops-nix/key.txt
|
||||||
|
sudo age-keygen -y /var/lib/sops-nix/key.txt
|
||||||
|
# copy this public key into .sops.yaml as a new recipient
|
||||||
|
```
|
||||||
|
|
||||||
|
In `modules/secrets.nix`, swap `age.keyFile` for the host-local path:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
|
||||||
|
```
|
||||||
|
|
||||||
|
Then in `.sops.yaml`, list every host's public key alongside the
|
||||||
|
operator's, scoped via `path_regex` if you want per-host isolation:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
keys:
|
||||||
|
- &admin age1xxx... # operator (laptop / dev box)
|
||||||
|
- &host-a age1aaa... # server A
|
||||||
|
- &host-b age1bbb... # server B
|
||||||
|
creation_rules:
|
||||||
|
- path_regex: secrets/host-a\.yaml$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin
|
||||||
|
- *host-a
|
||||||
|
- path_regex: secrets/host-b\.yaml$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin
|
||||||
|
- *host-b
|
||||||
|
```
|
||||||
|
|
||||||
|
That way `host-a` can only decrypt its own secrets, `host-b` only
|
||||||
|
its own, and the operator can decrypt both.
|
||||||
|
|
||||||
|
## Pitfalls
|
||||||
|
|
||||||
|
- **Don't commit unencrypted YAML under `secrets/`.** The pre-commit
|
||||||
|
hook most consumers use catches obvious cases, but it's not
|
||||||
|
foolproof. Always verify with `cat secrets/<file>.yaml` that you
|
||||||
|
see `sops:` metadata + base64 blobs, not plaintext.
|
||||||
|
- **Don't lose the private key.** No recovery. Back up the file at
|
||||||
|
`~/.config/sops/age/keys.txt` (or `/var/lib/sops-nix/key.txt` on
|
||||||
|
servers) somewhere safe and offline.
|
||||||
|
- **`updatekeys` re-encrypts but doesn't rotate secret values.** If
|
||||||
|
you're removing a recipient because they shouldn't have access
|
||||||
|
anymore, also change the underlying secrets — they could've
|
||||||
|
cached the decrypted values while they had access.
|
||||||
|
- **`sops -d` prints plaintext.** Don't pipe it into anything that
|
||||||
|
logs (`tee`, journalctl, history-aware shells). Use the file path
|
||||||
|
via `config.sops.secrets.<name>.path` in NixOS config; never
|
||||||
|
hardcode the decrypted value.
|
||||||
|
- **`environment.etc` writes to the Nix store** — readable by every
|
||||||
|
user on the host. Use `sops.secrets.<name>` (writes to
|
||||||
|
`/run/secrets/` with mode-bits you control) instead.
|
||||||
|
- **`builtins.pathExists` is checked at eval time.** If you delete
|
||||||
|
`secrets/<hostName>.yaml` after a successful build, the next
|
||||||
|
rebuild silently drops the sops block — the failure surfaces only
|
||||||
|
when a service tries to read a missing `/run/secrets/<name>` file.
|
||||||
|
Don't delete a host's encrypted file unless you also remove every
|
||||||
|
`sops.secrets.<name>` referencing it.
|
||||||
23
flake.lock
generated
23
flake.lock
generated
|
|
@ -56,7 +56,28 @@
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
"lnbits-src": "lnbits-src",
|
"lnbits-src": "lnbits-src",
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs",
|
||||||
|
"sops-nix": "sops-nix"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sops-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1777944972,
|
||||||
|
"narHash": "sha256-VfGRo1qTBKOe3s2gOv8LSoA6Fk19PvBlwQ1ECN0Evn8=",
|
||||||
|
"owner": "Mic92",
|
||||||
|
"repo": "sops-nix",
|
||||||
|
"rev": "c591bf665727040c6cc5cb409079acb22dcce33c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "Mic92",
|
||||||
|
"repo": "sops-nix",
|
||||||
|
"type": "github"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
11
flake.nix
11
flake.nix
|
|
@ -21,6 +21,17 @@
|
||||||
url = "github:lnbits/lnbits";
|
url = "github:lnbits/lnbits";
|
||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# sops-nix — declarative secrets via age-encrypted YAML files.
|
||||||
|
# Decryption happens at NixOS activation; values are exposed to
|
||||||
|
# services as files under /run/secrets/<name>. The host's age
|
||||||
|
# key lives at ~/.config/sops/age/keys.txt by default; recipients
|
||||||
|
# are declared in .sops.yaml. See modules/secrets.nix for wiring
|
||||||
|
# and docs/secrets-management.md for a walkthrough.
|
||||||
|
sops-nix = {
|
||||||
|
url = "github:Mic92/sops-nix";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs =
|
outputs =
|
||||||
|
|
|
||||||
42
modules/secrets.nix
Normal file
42
modules/secrets.nix
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# sops-nix per-host wiring.
|
||||||
|
# pragma: allowlist secret start
|
||||||
|
#
|
||||||
|
# Imports the sops-nix NixOS module and points it at this host's
|
||||||
|
# encrypted file + the consumer's age private key.
|
||||||
|
#
|
||||||
|
# - Recipients (which age public keys can decrypt) are declared in
|
||||||
|
# `.sops.yaml` at the repo root.
|
||||||
|
# - The encrypted file for this host lives at
|
||||||
|
# `secrets/${settings.hostName}.yaml`. Create it with:
|
||||||
|
# sops secrets/${settings.hostName}.yaml
|
||||||
|
# sops auto-encrypts on save using the recipients from .sops.yaml.
|
||||||
|
# - The matching private key lives at
|
||||||
|
# `/home/${settings.user}/.config/sops/age/keys.txt`. Generate it
|
||||||
|
# one-time with `age-keygen -o ~/.config/sops/age/keys.txt`.
|
||||||
|
#
|
||||||
|
# The whole sops block is gated on `builtins.pathExists` so flake
|
||||||
|
# eval succeeds before the encrypted file exists — useful for the
|
||||||
|
# scaffold-bootstrap phase. See `docs/secrets-management.md` for a
|
||||||
|
# walkthrough.
|
||||||
|
# pragma: allowlist secret end
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
inputs,
|
||||||
|
settings,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
sopsFile = ../secrets/${settings.hostName}.yaml;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ inputs.sops-nix.nixosModules.sops ];
|
||||||
|
|
||||||
|
sops = lib.mkIf (builtins.pathExists sopsFile) {
|
||||||
|
defaultSopsFile = sopsFile;
|
||||||
|
defaultSopsFormat = "yaml";
|
||||||
|
age.keyFile = "/home/${settings.user}/.config/sops/age/keys.txt";
|
||||||
|
};
|
||||||
|
}
|
||||||
39
secrets/README.md
Normal file
39
secrets/README.md
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
# secrets/
|
||||||
|
|
||||||
|
Encrypted YAML files in this directory are decrypted at NixOS
|
||||||
|
activation time and exposed under `/run/secrets/<name>` for any
|
||||||
|
service that declares `sops.secrets.<name>` to consume.
|
||||||
|
|
||||||
|
Recipients are declared in `../.sops.yaml`. The matching age
|
||||||
|
private key lives at `~/.config/sops/age/keys.txt` on the host
|
||||||
|
machine (see `modules/secrets.nix`).
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# First-time: create + encrypt this host's secrets file
|
||||||
|
sops secrets/<hostName>.yaml
|
||||||
|
# sops auto-encrypts on save using recipients from .sops.yaml
|
||||||
|
|
||||||
|
# Later edits go through sops (auto-decrypts, re-encrypts on save)
|
||||||
|
sops secrets/<hostName>.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
See [`../docs/secrets-management.md`](../docs/secrets-management.md)
|
||||||
|
for the full walkthrough — generating the age key, adding a recipient,
|
||||||
|
declaring a secret in NixOS, and rotating keys.
|
||||||
|
|
||||||
|
## What goes here
|
||||||
|
|
||||||
|
One YAML file per host, named after the host. Inside each file, a
|
||||||
|
flat or nested map of secret names → values:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# secrets/<hostName>.yaml — encrypted in place
|
||||||
|
lnbits-admin-key: changeme-real-key-goes-here
|
||||||
|
postgres:
|
||||||
|
lnbits-password: changeme-real-password-goes-here
|
||||||
|
```
|
||||||
|
|
||||||
|
NixOS modules reference these by name via `sops.secrets.<name>`
|
||||||
|
and read the runtime path via `config.sops.secrets.<name>.path`.
|
||||||
Reference in a new issue