Files
vault/README.md
2021-05-21 13:30:25 -04:00

10 KiB

Vault

Prereqs

brew tap hashicorp/tap
brew install hashicorp/tap/vault

brew install jq

Install (Standalone)

kubectl apply -f k8s/certificate.yaml

helm repo add hashicorp https://helm.releases.hashicorp.com
helm search repo hashicorp/vault
helm upgrade --install vault hashicorp/vault --values helm/values.yaml

mkdir ~/.vault-keys
kubectl exec -ti vault-0 -- vault operator init -key-shares=5 -key-threshold=3 -format=json > ~/.vault-keys/cluster-keys.json
kubectl exec -ti vault-0 -- vault operator unseal

Install (Cluster)

kubectl apply -f k8s/certificate.yaml

helm repo add hashicorp https://helm.releases.hashicorp.com
helm search repo hashicorp/vault
helm upgrade --install vault hashicorp/vault --values helm/values.yaml

mkdir ~/.vault-keys
kubectl exec -ti vault-0 -- vault operator init -key-shares=5 -key-threshold=3 -format=json > ~/.vault-keys/cluster-keys.json
kubectl exec -ti vault-0 -- vault operator unseal

kubectl exec -ti vault-1 -- vault operator raft join http://vault-0.vault-internal:8200
kubectl exec -ti vault-1 -- vault operator unseal

kubectl exec -ti vault-2 -- vault operator raft join http://vault-0.vault-internal:8200
kubectl exec -ti vault-2 -- vault operator unseal

Add/Update policy

vault policy write ducoterra policies/ducoterra.hcl

Add user

vault auth enable userpass
vault write auth/userpass/users/ducoterra \
    policies=ducoterra \
    password=password

Enable KV Secrets

vault secrets enable -path=secret kv-v2

vault kv put secret/okta username='static-user' password='static-password'
vault kv get secret/okta

TOTP

vault secrets enable totp

vault write totp/keys/okta \
    url="otpauth://totp/Vault:test@test.com?secret=SECRET&issuer=Vault"

vault read totp/code/okta

Policy:

path "totp/keys/*" {
    capabilities = ["update"]
}

path "totp/code/*" {
    capabilities = ["read"] 
}

Kubernetes Secrets

kubectl exec -it vault-0 -- /bin/sh

vault login -method=token <token>
vault auth enable kubernetes

vault write auth/kubernetes/config \
    token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
    kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \
    kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt

vault policy write internal-app - <<EOF
path "internal/data/database/config" {
  capabilities = ["read"]
}
EOF

vault write auth/kubernetes/role/internal-app \
    bound_service_account_names=internal-app \
    bound_service_account_namespaces=gitlab \
    policies=internal-app \
    ttl=24h

rm /home/vault/.vault-token

Set your kube config to the namespace you want to use vault with

kubectl apply -f k8s/service-account-internal-app.yaml

Add secrets to your pod

spec:
  template:
    metadata:
      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "internal-app"
        vault.hashicorp.com/agent-inject-secret-database-config.txt: "internal/data/database/config"

Gitlab Integration

Enable JWT auth for Gitlab

# Enable jwt auth
vault auth enable jwt

# Create a policy
vault policy write myproject-production - <<EOF
# Policy name: myproject-production
#
# Read-only permission on 'secret/data/myproject/production/*' path
path "secret/data/myproject/production/*" {
  capabilities = [ "read" ]
}
EOF

# Create a role
vault write auth/jwt/role/myproject-production - <<EOF
{
  "role_type": "jwt",
  "policies": ["myproject-production"],
  "token_explicit_max_ttl": 60,
  "user_claim": "user_email",
  "bound_claims_type": "glob",
  "bound_claims": {
    "project_id": "127",
    "ref_protected": "false",
    "ref_type": "branch",
    "ref": "main"
  }
}
EOF

# Write the auth method
vault write auth/jwt/config \
    jwks_url="https://gitlab.ducoterra.net/-/jwks" \
    bound_issuer="gitlab.ducoterra.net"

Pipeline syntax

read_secrets:
  script:
    # Check job's ref name
    - echo $CI_COMMIT_REF_NAME
    # and is this ref protected
    - echo $CI_COMMIT_REF_PROTECTED
    # Vault's address can be provided here or as CI/CD variable
    - export VAULT_ADDR=https://vault.ducoterra.net
    # Authenticate and get token. Token expiry time and other properties can be configured
    # when configuring JWT Auth - https://www.vaultproject.io/api/auth/jwt#parameters-1
    - export VAULT_TOKEN="$(vault write -field=token auth/jwt/login role=myproject-production jwt=$CI_JOB_JWT)"
    # Now use the VAULT_TOKEN to read the secret and store it in environment variable
    - export PASSWORD="$(vault kv get -field=password secret/myproject/production/db)"
    # Use the secret
    - echo $PASSWORD

Certificate Management

https://learn.hashicorp.com/tutorials/vault/pki-engine https://learn.hashicorp.com/tutorials/vault/kubernetes-cert-manager

Create a CA

vault secrets enable -path=pki_dnet pki
vault secrets tune -max-lease-ttl=87600h pki_dnet

vault write pki_dnet/root/generate/internal \
    common_name=vault.ducoterra.net \
    ttl=87600h

vault write pki_dnet/config/urls \
    issuing_certificates="https://vault.ducoterra.net/v1/pki_dnet/ca" \
    crl_distribution_points="https://vault.ducoterra.net/v1/pki_dnet/crl"

Create an intermediate CA

vault secrets enable -path=pki_dnet_int pki
vault secrets tune -max-lease-ttl=43800h pki_dnet_int
vault write -format=json pki_dnet_int/intermediate/generate/internal \
    common_name="vault.ducoterra.net Intermediate Authority" \
    | jq -r '.data.csr' > certs/pki_dnet_intermediate.csr

vault write -format=json pki_dnet/root/sign-intermediate \
    csr=@certs/pki_dnet_intermediate.csr format=pem_bundle ttl=43800h \
    | jq -r '.data.certificate' > certs/pki_dnet_intermediate.cert.pem

vault write pki_dnet_int/intermediate/set-signed certificate=@certs/pki_dnet_intermediate.cert.pem

vault write pki_dnet_int/config/urls \
    issuing_certificates="https://vault.ducoterra.net/v1/pki_dnet_int/ca" \
    crl_distribution_points="https://vault.ducoterra.net/v1/pki_dnet_int/crl"

vault write pki_dnet_int/roles/dnet \
    allowed_domains=dnet \
    allow_subdomains=true max_ttl=43800h

vault write pki_dnet_int/roles/pi_hole \
    allowed_domains=hole \
    allow_subdomains=true max_ttl=43800h

Navigate to https://vault.ducoterra.net/v1/pki_dnet_int/ca and download the CA. Import to your devices.

Issue a certificate

# Use -format=json to dump a json file
vault write pki_dnet_int/issue/dnet \
    common_name=freenas.dnet > certs/freenas.dnet.cert

vault write pki_dnet_int/issue/pi_hole \
    common_name=pi.hole > certs/pi.hole.cert

Revoke a certificate

vault write pki_dnet_int/revoke serial_number=<serial_number>
vault write pki_dnet_int/tidy tidy_cert_store=true tidy_revoked_certs=true

Use with cert-manager

vault policy write pki_dnet - <<EOF
path "pki_dnet_int*"                        { capabilities = ["read", "list"] }
path "pki_dnet_int/roles/*"   { capabilities = ["create", "update"] }
path "pki_dnet_int/sign/*"    { capabilities = ["create", "update"] }
path "pki_dnet_int/issue/*"   { capabilities = ["create"] }
EOF

vault write auth/kubernetes/role/issuer \
    bound_service_account_names=issuer \
    bound_service_account_namespaces=cert-manager \
    policies=pki_dnet \
    ttl=20m

kubectl -n cert-manager create serviceaccount issuer
ISSUER_SECRET_REF=$(kubectl -n cert-manager get serviceaccount issuer -o json | jq -r ".secrets[].name")

cat > cert-manager/vault-issuer.yaml <<EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: vault-issuer
spec:
  vault:
    server: https://vault.ducoterra.net
    path: pki_dnet_int/sign/dnet
    auth:
      kubernetes:
        mountPath: /v1/auth/kubernetes
        role: issuer
        secretRef:
          name: $ISSUER_SECRET_REF
          key: token
EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    # cert-manager.io/cluster-issuer: letsencrypt-prod
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.org/client-max-body-size: "0"
  name: {{ .Release.Name }}
spec:
  rules:
  - host: test.dnet
    http:
      paths:
      - backend:
          service:
            name: {{ .Release.Name }}
            port:
              number: 80
        path: /
        pathType: Prefix
  tls:
  - hosts:
    - test-dnet
    secretName: test-dnet-cert
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: test-dnet
spec:
  secretName: test-dnet-cert
  issuerRef:
    name: vault-issuer
    kind: ClusterIssuer
  commonName: test.dnet
  dnsNames:
  - test.dnet

Kubernetes External Vault Auth

https://learn.hashicorp.com/tutorials/vault/kubernetes-external-vault?in=vault/kubernetes

Connect to external vault

helm install vault hashicorp/vault \
    --set "injector.externalVaultAddr=https://vault.ducoterra.net"

VAULT_HELM_SECRET_NAME=$(kubectl get secrets --output=json | jq -r '.items[].metadata | select(.name|startswith("vault-token-")).name')
TOKEN_REVIEW_JWT=$(kubectl get secret $VAULT_HELM_SECRET_NAME --output='go-template={{ .data.token }}' | base64 --decode)
KUBE_CA_CERT=$(kubectl config view --raw --minify --flatten --output='jsonpath={.clusters[].cluster.certificate-authority-data}' | base64 --decode)
KUBE_HOST="https://3.14.3.104:6443"
vault auth enable -path=pikube kubernetes

vault write auth/pikube/config \
    token_reviewer_jwt="$TOKEN_REVIEW_JWT" \
    kubernetes_host="https://3.14.3.104:6443" \
    kubernetes_ca_cert="$KUBE_CA_CERT"

vault write auth/pikube/role/issuer \
    bound_service_account_names=issuer \
    bound_service_account_namespaces=cert-manager \
    policies=pki_dnet \
    ttl=20m

Install cert-manager

kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.3.1/cert-manager.yaml
kubectl -n cert-manager create serviceaccount issuer
ISSUER_SECRET_REF=$(kubectl -n cert-manager get serviceaccount issuer -o json | jq -r ".secrets[].name")

cat > cert-manager/pikube-vault-clusterissuer.yaml <<EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: vault-issuer
spec:
  vault:
    server: https://vault.ducoterra.net
    path: pki_dnet_int/sign/dnet
    auth:
      kubernetes:
        mountPath: /v1/auth/pikube
        role: issuer
        secretRef:
          name: $ISSUER_SECRET_REF
          key: token
EOF

kubectl apply -f cert-manager/pikube-vault-clusterissuer.yaml