跳转至

第二节(Extra) - Store Secrets using Hashicorp Vault with Docker

1.What is Hashicorp Vault

Vault is a tool for securely accessing secrets. A secret is anything that you want to tightly control access to, such as API keys, passwords, certificates, and more. Vault provides a unified interface to any secret, while providing tight access control and recording a detailed audit log. More details can be found at https://github.com/hashicorp/vault/

2.Configuration

The first step is to configure a Data Container to store the configuration for Vault.

$ touch vault.hcl

backend "consul" {
  address = "consul:8500"
  advertise_addr = "consul:8300"
  scheme = "http"
}
listener "tcp" {
  address = "0.0.0.0:8200"
  tls_disable = 1
}
disable_mlock = true

The config defines three important properties.

  • Firstly, it sets Vault to use Consul to store the secrets. Using Consul enables high availability mode as Consul manages to information and distribution to ensure HA.
  • Secondly, it binds Vault to listen on all IP addresses, this is for use with the HTTP API.
  • Finally, for development purposes, we disable TLS.

Create Data Container

To store the configuration we'll create a container. This will be used by Vault and Consul to read the required configuration files.

docker create -v /config --name config busybox; docker cp vault.hcl config:/config/;

3.Launch

With the configuration data container created we can launch the necessary processes to start Vault.

3-1 Launch Services

Launch a single Consul agent. In production, we'd want to have a cluster of 3 or 5 agents as a single node can lead to data loss.

docker run -d --name consul -p 8500:8500 consul:1.8.0 agent -dev -client=0.0.0.0

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                                                                NAMES
bf950a5631cc        consul:v0.6.4       "docker-entrypoint.s…"   14 seconds ago      Up 13 seconds       8300-8302/tcp, 8400/tcp, 8301-8302/udp, 8600/tcp, 8600/udp, 0.0.0.0:8500->8500/tcp   consul

Our Vault instance can now use Consul to store the data. All data stored within Consul will be encrypted.

docker run -d --name vault-dev \
  --link consul:consul \
  -p 8200:8200 \
  --volumes-from config \
   cgswong/vault:0.5.3 server -config=/config/vault.hcl
  • --detach , -d: Run container in background and print container ID
  • --link: Add link to another container
  • --publish , -p : Publish a container’s port(s) to the host
  • --volumes-from: Mount volumes from the specified container(s)

4. Initialise

With a vault instance running, we can now configure our environment and initialise the Vault.

4-1 Configure Environment

This command will create an alias which will proxy commands to vault to the Docker container. As we're in development, we need to define a non-HTTPS address for the vault.

alias vault='docker exec -it vault-dev vault "$@"'
export VAULT_ADDR=http://127.0.0.1:8200

4-2 Initialise Vault

With the alias in place, we can make calls to the CLI. The first step is to initialise the vault using the init command.

vault init -address=${VAULT_ADDR} > keys.txt

We store the results in cat keys.txt

cat keys.txt
Key 1: 04028656e5a5726aa2995540a30fe31663150d86ba36924cc2e65a69aa8463ce01
Key 2: 7dcbdb1003a37395e23181eb27bde8a4a68165251f57014ba518466da646c2c202
Key 3: 1bd673dda7ec11327f66dbac5abd0f5608d7ebdc3fd280128906c334607b148503
Key 4: 3d3911be81097c494374e0573b8e23aeab7ffd31b2991b34347ee01b8c7e762604
Key 5: 5b24b97325461eeede23ba10468ec45c052973c8921c9a6d186065424a43a06105
Initial Root Token: b550b1e3-042b-f327-0c44-41f05dcac228

Vault initialized with 5 keys and a key threshold of 3. Please
securely distribute the above keys. When the Vault is re-sealed,
restarted, or stopped, you must provide at least 3 of these keys
to unseal it again.

Vault does not store the master key. Without at least 3 keys,
your Vault will remain permanently sealed.

The file contains valuable information about how to access the vault and seal/unseal. The output includes our master key and token. We'll use this in the next step when communicating with our running instance.

5. Unseal Vault

When a Vault server is started, it starts in a sealed state. The server knows how to communicate with the backend storage, but it does not know how to decrypt any of the contents. Unsealing is the process of constructing the master key necessary to read the decryption key to decrypt the data, allowing read access to the Vault.

5-1 Unseal Vault

To unseal with Vault server you need access to three of the five keys defined when the Vault was initialised. The following command will grep the first three keys and unseal the Vault.

$ vault unseal -address=${VAULT_ADDR} $(grep 'Key 1:' keys.txt | awk '{print $NF}')
Sealed: true
Key Shares: 5
Key Threshold: 3
Unseal Progress: 1
$ vault unseal -address=${VAULT_ADDR} $(grep 'Key 2:' keys.txt | awk '{print $NF}')
Sealed: true
Key Shares: 5
Key Threshold: 3
Unseal Progress: 2
$ vault unseal -address=${VAULT_ADDR} $(grep 'Key 3:' keys.txt | awk '{print $NF}')

Sealed: false
Key Shares: 5
Key Threshold: 3
Unseal Progress: 0

In production, these keys should be stored separately and securely. Vault uses an algorithm known as Shamir's Secret Sharing to split the master key into shards. Each of the five keys is part of the shard.

5-2 Status

You can view the status of the vault using

vault status -address=${VAULT_ADDR}
$ vault status -address=${VAULT_ADDR}
Sealed: false
Key Shares: 5
Key Threshold: 3
Unseal Progress: 0

High-Availability Enabled: true
        Mode: active
        Leader: consul:8300

6. Vault Tokens

Tokens are used to communicate with the Vault. When the vault was initialised, a root token was outputted. Store this in a variable with the following command. We'll use it for future API calls. export

VAULT_TOKEN=$(grep 'Initial Root Token:' keys.txt | awk '{print substr($NF, 1, length($NF)-1)}')

You can use this token to login to vault.

vault auth -address=${VAULT_ADDR} ${VAULT_TOKEN}
$ vault auth -address=${VAULT_ADDR} ${VAULT_TOKEN}
Successfully authenticated!
token: b550b1e3-042b-f327-0c44-41f05dcac228
token_duration: 0
token_policies: [root]

The primary reason is to create new tokens using vault token-create.

7. Read/Write Data

The Vault CLI can be used to read and write data securely. Vault is a primarily a key/value store. Each pairing can have additional security configuration attached, such as policies on access, TTL, and the ability to be revoked.

7-1 Save Data

To store data, we use the write CLI command. In this case, we have a key named secret/api-key with the value 12345678

$ vault write -address=${VAULT_ADDR} \
> secret/api-key value=12345678
Success! Data written to: secret/api-key

7-2 Read Data

Reading the key will output the value, along with other information such as the lease duration.

$ vault read -address=${VAULT_ADDR} \
    secret/api-key
Key             Value
lease_duration  2592000
value           12345678

You can also use the -field flag to extract the value from the secret data

$ vault read -address=${VAULT_ADDR} \
> -field=value secret/api-key
12345678

8. HTTP API

$ curl -H "X-Vault-Token:$VAULT_TOKEN" -XGET http://127.0.0.1:8200/v1/secret/api-key
{"lease_id":"","renewable":false,"lease_duration":2592000,"data":{"value":"12345678"},"warnings":null,"auth":null}

Using the command like tool jq we can parse the data and extract the value for our key.

curl -s -H  "X-Vault-Token:$VAULT_TOKEN" \
     -XGET http://127.0.0.1:8200/v1/secret/api-key \
    | jq -r .data.value
12345678

This could be used to launch additional processes, such as MySQL, or access external services like AWS. Using this as part of a Docker Entrypoint will be discussed in future scenarios.