HashiCorp Vault on FreeBSD: Secrets Management Review
HashiCorp Vault is a secrets management tool that centralizes the storage, access, and distribution of sensitive data -- API keys, database credentials, TLS certificates, encryption keys, and any other secret your infrastructure needs. Instead of scattering secrets across configuration files, environment variables, and ad-hoc password managers, Vault provides a single API-driven system with fine-grained access control, audit logging, and automatic secret rotation. On FreeBSD, Vault runs as a native binary with no runtime dependencies, integrates with the rc.d service framework, and works well in jail-based architectures. This review covers installation, development and production deployment modes, secrets engines, authentication methods, PKI certificate management, policy configuration, and operational considerations.
What Vault Solves
The problem Vault addresses is secret sprawl. In a typical infrastructure:
- Database passwords are stored in application configuration files, sometimes in plaintext.
- API keys are committed to version control or passed through environment variables.
- TLS certificates are manually generated and distributed, with no central tracking of expiration.
- SSH keys are distributed to servers with no revocation mechanism.
- Different teams manage secrets in different ways with no audit trail.
Vault centralizes all of this. Applications request secrets from Vault at runtime via an HTTP API. Vault authenticates the requester, checks access policies, logs the access, and returns the secret. Secrets can be static (stored values) or dynamic (generated on demand with automatic expiration).
Installation on FreeBSD
Binary Package
shpkg install vault
This installs the Vault binary at /usr/local/bin/vault and the rc.d script at /usr/local/etc/rc.d/vault.
Manual Binary Installation
For the latest version not yet in the ports tree:
shfetch https://releases.hashicorp.com/vault/1.17.0/vault_1.17.0_freebsd_amd64.zip unzip vault_1.17.0_freebsd_amd64.zip -d /usr/local/bin/ chmod +x /usr/local/bin/vault
Verify Installation
shvault version vault -autocomplete-install
The autocomplete install adds shell completions for vault commands -- useful for interactive use.
Development Mode
Dev mode runs Vault entirely in memory with no configuration file. It is designed for local development and testing, not production.
Start Vault in dev mode:
shvault server -dev
Dev mode outputs a root token and sets up an in-memory storage backend. In a second terminal:
shexport VAULT_ADDR='http://127.0.0.1:8200' export VAULT_TOKEN='root-token-from-output' vault status vault secrets list
Dev mode characteristics:
- All data is in memory -- nothing survives a restart.
- TLS is disabled -- communication is over plain HTTP.
- The root token is printed to stdout.
- The KV secrets engine is mounted at
secret/by default. - Auto-unsealing is enabled -- no unseal keys needed.
Use dev mode for learning, testing policies, and developing integrations. Never use dev mode for production.
Production Deployment
A production Vault deployment requires persistent storage, TLS, and proper initialization.
Configuration File
Create /usr/local/etc/vault/vault.hcl:
shstorage "file" { path = "/var/db/vault/data" } listener "tcp" { address = "127.0.0.1:8200" tls_cert_file = "/usr/local/etc/vault/tls/vault.crt" tls_key_file = "/usr/local/etc/vault/tls/vault.key" } api_addr = "https://127.0.0.1:8200" cluster_addr = "https://127.0.0.1:8201" ui = true disable_mlock = false log_level = "info"
Create required directories:
shmkdir -p /var/db/vault/data mkdir -p /usr/local/etc/vault/tls chown -R vault:vault /var/db/vault chown -R vault:vault /usr/local/etc/vault
TLS Certificates
Generate a self-signed certificate for initial setup (replace with a proper certificate for production):
shopenssl req -x509 -newkey rsa:4096 -sha256 -days 365 -nodes \ -keyout /usr/local/etc/vault/tls/vault.key \ -out /usr/local/etc/vault/tls/vault.crt \ -subj "/CN=vault.example.com" \ -addext "subjectAltName=DNS:vault.example.com,IP:127.0.0.1" chmod 600 /usr/local/etc/vault/tls/vault.key chown vault:vault /usr/local/etc/vault/tls/*
Storage Backends
The file storage backend works for single-node deployments. For high availability:
- Raft (integrated storage) -- Vault's built-in consensus storage. No external dependencies. Recommended for most deployments.
- Consul -- HashiCorp's service mesh and KV store. Adds complexity but provides service discovery integration.
- PostgreSQL -- database-backed storage. Works well on FreeBSD where PostgreSQL is a first-class citizen.
Raft storage configuration:
shstorage "raft" { path = "/var/db/vault/raft" node_id = "vault-1" }
Start and Initialize
Enable and start Vault:
shsysrc vault_enable="YES" service vault start
Initialize Vault:
shexport VAULT_ADDR='https://127.0.0.1:8200' export VAULT_CACERT='/usr/local/etc/vault/tls/vault.crt' vault operator init -key-shares=5 -key-threshold=3
This produces 5 unseal keys and a root token. Store the unseal keys securely -- losing them means losing access to Vault permanently. The -key-threshold=3 means any 3 of the 5 keys are needed to unseal Vault.
Unseal Vault (repeat with 3 different keys):
shvault operator unseal <key-1> vault operator unseal <key-2> vault operator unseal <key-3>
Verify status:
shvault status
The output should show Sealed: false after successful unsealing.
Secrets Engines
Secrets engines are plugins that store, generate, or encrypt data. Each engine is mounted at a path and provides a set of API endpoints.
KV (Key-Value) Secrets
The simplest secrets engine -- stores and retrieves static secrets.
Enable KV v2 (versioned):
shvault secrets enable -path=kv kv-v2
Store a secret:
shvault kv put kv/database/production username="dbadmin" password="s3cret-p@ss"
Retrieve a secret:
shvault kv get kv/database/production vault kv get -field=password kv/database/production
KV v2 maintains version history:
shvault kv get -version=1 kv/database/production vault kv metadata get kv/database/production
Database Secrets Engine
Generates dynamic database credentials that expire automatically. Applications request credentials from Vault, use them, and Vault revokes them after the TTL expires.
Enable the database engine:
shvault secrets enable database
Configure a PostgreSQL connection:
shvault write database/config/production \ plugin_name=postgresql-database-plugin \ connection_url="postgresql://{{username}}:{{password}}@localhost:5432/myapp?sslmode=disable" \ allowed_roles="readonly,readwrite" \ username="vault_admin" \ password="vault_admin_password"
Create a role that defines what credentials are generated:
shvault write database/roles/readonly \ db_name=production \ creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \ default_ttl="1h" \ max_ttl="24h"
Generate credentials:
shvault read database/creds/readonly
This returns a temporary username and password that expire after 1 hour. The application uses these credentials, and Vault automatically drops the database role when the TTL expires.
Transit Secrets Engine
Encryption as a service -- applications send plaintext to Vault, which returns ciphertext. The encryption keys never leave Vault.
Enable the transit engine:
shvault secrets enable transit vault write -f transit/keys/myapp-encryption
Encrypt data:
shvault write transit/encrypt/myapp-encryption plaintext=$(echo -n "sensitive data" | base64)
Decrypt data:
shvault write transit/decrypt/myapp-encryption ciphertext="vault:v1:..."
This is useful for applications that need to encrypt data at rest without managing encryption keys themselves.
PKI Secrets Engine
Vault can act as a certificate authority, issuing TLS certificates on demand. This eliminates manual certificate management and integrates certificate issuance into your automated infrastructure.
Set Up a Root CA
shvault secrets enable pki vault secrets tune -max-lease-ttl=87600h pki vault write -field=certificate pki/root/generate/internal \ common_name="Example Root CA" \ issuer_name="root-ca" \ ttl=87600h > /tmp/root_ca.crt vault write pki/config/urls \ issuing_certificates="https://vault.example.com:8200/v1/pki/ca" \ crl_distribution_points="https://vault.example.com:8200/v1/pki/crl"
Set Up an Intermediate CA
shvault secrets enable -path=pki_int pki vault secrets tune -max-lease-ttl=43800h pki_int vault write -format=json pki_int/intermediate/generate/internal \ common_name="Example Intermediate CA" \ issuer_name="intermediate-ca" \ | jq -r '.data.csr' > /tmp/intermediate.csr vault write -format=json pki/root/sign-intermediate \ csr=@/tmp/intermediate.csr \ format=pem_bundle \ ttl=43800h \ | jq -r '.data.certificate' > /tmp/intermediate.crt vault write pki_int/intermediate/set-signed certificate=@/tmp/intermediate.crt
Create a Role and Issue Certificates
shvault write pki_int/roles/example-dot-com \ allowed_domains="example.com" \ allow_subdomains=true \ max_ttl="720h" vault write pki_int/issue/example-dot-com \ common_name="app.example.com" \ ttl="72h"
This issues a TLS certificate valid for 72 hours. Applications can request certificates programmatically, and Vault handles issuance, renewal tracking, and revocation.
Authentication Methods
Vault supports multiple authentication methods for different client types.
Token Authentication
The default method. Tokens are created by authenticated users or by Vault itself:
shvault token create -policy="readonly" -ttl="1h"
AppRole
Designed for machine-to-machine authentication. An application receives a role ID (public) and a secret ID (private) to authenticate:
shvault auth enable approle vault write auth/approle/role/myapp \ token_policies="myapp-policy" \ token_ttl=1h \ secret_id_ttl=720h vault read auth/approle/role/myapp/role-id vault write -f auth/approle/role/myapp/secret-id
Applications authenticate by providing both IDs:
shvault write auth/approle/login \ role_id="role-id-value" \ secret_id="secret-id-value"
LDAP
For user authentication against an LDAP directory:
shvault auth enable ldap vault write auth/ldap/config \ url="ldaps://ldap.example.com" \ binddn="cn=vault,dc=example,dc=com" \ bindpass="bind-password" \ userdn="ou=users,dc=example,dc=com" \ userattr="uid" \ groupdn="ou=groups,dc=example,dc=com" \ groupattr="cn"
Policies
Policies define what secrets a token can access. They use HCL (HashiCorp Configuration Language) syntax.
Create a policy file /usr/local/etc/vault/policies/myapp.hcl:
sh# Read database credentials path "database/creds/readonly" { capabilities = ["read"] } # Read KV secrets for myapp path "kv/data/myapp/*" { capabilities = ["read", "list"] } # Issue TLS certificates path "pki_int/issue/example-dot-com" { capabilities = ["create", "update"] } # Deny access to everything else (implicit)
Apply the policy:
shvault policy write myapp /usr/local/etc/vault/policies/myapp.hcl
List and read policies:
shvault policy list vault policy read myapp
Audit Logging
Enable audit logging to track all Vault access:
shvault audit enable file file_path=/var/log/vault/audit.log
Every request and response is logged in JSON format, with sensitive values hashed (HMAC-SHA256). The audit log tells you who accessed what secret, when, and from which IP address.
Create the log directory:
shmkdir -p /var/log/vault chown vault:vault /var/log/vault
FreeBSD-Specific Considerations
Memory Locking
Vault uses mlock to prevent secrets from being swapped to disk. On FreeBSD, the vault user needs appropriate permissions:
sh# In /etc/sysctl.conf security.bsd.unprivileged_mlock=1
Or run Vault as root with disable_mlock = false in the configuration (less ideal but functional).
Jail Deployment
Vault runs well in a FreeBSD jail. Key considerations:
- Ensure the jail has network access to port 8200 (and 8201 for cluster communication).
- The vault data directory must be persistent across jail restarts.
- Memory locking must be allowed in the jail.
Backup
Back up the Vault data directory:
sh# For file storage backend zfs snapshot tank/vault@backup-$(date +%Y%m%d) # For Raft storage vault operator raft snapshot save /var/backups/vault/snapshot-$(date +%Y%m%d).snap
The Raft snapshot command creates a consistent point-in-time backup that can be restored to a new Vault cluster.
Verdict
HashiCorp Vault is the most capable secrets management tool available for FreeBSD infrastructure. It replaces ad-hoc secret distribution with a centralized, audited, policy-controlled system. The dynamic secrets capability -- generating database credentials, TLS certificates, and cloud access tokens on demand with automatic expiration -- fundamentally changes how applications handle sensitive data.
On FreeBSD, Vault runs cleanly as a single binary with no runtime dependencies. The file and Raft storage backends require no external services. The PKI secrets engine alone justifies deployment for organizations that manage more than a handful of TLS certificates.
The learning curve is real. Vault's concepts (sealing, policies, secrets engines, auth methods) require time to understand. But the operational payoff -- centralized audit logging, automatic credential rotation, and elimination of secret sprawl -- makes Vault worth the investment for any FreeBSD infrastructure beyond a single server.
Frequently Asked Questions
Can Vault run in a FreeBSD jail?
Yes. Vault runs in jails without modification. Ensure the jail allows memory locking (security.bsd.unprivileged_mlock=1 or equivalent jail parameter), has persistent storage for the data directory, and has network access on port 8200.
What happens if I lose the unseal keys?
You permanently lose access to the Vault data. The unseal keys are the only way to decrypt the master encryption key. Store them in physically separate, secure locations. Consider using Vault's auto-unseal feature with a cloud KMS or an HSM to avoid manual key management.
Should I use the file or Raft storage backend on FreeBSD?
For single-node deployments, the file backend is simpler. For high availability (multiple Vault nodes), use Raft -- it provides built-in consensus with no external dependencies. Raft is the recommended default for new deployments.
How do I rotate the root token?
Generate a new root token using the unseal keys: vault operator generate-root. After generating the new root token, revoke the old one: vault token revoke . In production, avoid using the root token for daily operations -- create specific tokens with limited policies instead.
Can Vault manage SSH access on FreeBSD?
Yes. The SSH secrets engine can sign SSH public keys with a CA certificate, providing certificate-based SSH authentication. This eliminates the need to distribute and manage authorized_keys files across servers. Configure the SSH CA, create roles, and have users request signed certificates from Vault before connecting.
How much memory does Vault need on FreeBSD?
Vault uses 50-200 MB of RAM for a typical deployment with the file or Raft backend. Memory usage increases with the number of concurrent connections and the size of the secrets engine data. For a small team (under 50 users/applications), 512 MB of RAM is sufficient.