credentials module

Classes for provisioning credentials like user names and passwords to controls.

The following credential sources are supported:

For providing credentials in one of various output formats, the TemporaryCredentials class creates a temporary file and auto-deletes it again. It is inspired by systemd’s credentials mechanism. The supported output formats are listed in Format.

Note: Due to inherent Python limitations, credentials in RAM cannot be safely shredded. That is, as long as the Python process lives, and even thereafter, plaintext credentials may exist in RAM until they are overwritten by another process.

Classes

class ctlbase.credentials.CredentialReader(**kwargs)

Bases: BaseReader

A universal credential reader backed by an instance of one of the following classes:

An instance of PromptReader is always created as a fallback reader.

This class is helpful to read credentials from one of various sources, based on values determined at runtime. For instance, a control can pass its environment configuration values to __init__(), dynamically creating a backing credential reader.

The reader’s readCredential() method may raise instances of ErrorMessage with the following message keys:

  • CREDENTIALS_FALLING_BACK – If the backing credential reader failed and the fallback reader is used.

  • All the message keys from the backing credential reader.

__init__(**kwargs)

Creates a new credential reader.

Parameters:

kwargs (dict) –

Based on the following keyword arguments, the class of the backing credential reader is determined and instantiated:

Raises:

ValueError – If the class for the backing credential reader could not be determined unambiguously.

class ctlbase.credentials.KeyringReader(keyringService, username, keyringBackend='keyring.backends.libsecret.Keyring', credentialKeys=None)

Bases: BaseReader

A credential reader based on the keyring package. The keyring backend defaults to the Freedesktop Secret Service, i.e. the keyring.backends.libsecret.Keyring class. libsecret must be available on the system for that class to work.

The password must be stored under a specific service and user name. The following is the basic command to execute on the command line:

keyring -b=keyring.backends.libsecret.Keyring set <service> <username>

The reader’s readCredential() method returns a dict with the following keys:

Furthermore, the method may raise an instance of ErrorMessage with one of the following message keys:

  • CREDENTIALS_NOT_FOUND – If there is no entry in the keyring with the given service and user name.

__init__(keyringService, username, keyringBackend='keyring.backends.libsecret.Keyring', credentialKeys=None)

Creates a new credential reader.

Parameters:
  • keyringService (str) – The service name under which the password is stored in the keyring. Corresponds to <service> in the basic command above.

  • username (str) – The user name under which the password is stored in the keyring. Corresponds to <username> in the basic command above. The user name is also included in the dict returned by readCredential() under the USERNAME key.

  • keyringBackend (keyring.backend.KeyringBackend) – The keyring backend to use if deviating from the default.

  • credentialKeys (list[CredentialKey] | tuple[CredentialKey]) – See credentialKeys.

class ctlbase.credentials.Tpm2Reader(commandExecutor, tpmHandle, tpmAuth=None, username=None, credentialKeys=None)

Bases: BaseReader

A credential reader accessing the TPM2 on the local machine. It uses the binaries from tpm2-tools, e.g. tpm2_unseal.

The reader’s readCredential() method returns a dict with the following keys:

__init__(commandExecutor, tpmHandle, tpmAuth=None, username=None, credentialKeys=None)

Creates a new credential reader.

Parameters:
  • commandExecutor (CommandExecutor) – The command executor for tpm2-tools binaries.

  • tpmHandle (str) – Handle for the TPM object that represents the password. Example: 0x81000041

  • tpmAuth (str) – Hash and PCR register specfication for authorizing the access to the password. Takes the form <hash algorithm>:<PCR numbers> where <PCR numbers> is a comma-separated list of numbers from 0 through 23. Example: sha256:0,2,3,7 If None, the value of PCR_AUTH_DEFAULT is assumed.

  • username (str) – The user name to include in the dict returned by readCredential() under the USERNAME key. This is a mere pass-through as no user name is read from the TPM2.

  • credentialKeys (list[CredentialKey] | tuple[CredentialKey]) – See credentialKeys.

PCR_AUTH_DEFAULT = 'pcr:sha256:0,2,3,7'
class ctlbase.credentials.FileReader(inputFilePath, hostname=None, username=None, credentialKeys=None)

Bases: BaseReader

A credential reader based on text files. The file must be in netrc format or contain a single line with the credential.

The reader’s readCredential() method returns a dict with the following keys:

Furthermore, the method may raise an instance of ErrorMessage with one of the following message keys:

  • CREDENTIALS_NOT_FOUND – If the input file does not exist or is not readable to the current user.

  • CREDENTIALS_FORMAT_INVALID – If the input file is in netrc format but the given host name could not be found.

__init__(inputFilePath, hostname=None, username=None, credentialKeys=None)
Parameters:
  • inputFilePath (str) – The path to the input file from which to read the password.

  • hostname (str) – The hostname of the target entry. Required if the input file is in netrc format.

  • username (str) – The user name of the target entry. Required if the input file is in netrc format. Regardless of the input file format, the user name is returned in the dict from readCredential() under the USERNAME key.

  • credentialKeys (list[CredentialKey] | tuple[CredentialKey]) – See credentialKeys.

class ctlbase.credentials.PromptReader(username=None, purpose=None, credentialKeys=None)

Bases: BaseReader

A credential reader prompting the user for a credential on the command line.

The reader’s readCredential() method returns a dict with the following message keys:

Furthermore, the method may raise an instance of ErrorMessage with one of the following message keys:

  • CREDENTIALS_PROMPT_SUPPRESSED – If user prompts are suppressed, i.e. mayPrompt is False.

  • CREDENTIALS_PROMPT_ABORTED – If entering the credential was aborted by the user.

__init__(username=None, purpose=None, credentialKeys=None)

Creates a new credential reader.

Parameters:
class ctlbase.credentials.BaseReader(credentialKeys=None, msgBuffer=None, ns=None)

Base class for credential readers.

__init__(credentialKeys=None, msgBuffer=None, ns=None)

Creates a new credential reader.

Parameters:
  • credentialKeys (list[CredentialKey] | tuple[CredentialKey]) – The credential keys supported by the reader, as a subset of CredentialKey.

  • msgBuffer (str) – A message buffer for appending all occurring messages. May be None if the reader does not append any messages.

  • ns (str) – The message namespace to use when appending messages to the message buffer. May be None if the reader does not append any messages.

abstract async _readCredential(mayPrompt=None, purpose=None) dict[CredentialKey, str]

Implements the logic for reading a set of credentials from an actual source.

The parameters correspond to mayPrompt and purpose, respectively. Both are optional and may be omitted in the signature.

Returns:

A dict containing the keys listed in credentialKeys, and the corresponding values read from the source.

Return type:

dict[CredentialKey, str]

async readCredential(mayPrompt=None, purpose=None)

Reads a set of credentials.

Access to the credential source is delegated to _readCredential().

Parameters:
  • mayPrompt (bool) – If True, the user may be prompted interactively for missing credentials. If False, an exception is raised in the case of missing credentials.

  • purpose (str) – A language-agnostic str to present to the user as the purpose of the requested credentials. Example: HomeServer via SMB Only relevant when interactively prompting the user for credentials.

Raises:
  • ValueError – If the dict returned by _readCredential() does not contain all the keys listed in credentialKeys.

  • ValueError – If arguments provided in the constructor turn out to be invalid before actually accessing the source.

  • ErrorMessage – If the credentials could not be read, apart from invalid arguments.

Returns:

A dict containing the keys listed in credentialKeys, and the corresponding values read from the source.

Return type:

dict[CredentialKey, str]

credentialKeys: list[CredentialKey] | tuple[CredentialKey]

The credential keys supported by the reader, as a subset of CredentialKey.

class ctlbase.credentials.TemporaryCredentials(path, readers, format=Format.PLAIN, mountpoint=None, delete=True, msgBuffer=None, ns=True, mayPrompt=None, purpose=None)

An async context manager temporarily providing a set of credentials. When entering, credentials are read from a credential reader and written to a temporary file in one of the formats listed in Format. When exiting, the temporary file is deleted again unless delete is set to False.

Example usage:

tmp = TemporaryCredentials(
    '/tmp/smb', 
    PromptReader('hugo', 'HomeServer via SMB'), 
    format=TemporaryCredentials.Format.SMB
)

async with tmp:
    with open(tmp.path) as file:
        print(file.read())

When entering the context manager, an instance of ErrorMessage may be raised with one the following message keys:

  • CREDENTIALS_ALL_FAILED – If all credential readers in readers failed.

class Format(value)

Supported output formats for credentials.

BASH_LITERAL = '%s'

A bash literal with an associative array, containing one or more keys from CredentialKey with the corresponding values, e.g. (['username']='hugo' ['password']='q3o##3jHSNTxw~cr').

DAVFS = '%s %s %s\n'

DAVFS2 format, i.e. a single line with the mountpoint, the user name and password, separated by whitespace.

DAVFS2 = '%s %s %s\n'

An alias for DAVFS.

PLAIN = '%s\n'

A single plaintext password.

SMB = 'username=%s\npassword=%s\n'

SMB / CIFS mount format, i.e. username= on the first line and password= on the second.

__init__(path, readers, format=Format.PLAIN, mountpoint=None, delete=True, msgBuffer=None, ns=True, mayPrompt=None, purpose=None)

Creates a new context manager for a set of credentials. At his point, credentials are neither read nor written. This only happens when entering the context manager.

Parameters:
  • path (str) – The path under which the temporary file is expected. If only a file name is given, without any path specification, the value of CREDENTIALS_DIRECTORY_DEFAULT is assumed. If None, the temporary file can only be accessed via namedTemp.

  • readers (list[BaseReader] | tuple[BaseReader] | BaseReader) – The credential readers to be called in sequence to actually read the credentials. If the first one fails, the second is called and so on until one of them succeeds. For every failing reader, its error message is appended to the message buffer.

  • format (TemporaryCredentials.Format) – See format.

  • mountpoint (str) – See mountpoint.

  • delete (bool) – See delete.

  • msgBuffer (MessageBuffer) – The message buffer to which all occurring messages are appended. It is propagated to all credential readers given in readers unless they have a message buffer of their own.

  • ns (str | True | None) – The message namespace to use when appending messages to the message buffer. If True, the namespace is derived from the caller name. See CallerInspector for the discovery algorithm. The namespace is propagated to all credential readers given in readers unless they have a message namespace of their own. The default namespace of the message buffer is left unchanged. This allows for a separate namespace helpful for security audits and debugging.

  • mayPrompt (bool) – See mayPrompt.

  • purpose (str) – See purpose.

async deleteCredential()

Deletes the temporary file namedTemp along with its hardlink path.

Unless delete is set to False, this method is called automatically when the context manager exits.

Returns:

True if a temporary file existed and was deleted. False if no temporary file exists.

Return type:

bool

delete: bool
If True, the temporary credentials file is auto-deleted when the context manager exists.
The attribute name is borrowed from Python’s tempfile.NamedTemporaryFile() class.
Note: Set to False only for debugging purposes!
format: Format

The format in which credentials are written to the temporary file.

mayPrompt: bool

If True, the user may be prompted interactively for credentials.

mountpoint: str

The DAVFS mounpoint for which the credentials are valid. Only relevant when using the DAVFS format.

namedTemp: str

The path to the randomly named temporary file to which the credentials are written.

path: str

The fixed path to which the randomly named temporary file is hardlinked.

purpose: str

A language-agnostic str to present to the user as the purpose of the requested credentials. Example: HomeServer via SMB. Only relevant when interactively prompting the user for credentials.

Constants and defaults

class ctlbase.credentials.CredentialKey(value)

Common keys for a set of credentials in their dict representation.

CRYPTOPHRASE = 'cryptophrase'
LOGIN = 'username'
LOGINNAME = 'username'
PASSWORD = 'password'
TOTP = 'totp'
USERNAME = 'username'
ctlbase.credentials.CREDENTIALS_DIRECTORY_DEFAULT = '/run/reader'