Secrets Management
This guide covers best practices for managing sensitive configuration values like API keys, database credentials, and authentication secrets.
Overview
Section titled “Overview”Secrets that require protection:
| Secret | Component | Purpose |
|---|---|---|
LICENSE_MONITOR_API_KEY | Both | API authentication |
AUTH_OKTA_SECRET | Dashboard | OAuth client secret |
AUTH_SECRET | Dashboard | JWT signing key |
AUTH_OKTA_ID | Dashboard | OAuth client ID |
| Database credentials | Dashboard | Convex connection |
Secret Sources
Section titled “Secret Sources”Priority Order
Section titled “Priority Order”Secrets are loaded in this priority (highest to lowest):
- Secrets manager (HashiCorp Vault, AWS Secrets Manager)
- Environment variables
- Configuration files
- Default values (never use for secrets)
Environment Variables
Section titled “Environment Variables”Linux/macOS
Section titled “Linux/macOS”# Set secrets in shell profileexport LICENSE_MONITOR_API_KEY="your-api-key"export AUTH_SECRET="your-jwt-secret"export AUTH_OKTA_SECRET="your-okta-secret"
# Or use a secrets filecat > /etc/license-monitor/secrets.env << 'EOF'LICENSE_MONITOR_API_KEY=your-api-keyAUTH_SECRET=your-jwt-secretAUTH_OKTA_SECRET=your-okta-secretEOF
chmod 600 /etc/license-monitor/secrets.envWindows
Section titled “Windows”# Set machine-level environment variables[Environment]::SetEnvironmentVariable( "LICENSE_MONITOR_API_KEY", "your-api-key", [EnvironmentVariableTarget]::Machine)
# Or use Windows Credential Managercmdkey /generic:LicenseMonitorApiKey /user:service /pass:your-api-keySystemd Integration
Section titled “Systemd Integration”[Service]EnvironmentFile=/etc/license-monitor/secrets.envSecure the secrets file:
sudo chown root:license-monitor /etc/license-monitor/secrets.envsudo chmod 640 /etc/license-monitor/secrets.envDocker Secrets
Section titled “Docker Secrets”Docker Compose
Section titled “Docker Compose”version: '3.8'
services: license-monitor: image: license-monitor:latest secrets: - license_monitor_api_key environment: - LICENSE_MONITOR_API_KEY_FILE=/run/secrets/license_monitor_api_key
license-server-detail: image: license-server-detail:latest secrets: - auth_secret - okta_secret environment: - AUTH_SECRET_FILE=/run/secrets/auth_secret - AUTH_OKTA_SECRET_FILE=/run/secrets/okta_secret
secrets: license_monitor_api_key: file: ./secrets/license_monitor_api_key.txt auth_secret: file: ./secrets/auth_secret.txt okta_secret: file: ./secrets/okta_secret.txtDocker Swarm
Section titled “Docker Swarm”# Create secretsecho "your-api-key" | docker secret create license_monitor_api_key -echo "your-jwt-secret" | docker secret create auth_secret -
# Use in servicedocker service create \ --name license-monitor \ --secret license_monitor_api_key \ license-monitor:latestHashiCorp Vault
Section titled “HashiCorp Vault”# Enable KV secrets enginevault secrets enable -path=license-monitor kv-v2
# Store secretsvault kv put license-monitor/api-keys \ license_monitor_api_key="your-api-key" \ auth_secret="your-jwt-secret"
# Create policyvault policy write license-monitor - << 'EOF'path "license-monitor/data/api-keys" { capabilities = ["read"]}EOF
# Create AppRole for applicationvault auth enable approlevault write auth/approle/role/license-monitor \ policies="license-monitor" \ secret_id_ttl=0 \ token_ttl=1h \ token_max_ttl=24hApplication Integration
Section titled “Application Integration”# Get role ID and secret IDROLE_ID=$(vault read -field=role_id auth/approle/role/license-monitor/role-id)SECRET_ID=$(vault write -field=secret_id -f auth/approle/role/license-monitor/secret-id)
# Login and get tokenVAULT_TOKEN=$(vault write -field=token auth/approle/login \ role_id=$ROLE_ID \ secret_id=$SECRET_ID)
# Read secretsexport LICENSE_MONITOR_API_KEY=$(vault kv get -field=license_monitor_api_key license-monitor/api-keys)Vault Agent
Section titled “Vault Agent”auto_auth { method "approle" { config = { role_id_file_path = "/etc/vault/role_id" secret_id_file_path = "/etc/vault/secret_id" } }
sink "file" { config = { path = "/etc/vault/token" } }}
template { source = "/etc/license-monitor/secrets.env.tpl" destination = "/etc/license-monitor/secrets.env" perms = 0640}AWS Secrets Manager
Section titled “AWS Secrets Manager”Store Secrets
Section titled “Store Secrets”# Create secretaws secretsmanager create-secret \ --name license-monitor/api-keys \ --secret-string '{ "LICENSE_MONITOR_API_KEY": "your-api-key", "AUTH_SECRET": "your-jwt-secret", "AUTH_OKTA_SECRET": "your-okta-secret" }'Retrieve Secrets
Section titled “Retrieve Secrets”# Get secret valueaws secretsmanager get-secret-value \ --secret-id license-monitor/api-keys \ --query SecretString \ --output text | jq -r '.LICENSE_MONITOR_API_KEY'const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager');
const client = new SecretsManagerClient({ region: 'us-east-1' });
async function getSecrets() { const command = new GetSecretValueCommand({ SecretId: 'license-monitor/api-keys', });
const response = await client.send(command); return JSON.parse(response.SecretString);}IAM Policy
Section titled “IAM Policy”{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue" ], "Resource": [ "arn:aws:secretsmanager:us-east-1:123456789:secret:license-monitor/*" ] } ]}Secret Rotation
Section titled “Secret Rotation”Rotation Strategy
Section titled “Rotation Strategy”- Generate new secret
- Update secrets manager
- Deploy to applications (rolling update)
- Verify functionality
- Remove old secret
Automated Rotation Script
Section titled “Automated Rotation Script”#!/bin/bash# Generate new keyNEW_KEY=$(openssl rand -hex 32)
# Update Vaultvault kv patch license-monitor/api-keys \ license_monitor_api_key="$NEW_KEY"
# Restart services to pick up new secretsystemctl restart license-monitorsystemctl restart license-server-detail
# Verify healthsleep 10curl -f http://localhost:8080/api/health || exit 1curl -f http://localhost:3000/api/health || exit 1
echo "Rotation complete"Rotation Schedule
Section titled “Rotation Schedule”| Secret | Rotation Frequency | Notes |
|---|---|---|
| API Keys | 90 days | Automated rotation |
| OAuth Secrets | Annually | Coordinate with IdP |
| JWT Secrets | 90 days | Plan for session invalidation |
| SSL Certificates | Before expiry | Use cert-manager or similar |
CI/CD Integration
Section titled “CI/CD Integration”GitHub Actions
Section titled “GitHub Actions”name: Deploy
on: push: branches: [main]
jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Deploy to production env: LICENSE_MONITOR_API_KEY: ${{ secrets.LICENSE_MONITOR_API_KEY }} AUTH_SECRET: ${{ secrets.AUTH_SECRET }} AUTH_OKTA_SECRET: ${{ secrets.AUTH_OKTA_SECRET }} run: | # Deploy with secrets ./deploy.shGitLab CI
Section titled “GitLab CI”deploy: stage: deploy script: - ./deploy.sh variables: LICENSE_MONITOR_API_KEY: $LICENSE_MONITOR_API_KEY AUTH_SECRET: $AUTH_SECRET only: - mainBest Practices
Section titled “Best Practices”- Use a secrets manager in production
- Rotate secrets regularly
- Use least-privilege access
- Audit secret access
- Encrypt secrets at rest
- Use separate secrets per environment
Do Not
Section titled “Do Not”- Commit secrets to version control
- Log secrets (even accidentally)
- Share secrets via email or chat
- Use default or example secrets
- Store secrets in plain text
- Reuse secrets across environments
Auditing
Section titled “Auditing”# Monitor secret access (Vault example)vault audit enable file file_path=/var/log/vault/audit.log
# Review access logsgrep "license-monitor" /var/log/vault/audit.log | jq '.request.path'Next Steps
Section titled “Next Steps”- API Authentication - Using API keys
- Production Setup - Production deployment
- Security Overview - Complete security guide