1069 lines
27 KiB
Markdown
1069 lines
27 KiB
Markdown
# Vault
|
|
|
|
## Prereqs
|
|
|
|
```bash
|
|
brew tap hashicorp/tap
|
|
brew install hashicorp/tap/vault
|
|
|
|
brew install jq
|
|
```
|
|
|
|
## Install (bare metal)
|
|
|
|
1. [Download Vault](https://www.vaultproject.io/downloads)
|
|
|
|
2. Create a vault user
|
|
|
|
```bash
|
|
useradd --system --home /etc/vault.d --shell /bin/false vault
|
|
```
|
|
|
|
2. Create a config file
|
|
|
|
```bash
|
|
mkdir -p /etc/vault.d
|
|
touch /etc/vault.d/vault.hcl
|
|
chmod 640 /etc/vault.d/vault.hcl
|
|
chown --recursive vault:vault /etc/vault.d
|
|
```
|
|
|
|
3. Add the following
|
|
|
|
```hcl
|
|
storage "file" {
|
|
path = "/vault/data"
|
|
}
|
|
|
|
listener "tcp" {
|
|
address = "0.0.0.0:8200"
|
|
tls_disable = 0
|
|
tls_cert_file = "/etc/ssl/certs/pi-vault.pem"
|
|
tls_key_file = "/etc/vault.d/pi-vault.key"
|
|
}
|
|
|
|
ui = true
|
|
```
|
|
|
|
4. Add your CA (if you have one) to /etc/ssl/certs and run `update-ca-certificates`
|
|
|
|
5. Create a vault storage location
|
|
|
|
```bash
|
|
mkdir -p /vault/data
|
|
chown --recursive vault:vault /vault/data/
|
|
```
|
|
|
|
6. Allow vault to use mlock syscall to prevent memory from being swapped
|
|
|
|
```bash
|
|
setcap cap_ipc_lock=+ep /usr/local/bin/vault
|
|
```
|
|
|
|
7. Create a systemd process
|
|
|
|
```bash
|
|
touch /etc/systemd/system/vault.service
|
|
```
|
|
|
|
8. Add the following
|
|
|
|
```conf
|
|
[Unit]
|
|
Description="HashiCorp Vault - A tool for managing secrets"
|
|
Documentation=https://www.vaultproject.io/docs/
|
|
Requires=network-online.target
|
|
After=network-online.target
|
|
ConditionFileNotEmpty=/etc/vault.d/vault.hcl
|
|
StartLimitIntervalSec=60
|
|
StartLimitBurst=3
|
|
|
|
[Service]
|
|
User=vault
|
|
Group=vault
|
|
ProtectSystem=full
|
|
ProtectHome=read-only
|
|
PrivateTmp=yes
|
|
PrivateDevices=yes
|
|
SecureBits=keep-caps
|
|
AmbientCapabilities=CAP_IPC_LOCK
|
|
Capabilities=CAP_IPC_LOCK+ep
|
|
CapabilityBoundingSet=CAP_SYSLOG CAP_IPC_LOCK
|
|
NoNewPrivileges=yes
|
|
ExecStart=/usr/local/bin/vault server -config=/etc/vault.d/vault.hcl
|
|
ExecReload=/bin/kill --signal HUP $MAINPID
|
|
KillMode=process
|
|
KillSignal=SIGINT
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
TimeoutStopSec=30
|
|
StartLimitInterval=60
|
|
StartLimitIntervalSec=60
|
|
StartLimitBurst=3
|
|
LimitNOFILE=65536
|
|
LimitMEMLOCK=infinity
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
```
|
|
|
|
9. Start the service with `systemctl start vault`
|
|
10. Run `vault operator init` on the server to generate keys
|
|
11. Unseal the vault
|
|
12. Add the following backup job to /etc/crontab
|
|
|
|
```crontab
|
|
0 0 * * * root tar czvf /vault/backups/$(date +\%y-\%m-\%d-\%H-\%M)-pivault.tar.gz /vault/data
|
|
0 0 * * * root find /vault/backups/* -mtime +7 -exec rm {} +
|
|
```
|
|
|
|
## Install (Standalone)
|
|
|
|
```bash
|
|
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)
|
|
|
|
This assumes you have an unseal vault available! See ha-test for manual unseal.
|
|
|
|
```bash
|
|
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/ha.yaml
|
|
|
|
mkdir ~/.vault-keys
|
|
kubectl exec -ti vault-0 -- vault operator init -format=json > ~/.vault-keys/vault-cluster-keys.json
|
|
# Only run the unseal if not autounsealing
|
|
# 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
|
|
|
|
```bash
|
|
vault policy write ducoterra policies/ducoterra.hcl
|
|
```
|
|
|
|
## Add user
|
|
|
|
```bash
|
|
vault auth enable userpass
|
|
vault auth tune -default-lease-ttl=24h 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:reese.wells@nimbisservices.com?secret=SECRET&issuer=Okta'
|
|
|
|
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 k8s/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": "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
|
|
|
|
```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
|
|
```
|
|
|
|
## Certificate Management
|
|
|
|
<https://learn.hashicorp.com/tutorials/vault/pki-engine>
|
|
<https://learn.hashicorp.com/tutorials/vault/kubernetes-cert-manager>
|
|
|
|
### Create a CA
|
|
|
|
```bash
|
|
export ROOT_PATH=dnet
|
|
|
|
# Note: 19800h is Apple's limit
|
|
vault secrets enable -path=$ROOT_PATH pki
|
|
vault secrets tune -max-lease-ttl=19800h $ROOT_PATH
|
|
|
|
# Create a root CA
|
|
vault write $ROOT_PATH/root/generate/internal \
|
|
common_name="Ducoterra Root CA" \
|
|
ttl=19800h
|
|
|
|
# Create a CA URL
|
|
vault write $ROOT_PATH/config/urls \
|
|
issuing_certificates="https://vault.ducoterra.net/v1/$ROOT_PATH/ca" \
|
|
crl_distribution_points="https://vault.ducoterra.net/v1/$ROOT_PATH/crl"
|
|
```
|
|
|
|
Navigate to <https://vault.ducoterra.net/v1/$ROOT_PATH/ca> and download the CA. Import to your devices.
|
|
|
|
### Create an intermediate CA
|
|
|
|
```bash
|
|
export ROOT_PATH=dnet
|
|
export PKI_PATH=dnet_inter
|
|
|
|
vault secrets enable -path=$PKI_PATH pki
|
|
vault secrets tune -max-lease-ttl=9900h $PKI_PATH
|
|
|
|
# Create CSR to sign with root CA
|
|
vault write -format=json $PKI_PATH/intermediate/generate/internal \
|
|
common_name="Ducoterra Intermediate CA" \
|
|
| jq -r '.data.csr' > certs/$PKI_PATH.csr
|
|
|
|
# Sign the CSR with the root CA
|
|
vault write -format=json $ROOT_PATH/root/sign-intermediate \
|
|
csr=@certs/$PKI_PATH.csr format=pem_bundle ttl=9900h \
|
|
| jq -r '.data.certificate' > certs/$PKI_PATH.cert.pem
|
|
|
|
# Save the signed cert to vault
|
|
vault write $PKI_PATH/intermediate/set-signed certificate=@certs/$PKI_PATH.cert.pem
|
|
|
|
# Create a CA URL
|
|
vault write $PKI_PATH/config/urls \
|
|
issuing_certificates="https://vault.ducoterra.net/v1/$PKI_PATH/ca" \
|
|
crl_distribution_points="https://vault.ducoterra.net/v1/$PKI_PATH/crl"
|
|
```
|
|
|
|
Navigate to <https://vault.ducoterra.net/v1/$PKI_PATH/ca> and download the CA. Import to your devices.
|
|
|
|
### Allow .dnet and .hole certificates
|
|
|
|
```bash
|
|
export PKI_PATH=dnet_inter
|
|
|
|
# Allow .dnet subdomain
|
|
vault write $PKI_PATH/roles/dnet \
|
|
allowed_domains=dnet \
|
|
allow_subdomains=true max_ttl=8760h
|
|
|
|
# Allow .hole subdomain
|
|
vault write $PKI_PATH/roles/pi_hole \
|
|
allowed_domains=hole \
|
|
allow_subdomains=true max_ttl=8760h
|
|
```
|
|
|
|
### Issue a certificate
|
|
|
|
```bash
|
|
export PKI_PATH=dnet # or dnet_inter
|
|
export CNAME=freenas.dnet
|
|
|
|
# Use -format=json to dump a json file
|
|
vault write $PKI_PATH/issue/dnet \
|
|
common_name=$CNAME \
|
|
ttl=8760h > certs/$CNAME.cert
|
|
|
|
# Pihole Example
|
|
vault write $PKI_PATH/issue/pi_hole \
|
|
common_name=$CNAME \
|
|
ttl=8760h > certs/$CNAME.cert
|
|
```
|
|
|
|
#### Adding cert to freenas
|
|
|
|
Only caveat here is to paste the certificate and then the full chain cert below in the
|
|
"certificate" section. iOS will infintely refresh the page if the full chain isn't provided.
|
|
|
|
#### Adding cert to pihole
|
|
|
|
```bash
|
|
# Install lighttpd-mod-openssl
|
|
apt install -y lighttpd-mod-openssl
|
|
|
|
# Paste the isser CA in here:
|
|
vim /etc/ssl/certs/vault-ca.pem
|
|
chown www-data /etc/ssl/certs/vault-ca.pem
|
|
chmod 770 /etc/ssl/certs/vault-ca.pem
|
|
|
|
# Paste the private key first, then the certificate in here:
|
|
vim /etc/ssl/certs/pihole.pem
|
|
chown www-data /etc/ssl/certs/pihole.pem
|
|
chmod 770 /etc/ssl/certs/pihole.pem
|
|
|
|
# Add the config below to /etc/lighttpd/external.conf
|
|
vim /etc/lighttpd/external.conf
|
|
```
|
|
|
|
```conf
|
|
$HTTP["host"] == "pi.hole" {
|
|
# Ensure the Pi-hole Block Page knows that this is not a blocked domain
|
|
setenv.add-environment = ("fqdn" => "true")
|
|
|
|
# Enable the SSL engine with a LE cert, only for this specific host
|
|
$SERVER["socket"] == ":443" {
|
|
ssl.engine = "enable"
|
|
ssl.pemfile = "/etc/ssl/certs/pihole.pem"
|
|
ssl.ca-file = "/etc/ssl/certs/vault-ca.pem"
|
|
ssl.honor-cipher-order = "enable"
|
|
ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
|
|
ssl.use-sslv2 = "disable"
|
|
ssl.use-sslv3 = "disable"
|
|
}
|
|
|
|
# Redirect HTTP to HTTPS
|
|
$HTTP["scheme"] == "http" {
|
|
$HTTP["host"] =~ ".*" {
|
|
url.redirect = (".*" => "https://%0$0")
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
```bash
|
|
# Restart the service
|
|
service lighttpd restart
|
|
```
|
|
|
|
#### Adding cert to vault
|
|
|
|
```bash
|
|
# Paste the primary cert and then the issuer CA in here:
|
|
vim /etc/ssl/certs/vault-cert.pem
|
|
chown vault:vault /etc/ssl/certs/vault-cert.pem
|
|
chmod 770 /etc/ssl/certs/vault-cert.pem
|
|
|
|
# Paste the private key in here:
|
|
vim /etc/ssl/certs/vault-key.pem
|
|
chown vault:vault /etc/ssl/certs/vault-key.pem
|
|
chmod 770 /etc/ssl/certs/vault-key.pem
|
|
```
|
|
|
|
Edit /etc/vault.d/vault.hcl and add the following:
|
|
|
|
```hcl
|
|
listener "tcp" {
|
|
address = "0.0.0.0:8200"
|
|
tls_disable = 0
|
|
tls_cert_file = "/etc/ssl/certs/vault-cert.pem"
|
|
tls_key_file = "/etc/ssl/certs/vault-key.pem"
|
|
}
|
|
```
|
|
|
|
Run `service vault restart`
|
|
|
|
#### Adding cert to cloudkey
|
|
|
|
**THIS DOESN'T WORK**
|
|
|
|
```bash
|
|
service unifi stop
|
|
|
|
# Remove the UNIFI_SSL_KEYSTORE=/etc/ssl/private/unifi.keystore.jks line from config
|
|
cp /etc/default/unifi /etc/default/unifi.back
|
|
vim /etc/default/unifi
|
|
|
|
# Remove the keystore reference
|
|
mv /usr/lib/unifi/data/keystore /usr/lib/unifi/data/keystore.back
|
|
|
|
# Copy your primary SSL Certificate into /etc/ssl/private/cloudkey.crt
|
|
cp /etc/ssl/private/cloudkey.crt /etc/ssl/private/cloudkey.crt.back
|
|
vim /etc/ssl/private/cloudkey.crt
|
|
|
|
# Copy your private key into /etc/ssl/private/cloudkey.key
|
|
cp /etc/ssl/private/cloudkey.key /etc/ssl/private/cloudkey.key.back
|
|
vim /etc/ssl/private/cloudkey.key
|
|
|
|
# Add the CA to /etc/ssl/private/vault-ca.pem
|
|
vim /etc/ssl/private/vault-ca.pem
|
|
|
|
# Generate the key bundle
|
|
openssl pkcs12 -export -in /etc/ssl/private/cloudkey.crt -inkey \
|
|
/etc/ssl/private/cloudkey.key -out /etc/ssl/private/cloudkey.p12 \
|
|
-name unifi -CAfile /etc/ssl/private/vault-ca.pem -caname vault -password pass:temppass
|
|
|
|
# Import to keystore
|
|
# Default password is "aircontrolenterprise"
|
|
keytool -importkeystore -deststorepass aircontrolenterprise \
|
|
-destkeypass aircontrolenterprise -destkeystore /usr/lib/unifi/data/keystore \
|
|
-srckeystore /etc/ssl/private/cloudkey.p12 -srcstoretype PKCS12 -srcstorepass temppass -alias unifi -noprompt
|
|
|
|
service unifi start
|
|
```
|
|
|
|
### Revoke a certificate
|
|
|
|
```bash
|
|
export PKI_PATH=dnet_inter
|
|
|
|
vault write $PKI_PATH/revoke serial_number=<serial_number>
|
|
vault write $PKI_PATH/tidy tidy_cert_store=true tidy_revoked_certs=true
|
|
```
|
|
|
|
### Use with cert-manager
|
|
|
|
```bash
|
|
export PKI_PATH=dnet_inter
|
|
|
|
vault policy write $PKI_PATH - <<EOF
|
|
path "$PKI_PATH*" { capabilities = ["read", "list"] }
|
|
path "$PKI_PATH/roles/*" { capabilities = ["create", "update"] }
|
|
path "$PKI_PATH/sign/*" { capabilities = ["create", "update"] }
|
|
path "$PKI_PATH/issue/*" { capabilities = ["create"] }
|
|
EOF
|
|
|
|
vault write auth/kubernetes/role/issuer \
|
|
bound_service_account_names=issuer \
|
|
bound_service_account_namespaces=cert-manager \
|
|
policies=$PKI_PATH \
|
|
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")
|
|
|
|
kubectl -n cert-manager apply -f - <<EOF
|
|
apiVersion: cert-manager.io/v1
|
|
kind: ClusterIssuer
|
|
metadata:
|
|
name: vault-issuer
|
|
spec:
|
|
vault:
|
|
server: https://vault.ducoterra.net
|
|
path: $PKI_PATH/sign/dnet
|
|
auth:
|
|
kubernetes:
|
|
mountPath: /v1/auth/kubernetes
|
|
role: issuer
|
|
secretRef:
|
|
name: $ISSUER_SECRET_REF
|
|
key: token
|
|
EOF
|
|
```
|
|
|
|
```yaml
|
|
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
|
|
|
|
```bash
|
|
# On the new server (pikube - 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"
|
|
```
|
|
|
|
```bash
|
|
# On the existing vault server
|
|
|
|
export PKI_PATH=dnet_inter
|
|
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_PATH \
|
|
ttl=20m
|
|
```
|
|
|
|
### Install cert-manager
|
|
|
|
```bash
|
|
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.3.1/cert-manager.yaml
|
|
kubectl -n cert-manager create serviceaccount issuer
|
|
export ISSUER_SECRET_REF=$(kubectl -n cert-manager get serviceaccount issuer -o json | jq -r ".secrets[].name")
|
|
export PKI_PATH=dnet_inter
|
|
|
|
kubectl apply -f - <<EOF
|
|
apiVersion: cert-manager.io/v1
|
|
kind: ClusterIssuer
|
|
metadata:
|
|
name: vault-issuer
|
|
spec:
|
|
vault:
|
|
server: https://vault.ducoterra.net
|
|
path: $PKI_PATH/sign/dnet
|
|
auth:
|
|
kubernetes:
|
|
mountPath: /v1/auth/pikube
|
|
role: issuer
|
|
secretRef:
|
|
name: $ISSUER_SECRET_REF
|
|
key: token
|
|
EOF
|
|
```
|
|
|
|
## Auto-unseal
|
|
|
|
<https://learn.hashicorp.com/tutorials/vault/autounseal-transit>
|
|
|
|
### Enable transit on vault that will do the unsealing
|
|
|
|
```bash
|
|
# Up the token ttl to 10 years (defaults to 32 days)
|
|
vault write sys/auth/token/tune max_lease_ttl=87600h
|
|
|
|
vault secrets enable transit
|
|
vault write -f transit/keys/autounseal
|
|
|
|
tee policies/autounseal.hcl <<EOF
|
|
path "transit/encrypt/autounseal" {
|
|
capabilities = [ "update" ]
|
|
}
|
|
|
|
path "transit/decrypt/autounseal" {
|
|
capabilities = [ "update" ]
|
|
}
|
|
EOF
|
|
|
|
vault policy write autounseal policies/autounseal.hcl
|
|
|
|
vault token create -policy="autounseal" -wrap-ttl=120 -ttl=87600h
|
|
VAULT_TOKEN="wrapping token" vault unwrap
|
|
```
|
|
|
|
### Enable auto unseal with kube cluster
|
|
|
|
#### Create a test vault in a new namespace
|
|
|
|
```bash
|
|
kubectl apply -f k8s/certificate.yaml
|
|
|
|
# Get token from above output
|
|
kubectl create secret generic auto-unseal-token --from-literal=VAULT_TOKEN="token from output"
|
|
helm upgrade --install vault hashicorp/vault --values helm/ha.yaml
|
|
```
|
|
|
|
#### Test Auto Unseal
|
|
|
|
```bash
|
|
kubectl exec -it vault-0 -- vault operator init
|
|
kubectl exec -ti vault-1 -- vault operator raft join http://vault-0.vault-internal:8200
|
|
kubectl exec -ti vault-2 -- vault operator raft join http://vault-0.vault-internal:8200
|
|
```
|
|
|
|
## SSH
|
|
|
|
### Client key signing
|
|
|
|
Enable secrets engine and generate a key
|
|
|
|
```bash
|
|
vault secrets enable -path=ssh-client-signer ssh
|
|
vault write ssh-client-signer/config/ca generate_signing_key=true
|
|
```
|
|
|
|
To enable port-forwarding you'll need to update the parameters:
|
|
|
|
```hcl
|
|
{
|
|
"permit-port-forwarding": ""
|
|
"permit-pty"
|
|
}
|
|
```
|
|
|
|
Retrieve the public CA (and add it to your /etc/ssh/trusted-user-ca-keys.pem) with
|
|
|
|
```bash
|
|
curl -o /etc/ssh/trusted-user-ca-keys.pem https://vault.ducoterra.net/v1/ssh-client-signer/public_key
|
|
```
|
|
|
|
Add it to your sshd_config
|
|
|
|
```bash
|
|
echo "TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem" >> /etc/ssh/sshd_config
|
|
```
|
|
|
|
Restart the SSH service
|
|
|
|
```bash
|
|
service ssh restart
|
|
```
|
|
|
|
Add signing role
|
|
|
|
```bash
|
|
vault write ssh-client-signer/roles/ducoterra -<<"EOH"
|
|
{
|
|
"allow_user_certificates": true,
|
|
"allowed_users": "ducoterra",
|
|
"allowed_extensions": "permit-pty,permit-port-forwarding",
|
|
"default_extensions": [
|
|
{ "permit-pty": "" }
|
|
],
|
|
"algorithm_signer": "rsa-sha2-512",
|
|
"key_type": "ca",
|
|
"default_user": "ducoterra",
|
|
"ttl": "30m0s"
|
|
}
|
|
EOH
|
|
```
|
|
|
|
```bash
|
|
vault write ssh-client-signer/roles/pi -<<"EOH"
|
|
{
|
|
"allow_user_certificates": true,
|
|
"allowed_users": "pi",
|
|
"allowed_extensions": "permit-pty,permit-port-forwarding",
|
|
"default_extensions": [
|
|
{ "permit-pty": "" }
|
|
],
|
|
"algorithm_signer": "rsa-sha2-512",
|
|
"key_type": "ca",
|
|
"default_user": "pi",
|
|
"ttl": "30m0s"
|
|
}
|
|
EOH
|
|
```
|
|
|
|
```bash
|
|
vault write ssh-client-signer/roles/rancher -<<"EOH"
|
|
{
|
|
"allow_user_certificates": true,
|
|
"allowed_users": "rancher",
|
|
"allowed_extensions": "permit-pty,permit-port-forwarding",
|
|
"default_extensions": [
|
|
{ "permit-pty": "" }
|
|
],
|
|
"algorithm_signer": "rsa-sha2-512",
|
|
"key_type": "ca",
|
|
"default_user": "rancher",
|
|
"ttl": "30m0s"
|
|
}
|
|
EOH
|
|
```
|
|
|
|
Sign a key
|
|
|
|
```bash
|
|
export SSHUSER=pi; vault write -field=signed_key ssh-client-signer/sign/$SSHUSER public_key=@$HOME/.ssh/id_rsa.pub > ~/.ssh/id_rsa-cert.pub
|
|
```
|
|
|
|
SSH using the signed key
|
|
|
|
```bash
|
|
# If you saved the signed pub as key_name"-cert.pub" then you don't need to specify the signed-cert.pub part.
|
|
ssh -i signed-cert.pub -i ~/.ssh/test_rsa client
|
|
|
|
# without the cert (using default client)
|
|
ssh -i ~/.ssh/test_rsa client
|
|
```
|
|
|
|
### Server Host Signing
|
|
|
|
Enable secrets engine
|
|
|
|
```bash
|
|
vault secrets enable -path=ssh-host-signer ssh
|
|
```
|
|
|
|
Generate keys:
|
|
|
|
```bash
|
|
vault write ssh-host-signer/config/ca generate_signing_key=true
|
|
```
|
|
|
|
Extend host key's TTL
|
|
|
|
```bash
|
|
vault secrets tune -max-lease-ttl=87600h ssh-host-signer
|
|
```
|
|
|
|
Create host role
|
|
|
|
```bash
|
|
vault write ssh-host-signer/roles/hostrole \
|
|
key_type=ca \
|
|
ttl=87600h \
|
|
allow_host_certificates=true \
|
|
allowed_domains="localdomains,dnet,hole,ducoterra.net" \
|
|
allow_subdomains=true
|
|
```
|
|
|
|
Sign the host's public key
|
|
|
|
```bash
|
|
vault write ssh-host-signer/sign/hostrole \
|
|
cert_type=host \
|
|
public_key=@$HOME/.ssh/id_rsa.pub
|
|
```
|
|
|
|
Write the signed certificate to the ssh config on the host
|
|
|
|
```bash
|
|
vault write -field=signed_key ssh-host-signer/sign/hostrole \
|
|
cert_type=host \
|
|
public_key=@$HOME/.ssh/id_rsa.pub > /etc/ssh/ssh_host_rsa_key-cert.pub
|
|
```
|
|
|
|
Assign correct permissions
|
|
|
|
```bash
|
|
chmod 0640 /etc/ssh/ssh_host_rsa_key-cert.pub
|
|
```
|
|
|
|
Add to sshd_config
|
|
|
|
```bash
|
|
echo HostKey /etc/ssh/ssh_host_rsa_key >> /etc/ssh/sshd_config
|
|
echo HostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub >> /etc/ssh/sshd_config
|
|
```
|
|
|
|
Restart the ssh service
|
|
|
|
```bash
|
|
service ssh restart
|
|
```
|
|
|
|
Add certificate to client
|
|
|
|
```bash
|
|
echo '@cert-authority *.ducoterra.net '$(vault read -field=public_key ssh-host-signer/config/ca) >> ~/.ssh/known_hosts
|
|
```
|
|
|
|
## AWS
|
|
|
|
### Credential Management
|
|
|
|
https://www.vaultproject.io/docs/secrets/aws
|
|
|
|
Enable AWS secrets
|
|
|
|
```bash
|
|
vault secrets enable aws
|
|
vault secrets tune -default-lease-ttl=24h aws
|
|
```
|
|
|
|
Allow user to access AWS path
|
|
|
|
```hcl
|
|
path "aws/*" {
|
|
capabilities = ["create", "read", "update", "delete", "list"]
|
|
}
|
|
```
|
|
|
|
#### Create a vault-secret user
|
|
|
|
```bash
|
|
# Generate a vault-root-user
|
|
export VAULT_ROOT_USER_ARN=$(aws iam create-user --user-name vault-root-user | jq -r '.User.Arn')
|
|
|
|
# Create a root user policy template with the user arn
|
|
export ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
|
|
export VAULT_PUPPET_ARNS='arn:aws:iam::'$ACCOUNT_ID':user/vault-*'
|
|
jq -r ".Statement[].Resource[0] = \"$VAULT_PUPPET_ARNS\"" < aws/vault_root_policy.json > /tmp/vault_root_policy.json
|
|
# Create the vault root user policy from the template
|
|
export VAULT_ROOT_USER_POLICY_ARN=$(aws iam create-policy --policy-name vault-root-policy --policy-document file:///tmp/vault_root_policy.json | jq -r '.Policy.Arn')
|
|
# Attach the vault root user policy
|
|
aws iam attach-user-policy --user-name vault-root-user --policy-arn $VAULT_ROOT_USER_POLICY_ARN
|
|
|
|
# Create the key rotation policy
|
|
jq -r ".Statement[].Resource[0] = \"$VAULT_ROOT_USER_ARN\"" < aws/vault_root_rotate_policy.json > /tmp/vault_root_rotate_policy.json
|
|
export VAULT_ROOT_USER_ROTATE_POLICY_ARN=$(aws iam create-policy --policy-name vault-root-rotate-policy --policy-document file:///tmp/vault_root_rotate_policy.json | jq -r '.Policy.Arn')
|
|
# Attach the key rotation policy
|
|
aws iam attach-user-policy --user-name vault-root-user --policy-arn $VAULT_ROOT_USER_ROTATE_POLICY_ARN
|
|
|
|
# Create access keys for the vault-root-user
|
|
export $(aws iam create-access-key --user-name vault-root-user | jq -r '"VAULT_ROOT_ACCESS_KEY_ID=\(.AccessKey.AccessKeyId) VAULT_ROOT_SECRET_ACCESS_KEY=\(.AccessKey.SecretAccessKey)"')
|
|
```
|
|
|
|
Write aws root config with access key and secret key from above
|
|
|
|
```bash
|
|
vault write aws/config/root \
|
|
access_key=$VAULT_ROOT_ACCESS_KEY_ID \
|
|
secret_key=$VAULT_ROOT_SECRET_ACCESS_KEY \
|
|
region=us-east-2
|
|
```
|
|
|
|
Rotate your credentials
|
|
|
|
```bash
|
|
vault write -f aws/config/rotate-root
|
|
```
|
|
|
|
#### Creating IAM Users
|
|
|
|
Create a terraform-user role
|
|
|
|
```bash
|
|
vault write aws/roles/terraform-user \
|
|
credential_type=iam_user \
|
|
policy_document=@aws/terraform_policy.json
|
|
```
|
|
|
|
Read credentials
|
|
|
|
```bash
|
|
vault read aws/creds/terraform-user
|
|
```
|
|
|
|
Automate credentials generation (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html)
|
|
|
|
```bash
|
|
export $(vault read -format json aws/creds/terraform-user | jq -r '"AWS_ACCESS_KEY_ID=\(.data.access_key) AWS_SECRET_ACCESS_KEY=\(.data.secret_key)"')
|
|
export AWS_DEFAULT_REGION=us-east-2
|
|
export AWS_PROFILE=default
|
|
aws configure set region $AWS_DEFAULT_REGION --profile default
|
|
aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID --profile default
|
|
aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY --profile default
|
|
```
|
|
|
|
#### STS Tokens
|
|
|
|
To create STS federated tokens use the following permissions:
|
|
|
|
```json
|
|
{
|
|
"Version": "2012-10-17",
|
|
"Statement": {
|
|
"Effect": "Allow",
|
|
"Action": [
|
|
"sts:GetFederationToken"
|
|
],
|
|
"Resource": "*"
|
|
}
|
|
}
|
|
```
|
|
|
|
Create a role with the example policy
|
|
|
|
```bash
|
|
vault write aws/roles/ec2_admin \
|
|
credential_type=federation_token \
|
|
policy_document=@aws/ec2_admin.json
|
|
```
|
|
|
|
Generate a role with a ttl of 60 minutes
|
|
|
|
```bash
|
|
vault write aws/sts/ec2_admin ttl=24h
|
|
```
|
|
|
|
### Authentication
|
|
|
|
#### Create a vault aws policy
|
|
|
|
```bash
|
|
vault policy write aws_wireguard policies/aws_wireguard.hcl
|
|
```
|
|
|
|
#### Create a vault-auth user
|
|
|
|
```bash
|
|
# Generate a vault-auth-user
|
|
export VAULT_AUTH_USER_ARN=$(aws iam create-user --user-name vault-auth-user | jq -r '.User.Arn')
|
|
|
|
# Create an auth user policy template with the user arn
|
|
export ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
|
|
# Create the vault auth user policy from the template
|
|
export VAULT_AUTH_USER_POLICY_ARN=$(aws iam create-policy --policy-name vault-auth-policy --policy-document file://aws/vault_auth_policy.json | jq -r '.Policy.Arn')
|
|
# Attach the vault root user policy
|
|
aws iam attach-user-policy --user-name vault-auth-user --policy-arn $VAULT_AUTH_USER_POLICY_ARN
|
|
|
|
# Generate credentials
|
|
export $(aws iam create-access-key --user-name vault-auth-user | jq -r '"VAULT_AUTH_ACCESS_KEY_ID=\(.AccessKey.AccessKeyId) VAULT_AUTH_SECRET_ACCESS_KEY=\(.AccessKey.SecretAccessKey)"')
|
|
# Write credentials to vault
|
|
vault write auth/aws/config/client secret_key=$VAULT_AUTH_SECRET_ACCESS_KEY access_key=$VAULT_AUTH_ACCESS_KEY_ID
|
|
```
|
|
|
|
#### Create a vault-auth role
|
|
|
|
```bash
|
|
# Create the role
|
|
exort VAULT_AUTH_ROLE_ARN=$(aws iam create-role --role-name vault-auth-role --assume-role-policy-document file://aws/vault_auth_trust_policy.json | jq -r '.Role.Arn')
|
|
```
|
|
|
|
#### Create the vault role with the role arn
|
|
|
|
```bash
|
|
vault write auth/aws/role/aws-wireguard auth_type=iam bound_iam_principal_arn=$VAULT_AUTH_ROLE_ARN policies=aws_wireguard max_ttl=30m
|
|
vault write auth/aws/config/client iam_server_id_header_value=vault.ducoterra.net
|
|
```
|
|
|
|
#### Login on the ec2 instance
|
|
|
|
```bash
|
|
vault login -method=aws header_value=vault.ducoterra.net role=aws-wireguard
|
|
```
|