Read and set environmental variables for processes from Consul.
Go Makefile Shell
Latest commit 4070d9a Jan 13, 2017 @sethvargo sethvargo Bump version for dev

README.md

envconsul

Latest Version Build Status

envconsul provides a convenient way to populate values from Consul into a child process environment using the envconsul daemon.

The daemon envconsul allows applications to be configured with environment variables, without having knowledge about the existence of Consul. This makes it especially easy to configure applications throughout all your environments: development, testing, production, etc.

envconsul is inspired by envdir in its simplicity, name, and function.

The documentation in this README corresponds to the master branch of envconsul. It may contain unreleased features or different APIs than the most recently released version. Please see the Git tag that corresponds to your version of envconsul for the proper documentation.

Installation

You can download a released envconsul artifact from the envconsul release page. If you wish to compile from source, you will need to have buildtools and Go installed:

$ git clone https://github.com/hashicorp/envconsul.git
$ cd envconsul
$ make

This process will create bin/envconsul which may be invoked as a binary.

Usage

Options

Option Description
auth The basic authentication username (and optional password), separated by a colon. There is no default value.
consul* The location of the Consul instance to query (may be an IP address or FQDN) with port.
max-stale The maximum staleness of a query. If specified, Consul will distribute work among all servers instead of just the leader. The default value is 0 (none).
ssl Use HTTPS while talking to Consul. Requires the Consul server to be configured to serve secure connections. The default value is false.
ssl-ca-cert Path to a CA certificate file, containing one or more CA certificates to use to validate the certificate sent by the consul server to us. This is a handy alternative to setting --ssl-verify=false if you are using your own CA.
ssl-cert Path to an SSL client certificate to use to authenticate to the consul server. Useful if the consul server "verify_incoming" option is set.
ssl-verify Verify certificates when connecting via SSL. This requires the use of -ssl. The default value is true.
syslog Send log output to syslog (in addition to stdout and stderr). The default value is false.
syslog-facility The facility to use when sending to syslog. This requires the use of -syslog. The default value is LOCAL0.
token The Consul API token. There is no default value.
kill-signal Kill signal to send to child process. Defaults to SIGTERM but can be one of SIGHUP,SIGTERM,SIGINT,SIGQUIT,SIGUSR1,SIGUSR2
wait The minimum(:maximum) to wait before rendering a command to fire, separated by a colon (:). If the optional maximum value is omitted, it is assumed to be 4x the required minimum value. There is no default value.
retry The amount of time to wait if Consul returns an error when communicating with the API. The default value is 5 seconds.
prefix A prefix to watch in Consul. This may be specified multiple times.
secret A secret to watch in Vault. This may be specified multiple times.
sanitize Replace invalid characters in keys to underscores.
splay The maximum time to wait before restarting the program, from which a random value is chosen.
upcase Convert all environment variable keys to uppercase.
config The path to a configuration file or directory of configuration files on disk, relative to the current working directory. Values specified on the CLI take precedence over values specified in the configuration file. There is no default value.
log-level The log level for output. This applies to the stdout/stderr logging as well as syslog logging (if enabled). Valid values are "debug", "info", "warn", and "err". The default value is "warn".
pristine Only use variables retrieved from consul, do not inherit existing environment variables.
once Run envconsul once and exit (as opposed to the default behavior of daemon). (CLI-only)
version Output version information and quit. (CLI-only)

* = Required parameter

Multiple prefixes are merged in the order they are specified, with the right-most prefix taking precedence over its left siblings/ Vault secrets always take precedence over consul prefixes.

For example, consider:

$ envconsul -prefix global/config -prefix redis/config

In this example, the values of redis take precedence over the values in global. If they had the following structure:

# Global
A=1
B=1
C=1

# Redis
A=2

The resulting environment would be:

A=2
B=1
C=1

Custom Kill Signal

Envconsul by default will send the SIGTERM signal to the child process. If you want to override this to pass in a custom signal use the kill-signal config option. This option takes one of SIGHUP,SIGTERM,SIGINT,SIGQUIT,SIGUSR1,SIGUSR2 or for Windows SIGINT,SIGTERM,SIGQUIT.

Command Line

The CLI interface supports all of the options detailed above.

Query the nyc1 demo Consul instance, rending all the keys in config/redis, and printing the environment.

$ envconsul \
    -consul demo.consul.io \
    -prefix redis/config@nyc3 \
    env

Query a local Consul instance, converting special characters in keys to undercores and uppercasing the keys:

$ envconsul \
    -consul 127.0.0.1:8500 \
    -sanitize \
    -upcase \
    -prefix redis/config \
    env

Configuration File

The envconsul configuration file is written in HashiCorp Configuration Language (HCL). By proxy, this means the envconsul configuration file is JSON-compatible. For more information, please see the HCL specification.

The Configuration file syntax interface supports all of the options detailed above, but the dashes are replaced with underscores.

consul    = "127.0.0.1:8500"
token     = "abcd1234"
max_stale = "10m"
timeout   = "5s"
retry     = "10s"
sanitize  = true
splay     = "5s"

kill_signal = "SIGHUP"

vault {
  address = "https://vault.service.consul:8200"
  token   = "abcd1234" // May also be specified via the envvar VAULT_TOKEN
  renew   = true

  ssl {
    enabled = true
    verify  = true
    cert    = "/path/to/client/cert.pem"
    ca_cert = "/path/to/ca/cert.pem"
  }
}

prefix {
  path = "config/global"
}

prefix {
  path   = "config/redis"
  format = "prod_{{ key }}"
}

secret {
  path = "secret/creds"
}

auth {
  enabled = true
  username = "test"
  password = "test"
}

ssl {
  enabled = true
  verify = false
}

syslog {
  enabled = true
  facility = "LOCAL5"
}

Please note: Vault secrets always take precedence over consul prefixes. This is to mitigate a security vulnerability.

Examples

Redis

Redis is a command key-value storage engine. If Redis is configured to read the given environment variables, you can use envconsul to start and manage the process:

# Ensure "daemonize no" is set in the redis configuration first.
$ envconsul \
  -consul demo.consul.io \
  -prefix redis/config \
  redis-server [opts...]

Env

This example is a great way to see envconsul in action. In practice, it is unlikely to be a useful use of envconsul though:

$ envconsul \
  -consul=demo.consul.io \
  -prefix redis/config \
  -once \
  env
ADDRESS=1.2.3.4
PORT=55

We can also ask envconsul to poll for configuration changes and automatically restart the process:

$ envconsul \
  -consul=demo.consul.io \
  -prefix redis/config \
  python -c 'import os, time; print os.environ; time.sleep(1000);'
{ 'ADDRESS': '1.2.3.4', 'PORT': '55' }
-----
{ 'ADDRESS': '1.2.3.4' }
-----
{ 'ADDRESS': '1.2.3.4', 'MAXCONNS': '50' }
-----

Vault Secrets

With the Vault integration, it is possible to pull secrets from Vault directly into the environment using envconsul. The only restriction is that the data must be "flat" and all keys and values must be strings or string-like values. envconsul will return an error if you try to read from a value that returns a map, for example.

First, you must add the vault address and token information to the configuration file. It is not possible to specify these values via the command line:

vault {
  address = "https://vault.service.consul:8200"
  token   = "abcd1234" // May also be specified via the envvar VAULT_TOKEN
  renew   = true

  ssl {
    enabled = true
    verify  = true
    cert    = "/path/to/client/cert.pem"
    ca_cert = "/path/to/ca/cert.pem"
  }
}

Assuming a secret exists at secret/passwords that was created like so:

$ vault write secret/passwords username=foo password=bar

envconsul can pull those values into the environment:

$ envconsul \
    -config="./config.hcl" \
    -secret="secret/passwords" \
    env

secret_passwords_username=foo
secret_passwords_password=bar

Notice that the environment variables are prefixed with the path. The slashes in the path are converted to underscores, followed by the key:

secret/passwords     => secret_passwords
mysql/creds/readonly => mysql_creds_readonly

This behavior may be disabled by setting no_prefix

secret {
  no_prefix = true
  path   = "secret/passwords"
}

username=foo
password=bar

You can also apply key transformations to the data:

$ envconsul \
    -config="./config.hcl" \
    -secret="mysql/creds/readonly" \
    -upcase \
    env

MYSQL_CREDS_READONLY_USERNAME=root-aefa635a-18
MYSQL_CREDS_READONLY_PASSWORD=132ae3ef-5a64-7499-351e-bfe59f3a2a21

It is highly encouraged that you specify the format for vault keys to include a common prefix, like:

secret {
  path   = "secret/passwords"
  format = "secret_{{ key }}"
}

The format string is passed to the go formatter and "{{ key }}" dictates where the key will go. This will help filter out the environment when execing to a child-process, for example.

Debugging

envconsul can print verbose debugging output. To set the log level for envconsul, use the -log-level flag:

$ envconsul -log-level info ...
<timestamp> [INFO] (cli) received redis from Watcher
<timestamp> [INFO] (cli) invoking Runner
# ...

You can also specify the level as debug:

$ envconsul -log-level debug ...
<timestamp> [DEBUG] (cli) creating Runner
<timestamp> [DEBUG] (cli) creating Consul API client
<timestamp> [DEBUG] (cli) creating Watcher
<timestamp> [INFO] (watcher) adding "storeKeyPrefix(redis/config)"
<timestamp> [DEBUG] (watcher) "storeKeyPrefix(redis/config)" starting
<timestamp> [DEBUG] (cli) looping for data
<timestamp> [DEBUG] (view) "storeKeyPrefix(redis/config)" starting fetch
<timestamp> [DEBUG] ("storeKeyPrefix(redis/config)") querying Consul with ...
<timestamp> [DEBUG] ("storeKeyPrefix(redis/config)") Consul returned 0 key pairs
<timestamp> [INFO] (view) "storeKeyPrefix(redis/config)" received data from consul
<timestamp> [INFO] (cli) received "storeKeyPrefix(redis/config)" from Watcher
<timestamp> [DEBUG] (cli) detected quiescence, starting timers
<timestamp> [DEBUG] (cli) looping for data
<timestamp> [DEBUG] (cli) quiescence minTimer fired, invoking Runner
<timestamp> [DEBUG] (view) "storeKeyPrefix(redis/config)" starting fetch
<timestamp> [DEBUG] ("storeKeyPrefix(redis/config)") querying Consul with ...
# ...

Quiescence

If you have a large number of services that are in flux, you may want to specify a quiescence timer. This will prevent commands from running until a stable state is reached (or a maximum timeout you specify). You can specify the quiescence interval using the -wait flag on the command line:

envconsul -wait "10s:50s"

This tells envconsul to wait for a period of 10 seconds while we do not have data before running/restarting the command, but to wait no more than 50 seconds.

Contributing

To hack on envconsul, you will need a modern Go environment. To compile the envconsul binary and run the test suite, simply execute:

$ make

This will compile the envconsul binary into bin/envconsul and run the test suite.

If you just want to run the tests:

$ make

Or to run a specific test in the suite:

go test ./... -run SomeTestFunction_name

Submit Pull Requests and Issues to the envconsul project on GitHub.