Skip to main content

Module secrets

Module secrets 

Source
Expand description

At-rest encryption of secrets in config.toml (GAP-009 / R-SECRETS-DEFAULT).

Primary-key resolution order (32 bytes), 0.5.1:

  1. CLI flags (--secrets-key-file, --use-keyring, --allow-plaintext-secrets)
  2. OS keyring when enabled (service=ssh-cli, user=secrets-primary-key; legacy read alias)
  3. XDG secrets.key file (next to config.toml), auto-created on first write

Env-as-store is forbidden (G-ERR-13 / G-UNSAFE): if SSH_CLI_SECRETS_KEY or SSH_CLI_SECRETS_KEY_FILE is present, load fails closed with a clear error pointing to XDG secrets.key or --secrets-key-file.

Plaintext at-rest opt-out: only CLI --allow-plaintext-secrets (no env store).

With a key: serialization writes sshcli-enc:v2:<base64(nonce||ciphertext)>.

§Blob versions (A7)

v1 blobs were sealed without associated data, so the AEAD tag only proved “encrypted by this key” and said nothing about where the blob belongs. Anyone able to edit config.toml could move a password blob to another host, or paste it into su_password, and decryption would still succeed — context confusion with no detection.

v2 binds the ciphertext to a SecretContext (host name + field name) via AEAD associated data, so a relocated blob fails tag verification.

Compatibility is deliberate and dual-read:

  • v1 is still accepted on read (existing configs must keep working).
  • v2 is always written.
  • A v2 blob sealed under SecretContext::unbound — the context used by call sites not yet passing host/field — is accepted under any context. That keeps the migration monotonic: today’s writes are no weaker than v1, and once a call site passes a real context the rewritten blob becomes strictly bound and can never be relocated afterwards.

Never log or return the key or plaintext in public errors.

Structs§

SecretContext
Where a secret belongs: the host that owns it and the field that holds it.
SecretsStatus
Secrets mode report (no sensitive material).

Enums§

KeySource
Primary-key source (without exposing material).

Constants§

ENC_PREFIX
Prefix for legacy encrypted blobs without associated data (read-only).
ENC_PREFIX_V2
Prefix for context-bound encrypted blobs (written by this version).
KEY_FILE_NAME
File name of the primary key in the config directory (XDG sibling of config.toml).

Functions§

auto_key_created
Returns true if a key was auto-created (non-consuming).
deserialize_secret
Deserializes from TOML: decrypts sshcli-enc: blobs; otherwise returns as-is.
deserialize_secret_in_context
Deserializes a secret expected to belong to ctx.
ensure_key_for_write
Ensures a key for write: loads existing or auto-creates secrets.key (unless plaintext opt-out).
generate_hex_key
Generates PRIMARY_KEY_LEN_BYTES random bytes as PRIMARY_KEY_HEX_LEN hex chars.
init_primary_key
Initializes primary-key in XDG file or keyring. Never prints the key.
is_encrypted_blob
True if the string is already an encrypted blob (any supported version).
load_primary_key
Resolves primary key and source (does not auto-create).
plaintext_allowed
True if plaintext opt-out is active (CLI flag only — G-ERR-13, no env store).
secrets_config_dir
Config directory used for secrets.key (CLI/test override > XDG).
secrets_key_path
Canonical path of the local primary-key file.
secrets_status
Current status (without loading material into logs).
serialize_secret
Serializes a secret for TOML: encrypts if a key exists (or is auto-created); otherwise plaintext.
serialize_secret_in_context
Serializes a secret bound to ctx (A7): writes a v2 blob sealed with AAD.
set_config_dir
Sets the config directory used to resolve secrets.key (one-shot; called from dispatch).
set_runtime_flags
Applies one-shot CLI flags for secrets resolution (GAP-AUD-006).
take_auto_key_created
Returns true once if a key was auto-created since the last flag reset (consume).
write_key_file
Writes hex key to file with 0o600 (when supported).
write_key_to_keyring
Stores the primary key (hex) in the OS keyring. Never prints the key.

Type Aliases§

PrimaryKey
Primary key material that scrubs itself on drop.