What is Hashicorp Vault?
Vault is software the provides secure secret management to protect sensitive data and in this article we will be demonstrating how to use vault docker to create and manage a secrets engine. Secrets may be anything from text properties or data to tokens, passwords, X509 certificates, and both symmetric and asymmetric keys used for encryption, authentication, and signing. Vault is API driven and can be used with standard REST API client software or their built in CLI tools, or even the Vault UI.
The primary purpose of this article is to cover example use of vault in a docker environment. With just a bit of configuration and Docker knowledge, Hashicorp Vault can be can be up and running with docker-compose in a few minutes.
Running a local instance of vault with docker and docker-compose
The official vault docker image is available in Docker Hub. The latest version can be pulled as demonstrated below in the docker-compose.yml file with vault:latest.
To keep things together and hopefully simple, create a new directory on your system and navigate to it to install vault docker.
mkdir docker-vault; cd docker-vault
vault docker compose
In the newly created directory, create and then open a docker-compose.yml file.
vi docker-compose.yml
version: "3.8"
services:
vault-server:
image: vault:latest
ports:
- "8200:8200"
environment:
VAULT_ADDR: "http://0.0.0.0:8200"
VAULT_DEV_ROOT_TOKEN_ID: "vault-plaintext-root-token"
cap_add:
- IPC_LOCK
networks:
vault-network:
ipv4_address: 172.21.0.10
aliases:
- vault-server
vault-client:
build: .
environment:
VAULT_ADDR: "http://vault-server:8200"
networks:
vault-network:
ipv4_address: 172.21.0.20
aliases:
- vault-client
networks:
vault-network:
ipam:
config:
- subnet: 172.21.0.0/24
To breakdown this docker-compose.yml file, we have two containers being composed in addition to a local network.
- vault-server
- Uses the latest vault image
- exposes port 8200
- Sets the VAULT_ADDR environment variable as recommended at server startup and sets the VAULT_DEV_ROOT_TOKEN_ID environment variable in order to initialize the root token to be used by the vault-client container.
- Sets the cap_add IPC_LOCK to allow vault to lock memory.
- Add vault-server to the local network.
- vault-client
- Builds from a Dockerfile in the same directory. This will be detailed below.
- Sets the VAULT_ADDR environment variable to talk to the vault server.
- Add vault-client to the local network.
- Create a local network for the vault-client and vault-server to run in.
vault client Dockerfile
vault install docker
Next, create the Dockerfile mentioned above to build the vault-clien. Make sure this is in the same directory as the docker-compose.yml file.
vi Dockerfile
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y software-properties-common curl gnupg2 && \
curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add - && \
apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main" && \
apt-get update && apt-get install -y \
vault && \
setcap cap_ipc_lock= /usr/bin/vault
COPY run.sh ./
CMD ./run.sh
The Dockerfile above will create a container from the ubuntu 20.04 image and install the necessary dependencies to run as a vault client. The Dockerfile then instructs a COPY and CMD of run.sh, detailed below.
vi run.sh
#!/bin/bash
VAULT_RETRIES=5
echo "Vault is starting..."
until vault status > /dev/null 2>&1 || [ "$VAULT_RETRIES" -eq 0 ]; do
echo "Waiting for vault to start...: $((VAULT_RETRIES--))"
sleep 1
done
echo "Authenticating to vault..."
vault login token=vault-plaintext-root-token
echo "Initializing vault..."
vault secrets enable -version=2 -path=my.secrets kv
echo "Adding entries..."
vault kv put my.secrets/dev username=test_user
vault kv put my.secrets/dev password=test_password
echo "Complete..."
The run.sh is the script being ran as the vault-client.
- The client waits on the server to start by way of the vault status command. In this example, 5 seconds should be plenty. For your use case adjust as necessary.
- Before managing secrets it’s necessary to first authenticate to vault. In this example, we’re authenticating with the root token set as an environment variable to the vault-server container in the docker-compose.yml file. Note that this is used in a dev environment for simplicity but is not meant to run in a production or sensitive environment.
- Initialize a vault secrets engine.
- Add secrets to the secrets engine created in the previous step.
In the same directory as the three files created above, run the docker-compose script.
docker-compose up
Because neither the vault-client nor the vault-server depend on one another for startup, they will both start at the same time. However, the vault-client will not attempt to setup the vault secrets engine until the vault-server is ready because of the wait functionality in the run.sh script. After the vault-server is initialized, you will see output similar to:
vault-server_1 | WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory
vault-server_1 | and starts unsealed with a single unseal key. The root token is already
vault-server_1 | authenticated to the CLI, so you can immediately begin using Vault.
vault-server_1 |
vault-server_1 | You may need to set the following environment variable:
vault-server_1 |
vault-server_1 | $ export VAULT_ADDR='http://0.0.0.0:8200'
vault-server_1 |
vault-server_1 | The unseal key and root token are displayed below in case you want to
vault-server_1 | seal/unseal the Vault or re-authenticate.
vault-server_1 |
vault-server_1 | Unseal Key: a6aBk5g4VY90GQeoZR9b9DmLw3VYfDZSt+rvBSK5eA0=
vault-server_1 | Root Token: vault-plaintext-root-token
vault-server_1 |
vault-server_1 | Development mode should NOT be used in production installations!
vault-server_1 |
After the vault-server is initialized you will see the vault client begin to authenticate and create the secrets engine and subsequent secrets. You will see something similar to the following in your console output.
vault-client_1 | Authenticating to vault...
vault-client_1 | Success! You are now authenticated. The token information displayed below
vault-client_1 | is already stored in the token helper. You do NOT need to run "vault login"
vault-client_1 | again. Future Vault requests will automatically use this token.
vault-client_1 |
vault-client_1 | Key Value
vault-client_1 | --- -----
vault-client_1 | token vault-plaintext-root-token
vault-client_1 | token_accessor OwRbae1JS0h5BpRiI7x8zC2L
vault-client_1 | token_duration ∞
vault-client_1 | token_renewable false
vault-client_1 | token_policies ["root"]
vault-client_1 | identity_policies []
vault-client_1 | policies ["root"]
vault-client_1 | Initializing vault...
vault-client_1 | Success! Enabled the kv secrets engine at: my.secrets/
vault-client_1 | Adding entries...
vault-client_1 | Key Value
vault-client_1 | --- -----
vault-client_1 | created_time 2021-03-24T01:32:00.912828188Z
vault-client_1 | deletion_time n/a
vault-client_1 | destroyed false
vault-client_1 | version 1
vault-client_1 | Key Value
vault-client_1 | --- -----
vault-client_1 | created_time 2021-03-24T01:32:00.944778246Z
vault-client_1 | deletion_time n/a
vault-client_1 | destroyed false
vault-client_1 | version 2
vault-client_1 | Complete...
docker-vault_vault-client_1 exited with code 0
Notice the exit code 0 of the vault-client. This indicates success, and will shutdown the vault-client. Any exit code other than 0 will indicate an error.
Conclusion
Let us know in the comments if you would like to see more examples of what you can do with vault in a docker environment. While we have attempted to provide a Hashicorp vault tutorial and examples, let us know what examples will provide additional value.
For access to the vault docker project, visit our GitHub Repository.
Read this post for details on using the Vault PKI Secrets Engine.
If you are interested in reading more of our content check out our Blog page.
Thx for the interesting Blog, how can I access directly to the vault server container after that?
Br.
Shamir
Hello, thank you for the tutorial, very nice. I am able to start the services, but it still ask me to use a login Method. Am I missing a step ?
Thanks for your help
This example creates a root token. The run.sh demonstrates some command line examples using the root token, set in the docker-compose.yml file with VAULT_DEV_ROOT_TOKEN_ID. Are you talking about a login method using the UI?
Hello, thank you for this guide, it was usefull. I don’t understand how in this case you will read secrets, if your client is shutdowns after putting secrets. Do you do it from code?
Some small addition to your material – vault kv put command will rewrite all secrets added before. It still be available in secret versions, but I think it’s not what you want. So I suggest use path instead of put.