项目作者: mmohamed

项目描述 :
HashiCorp Vault with Hashicorp Consul , deployment guide for Kubernetes
高级语言: Dockerfile
项目地址: git://github.com/mmohamed/vault-kubernetes.git
创建时间: 2021-02-16T20:39:48Z
项目社区:https://github.com/mmohamed/vault-kubernetes

开源协议:

下载


Vault & Consul Kubernetes Deployment

Following this project, you will be able to deploy, configure and use an HashiCorp Vault with Hashicorp Consul and try it in your Kubernetes Cluster with sample application.

Secure, store and tightly control access to tokens, passwords, certificates, encryption keys for protecting secrets and other sensitive data using a UI, CLI, or HTTP API.

Consul is a service mesh solution providing a full featured control plane with service discovery, configuration, and segmentation functionality. Each of these features can be used individually as needed, or they can be used together to build a full-service mesh

1- Stack :

  • Kubelet : v1.17.2 / v1.18.5
  • Kubectl : v1.17.1
  • Docker : 19.03.5 / 19.03.8
  • Consul : 1.9.3
  • Vault : 1.6.2 (Agent 0.8.0)
  • Cfssl : 1.2.0
  • Kube namespace : vault (if you use a different namespace, it must be changed in service and pod hostnames)
  • Architecture : AMD64 / ARM64

2- Consul deployment :

  1. First, generate SSL certificates for Consul (can be done in workstation) with Cfssl, after editing configuration files in consul/ca directory
  1. # Generate CA and sign request for Consul
  2. cfssl gencert -initca ca/ca-csr.json | cfssljson -bare ca
  3. # Generate SSL certificates for Consul
  4. cfssl gencert \
  5. -ca=ca.pem \
  6. -ca-key=ca-key.pem \
  7. -config=ca/ca-config.json \
  8. -profile=default \
  9. ca/consul-csr.json | cfssljson -bare consul
  10. # Perpare a GOSSIP key for Consul members communication encryptation
  11. GOSSIP_ENCRYPTION_KEY=$(consul keygen)
  1. Create secret with Gossip key and public/private keys

    1. kubectl create secret generic consul \
    2. --from-literal="gossip-encryption-key=${GOSSIP_ENCRYPTION_KEY}" \
    3. --from-file=ca.pem \
    4. --from-file=consul.pem \
    5. --from-file=consul-key.pem
  2. Deploy 3 Consul members (Statefulset)

    1. kubectl apply -f consul/service.yaml
    2. kubectl apply -f consul/rbac.yaml
    3. kubectl apply -f consul/config.yaml
    4. kubectl apply -f consul/consul.yaml
  3. Prepare SSL certificates for Consul client, it will be used by vault consul client (sidecar).

    1. cfssl gencert \
    2. -ca=ca.pem \
    3. -ca-key=ca-key.pem \
    4. -config=ca/ca-config.json \
    5. -profile=default \
    6. ca/consul-csr.json | cfssljson -bare client-vault
  4. Create secret for Consul client (like members)

    1. kubectl create secret generic client-vault \
    2. --from-literal="gossip-encryption-key=${GOSSIP_ENCRYPTION_KEY}" \
    3. --from-file=ca.pem \
    4. --from-file=client-vault.pem \
    5. --from-file=client-vault-key.pem

3- Vault deployment :

Before deploy Vault, you need to configure a Consul client to give Vault access to Consul members

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: vault-config
  5. data:
  6. ...
  7. concul.config: |
  8. {
  9. "verify_incoming": false,
  10. "verify_outgoing": true,
  11. "server": false,
  12. "ca_file": "/etc/tls/ca.pem",
  13. "cert_file": "/etc/tls/client-vault.pem",
  14. "datacenter": "vault",
  15. "key_file": "/etc/tls/client-vault-key.pem",
  16. "client_addr": "127.0.0.1",
  17. "ui": false,
  18. "raft_protocol": 3,
  19. "retry_join": [ "provider=k8s label_selector=\"app=consul,role=server\" namespace=\"vault\"" ]
  20. }

The consul client will be deployed as a Sidecar for Vault server, so the “client_addr” must be “127.0.0.1”. For certificates parameters, we will use the client-vault secret and same join expression of members configuration.

In another side, we need to configure Vault to request this client by the “listeners” parameter.

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: vault-config
  5. data:
  6. vault.config: |
  7. {
  8. "ui": true,
  9. "listener": [{
  10. "tcp": {
  11. "address": "0.0.0.0:8200",
  12. "tls_disable": true
  13. }
  14. }],
  15. ...

OK, let’s deploy

  1. kubectl apply -f vault/service.yaml
  2. kubectl apply -f vault/config.yaml
  3. kubectl apply -f vault/vault.yaml

5- UI:

At this point, we have 3 instances on Consul deployed and 1 instance of Vault connected to Consul members.

We can use a port forwarding to acces UI of Consul and Vault. In our case, we will use an “Ingress” to expose UIs to internet.

  1. kubectl apply -f ingress.yaml

If you use this option with SSL (HTTPS), you need to configure the TLS secret.

6- Vault Injector deployment

  • Install vault agent injector (single and simple instance without leader & leader election)
    1. kubectl apply -f vault-injector/serivce.yaml
    2. kubectl apply -f vault-injector/rbac.yaml
    3. kubectl apply -f vault-injector/deployment.yaml
    4. kubectl apply -f vault-injector/webhook.yaml # webhook must be created after deployment

The injector will detect Vault “Annotation” or “Configmap”, and will inject an initContainer in the init process of your application Pod to request Vault server for secret. After initialization, an agent will be injected inside the pod to give your application container the requested secret.

For agent injector, we will use our docker image, it’s similar of official image with arm64 supporting (At 02/2021 only amd64 arch are distributed by Hashicorp), see docker files.

7- Sample deployment :

We can use UI to configure and use Vault, in this project we use CLI.

  1. Start by installing Vault locally (in workspace) for CLI use only

    1. curl https://releases.hashicorp.com/vault/1.6.2/vault_1.6.2_linux_amd64.zip -o vault_1.6.2_linux_amd64.zip
    2. unzip vault_1.6.2_linux_amd64.zip
    3. chmod +x vault
    4. # With ingress, you can use the root url of Vault ui, or use the port forward
    5. export VAULT_ADDR="YOUR_VAULT_HOST"
  2. Check the server status and login (using token like UI)
    ```bash
    ./vault status

    Key Value


    Seal Type shamir
    Initialized true
    Sealed false
    Total Shares 1
    Threshold 1
    Version 1.6.2
    Storage Type consul
    Cluster Name vault-cluster-…
    Cluster ID …
    HA Enabled true
    HA Cluster ..
    HA Mode active

./vault login
<< your_token

  1. 3. Create key/value for testing
  2. ```bash
  3. ./vault secrets enable kv
  4. ./vault kv put kv/myapp/config username="admin" password="adminpassword"
  1. Connect Kube to Vault

    1. # Create the service account to access secret
    2. kubectl apply -f myapp/service-account.yaml
    3. # Enable kubernetes support
    4. ./vault auth enable kubernetes
    5. # Prepare kube api server data
    6. export SECRET_NAME="$(kubectl get serviceaccount vault-auth -o go-template='{{ (index .secrets 0).name }}')"
    7. export TR_ACCOUNT_TOKEN="$(kubectl get secret ${SECRET_NAME} -o go-template='{{ .data.token }}' | base64 --decode)"
    8. export K8S_API_SERVER="$(kubectl config view --raw -o go-template="{{ range .clusters }}{{ index .cluster \"server\" }}{{ end }}")"
    9. export K8S_CACERT="$(kubectl config view --raw -o go-template="{{ range .clusters }}{{ index .cluster \"certificate-authority-data\" }}{{ end }}" | base64 --decode)"
    10. # Send kube config to vault
    11. ./vault write auth/kubernetes/config kubernetes_host="${K8S_API_SERVER}" kubernetes_ca_cert="${K8S_CACERT}" token_reviewer_jwt="${TR_ACCOUNT_TOKEN}"
  2. Create Vault policy and role for “myapp”

Edit policy file myapp/policy.json

  1. path "kv/myapp/*" {
  2. capabilities = ["read", "list"]
  3. }

Create the application role

  1. ./vault policy write myapp-ro myapp/policy.json
  2. ./vault write auth/kubernetes/role/myapp-role bound_service_account_names=vault-auth bound_service_account_namespaces=vault policies=default,myapp-ro ttl=15m
  1. Deploy “myapp” for testing

Edit annotations for secret output, @see myapp/deployment.yaml

  1. annotations:
  2. vault.hashicorp.com/agent-inject: "true"
  3. vault.hashicorp.com/agent-inject-secret-account: "kv/myapp/config"
  4. vault.hashicorp.com/agent-inject-template-account: |
  5. {{- with secret "kv/myapp/config" -}}
  6. dsn://{{ .Data.username }}:{{ .Data.password }}@database:port/mydb?sslmode=disable
  7. {{- end }}
  8. vault.hashicorp.com/role: "myapp-role"

Deploy app and log secret

  1. kubectl apply -f myapp/deployment.yaml
  2. export POD=$(kubectl get pods --selector=app=myapp --output=jsonpath={.items..metadata.name})
  3. kubectl log ${POD} myapp

8- Tips

To activate an HTTP Basic security for Consul UI (it’s run without), you can use Nginx ingress annotations,
after authentication secret generation.

  1. htpasswd -c auth foo
  2. kubectl create secret generic consul-auth --from-file=auth

Add annotations

  1. nginx.ingress.kubernetes.io/auth-type: basic
  2. nginx.ingress.kubernetes.io/auth-secret: consul-auth
  3. nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - Consul - MedInvention'

More information