15 KiB
Vaultwarden API Proxy
A secure, lightweight Go API that acts as a proxy between your local apps and Vaultwarden (self-hosted Bitwarden server). Use it as a replacement for .env files in development and production environments.
Features
- Secure: API key authentication, IP whitelisting, constant-time comparison, no sensitive logging
- Lightweight: ~15MB Docker image, minimal resource usage
- Fast: Built-in caching with configurable TTL
- Simple: Clean architecture, easy to deploy and maintain
- Production-ready: Rate limiting, health checks, graceful shutdown, security headers
- IP Whitelisting: Restrict access by IP/CIDR with optional GitHub Actions support
Architecture
┌─────────────┐ HTTPS + API Key ┌──────────────────┐
│ Your App │ ───────────────────────> │ Vaultwarden API │
└─────────────┘ │ Proxy │
└──────────────────┘
│
│ Vaultwarden API
▼
┌──────────────────┐
│ Vaultwarden │
│ Server │
└──────────────────┘
Project Structure
.
├── cmd/
│ └── api/
│ └── main.go # Application entry point
├── internal/
│ ├── auth/
│ │ └── middleware.go # API key authentication
│ ├── config/
│ │ └── config.go # Configuration management
│ ├── handlers/
│ │ └── handlers.go # HTTP handlers
│ ├── ipwhitelist/
│ │ └── ipwhitelist.go # IP-based access control
│ ├── validators/
│ │ └── validators.go # Input validation
│ └── vaultwarden/
│ ├── client.go # Vaultwarden client
│ ├── client_cli.go # CLI-based secret retrieval
│ └── init.go # Bitwarden CLI initialization
├── pkg/
│ └── logger/
│ └── logger.go # Secure logging
├── .env.example # Environment variables template
├── .gitignore # Git ignore rules
├── Dockerfile # Multi-stage Docker build
├── docker-compose.yml # Docker Compose configuration
├── go.mod # Go module definition
├── go.sum # Go module checksums
├── Makefile # Build automation
└── README.md # This file
Quick Start
Prerequisites
- Go 1.25+ (for local development)
- Docker & Docker Compose (for containerized deployment)
- Vaultwarden instance with API access
Local Development
-
Clone the repository
git clone https://github.com/turbootzz/vaultwarden-api.git cd vaultwarden-api -
Create environment file
cp .env.example .env -
Configure your
.envfile# Generate a strong API key make generate-api-key # Edit .env and add: # - API_KEY (generated above) # - VAULTWARDEN_URL (your Vaultwarden instance URL) # - VAULTWARDEN_CLIENT_ID, VAULTWARDEN_CLIENT_SECRET, VAULTWARDEN_PASSWORD # (from Vaultwarden Account Settings → Security → Keys → View API Key) # - ALLOWED_IPS (your IP address or CIDR range for security) -
Run the application
make run
Docker Deployment
-
Build the Docker image
make docker-build -
Run with Docker Compose
make docker-compose-up
API Endpoints
Health Check
curl http://localhost:8080/health
Response:
{
"status": "ok",
"service": "vaultwarden-api"
}
Get Secret
curl -H "Authorization: Bearer YOUR_API_KEY" \
http://localhost:8080/secret/DATABASE_URL
Response:
{
"name": "DATABASE_URL",
"value": "postgresql://user:pass@localhost:5432/db"
}
Refresh Cache
curl -X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
http://localhost:8080/refresh
Response:
{
"status": "ok",
"message": "cache cleared successfully"
}
Configuration
All configuration is done via environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
API_PORT |
No | 8080 |
Port the API listens on |
ENVIRONMENT |
No | development |
Environment (development/production) |
API_KEY |
Yes | - | API key for authentication (min 32 chars) |
VAULTWARDEN_URL |
Yes | - | Vaultwarden instance URL |
VAULTWARDEN_CLIENT_ID |
Yes* | - | Vaultwarden API client ID |
VAULTWARDEN_CLIENT_SECRET |
Yes* | - | Vaultwarden API client secret |
VAULTWARDEN_PASSWORD |
Yes* | - | Your Vaultwarden master password |
VAULTWARDEN_ACCESS_TOKEN |
Yes* | - | Alternative: pre-generated session token |
ALLOWED_IPS |
No | - | Comma-separated IPs/CIDRs to whitelist |
ENABLE_GITHUB_IP_RANGES |
No | false |
Allow GitHub Actions IPs (updates every 24h) |
TRUSTED_PROXY_IP |
No | localhost |
Trusted reverse proxy IPs/CIDRs |
CORS_ALLOWED_ORIGINS |
No | localhost:3000 |
CORS allowed origins |
CACHE_TTL |
No | 5m |
Cache duration (0 to disable) |
READ_TIMEOUT |
No | 10s |
HTTP read timeout |
WRITE_TIMEOUT |
No | 10s |
HTTP write timeout |
DEBUG |
No | false |
Enable debug logging |
*Note: Either use VAULTWARDEN_CLIENT_ID + VAULTWARDEN_CLIENT_SECRET + VAULTWARDEN_PASSWORD (recommended) OR VAULTWARDEN_ACCESS_TOKEN (will expire).
Getting Vaultwarden API Credentials
Recommended Method: API Key Authentication
-
Get API Credentials from Vaultwarden Web UI:
- Log into your Vaultwarden instance
- Go to Account Settings → Security → Keys
- Click View API Key
- You'll see:
client_id(starts withuser.)client_secret(long random string)
- Add these to your
.envasVAULTWARDEN_CLIENT_IDandVAULTWARDEN_CLIENT_SECRET - Add your master password as
VAULTWARDEN_PASSWORD
-
Benefits:
- ✅ Bypasses 2FA
- ✅ Automatically logs in on startup
- ✅ Never expires
- ✅ More secure than session tokens
Alternative Method: Session Token
-
Generate a session token manually:
# Login and unlock your vault bw config server https://vault.yourdomain.com bw login SESSION_TOKEN=$(bw unlock --raw) echo $SESSION_TOKEN -
Add to
.env:VAULTWARDEN_ACCESS_TOKEN=your-session-token-here -
Limitations:
- ⚠️ Tokens expire after inactivity
- ⚠️ Requires manual regeneration
- ⚠️ Not recommended for production
IP Whitelisting
The API supports IP-based access control for enhanced security.
Configuration
Allow specific IPs:
ALLOWED_IPS=203.0.113.50
Allow multiple IPs:
ALLOWED_IPS=203.0.113.50,198.51.100.25
Allow CIDR ranges:
ALLOWED_IPS=192.168.1.0/24
Mix IPs and CIDRs:
ALLOWED_IPS=203.0.113.50,192.168.1.0/24,10.0.0.0/8
Enable GitHub Actions (optional):
ENABLE_GITHUB_IP_RANGES=true
This automatically whitelists all GitHub Actions IP ranges and updates them every 24 hours.
Trusted Proxies
If you're running behind a reverse proxy (Nginx Proxy Manager, Traefik, Cloudflare), configure trusted proxies:
Single proxy IP:
TRUSTED_PROXY_IP=172.18.0.5
Docker network range:
TRUSTED_PROXY_IP=172.16.0.0/12
Multiple proxies:
TRUSTED_PROXY_IP=172.18.0.5,192.168.1.10
Important Notes:
- Invalid IPs/CIDRs are logged and ignored
- Duplicates are automatically removed
- The API extracts real client IPs from
X-Forwarded-Forheader - If no whitelist is configured, all IPs are allowed (not recommended for production)
Deployment Guide
Deploy to Portainer
-
Prepare your environment
- Ensure Vaultwarden is accessible from the Portainer host
- Have your API credentials ready
-
Create a new Stack in Portainer
- Navigate to Stacks → Add Stack
- Name it
vaultwarden-api - Paste the contents of
docker-compose.yml
-
Set environment variables in Portainer
- Click "Add environment variable"
- Add all required variables (see Configuration table)
- RECOMMENDED: Add
ALLOWED_IPSwith your IP address - IMPORTANT: Never hardcode secrets in the stack file
-
Deploy the stack
- Click "Deploy the stack"
- Monitor logs for any errors
-
Set up reverse proxy
- Use Nginx Proxy Manager, Traefik, or similar
- Configure HTTPS with Let's Encrypt
- Point to
vaultwarden-api:8080
Deploy Behind Nginx Proxy Manager
-
Add a new Proxy Host
- Domain:
api.yourdomain.com - Forward to:
vaultwarden-api:8080 - Enable "Block Common Exploits"
- Enable "Websockets Support"
- Domain:
-
Configure SSL
- Request a new SSL certificate (Let's Encrypt)
- Force SSL
- Enable HTTP/2
-
Add custom headers (optional)
X-Real-IP $remote_addr X-Forwarded-For $proxy_add_x_forwarded_for X-Forwarded-Proto $scheme
Security Best Practices
For Public Repositories
- ✅ Use
.env.example(committed) for documentation - ✅ Add
.envto.gitignore(already done) - ✅ Never commit actual credentials
- ✅ Use Portainer's environment variable feature
- ✅ Rotate API keys regularly
- ✅ Use strong, randomly generated API keys
For Private Repositories
- ✅ Still use
.env.examplefor documentation - ✅ Still never commit
.envfiles - ✅ Use secret managers (e.g., Docker secrets, Vault)
- ✅ Implement additional authentication if needed
- ✅ Monitor access logs
Production Deployment
- Always use HTTPS via reverse proxy
- Enable IP whitelisting - Set
ALLOWED_IPSto your trusted IPs/networks - Configure trusted proxies - Set
TRUSTED_PROXY_IPto your reverse proxy range - Restrict CORS to specific domains via
CORS_ALLOWED_ORIGINS - Set ENVIRONMENT=production to hide detailed error messages
- Use API key authentication (not session tokens) for reliability
- Enable rate limiting (already configured: 30 req/min)
- Use read-only filesystem (already in docker-compose)
- Run as non-root user (already in Dockerfile)
- Monitor logs for suspicious activity and blocked IPs
- Use Docker secrets instead of environment variables when possible
- Regular security updates (rebuild images monthly)
- Enable DEBUG=false in production (default)
Usage Examples
From a Shell Script
#!/bin/bash
API_KEY="your-api-key"
API_URL="https://api.yourdomain.com"
# Fetch database URL
DB_URL=$(curl -s -H "Authorization: Bearer $API_KEY" \
"$API_URL/secret/DATABASE_URL" | jq -r '.value')
echo "Database URL: $DB_URL"
From Python
import requests
API_KEY = "your-api-key"
API_URL = "https://api.yourdomain.com"
def get_secret(name):
headers = {"Authorization": f"Bearer {API_KEY}"}
response = requests.get(f"{API_URL}/secret/{name}", headers=headers)
return response.json()["value"]
# Usage
db_url = get_secret("DATABASE_URL")
print(f"Database URL: {db_url}")
From Node.js
const axios = require('axios');
const API_KEY = 'your-api-key';
const API_URL = 'https://api.yourdomain.com';
async function getSecret(name) {
const response = await axios.get(`${API_URL}/secret/${name}`, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
return response.data.value;
}
// Usage
(async () => {
const dbUrl = await getSecret('DATABASE_URL');
console.log(`Database URL: ${dbUrl}`);
})();
Makefile Commands
make help # Show available commands
make build # Build the binary
make run # Run locally (requires .env)
make dev # Run with hot reload
make test # Run tests
make clean # Clean build artifacts
make docker-build # Build Docker image
make docker-run # Run Docker container
make docker-push # Push to Docker Hub
make generate-api-key # Generate a random API key
Troubleshooting
"Failed to load configuration: API_KEY is required"
- Make sure you have a
.envfile in the project root - Copy
.env.exampleto.envand fill in all required values
"Secret not found"
- Verify the secret name matches exactly (case-sensitive)
- Check that your Vaultwarden token has access to the vault
- Use the Vaultwarden web UI to confirm the secret exists
"Vaultwarden api returned status 401"
- Your credentials are invalid or expired
- For API key method: Check
VAULTWARDEN_CLIENT_ID,VAULTWARDEN_CLIENT_SECRET,VAULTWARDEN_PASSWORD - For session token: Generate a new
VAULTWARDEN_ACCESS_TOKEN
"IP blocked (not whitelisted)"
- Your IP is not in the
ALLOWED_IPSlist - Check your current public IP:
curl ifconfig.me - Add your IP to
ALLOWED_IPSin.envor Portainer - If behind a proxy, ensure
TRUSTED_PROXY_IPis set correctly - Enable
DEBUG=trueto see which IP is being detected
"Failed to initialize Bitwarden CLI"
- Ensure Bitwarden CLI is installed in the Docker container (already done)
- Check that
VAULTWARDEN_URLis accessible from the container - Verify your master password is correct
- Check logs for specific error messages
Docker build fails
- Ensure you have the latest Go version in Dockerfile
- Run
go mod tidybefore building - Check for network issues when downloading dependencies
Recent Enhancements
- ✅ IP Whitelisting - CIDR support, GitHub Actions IPs, automatic validation
- ✅ CLI Authentication - Bitwarden CLI integration with API key support
- ✅ Trusted Proxies - Proper client IP detection behind reverse proxies
- ✅ Input Validation - Secure secret name validation
- ✅ Debug Logging - Optional debug mode for troubleshooting
Future Enhancements
- Support for multiple Vaultwarden instances
- JWT-based authentication (in addition to API keys)
- Token rotation mechanism
- Metrics and monitoring (Prometheus)
- Support for secret versioning
- Webhook notifications for secret changes
- Redis-based distributed cache
- Rate limiting per API key
- Audit logging
- GraphQL API
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
License
MIT License - see LICENSE file for details
Disclaimer
This is a security-sensitive tool. Please:
- Review the code before deploying to production
- Keep your API keys and tokens secure
- Monitor access logs regularly
- Report security issues privately
Made by Thijs Herman