git.delta.rocks / fleet / refs/heads / trunk

difftreelog

docs extend secret docs

rrwrwlpnYaroslav Bolyukin2026-05-12parent: #c4d3d0b.patch.diff

1 file changed

modifieddocs/features/secrets.adocdiffbeforeafterboth
9remote system/add secret to fleet configuration, fleet users are encrypting secrets using received public key instead,9remote system/add secret to fleet configuration, fleet users are encrypting secrets using received public key instead,
10they don't need the root access to receive the public encryption key.10they don't need the root access to receive the public encryption key.
1111
12== Example12== Per-host and shared secrets
1313
14Secrets come in two flavours; both are declared with the same `secrets.<name>` syntax, but in different
15scopes:
16
17Per-host:: declared inside a host's `nixos` block. Generated and encrypted only for that one host.
18 Use for things like a machine-specific tokens, or per-peer WireGuard keys.
19Shared:: declared at the top-level fleet config with `expectedOwners = [...]`. The same value is
20 encrypted independently for every owner. Each owning host must claim it with
21 `secrets.<name>.generator = "shared"` in its `nixos` block (the assertion at
22 link:../../modules/nixos/secrets.nix[modules/nixos/secrets.nix] enforces this).
23
14[source,nix]24[source,nix]
15----25----
26{ config, fleetLib, ... }:
16{27{
28 # Shared - top-level, with expectedOwners
29 secrets."wg-psk" = {
30 expectedOwners = [ "alpha" "beta" ];
31 generator = fleetLib.mkBase64Bytes { count = 32; };
32 # Optional: rotate when ownership changes
33 regenerateOnOwnerAdded = true;
34 regenerateOnOwnerRemoved = true;
35 };
36
37 hosts.alpha.nixos = { ... }: {
38 # Per-host - lives only on alpha
39 secrets."alpha-api-token".generator = fleetLib.mkPassword { };
40
41 # Claim of the shared secret declared above
42 secrets."wg-psk".generator = "shared";
43 };
44
45 hosts.beta.nixos = { ... }: {
46 secrets."wg-psk".generator = "shared";
47 };
48}
49----
50
51See link:../examples/wireguard.adoc[the WireGuard example] for a fuller example combining both forms.
52
53== Built-in secret generators
54
55Fleet ships a set of generators in link:../../lib/default.nix[`fleetLib`] covering the common cases.
56Pass them directly as `generator` (shown here as shared secrets; the same calls work in a per-host
57`nixos.secrets.<name>.generator` block - just drop `expectedOwners`):
58
59[source,nix]
60----
61{ config, lib, fleetLib, ... }:
62{
17 secrets = {63 secrets = {
64 # Random 32-char ASCII password (single encrypted `secret` part)
65 db-password = {
66 expectedOwners = [ "db" ];
67 generator = fleetLib.mkPassword { };
68 };
69
70 # 64-char password
71 long-password = {
72 expectedOwners = [ "db" ];
73 generator = fleetLib.mkPassword { size = 64; };
74 };
75
76 # 32 random bytes, raw/hex/base64 encoded
77 api-token = {
78 expectedOwners = [ "api" ];
79 generator = fleetLib.mkBytes { count = 32; encoding = "base64"; };
80 };
81 session-key = {
82 expectedOwners = [ "api" ];
83 generator = fleetLib.mkHexBytes { count = 32; };
84 };
85 wg-psk = {
86 expectedOwners = [ "alpha" "beta" ];
87 generator = fleetLib.mkBase64Bytes { count = 32; };
88 };
89
90 # Ed25519 keypair, two parts: plaintext `public`, encrypted `secret`
91 # `noEmbedPublic` strips the embedded public half, leaving only the 32-byte private scalar
92 garage-rpc = {
93 expectedOwners = [ "storage" ];
94 generator = fleetLib.mkEd25519 { encoding = "base64"; };
95 };
96
97 # X25519 keypair (e.g. WireGuard peer keys) - usually per-host, shown shared here for brevity
98 wg-key = {
99 expectedOwners = [ "alpha" ];
100 generator = fleetLib.mkX25519 { encoding = "base64"; };
101 };
102
103 # RSA keypair, PEM-encoded; default size is 4096
104 tls-key = {
105 expectedOwners = [ "edge" ];
106 generator = fleetLib.mkRsa { size = 2048; };
107 };
108 };
109}
110----
111
112=== Interactive generators
113
114These prompt the operator on the host running `fleet` and are useful for secrets that can't be generated
115algorithmically (vendor API keys, manually-issued certificates, etc.). They are impure - the value you type
116is what gets encrypted.
117
118Note that they also require GUI environment on the machine where the systems are built first time. This is intentional,
119because fleet console optput is quite cluttered during the deployment, and it is hard to properly show it.
120If this constraint bothers you - feel free to implement those however you like, all of those generators are not handled
121in a special way on fleet level: link:../../lib/default.nix[lib/default.nix].
122
123[source,nix]
124----
125{
126 secrets = {
127 # Single-line input via kdialog
128 stripe-api-key = {
129 expectedOwners = [ "billing" ];
130 generator = fleetLib.mkAskPass {
131 prompt = "Stripe live secret key";
132 };
133 };
134
135 # Multi-line input opened in Kate; useful for pasted certificates/blobs
136 upstream-ca = {
137 expectedOwners = [ "edge" ];
138 generator = fleetLib.mkAskFile {
139 header = "Paste upstream CA bundle (PEM)";
140 part = "cert";
141 };
142 };
143
144 # Convenience wrapper that pre-populates an env-file template
145 service-env = {
146 expectedOwners = [ "app" ];
147 generator = fleetLib.mkAskEnv {
148 header = "Fill in upstream credentials";
149 variables = [ "DATABASE_URL" "API_KEY" "SECRET_TOKEN" ];
150 };
151 };
152 };
153}
154----
155
156== Custom secret generator example
157
158[source,nix]
159----
160{
161 secrets = {
18 "my-secret" = {162 "my-secret" = {
163 # Shared secret will be encrypted for those two hosts
19 expectedOwners = [ "host1" "host2" ];164 expectedOwners = [ "host1" "host2" ];
165 # We can force secret to be regenerated when the owner was added
20 regenerateOnOwnerAdded = true;166 regenerateOnOwnerAdded = true;
167
21 generator = {mkImpureSecretGenerator}:168 # generator is a function from pkgs extended with helpers, so if you need something special, you can just get
169 # it from arguments, fleet will do callPackage for you, and provide you with the packages for the machine
170 # that will be generating this secret
171 generator = {
172 mkImpureSecretGenerator,
173 openssl,
174 }:
22 mkImpureSecretGenerator {175 mkImpureSecretGenerator {
176 # Impure secret generator executes arbitrary code on arbitrary host over SSH
177 impureOn = "host3";
178
179 # Secret may produce multiple parts, for example, for RSA keypair it would be secret and public part,
180 # where only secret part should be encrypted. Non-encrypted parts are available as nix store path and as
181 # an expression, encrypted parts will be only available on owning machines after the deployment.
182 parts.secret.encrypted = true;
183
184 # In impure secret generator, script is a code that thould prepare the target directory structure
185 # Target directory should contain one file for every declared part (parts argument in this attrset),
186 # and may contain expires_at/created_at files if you want to use it with PKI.
187 #
188 # Secret parts should be encoded/encrypted to be consumed by fleet, you can use `gh` alias for that,
189 # it will be prepared implicitly, and for arguments - look at the `fleet-generator-helper` section of the docs.
23 script = ''190 script = ''
24 echo "secret content" | gh private -o $out/secret191 mkdir $out
192 ${getExe openssl} rand -base64 16 | gh private -o $out/secret
25 '';193 '';
26 };194 };
27 };195 };
28 }196 }
29}197}
198----
199
200== fleet-generator-helper
201
202[source]
203----
204Usage: fleet-generator-helper <COMMAND>
205
206Commands:
207 public Encode public part from stdin
208 private Encrypt private part from stdin
209 help Print this message or the help of the given subcommand(s)
210
211Options:
212 -h, --help Print help
213----
214
215[source]
216----
217Encode public part from stdin
218
219Usage: fleet-generator-helper public [OPTIONS] --output <OUTPUT>
220
221Options:
222 -o, --output <OUTPUT>
223
224 -e, --encoding <ENCODING>
225 Possible values:
226 - raw: Do not encode data, store as is
227 - base64: Encode as base64 (with padding)
228 - hex: Encode as hex (without leading 0x)
229
230 [default: raw]
231
232 -h, --help
233 Print help (see a summary with '-h')
234----
235
236[source]
237----
238Encrypt private part from stdin
239
240Usage: fleet-generator-helper private [OPTIONS] --output <OUTPUT>
241
242Options:
243 -o, --output <OUTPUT>
244
245 -e, --encoding <ENCODING>
246 Possible values:
247 - raw: Do not encode data, store as is
248 - base64: Encode as base64 (with padding)
249 - hex: Encode as hex (without leading 0x)
250
251 [default: raw]
252
253 -h, --help
254 Print help (see a summary with '-h')
30----255----
31256