Vault setup

For proper connection from Docker/Podman containers, use this vault configuration and bind Vault interface to 0.0.0.0.

storage "file" {
  path = "/opt/vault/data"
}

listener "tcp" {
  address     = "0.0.0.0:8200"
  tls_disable = 1
}

disable_mlock = true
ui = true
export VAULT_ADDR=http://localhost:8200
export VAULT_TOKEN=root

# Enable KV v2 (if not already): vault secrets enable -path=kv kv-v2
vault secrets enable -path=kv kv-v2

# Put secrets (example)
vault kv put kv/snoop \
  db_dsn="postgres://snoop:snoop@postgres:5432/snoop?sslmode=disable" \
  minio_endpoint="minio:9000" \
  minio_access_key="minioadmin" \
  minio_secret_key="minioadmin" \
  minio_use_ssl="false" \
  jwt_secret="supersecretjwt" \
  minio_records_bucket="records" \
  minio_livestream_bucket="livestream" \
  minio_presign_ttl_seconds="900"

Unseal Key 1: AMLVUGoP2hlEd02nWWghAiVYT4jtiXv50WsZyQ2MbpP/ Unseal Key 2: OtaDsNoGE2EF6UfrQUkU0NoDVxPK/KwBFg9cUfQuhBs+

Initial Root Token: hvs.rKzgIc5aaucOCtlJNsUdZuEH

{ "db_dsn": "postgres://snoop:snoop@postgres:5432/snoop?sslmode=disable", "minio_endpoint": "minio:9000", "minio_access_key": "minioadmin", "minio_secret_key": "minioadmin", "minio_use_ssl": "false", "jwt_secret": "supersecretjwt", "minio_records_bucket": "records", "minio_livestream_bucket": "livestream", "minio_presign_ttl_seconds": "900" }

Stand up internal CA (root + intermediate)

# Enable PKI backends (root + intermediate)
vault secrets enable -path=pki_root pki
vault secrets tune -max-lease-ttl=87600h pki_root      # 10y

vault write pki_root/root/generate/internal \
  common_name="Snoop Root CA" key_type=ec key_bits=256 ttl=87600h

vault secrets enable -path=pki_iot pki
vault secrets tune -max-lease-ttl=17520h pki_iot       # 2y

# Create an intermediate CSR
vault write -field=csr pki_iot/intermediate/generate/internal \
  common_name="Snoop IoT Intermediate" key_type=ec key_bits=256 ttl=17520h > iot_int.csr

# Sign intermediate with root
vault write -field=certificate pki_root/root/sign-intermediate csr=@iot_int.csr \
  format=pem_bundle ttl=17520h > iot_int_signed.pem

vault write -field=issuing_ca pki_root/root/sign-intermediate \
  csr=@iot_int.csr format=pem_bundle ttl=17520h > root_ca.pem

# Set the signed intermediate in Vault
vault write pki_iot/intermediate/set-signed certificate=@iot_int_signed.pem

Configure URLs + a role for devices

# Publish issuing + CRL URLs (Nginx will fetch CRL periodically)
vault write pki_iot/config/urls \
  issuing_certificates="https://vault.example.local/v1/pki_iot/ca" \
  crl_distribution_points="https://vault.example.local/v1/pki_iot/crl"

# Role that limits cert subjects to your device GUIDs
vault write pki_iot/roles/device \
  allow_any_name=true  \
  allowed_uri_sans="urn:device:*" \
  allowed_other_sans="" \
  require_cn=false \
  cn_validations= \
  allow_ip_sans=false \
  allowed_domains="" \
  enforce_hostnames=false \
  key_type=ec \
  key_bits=256 \
  max_ttl=720h ttl=720h  # 30 days

Enrollment flow (device CSR → signed cert)

  • Device side

    • Generate keypair on the device (never export private key).
    • Create CSR with CN= and URI SAN urn:device:.
  • Server side

    • Backend verifies a one-time enrollment token (or pre-shared bootstrap secret), calls:
vault write pki_iot/sign/device \
  csr=@device.csr \
  uri_sans="urn:device:<GUID>" \
  ttl=720h > device_cert.pem

Revocation

  • Immediate kill: your backend stores bad serials; deny in app logic.
    • CRL: vault write pki_iot/revoke serial_number="<SERIAL>".
    • Fetch CRL periodically to a local file for Nginx:
curl -fsSL https://vault.example.local/v1/pki_iot/crl -o /etc/nginx/iot.crl
nginx -s reload
  • Automate via systemd timer/cron (e.g., every 1015 min).

  • Nginx fix for DER CRL format

# sed -i 's/\r$//' ./nginx/nginx_ssl/int_iot.crl
curl http://vault.local:8200/v1/pki_iot/crl -o nginx/nginx_ssl/int_iot.crl
openssl crl -in ./nginx/nginx_ssl/int_iot.crl -out ./nginx/nginx_ssl/int_iot.crl.clean -outform PEM
mv ./nginx/nginx_ssl/int_iot.crl.clean ./nginx/nginx_ssl/int_iot.crl
curl http://vault.local:8200/v1/pki_root/crl -o nginx/nginx_ssl/root_iot.crl
openssl crl -in ./nginx/nginx_ssl/root_iot.crl -out ./nginx/nginx_ssl/root_iot.crl.clean -outform PEM
mv ./nginx/nginx_ssl/root_iot.crl.clean ./nginx/nginx_ssl/root_iot.crl
(sed 's/\r$//' ./nginx/nginx_ssl/int_iot.crl; echo; sed 's/\r$//' ./nginx/nginx_ssl/root_iot.crl; echo) \
> ./nginx/nginx_ssl/iot.crl
Description
No description provided
Readme 829 KiB
Languages
Vue 66.2%
Go 22%
TypeScript 7.1%
Shell 2%
CSS 1.9%
Other 0.8%