Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

puppet-sc_secrets

Derive named secrets from one root secret, declared in hiera.

Companion to sc_random, with the same ergonomics and a different guarantee. sc_random produces repeatable values from fqdn_rand, which is a pure function of the node's FQDN and the resource name — fine for spreading cron minutes across a fleet, and not a secret, because anyone who can guess a hostname can reproduce every value it makes. This module produces values that cannot be reproduced without the root secret.

The root

Not from this module. $root defaults to the top-scope $sc_project_secret, which has to be set from outside — how that value is produced is not this module's concern, and it never learns. It only derives from it.

Usage

Declare secrets in hiera:

sc_secrets::secrets:
  grafana-admin:          # no body: purpose is the key, size is 32
  archive-store:
    purpose: archive-store-key
    size: 48

size is in hex characters and defaults to 32, which is 128 bits. purpose defaults to the key name and is what actually gets hashed.

Then interpolate them anywhere hiera does — most often into another class's parameter:

grafana::admin_password: "%{sc_secrets::secret.grafana-admin}"

Or from Puppet code:

include sc_secrets
$password = $sc_secrets::secret['grafana-admin']

The function

The class is sugar over one function, which takes the root explicitly and knows nothing else:

sc_secrets::derive($root, $purpose, $size = 32)
$token = sc_secrets::derive($sc_project_secret, 'metrics-scrape-token')

Reach for it directly when you need a secret in Puppet code without declaring it in hiera, or when the root is not the default.

How it derives

HMAC-SHA256(root, purpose), lowercase hex, truncated to size.

Hex rather than a denser alphabet is deliberate: these values land in my.cnf files, YAML, service environments and shell, and a character set with no quoting hazards is worth more than the few bits per character it costs.

Two purposes never collide, and one derived secret does not yield another — so a leaked database password does not hand over the archive store.

Two traps worth knowing before you declare one

The key name is the default purpose, and the purpose is what gets hashed. Renaming a hiera key therefore rotates that secret, exactly as renaming an sc_random resource re-rolls its value. Set purpose explicitly if you want a name you can change freely, and treat any change to purpose or size as a password rotation on every consumer.

Declare secrets at the narrowest hierarchy layer that needs them. Every entry in sc_secrets::secrets is computed into the catalog of every node that reaches that layer, and is interpolatable from any yaml that node reads. A secret declared in common.yaml exists on every node; the same secret declared in a role exists only on nodes holding that role.

What it does not do

Rotation is not staged. Change the root and every derived secret changes at once, on each node's next run — so two nodes sharing a derived password are briefly out of step. Whichever side reconciles first fails against the other until both have run. Every consumer must be managed by Puppet, or it is stranded at the old value with nothing to update it.

There is no escrow. Derived values are not stored anywhere; lose the root and every secret it produced is gone, not recoverable. That is the point, and it makes the root the one value you have to be right about.

Parameters

secrets Hash of secrets to derive, keyed by name. Per entry: purpose (defaults to the key) and size in hex characters (defaults to 32).
root The root secret every entry is derived from, set from outside this module. Defaults to the top-scope $sc_project_secret. Undef is an error only when secrets are declared, so a node that declares none still compiles where no root is configured.

Requirements

Puppet >= 6, puppetlabs-stdlib. The derivation runs wherever catalogs are compiled — on a primary server that is the server, not the agent, so neither the root nor any derived value is computed on a node.

About

Derive named secrets from one root secret, declared in hiera

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages