progfiguration.sitehelpers.agesecrets#

Age secret file store.

Store progfiguration secrets as files. Each secret is encrypted by an Age key.

Wrap the age binary to encrypt and decrypt secret values

Module Contents#

Classes#

AgeKey

An age keypair

AgeSecret

An age-encrypted secret value

AgeSecretReference

A reference to a secret by name

AgeSecretStore

Age secret file store for progfiguration inventories.

Functions#

encrypt(value, pubkeys)

Encrypt a value to a list of public age keys

decrypt(→ str)

Decrypt an encrypted age value

exception progfiguration.sitehelpers.agesecrets.AgeParseException#

Bases: Exception

Common base class for all non-exit exceptions.

exception progfiguration.sitehelpers.agesecrets.MissingAgeKeyException#

Bases: Exception

Common base class for all non-exit exceptions.

class progfiguration.sitehelpers.agesecrets.AgeKey(secret: str, public: str, created: datetime.datetime)#

An age keypair

Attributes:

secret: The secret key public: The public key created: The datetime the key was created

classmethod from_output(output: str) AgeKey#

Parse the output of age-keygen into an AgeKey

Here’s example output from age-keygen -y:

# created: 2022-09-28T16:01:22-05:00 # public key: age14e42u048nehghjj3ch9mmnkdh4nsujn774klqxn02mznppx3gflsuj6y5m AGE-SECRET-KEY-1ASKGXED4DVGUH7SA50DHE2UHAYQ00PV87N2RQ5J5S6AUN9MLNSGQ3TKFGJ

This function parses that output and returns an AgeKey object.

classmethod generate(path: pathlib.Path | None = None) AgeKey#

Generate a new age keypair using the age-keygen binary

classmethod from_file(path: str) AgeKey#

Load an age keypair from a file

class progfiguration.sitehelpers.agesecrets.AgeSecret#

Bases: progfiguration.inventory.invstores.Secret

An age-encrypted secret value

secret: str#

The encrypted secret value

privkey_path: str | None#

The path to the private key.

If this is None, then the secret cannot be decrypted.

decrypt() str#

Decrypt the secret.

class progfiguration.sitehelpers.agesecrets.AgeSecretReference#

Bases: progfiguration.inventory.invstores.SecretReference

A reference to a secret by name

This is a wrapper type that allows us to pass around a reference to a secret without having to know the secret’s value.

name: str#

The name of the secret

dereference(nodename: str, hoststore: progfiguration.inventory.invstores.HostStore, secretstore: progfiguration.inventory.invstores.SecretStore) Any#

Get the final value of a role argument for a node.

Arguments to this method:

  • nodename: The name of the node that the argument is being applied to

  • hoststore: The inventory object

This function must retrieve or calculate the final value from its internal data and these arguments.

progfiguration.sitehelpers.agesecrets.encrypt(value: str, pubkeys: List[str])#

Encrypt a value to a list of public age keys

progfiguration.sitehelpers.agesecrets.decrypt(value: str, privkey_path: str | None) str#

Decrypt an encrypted age value

class progfiguration.sitehelpers.agesecrets.AgeSecretStore(controller_age_pubkey: str, decryption_age_privkey_path_list: List[str] | None = None)#

Bases: progfiguration.inventory.invstores.SecretStore

Age secret file store for progfiguration inventories.

Store progfiguration secrets as files in the site’s nodes/ and groups/ submodules. Each secret is encrypted by an Age key.

Look for a decryption key in the list passed to the initializer.

The CLI arguments it accepts:

age_key

The path to an age private key to decrypt secrets.

The InventoryNode sitedata it looks for:

age_key_path

The path on the node to an age private key to decrypt secrets. If this is not set, we try to use the decryption_age_privkey_path. If this is set, but the key does not exist, we raise FileNotFoundError.

age_pubkey

The string of an age public key to encrypt secrets. If this is not set, encrypting secrets for the node (or any of its groups) will fail.

controller_age_pubkey: str#

The controller’s age public key

_cache: Dict[str, Dict[str, Dict[str, progfiguration.inventory.invstores.Secret]]]#

An in-memory cache.

self._cache[collection][name] = {secretname: Secret}

decryption_age_privkey_path: str = ''#

If this is not empty, use this path to an age private key to decrypt secrets.

During initialization, this is set to the first item in decryption_age_privkey_path_list that exists. Can be (re-)set after initialization.

TODO: how will the cli code in core allow setting this at runtime?

_get_secrets_file(collection: Literal[node, group, special], name: str) pathlib.Path#
_load_secrets(collection: Literal[node, group, special], name: str) Dict[str, progfiguration.inventory.invstores.Secret]#

Load a secrets from a secret store.

Cache them in memory for future use.

_save_secrets(collection: Literal[node, group, special], name: str)#

Save a secrets to a secret store.

Save them to disk.

list_secrets(collection: Literal[node, group, special], name: str) List[str]#

List secrets.

get_secret(collection: Literal[node, group, special], name: str, secret_name: str) progfiguration.inventory.invstores.Secret#

Retrieve a secret from the secret store.

This operation returns an opaque Secret type. The secret does not have to be decrypted yet, but can be, depending on the Secret implementation.

encrypt_secret(hoststore: progfiguration.inventory.invstores.HostStore, name: str, value: str, nodes: List[str], groups: List[str], controller_key: bool, store: bool = False) str#

Encrypt a secret for some list of nodes and groups.

Always encrypt for the controller so that it can decrypt too.

We assume this only ever happens on the controller.

apply_cli_arguments(args: Dict[str, str]) None#

Apply arguments from the command line

Arguments are passed as comma-separated key/value pairs, like --secret-store-arguments key1=value1,key2=value2.

find_node_key(node: progfiguration.inventory.nodes.InventoryNode)#

If called, this function should find the decryption key for a node.

It may be called by the progfigsite command-line program if the user specifies a node. The implementation may look up the key in the node’s sitedata.