cki_tools.credentials
Manage credentials for service accounts and their meta data as stored in CKI secrets.
Life cycle of rotatable tokens
In general, tokens can be rotated via a three-step procedure by preparing a new token version, switching to it, and cleaning up afterwards.
Life cycle of service-side rotatable tokens
For services that support rotation directly, rotating a token normally means
both creating a new token and invalidating the old one. As this would lead to
downtime, two such tokens are created in advance, one of which is deployed and
the other is prepared for deployment. The switch
command then switches the
deployed token to the prepared one.
step | version 1 | version 2 | version 3 |
---|---|---|---|
active | deployed | missing | |
MR 1: rotate the non-deployed version via prepare |
revoked | deployed | active |
MR 2: switch to the new version via switch |
revoked | active | deployed |
Life cycle tokens for services that do not support token rotation natively
For services that do not support token rotation natively, rotating a token is done by creating a new token version, switching over to it, and revoking the old token version.
step | version 1 | version 2 |
---|---|---|
deployed | missing | |
MR 1: create a new non-deployed version via prepare |
deployed | active |
MR 2: switch to the new version via switch |
active | deployed |
MR 3: revoke the old version via clean |
revoked | deployed |
Secrets
The secrets
tool allows access to CKI variables and secrets.
$ python3 -m cki_tools.credentials.secrets --help
usage: secrets.py [-h] {secret,variable,edit,validate,login,logout} ...
Access CKI variables and secrets
positional arguments:
{secret,variable,edit,validate,login,logout}
secret Retrieve a secret value
variable Retrieve a variable value
edit Edit a secret value
validate Validate stored secrets
login Log into secrets storage
logout Log out of secrets storage
-h, --help show this help message and exit
--json output in json format
The following CLI aliases are provided:
cki_secret
==python3 -m cki_tools.credentials.secrets secret
cki_variable
==python3 -m cki_tools.credentials.secrets variable
cki_edit_secret
==python3 -m cki_tools.credentials.secrets edit
cki_secrets_validate
==python3 -m cki_tools.credentials.secrets validate
cki_secrets_login
==python3 -m cki_tools.credentials.secrets login
cki_secrets_logout
==python3 -m cki_tools.credentials.secrets logout
CKI variables and secrets meta data are stored in YAML files. Encrypted secrets are stored in HashiCorp Vault.
Environment variables
Name | Secret | Required | Description |
---|---|---|---|
CKI_SECRETS_FILE |
no | yes | Path to the secrets meta data file |
CKI_VARS_FILE |
no | yes | Path to the variables file |
VAULT_ADDR |
no | yes | Address of the Vault server expressed as a URL, including port |
VAULT_MOUNT_POINT |
no | no | Mount point of the KV2 secrets engine |
VAULT_APPROLE_SECRET_ID |
yes | no | Secret ID of the approle (service account) |
VAULT_TOKEN |
yes | no | Vault client token, falls back to ~/.vault-token if not set |
cki_secret
usage: secrets.py secret [-h] [--json] key
Retrieve a secret value
positional arguments:
key secret name
options:
-h, --help show this help message and exit
--json output in json format
For secrets, the file name for the secrets meta data needs to be specified in
the CKI_SECRETS_FILE
environment variable. The actual secrets are stored in
HashiCorp Vault.
As an example, a secrets meta data file could look like:
foo:
meta:
token_type: aws_secret_access_key
Pointing CKI_SECRETS_FILE
to it and calling cki_secret foo
will try to
retrieve the value of foo
from HashiCorp Vault:
$ cki_secret foo
bar
$ cki_secret foo --json
"bar"
Conditions
Conditions allow to obtain a list of meta/data fields for multiple secrets via
something like path[meta-key-1,!meta-key-2,...]
. For all secrets that start
with the given path, delimited by /
, the given meta fields are obtained with
a default of False
. A secret is only included in the returned list if all
conditions match, i.e. if they are True
for a condition like meta-key-1
, or
False
for a condition like !meta-key-2
.
Supported locations
Location | Description |
---|---|
some/path# |
dictionary of all key-value meta pairs |
some/path#field |
value for the given meta field |
some/path: |
dictionary of all key-value data pairs |
some/path:field |
value for the given data field |
some/path |
value for the value data field |
some/path[cond1,cond2] |
list of the above for all matching secrets |
cki_variable
usage: secrets.py variable [-h] [--json] key
Retrieve a variable value
positional arguments:
key variable name
options:
-h, --help show this help message and exit
--json output in json format
For variables, the file name needs to be specified in the CKI_VARS_FILE
environment variable.
As an example, a variables file could look like:
foo: bar
baz: |
some
string
qux:
complex: value
bool: true
int: 15
Pointing CKI_VARS_FILE
to it and calling cki_variable key
will print the
values of the various variables:
$ for i in foo baz qux bool int; do cki_variable $i; done
bar
some
string
{'complex': 'value'}
True
15
With --json
, the output will be properly json-encoded:
$ for i in foo baz qux bool int; do cki_variable --json $i; done
"bar"
"some\nstring"
{"complex": "value"}
true
15
cki_edit_secret
usage: secrets.py edit [-h] key value
Edit a secret value
positional arguments:
key secret name
value new secret value
options:
-h, --help show this help message and exit
Secrets and their associated meta data can be edited as well. From the location syntax table above, all but the conditions are supported.
When editing meta data, the secrets meta data file is rewritten with the updated meta data. When editing secrets, the actual secret values are updated on HashiCorp Vault.
$ cat secrets.yml
foo:
meta:
token_type: aws_secret_access_key
$ cki_edit_secret foo#user_name aws-user-name
$ cat secrets.yml
foo:
meta:
token_type: aws_secret_access_key
user_name: aws-user-name
cki_secrets_validate
usage: secrets.py validate [-h]
Validate stored secrets
options:
-h, --help show this help message and exit
Compare the list of secrets available in HashiCorp Vault and the secrets meta data. Exits with an exit code of 2 if secrets are missing in HashiCorp Vault. Exits with an exit code of 1 if secrets are only missing in the secrets meta data. Exits with an exit code of 0 if no difference is found.
cki_secrets_login
usage: secrets.py login [-h] [--duration DURATION] (--oidc | --approle APPROLE)
Log into secrets storage
options:
-h, --help show this help message and exit
--duration DURATION validity of the token
--oidc login via OIDC
--approle APPROLE login via given approle
Log into HashiCorp Vault either via OIDC (human user) or an approle (service account).
For OIDC, a Kerberos ticket is necessary to get past SSO.
For an approle, the approle secret ID needs to be provided in the
VAULT_APPROLE_SECRET_ID
environment variable.
If successful, the client token will be stored in ~/.vault-token
as supported
by the official vault
CLI tooling.
cki_secrets_logout
usage: secrets.py logout [-h]
Log out of secrets storage
options:
-h, --help show this help message and exit
Log out of HashiCorp Vault by revoking the client token. Independent whether
the API call is successful, the ~/.vault-token
file will be removed.
Metrics
usage: python3 -m cki_tools.credentials.metrics [-h]
Print Prometheus metrics about secrets
options:
-h, --help show this help message and exit
This will output the following Prometheus metrics related to the stored credentials:
name | labels | description |
---|---|---|
cki_token_created_at |
name , active , deployed |
token creation timestamp |
cki_token_expires_at |
name , active , deployed |
token expiration timestamp |
Manager
usage: python3 -m cki_tools.credentials.manager
[--token-name TOKEN_NAME]
[--token-type-prefix TOKEN_TYPE_PREFIX]
[--force]
{create,destroy,rotate,update,validate,status,prepare,switch,clean}
Manage the lifecycle of secrets
positional arguments:
{create,destroy,rotate,update,validate,status,prepare,switch,clean}
What to do
options:
-h, --help show this help message and exit
--token-name TOKEN_NAME
Single token name
--token-type-prefix TOKEN_TYPE_PREFIX
Filter by token type prefix
--force Force token rotation even if new enough
Low-level commands
command | description |
---|---|
create |
create a new token as specified by the required meta data |
destroy |
destroy an existing token, e.g. by revoking it |
rotate |
create a new version of an existing token, which might invalidate the original version |
Three-step rotation commands
command | description |
---|---|
status |
show which tokens need rotation/preparation/cleanup |
prepare |
prepare token versions for rotation |
switch |
switch the deployed token version |
clean |
clean token versions after rotation |
Miscellaneous commands
command | description |
---|---|
update |
update token meta data from the remote service |
validate |
confirm with the remote services that active token versions are still valid |
purge |
remove secrets and meta data for inactive token versions |
Additionally, the validate
commands check the following invariants about the
versions of a token:
- all versions have
active
,deployed
andcreated_at
fields - only one version is marked as
deployed
GitLab project access tokens
- Supported low-level commands: create, destroy, rotate
- Supported features: three-step rotation, update, validate
See the API description for details.
name | create | description |
---|---|---|
(secret) | updated | secret token |
token_type |
required | gitlab_project_token |
project_url |
required | Project URL |
scopes |
required | Access scope |
access_level |
required | Access levels |
token_name |
required | Name of the token |
token_id |
updated | Project access token ID |
created_at |
updated | ISO8601 timestamp of creation |
expires_at |
updated | ISO8601 expiry date |
revoked |
updated | Whether the token is already revoked |
active |
updated | Whether the token is still active |
user_id |
updated | ID of associated user |
user_name |
updated | Name of associated user |
deployed |
updated | Whether the token is actually used |
GitLab group access tokens
- Supported low-level commands: create, destroy, rotate
- Supported features: three-step rotation, update, validate
See the API description for details.
name | create | description |
---|---|---|
(secret) | updated | secret token |
token_type |
required | gitlab_group_token |
group_url |
required | Group URL |
scopes |
required | Access scope |
access_level |
required | Access levels |
token_name |
required | Name of the token |
token_id |
updated | Group access token ID |
created_at |
updated | ISO8601 timestamp of creation |
expires_at |
updated | ISO8601 expiry date |
revoked |
updated | Whether the token is already revoked |
active |
updated | Whether the token is still active |
user_id |
updated | ID of associated user |
user_name |
updated | Name of associated user |
deployed |
updated | Whether the token is actually used |
GitLab personal access tokens
- Supported low-level commands: destroy, rotate
- Supported features: three-step rotation, update, validate
See the API description for details.
name | rotate | description |
---|---|---|
(secret) | updated | secret token |
token_type |
required | gitlab_personal_token |
instance_url |
required | GitLab instance URL |
scopes |
updated | Access scope |
token_name |
updated | Name of the token |
token_id |
required | Access token ID |
created_at |
updated | ISO8601 timestamp of creation |
expires_at |
updated | ISO8601 expiry date |
revoked |
updated | Whether the token is already revoked |
active |
updated | Whether the token is still active |
user_id |
required | ID of associated user |
user_name |
updated | Name of associated user |
deployed |
updated | Whether the token is actually used |
GitLab project deploy tokens
- Supported low-level commands: create, destroy
- Supported features: three-step rotation, update, validate
See the API description for details.
name | create | description |
---|---|---|
(secret) | updated | secret token |
token_type |
required | gitlab_project_deploy_token |
project_url |
required | Project URL |
scopes |
required | Access scope |
token_name |
required | Name of the token |
token_id |
updated | Project deploy token ID |
created_at |
updated | ISO8601 timestamp of creation |
expires_at |
optional | ISO8601 expiry date |
revoked |
updated | Whether the token is already revoked |
active |
updated | Whether the token is still active |
user_name |
updated | Associated user name |
deployed |
updated | Whether the token is actually used |
GitLab group deploy tokens
- Supported low-level commands: create, destroy
- Supported features: three-step rotation, update, validate
See the API description for details.
name | create | description |
---|---|---|
(secret) | updated | secret token |
token_type |
required | gitlab_group_deploy_token |
group_url |
required | Group URL |
scopes |
required | Access scope |
token_name |
required | Name of the token |
token_id |
updated | Group deploy token ID |
created_at |
updated | ISO8601 timestamp of creation |
expires_at |
optional | ISO8601 expiry date |
revoked |
updated | Whether the token is already revoked |
active |
updated | Whether the token is still active |
user_name |
updated | Associated user name |
deployed |
updated | Whether the token is actually used |
GitLab runner authentication tokens
- Supported features: update, validate
See the API description for details.
name | update | description |
---|---|---|
(secret) | required | secret token |
token_type |
required | gitlab_runner_authentication_token |
instance_url |
required | GitLab instance URL |
token_id |
updated | Group token ID |
expires_at |
updated | ISO8601 expiry date (optional) |
active |
updated | Whether the token is still active |
deployed |
Whether the token is actually used |
LDAP keytabs
- Supported features: update
name | update | description |
---|---|---|
(secret) | keytab, base64-encoded | |
token_type |
required | ldap_keytab |
ldap_server |
required | LDAP server |
dn |
required | LDAP distinguished name |
(LDAP attributes) | updated | LDAP object attributes |
LDAP passwords
- Supported features: update, validate
name | update | description |
---|---|---|
(secret) | password | |
token_type |
required | ldap_password |
ldap_server |
required | LDAP server |
dn |
required | LDAP distinguished name |
(LDAP attributes) | updated | LDAP object attributes |
AWS secret access keys
- Supported low-level commands: create, destroy
- Supported features: three-step rotation, update
name | update | description |
---|---|---|
(secret) | secret access key | |
token_type |
required | aws_secret_access_key |
access_key_id |
required | access key ID |
endpoint_url |
required | endpoint URL if not AWS |
account |
updated | AWS account number |
user_name |
updated | service account user name |
arn |
updated | service account user ARN |
created_at |
updated | ISO8601 timestamp of creation |
active |
updated | Whether the token is still active |
deployed |
Whether the token is actually used |
Dogtag certificates
- Supported low-level commands: create
- Supported features: three-step rotation, update, validate
name | create | description |
---|---|---|
(secret):private_key | updated | secret key |
(secret):certificate | updated | certificate |
token_type |
required | dogtag_certificate |
server_url |
required | Dogtag server URL |
serial_number |
updated | Certificate serial number |
issuer_dn |
updated | Issuer DN |
subject_dn |
required | Subject DN |
created_at |
updated | ISO8601 timestamp of creation |
expires_at |
updated | ISO8601 timestamp of expiry |
active |
updated | Whether the certificate is still valid |
deployed |
updated | Whether the certificate is actually used |
Splunk HEC tokens
- Supported features: validate
name | validate | description |
---|---|---|
(secret) | required | HEC token |
token_type |
required | splunk_hec_token |
endpoint_url |
required | Splunk HEC endpoint base URL |
index |
optional | Splunk index |
SSH keys
- Supported low-level commands: create
- Supported features: three-step rotation, update, validate
name | create | description |
---|---|---|
(secret):private_key | updated | secret key |
(secret):public_key | updated | certificate |
token_type |
required | ssh_private_key |
comment |
required | Public key comment (name) |
key_size |
required | RSA key size |
created_at |
updated | ISO8601 timestamp of creation |
active |
updated | Whether the key is still valid |
deployed |
updated | Whether the key is actually used |
Generic passwords
- Supported low-level commands: create
- Supported features: three-step rotation
Passwords are created via diceware.
name | create | description |
---|---|---|
(secret) | updated | password |
token_type |
required | password |
created_at |
updated | ISO8601 timestamp of creation |
active |
updated | Whether the key is still valid |
deployed |
updated | Whether the key is actually used |
HashiCorp Vault secret IDs
- Supported low-level commands: create, destroy
- Supported features: three-step rotation, validate
Only HashiCorp Vault AppRole auth secret IDs are supported.
name | create | description |
---|---|---|
(secret) | updated | secret ID |
token_type |
required | hv_approle_secret_id |
vault_addr |
required | HashiCorp Vault URL |
role_id |
required | role name |
secret_id_accessor |
updated | secret ID accessor |
created_at |
updated | ISO8601 timestamp of creation |
active |
updated | whether the secret ID is still valid |
deployed |
updated | whether the secret ID is actually used |
Configuration via environment variables
Name | Secret | Required | Description |
---|---|---|---|
GITLAB_TOKENS |
no | yes | URL/environment variable pairs of GitLab instances and private tokens |
GITLAB_TOKEN |
yes | yes | GitLab private tokens as configured in GITLAB_TOKENS above |
CKI_LOGGING_LEVEL |
no | no | logging level for CKI modules, defaults to WARN; to get meaningful output on the command line, set to INFO |