This Docker image is designed to bootstrap the Vault secrets in a Kubernetes pods. This uses the Kubernetes Auth Method to request the secrets required to generate an AppRole Auth role id and secret id.
This will then authenticate against the AppRole and provide the Vault token in a file that can be sourced by other containers that share the volume.
This is useful to externalise the Vault authentication from the containers you want to run. When used in conjunction with the Kubernetes Vault Auth Renewer, then the secrets can be injected into a container and renewed for as long as the pod is running, without the main container requiring any knowledge of Vault. This allows you to run public docker images without modification.
A vault installation with the following auth methods enabled and configured:
The Kubernetes auth method must be configured with a policy
This grants access to the AppRole role-id and secret-id which is required to generate an auth token for accessing secrets.
path "auth/approle/role/my-app-role/role-id" {
capabilities = ["read"]
}
path "auth/approle/role/my-app-role/secret-id" {
capabilities = ["update"]
}
This grants read access to the secrets our application needs.
path "consul/creds/my-acl-policy" {
capabilities = ["read"]
}
path "secret/some/secret" {
capabilities = ["read"]
}
This should have a limited lifespan as it is only needed to bootstrap the AppRole.
vault write auth/kubernetes/role/my-kube-role \
bound_service_account_names=my-app-service-account \
bound_service_account_namespaces=my-namespace \
policies=my-app-role-login-policy \
ttl=300 \
max_ttl=300 \
num_uses=3
The 'secret_id_ttl' and 'secret_id_num_uses' should be kept limited, as we only used it to get a token.
We want a periodic token - this means that as long as it is renewed it never expires. 'Period' should be set with consideration to how often you want to renew the token. If you are going to check if it needs renewing 6 hours, then a sensible period might be 24 hours. Read the AppRole documentation to customise the other options based on your requirements.
vault write auth/approle/role/my-app-role \
secret_id_ttl=5m \
secret_id_num_uses=3 \
period=24h \
bound_cidr_list="10.0.0.0/32" \
bind_secret_id="true" \
policies="my-app-role-policy"
This is designed to be run as a Kubernetes init container, which means that it is run and terminates before the main pod containers are started. This allows us to request access to Vault and the secrets before the container starts.
The init container requires the following environmental variables:
- VAULT_ADDR - the full address of Vault, e.g. https://vault.example.com
- KUBERNETES_AUTH_PATH - the name of the kubernetes auth method, e.g. kubernetes
- VAULT_LOGIN_ROLE - the name the approle/kubernetes roles created above if they are both the same, e.g. my-app-role, otherwise use KUBERNETES_ROLE and APPROLE_ROLE
- KUBERNETES_ROLE - the kubernetes role created above, defaults to VAULT_LOGIN_ROLE, e.g. my-kube-role
- APPROLE_ROLE - the approle role created above, defaults to VAULT_LOGIN_ROLE, e.g. my-app-role
The following are optional:
- KUBE_SA_TOKEN - used to inject the Kubernetes auth token, for testing without Kubernetes
- VAULT_TOKEN - used to inject a Vault token, for testing without kubernetes of app_roles
- VARIABLES_FILE - used to override the location of the outfile for testing
- SECRET_* - any environment variable that starts with 'SECRET_' will be injected into the container with the value retrieved from Vault
Any environment variables that start with 'SECRET_' are used to request secrets from Vault. The value must be the path to retrieve a secret from in Vault, with the data key optionally provided.
For example:
SECRET_CONSUL_TOKEN=consul/creds/my-acl-policy?token
Will create:
CONSUL_TOKEN=some-consul-acl-token
Where 'some-consul-acl-token' is stored in the the 'token' attribute of the following secret: consul/creds/my-acl-policy
Or
If there are multiple variables for the same vault path then only one request is made and all data keys are retrieved from the same Vault response. This is useful in the case of requesting access to secret backends, like a database, where you need more one field to access the resource.
For example:
SECRET_DATABASE_USER=database/creds/readonly?username
SECRET_DATABASE_PASSWORD=database/creds/readonly?password
Will create:
DATABASE_USER=some-database-username
DATABASE_PASSWORD=some-database-password
Where 'some-database-password' corresponds to the temporary user 'some-database-username' in the configured database.
Or:
SECRET_SOME_SECRET=secret/from/somewhere
Will create:
SOME_SECRET=some-secret-value
Where 'some-secret-value' is stored in the the 'value' attribute of the following secret: secret/from/somewhere
The output from this container is written to a file called /env/variables.
To make use of these you should mount this as a volume shared between this init container and your main
application container and then run source /env/variables
as part of your containers command.
The outputs are:
- VAULT_TOKEN - The Vault auth token to use, of your application can talk to Vault directly
- LEASE_IDS - A list of lease ids for the secrets defined in the SECRET_ variables above, this can be used to renew the secrets, see the Kubernetes Vault Auth Renew for a sidecar container that can do this for you
- secret values - One for each of the SECRET_ variables defined above containing the value of the secret that was retrieved from Vault
kubectl apply -f myfile.yml
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-service-account
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: my-app
spec:
template:
metadata:
labels:
app: my-app
tier: backend
spec:
serviceAccountName: my-app-service-account
volumes:
- name: shared-data
emptyDir: {}
initContainers:
- name: vault-init
image: quay.io/wealthwizards/kube-vault-auth-init
env:
- name: KUBERNETES_AUTH_PATH
value: "kubernetes"
- name: VAULT_ADDR
value: "https://vault.example.com"
- name: VAULT_LOGIN_ROLE
value: "my-app-role"
- name: SECRET_SOME_SECRET
value: "secret/from/somewhere"
volumeMounts:
- name: shared-data
mountPath: /env
containers:
- name: my-app
image: my-app
command: ["/bin/sh", "-c", "source /env/variables; ./run-my-app.sh"]
volumeMounts:
- name: shared-data
mountPath: /env
Tests can be run by executing:
make test
This will use docker-compose to start Vault, a mock server (for simulate kubernetes) and any other required Vault secrets backends required by the tests.
Tests can be added as a script within the test/tests directory, and executed from the main test/test.sh script.