This commit is contained in:
ducoterra
2021-05-20 12:58:44 -04:00
commit e67d6804b3
8 changed files with 330 additions and 0 deletions

15
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,15 @@
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

198
README.md Normal file
View File

@@ -0,0 +1,198 @@
# Vault
## Prereqs
```bash
brew tap hashicorp/tap
brew install hashicorp/tap/vault
brew install jq
```
## Install (Standalone)
```bash
kubectl apply -f certificate.yaml
helm repo add hashicorp https://helm.releases.hashicorp.com
helm search repo hashicorp/vault
helm upgrade --install vault hashicorp/vault --values 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)
```bash
kubectl apply -f certificate.yaml
helm repo add hashicorp https://helm.releases.hashicorp.com
helm search repo hashicorp/vault
helm upgrade --install vault hashicorp/vault --values 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 policy
```bash
vault policy write ducoterra policies/ducoterra.hcl
```
## Add user
```bash
vault auth enable userpass
vault write auth/userpass/users/ducoterra \
policies=ducoterra \
password=password
```
## Enable KV Secrets
```bash
vault secrets enable -path=secret kv-v2
vault kv put secret/okta username='static-user' password='static-password'
vault kv get secret/okta
```
## TOTP
```bash
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:
```bash
path "totp/keys/*" {
capabilities = ["update"]
}
path "totp/code/*" {
capabilities = ["read"]
}
```
## Kubernetes Secrets
```bash
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
```bash
kubectl apply -f service-account-internal-app.yaml
```
Add secrets to your pod
```bash
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
```bash
# 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": "22",
"ref_protected": "true",
"ref_type": "branch",
"ref": "auto-deploy-*"
}
}
EOF
# Write the auth method
vault write auth/jwt/config \
jwks_url="https://gitlab.ducoterra.net/-/jwks" \
bound_issuer="gitlab.ducoterra.net"
```
### Pipeline syntax
```yaml
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
```

12
certificate.yaml Normal file
View File

@@ -0,0 +1,12 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: vault.ducoterra.net
spec:
secretName: vault-tls-cert
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
commonName: vault.ducoterra.net
dnsNames:
- vault.ducoterra.net

11
policies/ducoterra.hcl Normal file
View File

@@ -0,0 +1,11 @@
path "totp/keys/*" {
capabilities = ["update"]
}
path "totp/code/*" {
capabilities = ["read"]
}
path "secret/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}

29
scripts/vault_unseal.py Executable file
View File

@@ -0,0 +1,29 @@
#!/usr/bin/python3
import json
import os
import subprocess
import threading
vaults = ["vault-0"]
home = os.getenv("HOME")
with open(os.path.join(home, ".vault-keys/cluster-keys.json")) as f:
vault_secrets = json.load(f)
procs = []
for vault in vaults:
procs += [
threading.Thread(
target = subprocess.run,
args = (
["kubectl", "exec", "-ti", vault, "--", "vault", "operator", "unseal", vault_secrets.get("unseal_keys_b64")[key]],))
for key in range(int(vault_secrets.get("unseal_threshold")))
]
for thread in procs:
thread.start()
for thread in procs:
thread.join()

View File

@@ -0,0 +1,4 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: internal-app

1
setenv.sh Executable file
View File

@@ -0,0 +1 @@
kubectl config set current-context k3os-alpha.dnet-admin-vault

60
values.yaml Normal file
View File

@@ -0,0 +1,60 @@
global:
enabled: true
server:
# ha:
# enabled: true
# config: |
# ui = true
# listener "tcp" {
# address = "[::]:8200"
# cluster_address = "[::]:8201"
# }
# storage "file" {
# path = "/vault/data"
# }
# raft:
# enabled: true
standalone:
enabled: true
config: |
ui = true
listener "tcp" {
tls_disable = 1
address = "[::]:8200"
cluster_address = "[::]:8201"
}
storage "file" {
path = "/vault/data"
}
dataStorage:
enabled: true
size: 32Gi
storageClass: null
accessMode: ReadWriteOnce
ingress:
enabled: true
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
kubernetes.io/ingress.class: nginx
hosts:
- host: vault.ducoterra.net
paths:
- /
tls:
- hosts:
- vault.ducoterra.net
secretName: vault-tls-cert
ui:
enabled: true
serviceType: ClusterIP