Reputation: 61
I am trying to find some guidance on what's the best method to add secrets to a vault container (running in dev mode) at startup. I came up with one way of doing it, albeit not very clean. So, I'm looking for some suggestions on better ways to implement this.
I took the Vault parent image, and created a shell script to call the parent image's entrypoint first, then add a loop to wait for vault to come up, initialize it, add secrets to it. If I leave the script at this point, the container simply stops and exits, so I also added a loop to keep a timer going as long as vault is up and running.
FROM vault
# Install Curl
RUN apk add --no-cache curl
# Instal jq
ADD https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 /usr/bin/jq
RUN chmod 755 /usr/bin/jq
ENV MYSQL_URL default
ENV MYSQL_USERNAME default
ENV MYSQL_PASSWORD default
ENV VAULT_DEV_ROOT_TOKEN_ID 0000
EXPOSE 8200
ADD vault-init.sh /
RUN chmod 755 vault-init.sh
ENTRYPOINT exec "./vault-init.sh"
Here's the vault-init.sh referenced above.
echo "Staring Vault..."
docker-entrypoint.sh server -dev &
echo "Sleeping 10..."
sleep 10
echo "Vault Started."
echo "Exporting address"
export VAULT_ADDR="http://localhost:8200"
echo "Authenticate into Vault"
# Authenticate to Vault
vault login $VAULT_DEV_ROOT_TOKEN_ID
echo "Adding secrets to Vault..."
vault kv put secret/fruit-basket mysql.username=$MYSQL_USERNAME mysql.password=$MYSQL_PASSWORD mysql.url=$MYSQL_URL
while [ "$(curl -XGET --insecure --silent -H "X-Vault-Token: $VAULT_DEV_ROOT_TOKEN_ID" http://localhost:8200/v1/sys/health | jq '.initialized')" == "true" ]
do
sleep 2
done
Upvotes: 4
Views: 6118
Reputation: 61
Thanks @David for your input. What I ended up doing was a little different. I think this was a much more elegant solution. I created two different vault container images, one that is the actual vault image running to do its job as vault, and the second that loads the secret (called vault-secret
). In the vault-secret
image, I rewrote the above unix script to wait for vault to come up, and then load the secrets in. Since I overrided the parent image's (vault
in this case) ENTRYPOINT in my script, vault only comes up on the image thats called vault
and the vault-secret
container stops and exits since it has no process running after loading the secrets (much like a Kubernetes Job). Here's my new Dockerfile and the unix script.
FROM vault
# Install Curl
RUN apk add --no-cache curl
# Instal jq
ADD https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 /usr/bin/jq
RUN chmod 755 /usr/bin/jq
ENV MYSQL_URL default
ENV MYSQL_USERNAME default
ENV MYSQL_PASSWORD default
ENV VAULT_DEV_ROOT_TOKEN_ID 0000
EXPOSE 8200
ADD vault-init.sh /
RUN chmod 755 vault-init.sh
ENTRYPOINT exec "./vault-init.sh"
vault-init.sh from above
echo "Waiting for Vault..."
while [ "$(curl -XGET --insecure --silent -H "X-Vault-Token: $VAULT_TOKEN" $VAULT_ADDR/v1/sys/health | jq '.initialized')" != "true" ]
do
echo 'Vault is Initializing...'
sleep 2
done
echo "Vault Started."
echo "Authenticate into Vault"
# Authenticate to Vault
vault login $VAULT_TOKEN
echo "Adding secrets to Vault..."
vault kv put secret/fruit-basket mysql.username=$MYSQL_USERNAME mysql.password=$MYSQL_PASSWORD mysql.url=$MYSQL_URL
In the above solution I use the vault
base image to run the "load secrets" job. Thats just so I can use the vault command line tool to load the secrets. If you were to replace that with curl, you could move to using alpine or even busybox reducing the container footprint.
Thanks for reading!
Upvotes: 2
Reputation: 158908
Three reasonable options come to mind. Mostly they aren't actually specific to Vault.
Load the data outside of Docker. docker run vault
as normal, without doing anything you describe in the question. When it actually comes up, use the vault
CLI or your application code, outside of Docker, to load in your seed data. (This is analogous to a Web application running an SQL migration script as part of the application startup, as distinct from the database startup.)
Store the data outside of Vault. Vault has several storage backends. A more typical setup for actually deploying Vault is to have it store data in Consul or S3 or somewhere else. If you do that, then that backend (which is actually designed to store data; Vault is designed to be stateless) can persist data while you tweak the Vault configuration. You'll still have to load your seed data once, but it can then survive restarts.
Write your own init-type program. You really want the main container process to be the service the container is running; in your example, if Vault dies, the only reason you notice is because you have a manual health-checking loop. If you really needed to run Vault this way you'd need to write your own process that took on all of the responsibilities of init(8), forwarded all signals to the Vault subprocess, and also did the things your script does (or runs a post-startup script). This probably needs to be in a language like C or Go. W. Richard Stevens's Advanced Programming in the UNIX Environment has all of the information you need to know to do this correctly, but it's a significant endeavor.
Upvotes: 1