Vault External TLS Configuration
Configure Vault to accept TLS connections from the network, enabling enterprise-grade automation without SSH tunnels.
Prerequisites
-
Vault cluster operational and unsealed
-
Vault PKI engine configured with
pki_int(DOMUS-ISSUING-CA) -
SSH access to vault-01.inside.domusdigitalis.dev
-
Root token access for initial setup
Related Documentation
| Document | Description |
|---|---|
SSH certificate authority (requires external TLS for automation) |
|
Certificate issuance procedures |
|
Backup procedures |
Architecture Overview
Phase 1: Issue TLS Certificate for Vault
1.3 Unseal and Authenticate
Check status:
vault status | awk 'NR <= 5'
If sealed, unseal (threshold 2):
vault operator unseal
vault operator unseal
Set token:
export VAULT_TOKEN='<paste-VAULT_TOKEN-from-dsec>'
1.4 Validate DNS Resolution
Before issuing certificate, verify DNS resolves correctly:
dig +short vault-01.inside.domusdigitalis.dev
10.50.1.60
dig +short -x 10.50.1.60
vault-01.inside.domusdigitalis.dev.
1.5 Verify PKI Role Constraints
Check the role allows localhost and subdomains:
vault read pki_int/roles/domus-server | awk '/allowed_domains|allow_subdomains|allow_bare_domains|allow_localhost/'
allow_bare_domains false allow_localhost true allow_subdomains true allowed_domains [inside.domusdigitalis.dev domusdigitalis.dev]
allow_bare_domains: false means short hostnames like vault-01 are not allowed in certificates. This is correct - enterprise best practice uses FQDNs only.
|
1.6 Issue TLS Certificate from Vault PKI
Issue a server certificate for Vault itself:
vault write -format=json pki_int/issue/domus-server \
common_name="vault-01.inside.domusdigitalis.dev" \
alt_names="localhost" \
ip_sans="10.50.1.60,127.0.0.1" \
ttl="8760h" > /tmp/vault-tls-cert.json
|
Best Practice: Do NOT include short hostnames (e.g.,
Short hostnames are ambiguous and not enterprise-scalable. |
1.7 Verify Certificate Issuance (jq)
Inspect the JSON response structure:
jq '.data | keys' /tmp/vault-tls-cert.json
["ca_chain", "certificate", "expiration", "issuing_ca", "private_key", "private_key_type", "serial_number"]
Verify certificate details directly from JSON + openssl pipeline:
jq -r '.data.certificate' /tmp/vault-tls-cert.json | openssl x509 -noout -subject -dates
subject=CN=vault-01.inside.domusdigitalis.dev notBefore=Feb 20 21:05:31 2026 GMT notAfter=Feb 20 21:06:01 2027 GMT
Extract serial and expiration (Unix epoch → human readable):
jq -r '.data | "Serial: \(.serial_number)\nExpires: \(.expiration | todate)"' /tmp/vault-tls-cert.json
1.8 Extract Certificate Components
jq -r '.data.certificate' /tmp/vault-tls-cert.json > /tmp/vault-tls.crt
jq -r '.data.private_key' /tmp/vault-tls-cert.json > /tmp/vault-tls.key
jq -r '.data.ca_chain[]' /tmp/vault-tls-cert.json > /tmp/vault-ca-chain.crt
Verify all files created with content:
for f in /tmp/vault-tls.{crt,key} /tmp/vault-ca-chain.crt; do
[[ -s "$f" ]] && echo "✓ $f ($(wc -c < "$f") bytes)" || echo "✗ $f MISSING"
done
1.9 Verify Certificate SANs
openssl x509 -in /tmp/vault-tls.crt -noout -text | awk '/Subject Alternative Name/{getline; gsub(/^[[:space:]]+/, ""); print}'
DNS:vault-01.inside.domusdigitalis.dev, DNS:localhost, IP Address:10.50.1.60, IP Address:127.0.0.1
No short hostname (vault-01) - this is correct per best practice.
|
Phase 2: Install TLS Certificate
2.1 Create Vault TLS Directory
sudo mkdir -p /opt/vault/tls
sudo chown vault:vault /opt/vault/tls
sudo chmod 750 /opt/vault/tls
2.2 Install Certificate and Key
sudo cp /tmp/vault-tls.crt /opt/vault/tls/vault.crt
sudo cp /tmp/vault-tls.key /opt/vault/tls/vault.key
sudo cp /tmp/vault-ca-chain.crt /opt/vault/tls/ca-chain.crt
Set ownership and permissions:
sudo chown vault:vault /opt/vault/tls/*
sudo chmod 640 /opt/vault/tls/vault.crt
sudo chmod 600 /opt/vault/tls/vault.key
sudo chmod 640 /opt/vault/tls/ca-chain.crt
2.3 Verify Installation
Check files exist with correct permissions (awk formatted):
sudo ls -la /opt/vault/tls/ | awk 'NR>1 {printf "%-12s %-6s %-6s %8s %s\n", $1, $3, $4, $5, $NF}'
sudo required - directory has 750 permissions (vault:vault only).
|
-rw-r----- vault vault 4248 ca-chain.crt -rw-r----- vault vault 1879 vault.crt -rw------- vault vault 1675 vault.key
If old files exist from previous attempts, clean up: sudo rm /opt/vault/tls/{tls.crt,tls.key,vault-tls.key} 2>/dev/null
|
Verify certificate subject matches hostname:
openssl x509 -in /opt/vault/tls/vault.crt -noout -subject | awk -F= '{print $NF}'
vault-01.inside.domusdigitalis.dev
Verify CA chain contains both certificates:
openssl storeutl -certs /opt/vault/tls/ca-chain.crt 2>/dev/null | awk '/subject=/ {gsub(/.*CN=/, ""); print}'
DOMUS-ISSUING-CA DOMUS-ROOT-CA
Phase 3: Configure Vault TLS Listener
3.1 Backup Current Configuration
sudo cp /etc/vault.d/vault.hcl /etc/vault.d/vault.hcl.bak.$(date +%Y%m%d)
3.3 Update Vault Configuration
|
Vault 1.20+ Breaking Change:
|
First, check your current storage backend:
awk '/storage/ {print NR": "$0}' /etc/vault.d/vault.hcl.bak.*
|
Storage backends are NOT interchangeable!
If your backup shows |
For File Storage (single node):
sudo tee /etc/vault.d/vault.hcl << 'EOF'
# Vault Server Configuration - External TLS with File Storage
ui = true
disable_mlock = true
storage "file" {
path = "/opt/vault/data"
}
# HTTPS listener (bound to specific IP - not all interfaces)
listener "tcp" {
address = "{vault-primary-ip}:8200"
tls_cert_file = "/opt/vault/tls/vault.crt"
tls_key_file = "/opt/vault/tls/vault.key"
tls_client_ca_file = "/opt/vault/tls/ca-chain.crt"
tls_min_version = "tls12"
}
api_addr = "https://vault-01.inside.domusdigitalis.dev:8200"
EOF
For Raft Storage (HA cluster - new installations only):
sudo tee /etc/vault.d/vault.hcl << 'EOF'
# Vault Server Configuration - External TLS with Raft Storage
ui = true
disable_mlock = true
storage "raft" {
path = "/opt/vault/data"
node_id = "vault-1"
}
# HTTPS listener (bound to specific IP - not all interfaces)
listener "tcp" {
address = "{vault-primary-ip}:8200"
tls_cert_file = "/opt/vault/tls/vault.crt"
tls_key_file = "/opt/vault/tls/vault.key"
tls_client_ca_file = "/opt/vault/tls/ca-chain.crt"
tls_min_version = "tls12"
}
cluster_addr = "https://{vault-primary-ip}:8201"
api_addr = "https://vault-01.inside.domusdigitalis.dev:8200"
EOF
|
Security hardening: Binding to |
3.4 Validate Configuration (Dry-Run)
Vault doesn’t support -check. Use dry-run in foreground instead:
sudo -u vault vault server -config=/etc/vault.d/vault.hcl
==> Vault server configuration:
Api Address: https://vault-01.inside.domusdigitalis.dev:8200
Cluster Address: https://10.50.1.60:8201
Listener 1: tcp (addr: "10.50.1.60:8200", ... tls: "enabled")
Mlock: supported: true, enabled: false
Storage: raft (HA available)
Version: Vault v1.21.2
==> Vault server started! Log data will stream in below:
Press Ctrl+C to stop the dry-run, then proceed to restart via systemd.
Phase 4: Restart Vault
| Restarting Vault will seal it. Have unseal keys ready. |
4.3 Copy CA Chain for Client Access
The CA chain in /opt/vault/tls/ has 640 permissions (vault:vault only). Clients need a world-readable copy:
sudo cp /opt/vault/tls/ca-chain.crt /etc/ssl/certs/DOMUS-CA-CHAIN.pem
sudo chmod 644 /etc/ssl/certs/DOMUS-CA-CHAIN.pem
The CA chain is public information - it’s meant to be distributed to all clients. Only the private key (vault.key) must remain restricted.
|
Phase 5: Configure Clients
5.1 Export CA Chain
Copy CA chain from vault-01 to workstation:
exit # Return to workstation
scp vault-01:/opt/vault/tls/ca-chain.crt /tmp/vault-ca-chain.crt
5.2 Install CA Chain on Workstation
For system-wide trust:
sudo cp /tmp/vault-ca-chain.crt /etc/ssl/certs/DOMUS-CA-CHAIN.pem
For Vault CLI only:
mkdir -p ~/.config/vault
cp /tmp/vault-ca-chain.crt ~/.config/vault/ca-chain.pem
Phase 6: Update dsec
Update the dsec vault configuration with external access:
dsec edit d000 dev/vault
Update/add these values:
VAULT_ADDR: 'https://vault-01.inside.domusdigitalis.dev:8200'
VAULT_CACERT: '/etc/ssl/certs/DOMUS-CA-CHAIN.pem'
VAULT_TOKEN: '<current-root-token>'
Get current root token from vault token lookup on vault-01 while you still have SSH access.
|
Phase 7: Update Automation Scripts
7.1 Update vault-ssh-sign Script
The automation script in Vault SSH CA now works without SSH tunnels:
tee ~/.local/bin/vault-ssh-sign << 'SCRIPT'
#!/bin/bash
set -euo pipefail
KEY="${1:-$HOME/.ssh/id_ed25519_vault}"
CERT="${KEY}-cert.pub"
# Load Vault credentials from dsec
eval "$(dsource d000 dev/vault 2>/dev/null)" || {
echo "ERROR: Failed to load Vault credentials from dsec" >&2
exit 1
}
# Verify VAULT_ADDR and VAULT_CACERT are set
[[ -z "${VAULT_ADDR:-}" ]] && { echo "ERROR: VAULT_ADDR not set" >&2; exit 1; }
[[ -z "${VAULT_CACERT:-}" ]] && { echo "ERROR: VAULT_CACERT not set" >&2; exit 1; }
# Sign the key
vault write -field=signed_key ssh/sign/domus-client \
public_key=@"${KEY}.pub" > "$CERT"
# Display validity
echo "Certificate signed successfully:"
ssh-keygen -Lf "$CERT" | awk '/Valid:/ {print " "$0}'
SCRIPT
chmod +x ~/.local/bin/vault-ssh-sign
Phase 8: Firewall Configuration
Completion Checklist
| Phase | Task | Status |
|---|---|---|
1 |
TLS certificate issued from Vault PKI |
[ ] |
2 |
Certificate installed in /opt/vault/tls/ |
[ ] |
3 |
Vault configuration updated with TLS listener |
[ ] |
4 |
Vault restarted and unsealed |
[ ] |
5 |
CA chain installed on workstation |
[ ] |
6 |
dsec updated with external VAULT_ADDR |
[ ] |
7 |
Automation scripts working without SSH tunnel |
[ ] |
8 |
Firewall rules configured (if needed) |
[ ] |
Certificate Renewal
Troubleshooting
TLS Handshake Failed
Check certificate validity:
openssl s_client -connect vault-01.inside.domusdigitalis.dev:8200 -CAfile /etc/ssl/certs/DOMUS-CA-CHAIN.pem
Look for:
-
Verify return code: 0 (ok)= Success -
Verify return code: 19= CA not trusted - install CA chain -
Verify return code: 10= Certificate expired - renew
Connection Refused
-
Check Vault is running:
ssh vault-01 "systemctl is-active vault" -
Check Vault is listening on network:
ssh vault-01 "ss -tlnp | grep 8200"Expected Output (network accessible)LISTEN 0 128 0.0.0.0:8200 ...
If showing
127.0.0.1:8200, TLS configuration not applied. -
Check firewall:
ssh vault-01 "sudo firewall-cmd --list-ports"
See Also
-
Vault SSH CA - Now uses external TLS