├── .gitignore ├── README.md ├── certs └── config │ ├── ca-config.json │ ├── ca-csr.json │ ├── consul-csr.json │ └── vault-csr.json ├── consul ├── config.json ├── service.yaml └── statefulset.yaml ├── create.sh └── vault ├── config.json ├── deployment.yaml └── service.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | ca/ca-config.json 2 | ca/ca-csr.json 3 | *.pem 4 | *.csr 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Running Vault and Consul on Kubernetes 2 | 3 | ## Want to learn how to build this? 4 | 5 | Check out the [post](https://testdriven.io/running-vault-and-consul-on-kubernetes). 6 | 7 | ## Want to use this project? 8 | 9 | ### Prerequisites 10 | 11 | Install: 12 | 13 | 1. [Go](https://golang.org/doc/install) 14 | 1. CloudFlare's [SSL ToolKit](https://github.com/cloudflare/cfssl) (`cfssl` and `cfssljson`) 15 | 1. [Consul](https://www.consul.io/docs/install/index.html) 16 | 1. [Vault](https://www.vaultproject.io/docs/install/) 17 | 1. [Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) 18 | 19 | ### Minikube 20 | 21 | Start the cluster: 22 | 23 | ```sh 24 | $ minikube config set vm-driver hyperkit 25 | $ minikube start 26 | $ minikube dashboard 27 | ``` 28 | 29 | ### TLS Certificates 30 | 31 | Create a Certificate Authority: 32 | 33 | ```sh 34 | $ cfssl gencert -initca certs/config/ca-csr.json | cfssljson -bare certs/ca 35 | ``` 36 | 37 | Create the private keys and TLS certificates: 38 | 39 | ```sh 40 | $ cfssl gencert \ 41 | -ca=certs/ca.pem \ 42 | -ca-key=certs/ca-key.pem \ 43 | -config=certs/config/ca-config.json \ 44 | -profile=default \ 45 | certs/config/consul-csr.json | cfssljson -bare certs/consul 46 | 47 | $ cfssl gencert \ 48 | -ca=certs/ca.pem \ 49 | -ca-key=certs/ca-key.pem \ 50 | -config=certs/config/ca-config.json \ 51 | -profile=default \ 52 | certs/config/vault-csr.json | cfssljson -bare certs/vault 53 | ``` 54 | 55 | ### Vault and Consul 56 | 57 | Spin up Vault and Consul on Kubernetes: 58 | 59 | ```sh 60 | $ sh create.sh 61 | ``` 62 | 63 | ### Environment Variables 64 | 65 | In a new terminal window, navigate to the project directory and set the following environment variables: 66 | 67 | ```sh 68 | $ export VAULT_ADDR=https://127.0.0.1:8200 69 | $ export VAULT_CACERT="certs/ca.pem" 70 | ``` 71 | 72 | ### Verify 73 | 74 | ```sh 75 | $ kubectl get pods 76 | $ vault status 77 | ``` 78 | -------------------------------------------------------------------------------- /certs/config/ca-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "signing": { 3 | "default": { 4 | "expiry": "87600h" 5 | }, 6 | "profiles": { 7 | "default": { 8 | "usages": [ 9 | "signing", 10 | "key encipherment", 11 | "server auth", 12 | "client auth" 13 | ], 14 | "expiry": "8760h" 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /certs/config/ca-csr.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosts": [ 3 | "cluster.local" 4 | ], 5 | "key": { 6 | "algo": "rsa", 7 | "size": 2048 8 | }, 9 | "names": [ 10 | { 11 | "C": "US", 12 | "ST": "Colorado", 13 | "L": "Denver" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /certs/config/consul-csr.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "server.dc1.cluster.local", 3 | "hosts": [ 4 | "server.dc1.cluster.local", 5 | "127.0.0.1" 6 | ], 7 | "key": { 8 | "algo": "rsa", 9 | "size": 2048 10 | }, 11 | "names": [ 12 | { 13 | "C": "US", 14 | "ST": "Colorado", 15 | "L": "Denver" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /certs/config/vault-csr.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosts": [ 3 | "vault", 4 | "127.0.0.1" 5 | ], 6 | "key": { 7 | "algo": "rsa", 8 | "size": 2048 9 | }, 10 | "names": [ 11 | { 12 | "C": "US", 13 | "ST": "Colorado", 14 | "L": "Denver" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /consul/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ca_file": "/etc/tls/ca.pem", 3 | "cert_file": "/etc/tls/consul.pem", 4 | "key_file": "/etc/tls/consul-key.pem", 5 | "verify_incoming": true, 6 | "verify_outgoing": true, 7 | "verify_server_hostname": true, 8 | "ports": { 9 | "https": 8443 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /consul/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: consul 5 | labels: 6 | name: consul 7 | spec: 8 | clusterIP: None 9 | ports: 10 | - name: http 11 | port: 8500 12 | targetPort: 8500 13 | - name: https 14 | port: 8443 15 | targetPort: 8443 16 | - name: rpc 17 | port: 8400 18 | targetPort: 8400 19 | - name: serflan-tcp 20 | protocol: "TCP" 21 | port: 8301 22 | targetPort: 8301 23 | - name: serflan-udp 24 | protocol: "UDP" 25 | port: 8301 26 | targetPort: 8301 27 | - name: serfwan-tcp 28 | protocol: "TCP" 29 | port: 8302 30 | targetPort: 8302 31 | - name: serfwan-udp 32 | protocol: "UDP" 33 | port: 8302 34 | targetPort: 8302 35 | - name: server 36 | port: 8300 37 | targetPort: 8300 38 | - name: consuldns 39 | port: 8600 40 | targetPort: 8600 41 | selector: 42 | app: consul 43 | -------------------------------------------------------------------------------- /consul/statefulset.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: consul 5 | spec: 6 | serviceName: consul 7 | replicas: 3 8 | selector: 9 | matchLabels: 10 | app: consul 11 | template: 12 | metadata: 13 | labels: 14 | app: consul 15 | spec: 16 | securityContext: 17 | fsGroup: 1000 18 | containers: 19 | - name: consul 20 | image: "consul:1.4.0" 21 | env: 22 | - name: POD_IP 23 | valueFrom: 24 | fieldRef: 25 | fieldPath: status.podIP 26 | - name: GOSSIP_ENCRYPTION_KEY 27 | valueFrom: 28 | secretKeyRef: 29 | name: consul 30 | key: gossip-encryption-key 31 | - name: NAMESPACE 32 | valueFrom: 33 | fieldRef: 34 | fieldPath: metadata.namespace 35 | args: 36 | - "agent" 37 | - "-advertise=$(POD_IP)" 38 | - "-bind=0.0.0.0" 39 | - "-bootstrap-expect=3" 40 | - "-retry-join=consul-0.consul.$(NAMESPACE).svc.cluster.local" 41 | - "-retry-join=consul-1.consul.$(NAMESPACE).svc.cluster.local" 42 | - "-retry-join=consul-2.consul.$(NAMESPACE).svc.cluster.local" 43 | - "-client=0.0.0.0" 44 | - "-config-file=/consul/myconfig/config.json" 45 | - "-datacenter=dc1" 46 | - "-data-dir=/consul/data" 47 | - "-domain=cluster.local" 48 | - "-encrypt=$(GOSSIP_ENCRYPTION_KEY)" 49 | - "-server" 50 | - "-ui" 51 | - "-disable-host-node-id" 52 | volumeMounts: 53 | - name: config 54 | mountPath: /consul/myconfig 55 | - name: tls 56 | mountPath: /etc/tls 57 | lifecycle: 58 | preStop: 59 | exec: 60 | command: 61 | - /bin/sh 62 | - -c 63 | - consul leave 64 | ports: 65 | - containerPort: 8500 66 | name: ui-port 67 | - containerPort: 8400 68 | name: alt-port 69 | - containerPort: 53 70 | name: udp-port 71 | - containerPort: 8443 72 | name: https-port 73 | - containerPort: 8080 74 | name: http-port 75 | - containerPort: 8301 76 | name: serflan 77 | - containerPort: 8302 78 | name: serfwan 79 | - containerPort: 8600 80 | name: consuldns 81 | - containerPort: 8300 82 | name: server 83 | volumes: 84 | - name: config 85 | configMap: 86 | name: consul 87 | - name: tls 88 | secret: 89 | secretName: consul 90 | -------------------------------------------------------------------------------- /create.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | echo "Generating the Gossip encryption key..." 5 | 6 | export GOSSIP_ENCRYPTION_KEY=$(consul keygen) 7 | 8 | 9 | echo "Creating the Consul Secret to store the Gossip key and the TLS certificates..." 10 | 11 | kubectl create secret generic consul \ 12 | --from-literal="gossip-encryption-key=${GOSSIP_ENCRYPTION_KEY}" \ 13 | --from-file=certs/ca.pem \ 14 | --from-file=certs/consul.pem \ 15 | --from-file=certs/consul-key.pem 16 | 17 | 18 | echo "Storing the Consul config in a ConfigMap..." 19 | 20 | kubectl create configmap consul --from-file=consul/config.json 21 | 22 | 23 | echo "Creating the Consul Service..." 24 | 25 | kubectl create -f consul/service.yaml 26 | 27 | 28 | echo "Creating the Consul StatefulSet..." 29 | 30 | kubectl create -f consul/statefulset.yaml 31 | 32 | 33 | echo "Creating a Secret to store the Vault TLS certificates..." 34 | 35 | kubectl create secret generic vault \ 36 | --from-file=certs/ca.pem \ 37 | --from-file=certs/vault.pem \ 38 | --from-file=certs/vault-key.pem 39 | 40 | 41 | echo "Storing the Vault config in a ConfigMap..." 42 | 43 | kubectl create configmap vault --from-file=vault/config.json 44 | 45 | 46 | echo "Creating the Vault Service..." 47 | 48 | kubectl create -f vault/service.yaml 49 | 50 | 51 | echo "Creating the Vault Deployment..." 52 | 53 | kubectl apply -f vault/deployment.yaml 54 | 55 | 56 | echo "All done! Forwarding port 8200..." 57 | 58 | POD=$(kubectl get pods -o=name | grep vault | sed "s/^.\{4\}//") 59 | 60 | while true; do 61 | STATUS=$(kubectl get pods ${POD} -o jsonpath="{.status.phase}") 62 | if [ "$STATUS" == "Running" ]; then 63 | break 64 | else 65 | echo "Pod status is: ${STATUS}" 66 | sleep 5 67 | fi 68 | done 69 | 70 | kubectl port-forward $POD 8200:8200 71 | -------------------------------------------------------------------------------- /vault/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "listener": { 3 | "tcp":{ 4 | "address": "127.0.0.1:8200", 5 | "tls_disable": 0, 6 | "tls_cert_file": "/etc/tls/vault.pem", 7 | "tls_key_file": "/etc/tls/vault-key.pem" 8 | } 9 | }, 10 | "storage": { 11 | "consul": { 12 | "address": "consul:8500", 13 | "path": "vault/", 14 | "disable_registration": "true", 15 | "ha_enabled": "true" 16 | } 17 | }, 18 | "ui": true 19 | } 20 | -------------------------------------------------------------------------------- /vault/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: vault 5 | labels: 6 | app: vault 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: vault 12 | template: 13 | metadata: 14 | labels: 15 | app: vault 16 | spec: 17 | containers: 18 | - name: vault 19 | command: ["vault", "server", "-config", "/vault/config/config.json"] 20 | image: "vault:0.11.5" 21 | imagePullPolicy: IfNotPresent 22 | securityContext: 23 | capabilities: 24 | add: 25 | - IPC_LOCK 26 | volumeMounts: 27 | - name: configurations 28 | mountPath: /vault/config/config.json 29 | subPath: config.json 30 | - name: vault 31 | mountPath: /etc/tls 32 | - name: consul-vault-agent 33 | image: "consul:1.4.0" 34 | env: 35 | - name: GOSSIP_ENCRYPTION_KEY 36 | valueFrom: 37 | secretKeyRef: 38 | name: consul 39 | key: gossip-encryption-key 40 | - name: NAMESPACE 41 | valueFrom: 42 | fieldRef: 43 | fieldPath: metadata.namespace 44 | args: 45 | - "agent" 46 | - "-retry-join=consul-0.consul.$(NAMESPACE).svc.cluster.local" 47 | - "-retry-join=consul-1.consul.$(NAMESPACE).svc.cluster.local" 48 | - "-retry-join=consul-2.consul.$(NAMESPACE).svc.cluster.local" 49 | - "-encrypt=$(GOSSIP_ENCRYPTION_KEY)" 50 | - "-config-file=/consul/myconfig/config.json" 51 | - "-domain=cluster.local" 52 | - "-datacenter=dc1" 53 | - "-disable-host-node-id" 54 | - "-node=vault-1" 55 | volumeMounts: 56 | - name: config 57 | mountPath: /consul/myconfig 58 | - name: tls 59 | mountPath: /etc/tls 60 | volumes: 61 | - name: configurations 62 | configMap: 63 | name: vault 64 | - name: config 65 | configMap: 66 | name: consul 67 | - name: tls 68 | secret: 69 | secretName: consul 70 | - name: vault 71 | secret: 72 | secretName: vault 73 | -------------------------------------------------------------------------------- /vault/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: vault 5 | labels: 6 | app: vault 7 | spec: 8 | type: ClusterIP 9 | ports: 10 | - port: 8200 11 | targetPort: 8200 12 | protocol: TCP 13 | name: vault 14 | selector: 15 | app: vault 16 | --------------------------------------------------------------------------------