secrets-management
Implement secure secrets management for CI/CD pipelines using Vault, AWS Secrets Manager, or native platform solutions. Use when handling sensitive credentials, rotating secrets, or securing CI/CD environments.
Secrets Management
Secure secrets management practices for CI/CD pipelines using Vault, AWS Secrets Manager, and other tools.
Purpose
Implement secure secrets management in CI/CD pipelines without hardcoding sensitive information.
Use this skill when
Do not use this skill when
Instructions
Safety
Secrets Management Tools
HashiCorp Vault
AWS Secrets Manager
Azure Key Vault
Google Secret Manager
HashiCorp Vault Integration
Setup Vault
# Start Vault dev server
vault server -devSet environment
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='root'Enable secrets engine
vault secrets enable -path=secret kv-v2Store secret
vault kv put secret/database/config username=admin password=secretGitHub Actions with Vault
name: Deploy with Vault Secretson: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Import Secrets from Vault
uses: hashicorp/vault-action@v2
with:
url: https://vault.example.com:8200
token: ${{ secrets.VAULT_TOKEN }}
<div class="overflow-x-auto my-6"><table class="min-w-full divide-y divide-border border border-border"><thead><tr><th class="px-4 py-2 text-left text-sm font-semibold text-foreground bg-muted/50">secrets:</th></tr></thead><tbody class="divide-y divide-border"><tr><td class="px-4 py-2 text-sm text-foreground">secret/data/database password</td></tr><tr><td class="px-4 py-2 text-sm text-foreground">secret/data/api key</td></tr></tbody></table></div>
- name: Use secrets
run: |
echo "Connecting to database as $DB_USERNAME"
# Use $DB_PASSWORD, $API_KEY
GitLab CI with Vault
deploy:
image: vault:latest
before_script:
- export VAULT_ADDR=https://vault.example.com:8200
- export VAULT_TOKEN=$VAULT_TOKEN
- apk add curl jq
script:
- |
DB_PASSWORD=$(vault kv get -field=password secret/database/config)
API_KEY=$(vault kv get -field=key secret/api/credentials)
echo "Deploying with secrets..."
# Use $DB_PASSWORD, $API_KEYReference: See references/vault-setup.md
AWS Secrets Manager
Store Secret
aws secretsmanager create-secret \
--name production/database/password \
--secret-string "super-secret-password"Retrieve in GitHub Actions
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-2name: Get secret from AWS
run: |
SECRET=$(aws secretsmanager get-secret-value \
--secret-id production/database/password \
--query SecretString \
--output text)
echo "::add-mask::$SECRET"
echo "DB_PASSWORD=$SECRET" >> $GITHUB_ENVname: Use secret
run: |
# Use $DB_PASSWORD
./deploy.shTerraform with AWS Secrets Manager
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "production/database/password"
}resource "aws_db_instance" "main" {
allocated_storage = 100
engine = "postgres"
instance_class = "db.t3.large"
username = "admin"
password = jsondecode(data.aws_secretsmanager_secret_version.db_password.secret_string)["password"]
}
GitHub Secrets
Organization/Repository Secrets
- name: Use GitHub secret
run: |
echo "API Key: ${{ secrets.API_KEY }}"
echo "Database URL: ${{ secrets.DATABASE_URL }}"Environment Secrets
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy
run: |
echo "Deploying with ${{ secrets.PROD_API_KEY }}"Reference: See references/github-secrets.md
GitLab CI/CD Variables
Project Variables
deploy:
script:
- echo "Deploying with $API_KEY"
- echo "Database: $DATABASE_URL"Protected and Masked Variables
Best Practices
Secret Rotation
Automated Rotation with AWS
import boto3
import jsondef lambda_handler(event, context):
client = boto3.client('secretsmanager')
# Get current secret
response = client.get_secret_value(SecretId='my-secret')
current_secret = json.loads(response['SecretString'])
# Generate new password
new_password = generate_strong_password()
# Update database password
update_database_password(new_password)
# Update secret
client.put_secret_value(
SecretId='my-secret',
SecretString=json.dumps({
'username': current_secret['username'],
'password': new_password
})
)
return {'statusCode': 200}
Manual Rotation Process
External Secrets Operator
Kubernetes Integration
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
namespace: production
spec:
provider:
vault:
server: "https://vault.example.com:8200"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "production"
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: database-credentials
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: database-credentials
creationPolicy: Owner
data:
- secretKey: username
remoteRef:
key: database/config
property: username
- secretKey: password
remoteRef:
key: database/config
property: passwordSecret Scanning
Pre-commit Hook
#!/bin/bash
.git/hooks/pre-commit
Check for secrets with TruffleHog
docker run --rm -v "$(pwd):/repo" \
trufflesecurity/trufflehog:latest \
filesystem --directory=/repoif [ $? -ne 0 ]; then
echo "❌ Secret detected! Commit blocked."
exit 1
fi
CI/CD Secret Scanning
secret-scan:
stage: security
image: trufflesecurity/trufflehog:latest
script:
- trufflehog filesystem .
allow_failure: falseReference Files
references/vault-setup.md - HashiCorp Vault configurationreferences/github-secrets.md - GitHub Secrets best practicesRelated Skills
github-actions-templates - For GitHub Actions integrationgitlab-ci-patterns - For GitLab CI integrationdeployment-pipeline-design - For pipeline architecture