Introduction
Every software project requires secrets—database passwords, API keys, encryption tokens, and other sensitive credentials. Managing these secrets securely is critical for both security and compliance, yet many small teams struggle with this challenge. The common practice of storing secrets in configuration files or environment variables creates significant risk: secrets can accidentally end up in version control, logs, or error reports.
Open source secret management tools provide a solution that was once only available to enterprises with large security budgets. These tools centralize secret storage, provide access controls, enable auditing, and automate credential rotation. For small teams, implementing proper secret management is not just a security best practice—it’s essential for protecting your users, your reputation, and your business.
In this guide, we’ll explore HashiCorp Vault, the leading open source secret management solution, along with alternative approaches and implementation strategies suitable for small teams. We’ll cover everything from initial setup to operational best practices, enabling you to implement enterprise-grade secret management without enterprise-level costs.
Understanding Secret Management Challenges
Before diving into specific tools, understanding the challenges helps frame why secret management matters and what solutions must address.
The Problem with Traditional Approaches
Most teams start by storing secrets in environment variables, configuration files, or even directly in code. These approaches create several problems. Environment variables can be accidentally logged or exposed in process listings. Configuration files often get committed to version control despite good intentions. When team members leave or rotate, changing shared credentials becomes a manual, error-prone process.
The consequences of poor secret management are severe. Accidental commits of secrets to GitHub happen regularly—attackers actively scan public repositories for exposed credentials. Once exposed, credentials must be rotated immediately, which is time-consuming when they’re embedded throughout your infrastructure.
What Secret Management Provides
A proper secret management solution addresses these challenges through centralization, encryption, access control, and auditing. Centralization means all secrets live in one secure location rather than scattered across files and environment variables. Encryption ensures secrets are never stored or transmitted in plain text. Access controls restrict who can read or modify secrets, following the principle of least privilege. Audit logs record every access to secrets, providing visibility into who accessed what and when.
Modern secret management also enables dynamic secrets—credentials generated on-demand that automatically expire. This approach eliminates the need to store long-lived credentials, significantly reducing the impact of any potential credential compromise.
HashiCorp Vault: The Industry Standard
HashiCorp Vault has become the de facto standard for secret management in the cloud-native era. Originally developed by HashiCorp and now used by organizations of all sizes, Vault provides comprehensive secret management with support for multiple secret types, strong encryption, fine-grained access controls, and extensive integrations.
Core Concepts
Vault operates using a unique architecture where secrets are stored in “secrets engines” and accessed through “auth methods.” Understanding these concepts is essential for effective Vault usage.
Secrets engines are pluggable backends that store, generate, or encrypt data. Vault ships with engines for key-value pairs, database credentials, PKI certificates, AWS credentials, and many other secret types. You can enable multiple engines simultaneously, each serving different purposes.
Auth methods determine how applications and users authenticate to Vault. Common methods include Kubernetes auth (for pods), GitHub auth (for developers), and AppRole (for automated systems). Each method maps authenticating entities to Vault policies that define their access permissions.
The storage backend persists all Vault data. For production deployments, this should be a highly available solution like Consul, etcd, or cloud storage backends. The storage backend is separate from the secrets engines—changing your storage solution doesn’t affect your secrets.
Getting Started with Vault
For small teams and development environments, running Vault in dev mode provides an easy entry point:
# Start Vault in development mode
vault server -dev
# Export the development token
export VAULT_TOKEN="root"
export VAULT_ADDR="http://127.0.0.1:8200"
# Verify it's running
vault status
For production or even staging environments, you should run Vault properly, typically in a container or on a dedicated server:
version: '3.8'
services:
vault:
image: vault:1.15
ports:
- "8200:8200"
environment:
- VAULT_ADDR=http://0.0.0.0:8200
volumes:
- vault_data:/vault/file
cap_add:
- IPC_LOCK
command: server -config=/vault/config.hcl
A minimal production configuration uses the file backend:
storage "file" {
path = "/vault/file"
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = "true"
}
disable_mlock = true
Managing Secrets with the KV Engine
The KV (Key-Value) secrets engine provides the simplest way to store and retrieve secrets. Enable it and store your first secrets:
# Enable the KV v2 secrets engine
vault secrets enable -path=secret kv-v2
# Store a secret
vault kv put secret/myapp database password=supersecret api_key=your-api-key
# Retrieve a secret
vault kv get secret/myapp
# Retrieve a specific field
vault kv get -field=password secret/myapp
Versioning is built into KV v2—you can retrieve previous versions of secrets if needed:
# List secret versions
vault kv list secret/myapp/
# Get previous version
vault kv get -version=1 secret/myapp
Access Control with Policies
Vault policies define what authenticated entities can do. Policies use HCL (HashiCorp Configuration Language) to specify permissions:
# Read access to all secrets under myapp/
path "secret/data/myapp/*" {
capabilities = ["read"]
}
# List access to myapp secrets
path "secret/metadata/myapp" {
capabilities = ["list"]
}
# Deny everything else
path "secret/*" {
capabilities = ["deny"]
}
Apply policies to tokens or auth methods:
# Create a policy
vault policy write myapp-policy myapp-policy.hcl
# Create a token with this policy
vault token create -policy=myapp-policy
Dynamic Secrets
One of Vault’s most powerful features is dynamic secrets—credentials generated on-demand that automatically expire. This approach eliminates long-lived credentials that could be compromised.
For database credentials:
# Enable the database secrets engine
vault secrets enable database
# Configure database connection
vault write database/config/myapp \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@localhost:5432/myapp" \
username="vaultuser" \
password="vaultpassword" \
allowed_roles="myapp-role"
# Define a role that creates credentials with 1-hour TTL
vault write database/roles/myapp-role \
db_name=myapp \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';" \
default_ttl="1h" \
max_ttl="24h"
# Generate dynamic credentials on-demand
vault read database/creds/myapp-role
Applications request credentials when they start, use them for their lifetime, and never need to store or rotate them—the credentials expire automatically.
Kubernetes Integration
For teams running Kubernetes, the Vault Agent Injector provides seamless secret injection into pods. Install the injector via Helm:
helm repo add hashicorp https://helm.releases.hashicorp.com
helm install vault hashicorp/vault \
--set "injector.enabled=true" \
--set "server.dev.enabled=true"
Then annotate pods to receive secrets:
apiVersion: v1
kind: Pod
metadata:
name: myapp
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "myapp"
vault.hashicorp.com/agent-inject-secret-config: "secret/data/myapp"
spec:
containers:
- name: myapp
image: myapp:latest
The injector automatically retrieves secrets from Vault and mounts them as files in the container. Your application reads secrets from files rather than environment variables, avoiding accidental exposure.
Alternative Secret Management Solutions
While HashiCorp Vault is the most comprehensive solution, other options may suit specific use cases better, particularly for smaller teams or simpler requirements.
Mozilla SOPS
Mozilla SOPS (Secrets OPerationS) provides a different approach—encrypting secrets directly in configuration files. Rather than centralizing secrets in a server, SOPS lets you keep secrets in YAML or JSON files while encrypting the sensitive values:
# Install SOPS
brew install sops
# Create an encrypted configuration file
sops myapp-config.yaml
The encrypted file looks like:
database:
password: ENC[AES256_GCM,data:xxx,iv:xxx,...]
username: admin
api_key: ENC[AES256_GCM,data:yyy,iv:yyy,...]
SOPS works with various key providers including age, GPG, and cloud KMS. For teams already storing configuration in Git, SOPS provides secret encryption without requiring a separate secrets server.
External Secrets Operator
The External Secrets Operator (ESO) integrates Kubernetes with external secret management systems including Vault, AWS Secrets Manager, Azure Key Vault, and GCP Secret Manager. If you’re already using a cloud provider’s secret management, ESO provides Kubernetes-native access:
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "http://vault:8200"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: kubernetes
role: "myapp"
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: myapp-secrets
spec:
refreshInterval: "1h"
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: myapp-secrets
creationPolicy: Owner
data:
- secretKey: database_password
remoteRef:
key: secret/data/myapp
property: password
This approach creates Kubernetes Secrets from external sources, letting applications use familiar Kubernetes secret mechanisms while leveraging centralized secret management.
Sealed Secrets
Bitnami Sealed Secrets encrypts Kubernetes secrets so they can be stored safely in Git. The Sealed Secrets controller running in your cluster decrypts them:
# Install kubeseal CLI
brew install kubeseal
# Create a sealed secret from a regular secret
kubeseal --format yaml < myapp-secret.yaml > sealed-secret.yaml
The sealed secret can be committed to Git and deployed normally—only the Sealed Secrets controller in your cluster can decrypt it.
Secret Management Best Practices
Implementing secret management requires more than installing software—it requires establishing practices that keep your secrets secure day-to-day.
Principle of Least Privilege
Grant access to secrets only when necessary. Create specific policies for each application rather than using broad permissions. Review access regularly and remove permissions when no longer needed.
# Good: Specific path access
path "secret/data/production/database" {
capabilities = ["read"]
}
# Avoid: Broad path access
path "secret/production/*" {
capabilities = ["read"]
}
Secret Rotation
Rotate secrets regularly and especially when team members leave. For static secrets, establish a rotation schedule. For dynamic secrets, use short TTLs to automatically rotate credentials. Vault’s database and cloud provider engines handle this automatically—configure appropriate TTL values based on your security requirements.
Audit Everything
Enable audit logging to track all secret access:
# Enable audit logging to file
vault audit enable file file_path=/var/log/vault_audit.log
Regularly review audit logs for unauthorized access attempts. Set up alerts for unusual access patterns.
Never Commit Secrets
Use .gitignore or git pre-commit hooks to prevent accidental secret commits:
# .gitignore
secrets.yaml
*.credentials
.env
Consider using tools like git-secrets or Talisman to scan commits for potential secrets before they’re pushed.
Use Namespaces for Multi-Tenancy
If multiple applications or teams share Vault, use namespaces to isolate secrets:
# Create a namespace for each team
vault namespace create team-a
vault namespace create team-b
Each namespace has its own secrets engines and policies, providing isolation without running separate Vault instances.
Cost Considerations
One of Vault’s advantages is that the open source version includes most features small teams need. The open source version provides all core functionality including the KV secrets engine, multiple auth methods, policies, and audit logging.
The enterprise version adds features like HSM support, sentinel policies, and replication that larger organizations may need. For most small teams, the open source version suffices.
The main costs are infrastructure: running Vault requires a server (which can be a small VM or container) and backup storage. For small deployments, these costs are minimal—often under $50 per month.
Conclusion
Secret management is essential for any team handling sensitive data, and open source tools make enterprise-grade security accessible to teams with limited budgets. HashiCorp Vault provides comprehensive functionality that scales from simple use cases to complex enterprise deployments.
Start with the KV secrets engine for straightforward secret storage. Add dynamic secrets for databases and cloud providers as your needs evolve. Integrate with Kubernetes for seamless operation in containerized environments.
Remember that secret management is as much about processes as tools. Establish practices for rotation, access control, and auditing. Review your configuration regularly to ensure it matches your evolving security requirements.
The effort invested in proper secret management pays dividends in reduced risk, easier compliance, and the confidence that comes from knowing your credentials are protected.
Resources
- HashiCorp Vault Documentation
- Vault Kubernetes Auth Method
- External Secrets Operator
- Mozilla SOPS
- Bitnami Sealed Secrets
- Vault Best Practices
Comments