Skip to main content

Python CLI for managing secrets (passwords, API keys, etc)

Project description

https://img.shields.io/pypi/v/python_secrets.svg https://img.shields.io/travis/davedittrich/python_secrets.svg Documentation Status

Python CLI for managing secrets (passwords, API keys, etc)

Features

  • Uses the openstack/cliff command line framework.

  • Supports a “drop-in” model for defining variables in a modular manner that is used to then construct a single file for use by Ansible or other applications. This is something like the python-update-dotdee program, but including secret setting and generation as well.

  • Like python-update-dotdee, produces a single master file for use by Ansible commands (e.g. ansible-playbook playbook.yml -e @$(python_secrets secrets path))

  • Support multiple simultaneous sets of secrets (environments) for flexibility and scalability in multi-environment deployments and to support different use cases or different combinations of secrets.

  • Define variable names and associate types (e.g., password, uuid4, random_base64).

  • Allow manual entry of values, or automatic generation of secrets according to their type.

  • Generate unique values for variables, or use a single value per type to simplify use of secrets in access control of services while supporting a “break-glass” process to quickly regenerate secrets when needed.

  • List the groups of variables (and how many in each group).

  • Show the variables and their unredacted values (or redacted them to maintain secrecy during demonstrations or in documentation).

  • Output the variables and values in multiple different formats (CSV, JSON, YAML) for use in shell scripts, etc. using cliff features.

Usage

Commands (and subcommands) generally follow the model set by the OpenStackClient for its Command Structure. The general structure of a command is:

$ python_secrets [<global-options>] <object-1> <action> [<object-2>] [<command-arguments>]

The actions are things like list, show, generate, set, delete, etc.

Getting help

To get help information on command arguments and options, use the help command or --help option flag:

$ python_secrets --help
usage: python_secrets [--version] [-v | -q] [--log-file LOG_FILE] [-h]
                      [--debug] [-d <secrets-basedir>] [-e <environment>]
                      [-s <secrets-file>] [--init]

Python secrets management app

optional arguments:
  --version             show program's version number and exit
  -v, --verbose         Increase verbosity of output. Can be repeated.
  -q, --quiet           Suppress output except warnings and errors.
  --log-file LOG_FILE   Specify a file to log output. Disabled by default.
  -h, --help            Show help message and exit.
  --debug               Show tracebacks on errors.
  -d <secrets-basedir>, --secrets-basedir <secrets-basedir>
                        Root directory for holding secrets (Env:
                        D2_SECRETS_BASEDIR; default: /Users/dittrich/.secrets)
  -e <environment>, --environment <environment>
                        Deployment environment selector (Env: D2_ENVIRONMENT;
                        default: None)
  -s <secrets-file>, --secrets-file <secrets-file>
                        Secrets file (default: secrets.yml)
  --init                Initialize directory for holding secrets.

Commands:
  complete       print bash completion command (cliff)
  environments create  Create environment(s)
  environments list  List the current environments
  groups list    Show a list of secrets groups.
  groups show    Show a list of secrets in a group.
  help           print detailed help for another command (cliff)
  secrets describe  Describe supported secret types
  secrets generate  Generate values for secrets
  secrets path   Return path to secrets file
  secrets send   Send secrets using GPG encrypted email.
  secrets set    Set values manually for secrets
  secrets show   List the contents of the secrets file or definitions

Directories and files

There are three file system concepts that are important to understand regarding secrets storage:

  1. The root directory for secrets storage;

  2. The environment for organizing a set of secrets and secret group descriptions;

  3. The secrets file and group descriptions.

Root directory

By default, python_secrets expects a root directory in the current user’s home directory. Unless you over-ride the name of this directory, it defaults to .secrets on Linux and secrets on Windows. The first time you use python_secrets, there will likely be no directory:

$ tree ~/.secrets
/Users/dittrich/.secrets [error opening dir]

0 directories, 0 files

The root directory will be created the first time you create an environment, which is covered next.

Environments

Environments are sub-directories within the root secrets directory. You create one environment per set of unique secrets that you need to manage. This could be one for open source Program A, one for Program B, etc., or it could be one for Development, one for Testing, one for Production, etc. (or any combination).

Use the command environments create to create an environment. Since this program is designed to support multiple environments, a name for the new environment is required. If one is not specified, the base name of the current working directory is used:

$ pwd
/Users/dittrich/git/python_secrets
$ python_secrets environments create
environment directory /Users/dittrich/.secrets/python_secrets created
$ tree ~/.secrets
/Users/dittrich/.secrets
└── python_secrets
    └── secrets.d

2 directories, 0 files

If you want or need to, you can control the name of the environment being used by (a) giving an argument on the command line, (b) using the -e or --environment command line flag, or (c) by setting the environment variable D2_ENVIRONMENT.

$ python_secrets environments create development
environment directory /Users/dittrich/.secrets/development created

$ python_secrets --environment testing environments create
environment directory /Users/dittrich/.secrets/testing created

$ D2_ENVIRONMENT=production python_secrets environments create
environment directory /Users/dittrich/.secrets/production created

$ tree ~/.secrets
/Users/dittrich/.secrets
├── development
   └── secrets.d
├── production
   └── secrets.d
├── python_secrets
   └── secrets.d
└── testing
    └── secrets.d

8 directories, 0 files

If you want to create more than one environment at once, you will have to specify all of the names on the command line as arguments:

$ python_secrets environments create development testing production
environment directory /Users/dittrich/.secrets/development created
environment directory /Users/dittrich/.secrets/testing created
environment directory /Users/dittrich/.secrets/production created

Secrets and group descriptions

The environment directories just created are all empty. Secrets are stored in a YML file (.yml) within the environment’s directory, and group descriptions are stored in a drop-in directory with the same base name, but with an extention of .d instead of .yml (following the Linux drop-in configuration style directories used by programs like rsyslog, dnsmasq, etc.)

The default secrets file name is secrets.yml, which means the default descriptions directory would be named secrets.d.

You can define environment variables to point to the root directory in which a set of different environments can be configured at one time, to define the current environment, and to change the name of the secrets file to something else.

$ env | grep ^D2_
D2_SECRETS_DIR=/Users/dittrich/.secrets
D2_ENVIRONMENT=do

Each environment is in turn rooted in a directory with the environment’s symbolic name (e.g., do for DigitalOcean in this example, and mantl for Cisco’s Mantl project.)

$ tree -L 1 ~/.secrets
/Users/dittrich/.secrets
├── do
└── mantl

3 directories, 0 files

Each set of secrets for a given service or purpose is described in its own file.

.
├── secrets.d
   ├── ca.yml
   ├── consul.yml
   ├── jenkins.yml
   ├── rabbitmq.yml
   ├── trident.yml
   ├── vncserver.yml
   └── zookeper.yml
└── secrets.yml

A description file looks like this:

---

- Variable: jenkins_admin_password
  Type: password

# vim: ft=ansible :

The python_secrets program uses the openstack/cliff command line interface framework, which supports multiple output formats. The default format the table format, which makes for nice clean output. (Other formats will be described later.)

The groups can be listed using the groups list command:

$ python_secrets groups list
+-----------+-------+
| Group     | Items |
+-----------+-------+
| ca        |     1 |
| consul    |     1 |
| jenkins   |     1 |
| rabbitmq  |     2 |
| trident   |     2 |
| vncserver |     1 |
| zookeper  |     1 |
+-----------+-------+

The variables in one or more groups can be shown with the groups show command:

$ psec groups show trident rabbitmq
+----------+----------------------------+
| Group    | Variable                   |
+----------+----------------------------+
| trident  | trident_sysadmin_pass      |
| trident  | trident_db_pass            |
| rabbitmq | rabbitmq_default_user_pass |
| rabbitmq | rabbitmq_admin_user_pass   |
+----------+----------------------------+

Showing Secrets

To examine the secrets, use the secrets show command:

$ python_secrets secrets show
+----------------------------+------------+----------+
| Variable                   | Type       | Value    |
+----------------------------+------------+----------+
| ca_rootca_password         | password   | REDACTED |
| consul_key                 | consul_key | REDACTED |
| jenkins_admin_password     | password   | REDACTED |
| rabbitmq_admin_user_pass   | password   | REDACTED |
| rabbitmq_default_user_pass | password   | REDACTED |
| trident_db_pass            | password   | REDACTED |
| trident_sysadmin_pass      | password   | REDACTED |
| vncserver_password         | password   | REDACTED |
| zookeeper_uuid4            | uuid4      | REDACTED |
+----------------------------+------------+----------+

By default, the values of secrets are redacted when output. To show the values in clear text in the terminal output, add the --no-redact flag:

$ python_secrets secrets show --no-redact
+----------------------------+------------+--------------------------------------+
| Variable                   | Type       | Value                                |
+----------------------------+------------+--------------------------------------+
| ca_rootca_password         | password   | verse envelope alkaline language     |
| consul_key                 | consul_key | GVLKCRqXqm0rxo0b4/ligQ==             |
| jenkins_admin_password     | password   | verse envelope alkaline language     |
| rabbitmq_admin_user_pass   | password   | verse envelope alkaline language     |
| rabbitmq_default_user_pass | password   | verse envelope alkaline language     |
| trident_db_pass            | password   | verse envelope alkaline language     |
| trident_sysadmin_pass      | password   | verse envelope alkaline language     |
| vncserver_password         | password   | verse envelope alkaline language     |
| zookeeper_uuid4            | uuid4      | c8314c91-bf7c-4dc3-8645-da37279d31aa |
+----------------------------+------------+--------------------------------------+

If you don’t care about redaction and want to turn it off and save the dozen keystrokes it takes to type `` –no-redact``, you can export the environment variable D2_NO_REDACT set to (case-insensitive) “true”, “1”, or “yes”. Anything else leaves the default the same. We’ll do this now for later examples.

$ export D2_NO_REDACT=true

The default is also to show all secrets. If you only want to process a subset of secrets, you have two ways to do this.

  1. Specify the variables you want to show on the command line as arguments:

    $ python_secrets secrets show rabbitmq_default_user_pass rabbitmq_admin_user_pass
    +----------------------------+----------+--------------------------------------+
    | Variable                   | Type     | Value                                |
    +----------------------------+----------+--------------------------------------+
    | rabbitmq_default_user_pass | password | handheld angrily letdown frisk       |
    | rabbitmq_admin_user_pass   | password | handheld angrily letdown frisk       |
    +----------------------------+----------+--------------------------------------+
  2. Use the --group flag and specify the group(s) you want to show as command line arguments:

    $ python_secrets secrets show --group jenkins trident
    +----------------------------+----------+--------------------------------------+
    | Variable                   | Type     | Value                                |
    +----------------------------+----------+--------------------------------------+
    | jenkins_admin_password     | password | handheld angrily letdown frisk       |
    | trident_db_pass            | password | handheld angrily letdown frisk       |
    | trident_sysadmin_pass      | password | handheld angrily letdown frisk       |
    +----------------------------+----------+--------------------------------------+
  3. Use secrets describe to see the supported secret types that are available for you to use:

    $ python_secrets secrets describe
    +------------------+----------------------------------+
    | Type             | Description                      |
    +------------------+----------------------------------+
    | password         | Simple (xkcd) password string    |
    | string           | Simple string                    |
    | crypt_6          | crypt() SHA512 ("$6$")           |
    | token_hex        | Hexadecimal token                |
    | token_urlsafe    | URL-safe token                   |
    | consul_key       | 16-byte BASE64 token             |
    | sha1_digest      | DIGEST-SHA1 (user:pass) digest   |
    | sha256_digest    | DIGEST-SHA256 (user:pass) digest |
    | zookeeper_digest | DIGEST-SHA1 (user:pass) digest   |
    | uuid4            | UUID4 token                      |
    | random_base64    | Random BASE64 token              |
    +------------------+----------------------------------+

The type string is for secrets that are managed by another entity that you must obtain and use to access some remote service (e.g., the pre-shared key for someone’s WiFi network, or an API key for accessing a cloud service provider’s platform). All other types are structured secret types that you generate for configuring services.

Generating and Setting variables

Secrets are generated using the secrets generate command and are set manually using the secrets set command.

$ psec help secrets generate
usage: psec secrets generate [-h] [-U] [args [args ...]]

Generate values for secrets

positional arguments:
  args

optional arguments:
  -h, --help    show this help message and exit
  -U, --unique  Generate unique values for each type of secret (default:
                False)

..
$ psec secrets set --help
usage: psec secrets set [-h] [--undefined] [args [args ...]]

Set values manually for secrets

positional arguments:
  args

optional arguments:
  -h, --help   show this help message and exit
  --undefined  Set values for undefined variables (default: False)

To regenerate all of the non-string secrets at once, using the same value for each type of secret to simplify things, use the secrets generate command:

$ python_secrets secrets generate
$ python_secrets secrets show --column Variable --column Value
+----------------------------+--------------------------------------+
| Variable                   | Value                                |
+----------------------------+--------------------------------------+
| trident_db_pass            | gargle earlobe eggplant kissable     |
| ca_rootca_password         | gargle earlobe eggplant kissable     |
| consul_key                 | zQvSe0kdf0Xarbhb80XULQ==             |
| jenkins_admin_password     | gargle earlobe eggplant kissable     |
| rabbitmq_default_user_pass | gargle earlobe eggplant kissable     |
| rabbitmq_admin_user_pass   | gargle earlobe eggplant kissable     |
| trident_sysadmin_pass      | gargle earlobe eggplant kissable     |
| vncserver_password         | gargle earlobe eggplant kissable     |
| zookeeper_uuid4            | 769a77ad-b06f-4018-857e-23f970c777c2 |
+----------------------------+--------------------------------------+

You can set one or more variables manually using secrets set and specifying the variable and value in the form variable=value:

$ python_secrets secrets set trident_db_pass="rural coffee purple sedan"
$ python_secrets secrets show --column Variable --column Value
+----------------------------+--------------------------------------+
| Variable                   | Value                                |
+----------------------------+--------------------------------------+
| trident_db_pass            | rural coffee purple sedan            |
| ca_rootca_password         | gargle earlobe eggplant kissable     |
| consul_key                 | zQvSe0kdf0Xarbhb80XULQ==             |
| jenkins_admin_password     | gargle earlobe eggplant kissable     |
| rabbitmq_default_user_pass | gargle earlobe eggplant kissable     |
| rabbitmq_admin_user_pass   | gargle earlobe eggplant kissable     |
| trident_sysadmin_pass      | gargle earlobe eggplant kissable     |
| vncserver_password         | gargle earlobe eggplant kissable     |
| zookeeper_uuid4            | 769a77ad-b06f-4018-857e-23f970c777c2 |
+----------------------------+--------------------------------------+

Or you can generate one or more variables in a similar manner by adding them to the command line as arguments to secrets generate:

$ python_secrets secrets generate rabbitmq_default_user_pass rabbitmq_admin_user_pass
$ python_secrets secrets show --column Variable --column Value
+----------------------------+--------------------------------------+
| Variable                   | Value                                |
+----------------------------+--------------------------------------+
| trident_db_pass            | rural coffee purple sedan            |
| ca_rootca_password         | gargle earlobe eggplant kissable     |
| consul_key                 | zQvSe0kdf0Xarbhb80XULQ==             |
| jenkins_admin_password     | gargle earlobe eggplant kissable     |
| rabbitmq_default_user_pass | embezzle xerox excess skydiver       |
| rabbitmq_admin_user_pass   | embezzle xerox excess skydiver       |
| trident_sysadmin_pass      | gargle earlobe eggplant kissable     |
| vncserver_password         | gargle earlobe eggplant kissable     |
| zookeeper_uuid4            | 769a77ad-b06f-4018-857e-23f970c777c2 |
+----------------------------+--------------------------------------+

A set of secrets for an open source project can be bootstrapped using the following steps:

  1. Create a template secrets environment directory that contains just the secrets definitions. This example uses the template found in the davedittrich/goSecure repository (directory https://github.com/davedittrich/goSecure/tree/master/secrets).

  2. Use this template to clone a secrets environment, which will initially be empty:

    $ psec environments create test --clone-from ~/git/goSecure/secrets
    environment directory /Users/dittrich/.secrets/test created
    $ psec -e test secrets show --no-redact --fit-width
    +-----------------------+----------+-------+
    | Variable              | Type     | Value |
    +-----------------------+----------+-------+
    | gosecure_app_password | password | None  |
    | gosecure_client_ssid  | string   | None  |
    | gosecure_client_psk   | string   | None  |
    | gosecure_pi_password  | password | None  |
    | gosecure_pi_pubkey    | string   | None  |
    +-----------------------+----------+-------+
  3. First, generate all secrets whose type is not string:

    $ psec -e test secrets generate
    $ psec -e test secrets show --no-redact --fit-width
    +-----------------------+----------+------------------------------+
    | Variable              | Type     | Value                        |
    +-----------------------+----------+------------------------------+
    | gosecure_app_password | password | brunt outclass alike turbine |
    | gosecure_client_psk   | string   | None                         |
    | gosecure_client_ssid  | string   | None                         |
    | gosecure_pi_password  | password | brunt outclass alike turbine |
    | gosecure_pi_pubkey    | string   | None                         |
    +-----------------------+----------+------------------------------+
  4. Finally, manually set the remaining string type variables:

    $ psec -e test secrets set --undefined
    gosecure_client_psk? [None]: atjhK5AlsQMw3Zh
    gosecure_client_ssid? [None]: YourWiFiSSID
    gosecure_pi_pubkey? [None]: @~/.ssh/new_rsa.pub
    $ psec -e test secrets show --no-redact --fit-width
    +-----------------------+----------+------------------------------------------------------------------------------------------+
    | Variable              | Type     | Value                                                                                    |
    +-----------------------+----------+------------------------------------------------------------------------------------------+
    | gosecure_app_password | password | brunt outclass alike turbine                                                             |
    | gosecure_client_psk   | string   | atjhK5AlsQMw3Zh
    | gosecure_client_ssid  | string   | YourWiFiSSID                                                                             |
    | gosecure_pi_password  | password | brunt outclass alike turbine                                                             |
    | gosecure_pi_pubkey    | string   | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+qUIucrPvRkTmY0tgxr9ac/VtBUHhYHfOdDVpU99AcryLMWiU |
    |                       |          | uQ2/NVikfOfPo5mt9YTQyqRbeBzKlNgbHnsxh0AZatjhK5AlsQMw3ZhZUcLYZbt7szuQy8ineN0potlCJoVaMSOb |
    |                       |          | 9htf9gAPvzwxUnHxg35jPCzAXYAi3Erc6y338+CL0XxQvCogXOA+MwH7wZGgdT3WpupLG/7HAr/3KJEQQk1FlS2m |
    |                       |          | Rd+WuewnLbKkqBP21N+48ccq6XhEhAmlzzr9SENw5DMmrvMAYIYkoTwUeD3Qx4YebjFkCxZw+w7AafEFn0Kz6vCX |
    |                       |          | 4mp/6ZF/Ko+o04HM2sVr6wtCu2dB dittrich@localhost                                          |
    +-----------------------+----------+------------------------------------------------------------------------------------------+

You are now ready to compile your software, or build your project!

Outputting structured information for use in other scripts

Once secrets are created and stored, they will eventually need to be accessed in order to use them in program execution. This can be done by passing the .yml secrets file itself to a program, or by outputting the variables in other formats like CSV, JSON, or as environment type variables.

Passing the secrets file by path

One way to do this is to take advantage of command line options like Ansible’s --extra-vars and passing it a path to the .yml secrets file. (See Passing Variables On The Command Line). You can do that like this.

Let’s assume we want to use consul_key variable to configure Consul using Ansible. Here is the variable as stored:

$ psec secrets show consul_key
+------------+------------+--------------------------+
| Variable   | Type       | Value                    |
+------------+------------+--------------------------+
| consul_key | consul_key | GVLKCRqXqm0rxo0b4/ligQ== |
+------------+------------+--------------------------+

Using Ansible’s debug module, we can verify that this variable is not set by any previously loaded Ansible inventory:

$ ansible -i localhost, -m debug -a 'var=consul_key' localhost
localhost | SUCCESS => {
    "consul_key": "VARIABLE IS NOT DEFINED!"
}

In order for Ansible to set the consul_key variable outside of any pre-defined inventory files, we need to pass a file path to the --extra-vars option. The path can be obtained using the psec secrets path command:

$ psec secrets path
/Users/dittrich/.secrets/python_secrets/secrets.yml

It is possible to run this command in an in-line command expansion operation in Bash. Ansible expects the file path passed to -extra-vars to start with an @ character, so the command line to use would look like this:

$ ansible -i localhost, -e @$(python_secrets secrets path) -m debug -a 'var=consul_key' localhost
localhost | SUCCESS => {
    "consul_key": "GVLKCRqXqm0rxo0b4/ligQ=="
}

Ansible now has the value and can use it in templating configuration files, or so forth.

Outputting Variables in Other Formats

The openstack/cliff framework also supports multiple output formats that help with accessing and using the secrets in applications or service configuration using Ansible. For example, CSV output (with header) can be produced like this:

$ python_secrets secrets show -f csv --column Variable --column Value
"Variable","Value"
"trident_db_pass","gargle earlobe eggplant kissable"
"ca_rootca_password","gargle earlobe eggplant kissable"
"consul_key","zQvSe0kdf0Xarbhb80XULQ=="
"jenkins_admin_password","gargle earlobe eggplant kissable"
"rabbitmq_default_user_pass","gargle earlobe eggplant kissable"
"rabbitmq_admin_user_pass","gargle earlobe eggplant kissable"
"trident_sysadmin_pass","gargle earlobe eggplant kissable"
"vncserver_password","gargle earlobe eggplant kissable"
"zookeeper_uuid4","769a77ad-b06f-4018-857e-23f970c777c2"

Or you can produce JSON and have structured data for consumption by other programs.

$ python_secrets secrets show -f json --column Variable --column Value
[
  {
    "Variable": "trident_db_pass",
    "Value": "gargle earlobe eggplant kissable"
  },
  {
    "Variable": "ca_rootca_password",
    "Value": "gargle earlobe eggplant kissable"
  },
  {
    "Variable": "consul_key",
    "Value": "zQvSe0kdf0Xarbhb80XULQ=="
  },
  {
    "Variable": "jenkins_admin_password",
    "Value": "gargle earlobe eggplant kissable"
  },
  {
    "Variable": "rabbitmq_default_user_pass",
    "Value": "gargle earlobe eggplant kissable"
  },
  {
    "Variable": "rabbitmq_admin_user_pass",
    "Value": "gargle earlobe eggplant kissable"
  },
  {
    "Variable": "trident_sysadmin_pass",
    "Value": "gargle earlobe eggplant kissable"
  },
  {
    "Variable": "vncserver_password",
    "Value": "gargle earlobe eggplant kissable"
  },
  {
    "Variable": "zookeeper_uuid4",
    "Value": "769a77ad-b06f-4018-857e-23f970c777c2"
  }
]

The JSON can be manipulated, filtered, and restructured using a program like jq, for example:

$ python_secrets secrets show -f json --column Variable --column Value |
> jq -r '.[] | { (.Variable): .Value } '
{
  "trident_db_pass": "gargle earlobe eggplant kissable"
}
{
  "ca_rootca_password": "gargle earlobe eggplant kissable"
}
{
  "consul_key": "zQvSe0kdf0Xarbhb80XULQ=="
}
{
  "jenkins_admin_password": "gargle earlobe eggplant kissable"
}
{
  "rabbitmq_default_user_pass": "gargle earlobe eggplant kissable"
}
{
  "rabbitmq_admin_user_pass": "gargle earlobe eggplant kissable"
}
{
  "trident_sysadmin_pass": "gargle earlobe eggplant kissable"
}
{
  "vncserver_password": "gargle earlobe eggplant kissable"
}
{
  "zookeeper_uuid4": "769a77ad-b06f-4018-857e-23f970c777c2"
}
$ python_secrets secrets show -f json --column Variable --column Value |
> jq -r '.[] | [ (.Variable), .Value ] '
[
  "trident_db_pass",
  "gargle earlobe eggplant kissable"
]
[
  "ca_rootca_password",
  "gargle earlobe eggplant kissable"
]
[
  "consul_key",
  "zQvSe0kdf0Xarbhb80XULQ=="
]
[
  "jenkins_admin_password",
  "gargle earlobe eggplant kissable"
]
[
  "rabbitmq_default_user_pass",
  "gargle earlobe eggplant kissable"
]
[
  "rabbitmq_admin_user_pass",
  "gargle earlobe eggplant kissable"
]
[
  "trident_sysadmin_pass",
  "gargle earlobe eggplant kissable"
]
[
  "vncserver_password",
  "gargle earlobe eggplant kissable"
]
[
  "zookeeper_uuid4",
  "769a77ad-b06f-4018-857e-23f970c777c2"
]
$ python_secrets secrets show -f json --column Variable --column Value |
> jq -r '.[] | [ (.Variable), .Value ] |@sh'
'trident_db_pass' 'gargle earlobe eggplant kissable'
'ca_rootca_password' 'gargle earlobe eggplant kissable'
'consul_key' 'zQvSe0kdf0Xarbhb80XULQ=='
'jenkins_admin_password' 'gargle earlobe eggplant kissable'
'rabbitmq_default_user_pass' 'gargle earlobe eggplant kissable'
'rabbitmq_admin_user_pass' 'gargle earlobe eggplant kissable'
'trident_sysadmin_pass' 'gargle earlobe eggplant kissable'
'vncserver_password' 'gargle earlobe eggplant kissable'
'zookeeper_uuid4' '769a77ad-b06f-4018-857e-23f970c777c2'
$ python_secrets secrets show -f json --column Variable --column Value |
> jq -r '.[] | [ (.Variable), .Value ] |@csv'
"trident_db_pass","gargle earlobe eggplant kissable"
"ca_rootca_password","gargle earlobe eggplant kissable"
"consul_key","zQvSe0kdf0Xarbhb80XULQ=="
"jenkins_admin_password","gargle earlobe eggplant kissable"
"rabbitmq_default_user_pass","gargle earlobe eggplant kissable"
"rabbitmq_admin_user_pass","gargle earlobe eggplant kissable"
"trident_sysadmin_pass","gargle earlobe eggplant kissable"
"vncserver_password","gargle earlobe eggplant kissable"
"zookeeper_uuid4","769a77ad-b06f-4018-857e-23f970c777c2"

Future Work

  • Add secrets create to add new secrets descriptions + secrets.

  • Add secrets delete to delete secrets.

  • Add groups create and groups delete commands.

  • The Mantl project (GitHub mantl/mantl) employs a security-setup script that takes care of setting secrets (and non-secret related variables) in a monolithic manner. It has specific command line options, specific secret generation functions, and specific data structures for each of the component subsystems used by mantl/mantl. This method is not modular or extensible, and the security-setup script is not generalized such that it can be used by any other project. These limitations are primary motivators for writing python_secrets, which could eventually replace security-setup.

    At this point, the Mantl security.yml file can be read in and values can be manually set, as seen here:

$ python_secrets -d ~/git/mantl --secrets-file security.yml secrets show -f yaml
secrets descriptions directory not found
- Value: admin:password
  Variable: chronos_http_credentials
- Value: chronos
  Variable: chronos_principal
- Value: S0JMz5z8oxQGQXMyZjwE0ZCmu4zeJV4oWDUrdc25MBLx
  Variable: chronos_secret
- Value: 88821cbe-c004-4cff-9f91-2bc36cd347dc
  Variable: consul_acl_agent_token
- Value: f9acbe14-28d3-4d06-a1c9-c617da5ebb4e
  Variable: consul_acl_mantl_api_token
- Value: de54ae85-8226-4146-959f-8926b0b8ee55
  Variable: consul_acl_marathon_token
- Value: dfc9b244-5140-41ad-b93a-ac5c2451fb95
  Variable: consul_acl_master_token
- Value: e149b50f-cb5c-4efe-be96-26a52efdc715
  Variable: consul_acl_secure_token
- Value: 719f2328-6446-4647-adf6-310013bac636
  Variable: consul_acl_vault_token
- Value: Z0niD1jeiTkx7xaoewJm2A==
  Variable: consul_gossip_key
- Value: true
  Variable: do_chronos_auth
- Value: true
  Variable: do_chronos_iptables
- Value: true
  Variable: do_chronos_ssl
- Value: true
  Variable: do_consul_auth
- Value: true
  Variable: do_consul_ssl
- Value: true
  Variable: do_mantl_api_auth
- Value: true
  Variable: do_mantlui_auth
- Value: true
  Variable: do_mantlui_ssl
- Value: true
  Variable: do_marathon_auth
- Value: true
  Variable: do_marathon_iptables
- Value: true
  Variable: do_marathon_ssl
- Value: true
  Variable: do_mesos_auth
- Value: true
  Variable: do_mesos_follower_auth
- Value: true
  Variable: do_mesos_framework_auth
- Value: true
  Variable: do_mesos_iptables
- Value: true
  Variable: do_mesos_ssl
- Value: false
  Variable: do_private_docker_registry
- Value: mantl-api
  Variable: mantl_api_principal
- Value: Se4R9nRy8WTAgmU9diJyIPwLYsBU+V1yBxTQumiOriK+
  Variable: mantl_api_secret
- Value: admin:password
  Variable: marathon_http_credentials
- Value: marathon
  Variable: marathon_principal
- Value: +Y5bvIsWliFvcWgbXGWa8kwT6Qf3etogQJe+cK+IV2hX
  Variable: marathon_secret
- Value:
  - principal: marathon
    secret: +Y5bvIsWliFvcWgbXGWa8kwT6Qf3etogQJe+cK+IV2hX
  - principal: chronos
    secret: S0JMz5z8oxQGQXMyZjwE0ZCmu4zeJV4oWDUrdc25MBLx
  - principal: mantl-api
    secret: Se4R9nRy8WTAgmU9diJyIPwLYsBU+V1yBxTQumiOriK+
  Variable: mesos_credentials
- Value: follower
  Variable: mesos_follower_principal
- Value: Q53uAa2mNM0UNe2RUjrX6k7QvK6ojjH1gHXYLcm3Lmfr
  Variable: mesos_follower_secret
- Value: password
  Variable: nginx_admin_password
- Value: true
  Variable: security_enabled
- Value: chronos
  Variable: zk_chronos_user
- Value: JWPO11z4lU5qeilZ
  Variable: zk_chronos_user_secret
- Value: hsr+R6YQBAOXoY84a8ne8bU0opg=
  Variable: zk_chronos_user_secret_digest
- Value: marathon
  Variable: zk_marathon_user
- Value: UBh77ok2svQAqWox
  Variable: zk_marathon_user_secret
- Value: mo2mQGXcsc21zB4wYD18jn+Csks=
  Variable: zk_marathon_user_secret_digest
- Value: mesos
  Variable: zk_mesos_user
- Value: L3t9FEMsXehqeBvl
  Variable: zk_mesos_user_secret
- Value: bHYvGteRBxou4jqJ8XWAYmOmzxs=
  Variable: zk_mesos_user_secret_digest
- Value: super
  Variable: zk_super_user
- Value: 2DyL/n/GLi3Q0pa75z9OjODGZKC1RCaEiKNV1ZXo1Wpk
  Variable: zk_super_user_secret
$ python_secrets -d ~/git/mantl --secrets-file security.yml secrets show -f csv | grep nginx_admin_password
secrets descriptions directory not found
"nginx_admin_password","password"
$ python_secrets -d ~/git/mantl --secrets-file security.yml secrets set nginx_admin_password=newpassword
secrets descriptions directory not found
$ python_secrets -d ~/git/mantl --secrets-file security.yml secrets show -f csv | grep nginx_admin_password
secrets descriptions directory not found
"nginx_admin_password","newpassword"

There are a few things that can be done to use python_secrets as a replacement for the security-setup script. These include:

  • Produce secrets descriptions in a security.d directory.

  • Remove the variables that are not secrets requiring regeneration for rotation or “break-glass” procedures (e.g., like chronos_principal, which is a userID value, and do_mesos_auth, which is a boolean flag).

  • Break down more complex data structures (specifically, the mesos_credentials list of dictionaries with keys principal and secret). These could instead be discrete variables like marathon_secret (which appears to be the secret associated with the invariant “variable” marathon_principal).

Credits

Tools used in rendering this package:

Development of this program was supported in part under an Open Source Development Grant from the Comcast Innovation Fund.

History

0.3.0 (2018-04-27)

  • First release on PyPI.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

python_secrets-0.10.0.tar.gz (53.4 kB view hashes)

Uploaded Source

Built Distributions

python_secrets-0.10.0-py3.6.egg (55.7 kB view hashes)

Uploaded Source

python_secrets-0.10.0-py2.py3-none-any.whl (29.4 kB view hashes)

Uploaded Python 2 Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page