Integrate Concourse with Vault for Credential Management

Pirix Technologies
4 min readApr 8, 2018

When it comes to credential management, two names come to mind: Hashicorp Vault and Cloud Foundry CredHub. There are plenty of tutorials and guides for both of them since Concourse is from Cloud Foundry (Pivotal Cloud Foundry to be exact) as well, integration of CredHub is well documented.

I decided to give Vault a try and find that even though there are plenty of guides around, most of them omit the pre-assumption of how Concourse is deployed. With different methods of deploying Concourse, there’s nuance to the Vault integration.

This article will explore deploying Concourse and Vault v0.9.6 (plus Consul v1.0.6 as service discovery and KV store backend) using docker containers separately (alternatively you can put both into the same docker compose file and life will become so much easier).

Deploy Vault and Consul Docker container

You can either:

Manually set up vault and consul docker container

follow the instructions on Consul official website

docker pull consul
docker exec -it consul

# configure Consul

follow the instructions on Vault official website

docker pull vault
docker exec -it vault

# Configure Vault

Or

use “ cault “ (thanks to @tolitius!)

git clone git@github.com:tolitius/cault.git
# write "policy.hcl" with following content
# and put it into config/vault/policies folder
path "concourse/*" {
policy = "read"
capabilities = ["read", "list"]
}

# spin up containers
docker-compose up -d

# connect to vault docker container
docker exec -it cault_vault_1 sh

# check vault status
vault status

# initialize vault
vault operator init

# record unsealing key and root token

# unseal vault with 3 unsealing key
vault unseal (3 times)

# authorize vault
vault auth

# back out of vault container
exit

# use vault command without logging into vault container
alias vault='docker exec -it cault_vault_1 vault "$@"'

# write concourse policy to Vault
vault policy write policy-concourse /policies/policy.hcl

# create a renewable token for Concourse atc to use
vault token create --policy=policy-concourse -period="24h" -format=json -tls-skip-verify -renewable

# The token will look like this
{
"request_id": "01f16efe-1ff7-7898-4101-2e257e2d6223",
"lease_id": "",
"lease_duration": 0,
"renewable": false,
"data": null,
"warnings": null,
"auth": {
"client_token": "",
"accessor": "",
"policies": [
"default",
"policy-concourse"
],
"metadata": null,
"lease_duration": 86400,
"renewable": true
}
}

# Write down the client_token, we'll need it for Concourse deployment

DEPLOY CONCOURSE DOCKER CONTAINER

git clone https://github.com/concourse/concourse-docker

# find out vault container IP address
docker exec -it cault_vault_1 sh
ip a => it should be 172.xx.xx.xx

# modify the docker-compose.yml for concourse
# for concourse web node, change command to
command: web --vault-insecure-skip-verify
# add vault environment variables to web node
CONCOURSE_VAULT_URL: http://:8200
CONCOURSE_VAULT_PATH_PREFIX: /concourse
CONCOURSE_VAULT_CLIENT_TOKEN: ${vault_client_token}
CONCOURSE_VAULT_INSECURE_SKIP_VERIFY: "true"

# deploy concourse server
docker-compose -f docker-compose.yml up -d

Container to Container Networking

Now Concourse server still can’t connect Vault server because they are not in the same network (you’ll see atc.credential_manager.renew connection refused failure in concourse web node log), we resolve this issue by using docker container bridge networking

# find out the network Concourse nodes are using
docker network ls

# user project specific network has "_default" after the project folder name by default
# add the vault container to concourse container networking
docker network connect

To be certain, log into Concourse docker container and do a curl on Vault container IP address and port to make sure the connection is not refused

docker exec -it  bash
curl http://:8200

Write Secrets to Vault

Last, write some secret to Vault and have a test pipeline ready to verify Concourse can read the secret from Vault, a caveat here is the secret needs to written in a path contains team name determined by Concourse look up rule

/concourse/TEAM_NAME/PIPELINE_NAME/foo_param
or
/concourse/TEAM_NAME/foo_param

From host server

Enable secrets

vault secrets enable -path=/concourse -description="Secrets for use by concourse pipelines" generic

Write Secrets

vault write -address=http://127.0.0.1:8200 concourse///username value=concourse

Read it back from Concourse pipeline

---
jobs:
- name: smoketest
plan:
- task: say-hello
config:
platform: linux
image_resource:
type: docker-image
source:
repository: alpine
tag: latest
run:
path: sh
args:
- -exc
- |
echo ${USERNAME}
params:
USERNAME: ((username))

Optional:

To check from Concourse you can indeed fetch credentials, we can use Vault HTTP API:

# Log into Concourse docker container
docker exec -it bash

# GET secret
curl -H "X-Vault-Token: " -X GET http://:8200/v1/concourse///username

# LIST secrets
curl -H "X-Vault-Token:" -X LIST http://:8200/v1/concourse/

I’m working on bring all these manual steps above under the same umbrella, stay tuned 🙂

--

--

Pirix Technologies

We provide cloud computing and software development services to our clients