# HashiCorp Vault Testing
This directory contains tools for testing fnox's HashiCorp Vault integration using a local Vault dev server.
## Quick Start
```bash
# 1. Start the local Vault dev server
source ./test/setup-vault-test.sh
# 2. Run the tests
mise run test:bats -- test/vault.bats
```
## What is HashiCorp Vault?
[HashiCorp Vault](https://www.vaultproject.io/) is a secrets management solution that provides secure storage and access control for secrets, encryption keys, and sensitive data. Vault's dev server mode is perfect for:
- Local development and testing
- Learning Vault features
- Integration testing without production infrastructure
The Vault CLI works seamlessly with both dev and production Vault servers.
## Files
- `docker-compose.vault.yml` - Docker Compose configuration for Vault dev server
- `setup-vault-test.sh` - Helper script to start server and configure environment
- `vault.bats` - Integration tests for Vault provider
## Manual Setup
If you prefer to set things up manually:
```bash
# Start Vault dev server
docker compose -f test/docker-compose.vault.yml up -d
# Export environment variables
export VAULT_ADDR="http://localhost:8200"
export VAULT_TOKEN="fnox-test-token"
# Verify connection
vault status
# Run tests
mise run test:bats -- test/vault.bats
```
## Dev Server Features
The Vault dev server:
- Runs in-memory (data is not persisted)
- Automatically unsealed and initialized
- KV v2 secrets engine mounted at `secret/`
- Root token: `fnox-test-token`
- Listens on: `http://localhost:8200`
**⚠️ WARNING:** Dev mode is for testing only. Never use dev mode in production!
## Vault Secrets Structure
Vault uses a Key-Value (KV) secrets engine. In KV v2:
```bash
# Create a secret with multiple fields
vault kv put secret/myapp \
password="secret123" \
username="admin" \
api_key="xyz789"
# Get specific field
vault kv get -field=password secret/myapp
# Output: secret123
# Get all fields as JSON
vault kv get -format=json secret/myapp
```
## fnox Configuration
### Basic Configuration
```toml
[providers.vault]
type = "vault"
address = "http://localhost:8200"
[secrets.DATABASE_PASSWORD]
provider = "vault"
value = "myapp" # Gets the "value" field by default
```
### With Specific Field
```toml
[secrets.DATABASE_USER]
provider = "vault"
value = "myapp/username" # Gets the "username" field
```
### With Custom Path Prefix
```toml
[providers.vault]
type = "vault"
address = "http://localhost:8200"
path = "secret/data/production" # Custom mount path
[secrets.API_KEY]
provider = "vault"
value = "service1" # Will read from secret/data/production/service1
```
### With Token in Config (Not Recommended)
```toml
[providers.vault]
type = "vault"
address = "http://localhost:8200"
token = "hvs.CAESIAabc123..." # Better to use environment variable
[secrets.MY_SECRET]
provider = "vault"
value = "myapp"
```
**Best Practice:** Use `VAULT_TOKEN` environment variable instead of storing token in config:
```bash
export VAULT_TOKEN=$(vault login -token-only -method=userpass username=myuser)
fnox get MY_SECRET
```
## Usage Examples
### Creating Secrets
```bash
# Create a secret with default field
vault kv put secret/database value="postgres://localhost/mydb"
# Create a secret with multiple fields
vault kv put secret/api \
token="abc123" \
endpoint="https://api.example.com" \
timeout="30s"
```
### Using with fnox
```bash
# Get secret
fnox get DATABASE_PASSWORD
# Use in command
fnox exec -- ./my-app
# Export to environment
eval "$(fnox export)"
echo $DATABASE_PASSWORD
```
## Cleanup
```bash
# Stop the server (data is lost anyway in dev mode)
docker compose -f test/docker-compose.vault.yml down
# View logs if needed
docker compose -f test/docker-compose.vault.yml logs
```
## CI/CD Integration
The project includes automated Vault testing in GitHub Actions using the official Vault dev server.
### How it works
On **all runners** (Linux and macOS with Docker):
1. GitHub Actions starts a Vault service container in dev mode
2. Environment variables are exported:
- `VAULT_ADDR=http://localhost:8200`
- `VAULT_TOKEN=fnox-test-token`
3. Tests run with full Vault integration
4. Each test creates/deletes its own secrets
### GitHub Actions Setup
The workflow includes:
```yaml
services:
vault:
image: hashicorp/vault:latest
env:
VAULT_DEV_ROOT_TOKEN_ID: fnox-test-token
VAULT_DEV_LISTEN_ADDRESS: 0.0.0.0:8200
ports:
- 8200:8200
options: >-
--cap-add=IPC_LOCK
steps:
- name: Export Vault environment
run: |
echo "VAULT_ADDR=http://localhost:8200" >> $GITHUB_ENV
echo "VAULT_TOKEN=fnox-test-token" >> $GITHUB_ENV
```
### Testing CI changes locally
You can simulate the CI environment locally:
```bash
# Start Vault
docker compose -f test/docker-compose.vault.yml up -d
# Export environment
source ./test/setup-vault-test.sh
# Run tests
mise run test:bats -- test/vault.bats
# Cleanup
docker compose -f test/docker-compose.vault.yml down
```
## Troubleshooting
### "VAULT_TOKEN not available"
Make sure you've:
1. Started Vault: `docker compose -f test/docker-compose.vault.yml up -d`
2. Exported environment: `source ./test/setup-vault-test.sh`
Or manually:
```bash
export VAULT_ADDR="http://localhost:8200"
export VAULT_TOKEN="fnox-test-token"
```
### "Cannot authenticate with Vault"
Check if Vault is running:
```bash
docker compose -f test/docker-compose.vault.yml ps
curl http://localhost:8200/v1/sys/health
```
### "vault CLI not installed"
Install via mise:
```bash
mise install vault
```
Or download from [releases](https://developer.hashicorp.com/vault/downloads):
```bash
# macOS
brew tap hashicorp/tap
brew install hashicorp/tap/vault
# Linux
wget https://releases.hashicorp.com/vault/1.15.0/vault_1.15.0_linux_amd64.zip
unzip vault_1.15.0_linux_amd64.zip
sudo mv vault /usr/local/bin/
```
### Docker not running
Make sure Docker Desktop is running:
```bash
docker info
```
## Production Usage
For production Vault instances:
```toml
[providers.vault]
type = "vault"
address = "https://vault.example.com"
[secrets.PROD_SECRET]
provider = "vault"
value = "production/database/password"
```
Authenticate using:
```bash
# Using token
export VAULT_TOKEN=$(vault login -token-only -method=userpass username=myuser)
# Or store encrypted token in fnox with age provider
fnox set VAULT_TOKEN "hvs.CAESIAabc..." --provider age
export VAULT_TOKEN=$(fnox get VAULT_TOKEN)
# Then use fnox
fnox get PROD_SECRET
```
## Comparison with Other Providers
| Self-hosted | ✅ Yes | ❌ No | ✅ Yes (vaultwarden) | ❌ No |
| Open source | ✅ Yes | ❌ No | ✅ Yes | ❌ No |
| Free tier | ✅ Yes | ❌ Limited | ✅ Yes | ✅ Yes |
| Enterprise features | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
| Dev mode | ✅ Yes | ❌ No | ✅ Yes | ❌ No |
| KV storage | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
| Dynamic secrets | ✅ Yes | ❌ No | ❌ No | ❌ No |
| Encryption as a Service | ✅ Yes | ❌ No | ❌ No | ✅ KMS |
## Security Best Practices
1. **Never store tokens in config files** - Use environment variables or fnox with age encryption
2. **Use short-lived tokens** - Enable token TTL and renewal
3. **Enable TLS in production** - Use HTTPS for Vault address
4. **Use least privilege** - Create tokens with minimal required policies
5. **Rotate tokens regularly** - Implement token rotation
6. **Enable audit logging** - Track all secret access
7. **Use namespaces** - Isolate different environments/teams
## References
- [Vault Documentation](https://developer.hashicorp.com/vault/docs)
- [Vault CLI Reference](https://developer.hashicorp.com/vault/docs/commands)
- [KV Secrets Engine](https://developer.hashicorp.com/vault/docs/secrets/kv)
- [Vault Dev Server](https://developer.hashicorp.com/vault/docs/concepts/dev-server)