Fleet Secrets Management System[src]
Overview
Secret management system is a built-in way to deploy secrets to remote hosts, similar to agenix and other systems.
Secrets are encrypted using system’s host ssh key (/etc/ssh/ssh_host_ed25519_key), which is not required to build the remote system/add secret to fleet configuration, fleet users are encrypting secrets using received public key instead, they don’t need the root access to receive the public encryption key.
Per-host and shared secrets
Secrets come in two flavours; both are declared with the same secrets.<name> syntax, but in different
scopes:
- Per-host
-
declared inside a host’s
nixosblock. Generated and encrypted only for that one host. Use for things like a machine-specific tokens, or per-peer WireGuard keys. - Shared
-
declared at the top-level fleet config with
expectedOwners = […]. The same value is encrypted independently for every owner. Each owning host must claim it withsecrets.<name>.generator = "shared"in itsnixosblock (the assertion at modules/nixos/secrets.nix enforces this).
{ config, fleetLib, ... }:
{
# Shared - top-level, with expectedOwners
secrets."wg-psk" = {
expectedOwners = [ "alpha" "beta" ];
generator = fleetLib.mkBase64Bytes { count = 32; };
# Optional: rotate when ownership changes
regenerateOnOwnerAdded = true;
regenerateOnOwnerRemoved = true;
};
hosts.alpha.nixos = { ... }: {
# Per-host - lives only on alpha
secrets."alpha-api-token".generator = fleetLib.mkPassword { };
# Claim of the shared secret declared above
secrets."wg-psk".generator = "shared";
};
hosts.beta.nixos = { ... }: {
secrets."wg-psk".generator = "shared";
};
}
See the WireGuard example for a fuller example combining both forms.
Built-in secret generators
Fleet ships a set of generators in fleetLib covering the common cases.
Pass them directly as generator (shown here as shared secrets; the same calls work in a per-host
nixos.secrets.<name>.generator block - just drop expectedOwners):
{ config, lib, fleetLib, ... }:
{
secrets = {
# Random 32-char ASCII password (single encrypted `secret` part)
db-password = {
expectedOwners = [ "db" ];
generator = fleetLib.mkPassword { };
};
# 64-char password
long-password = {
expectedOwners = [ "db" ];
generator = fleetLib.mkPassword { size = 64; };
};
# 32 random bytes, raw/hex/base64 encoded
api-token = {
expectedOwners = [ "api" ];
generator = fleetLib.mkBytes { count = 32; encoding = "base64"; };
};
session-key = {
expectedOwners = [ "api" ];
generator = fleetLib.mkHexBytes { count = 32; };
};
wg-psk = {
expectedOwners = [ "alpha" "beta" ];
generator = fleetLib.mkBase64Bytes { count = 32; };
};
# Ed25519 keypair, two parts: plaintext `public`, encrypted `secret`
# `noEmbedPublic` strips the embedded public half, leaving only the 32-byte private scalar
garage-rpc = {
expectedOwners = [ "storage" ];
generator = fleetLib.mkEd25519 { encoding = "base64"; };
};
# X25519 keypair (e.g. WireGuard peer keys) - usually per-host, shown shared here for brevity
wg-key = {
expectedOwners = [ "alpha" ];
generator = fleetLib.mkX25519 { encoding = "base64"; };
};
# RSA keypair, PEM-encoded; default size is 4096
tls-key = {
expectedOwners = [ "edge" ];
generator = fleetLib.mkRsa { size = 2048; };
};
};
}
Interactive generators
These prompt the operator on the host running fleet and are useful for secrets that can’t be generated
algorithmically (vendor API keys, manually-issued certificates, etc.). They are impure - the value you type
is what gets encrypted.
Note that they also require GUI environment on the machine where the systems are built first time. This is intentional, because fleet console optput is quite cluttered during the deployment, and it is hard to properly show it. If this constraint bothers you - feel free to implement those however you like, all of those generators are not handled in a special way on fleet level: lib/default.nix.
{
secrets = {
# Single-line input via kdialog
stripe-api-key = {
expectedOwners = [ "billing" ];
generator = fleetLib.mkAskPass {
prompt = "Stripe live secret key";
};
};
# Multi-line input opened in Kate; useful for pasted certificates/blobs
upstream-ca = {
expectedOwners = [ "edge" ];
generator = fleetLib.mkAskFile {
header = "Paste upstream CA bundle (PEM)";
part = "cert";
};
};
# Convenience wrapper that pre-populates an env-file template
service-env = {
expectedOwners = [ "app" ];
generator = fleetLib.mkAskEnv {
header = "Fill in upstream credentials";
variables = [ "DATABASE_URL" "API_KEY" "SECRET_TOKEN" ];
};
};
};
}
Custom secret generator example
{
secrets = {
"my-secret" = {
# Shared secret will be encrypted for those two hosts
expectedOwners = [ "host1" "host2" ];
# We can force secret to be regenerated when the owner was added
regenerateOnOwnerAdded = true;
# generator is a function from pkgs extended with helpers, so if you need something special, you can just get
# it from arguments, fleet will do callPackage for you, and provide you with the packages for the machine
# that will be generating this secret
generator = {
mkImpureSecretGenerator,
openssl,
}:
mkImpureSecretGenerator {
# Impure secret generator executes arbitrary code on arbitrary host over SSH
impureOn = "host3";
# Secret may produce multiple parts, for example, for RSA keypair it would be secret and public part,
# where only secret part should be encrypted. Non-encrypted parts are available as nix store path and as
# an expression, encrypted parts will be only available on owning machines after the deployment.
parts.secret.encrypted = true;
# In impure secret generator, script is a code that thould prepare the target directory structure
# Target directory should contain one file for every declared part (parts argument in this attrset),
# and may contain expires_at/created_at files if you want to use it with PKI.
#
# Secret parts should be encoded/encrypted to be consumed by fleet, you can use `gh` alias for that,
# it will be prepared implicitly, and for arguments - look at the `fleet-generator-helper` section of the docs.
script = ''
mkdir $out
${getExeopenssl} rand -base64 16 | gh private -o $out/secret
'';
};
};
}
}
fleet-generator-helper
Usage: fleet-generator-helper <COMMAND>
Commands:
public Encode public part from stdin
private Encrypt private part from stdin
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
Encode public part from stdin
Usage: fleet-generator-helper public [OPTIONS] --output <OUTPUT>
Options:
-o, --output <OUTPUT>
-e, --encoding <ENCODING>
Possible values:
- raw: Do not encode data, store as is
- base64: Encode as base64 (with padding)
- hex: Encode as hex (without leading 0x)
[default: raw]
-h, --help
Print help (see a summary with '-h')
Encrypt private part from stdin
Usage: fleet-generator-helper private [OPTIONS] --output <OUTPUT>
Options:
-o, --output <OUTPUT>
-e, --encoding <ENCODING>
Possible values:
- raw: Do not encode data, store as is
- base64: Encode as base64 (with padding)
- hex: Encode as hex (without leading 0x)
[default: raw]
-h, --help
Print help (see a summary with '-h')