CI/CD Integration Examples
This guide provides practical examples of integrating Voidkey with various CI/CD platforms for secure credential management.
GitHub Actions Integration
Section titled “GitHub Actions Integration”Basic Workflow
Section titled “Basic Workflow”name: Deploy to Production
on: push: branches: [main]
jobs: deploy: runs-on: ubuntu-latest permissions: id-token: write # Required for OIDC token contents: read
steps: - uses: actions/checkout@v4
- name: Install Voidkey CLI run: | curl -L -o voidkey https://github.com/voidkey-oss/voidkey/releases/latest/download/voidkey-linux-amd64 chmod +x voidkey sudo mv voidkey /usr/local/bin/
- name: Get temporary AWS credentials run: | # Get GitHub Actions OIDC token GITHUB_TOKEN=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://github.com/myorg" | jq -r .value)
# Mint AWS credentials using Voidkey eval $(voidkey mint \ --keys AWS_PRODUCTION_DEPLOY \ --broker-url https://voidkey.company.com \ --token "$GITHUB_TOKEN" \ --output env)
- name: Deploy to S3 run: | aws s3 sync ./dist s3://my-production-bucket/ aws cloudfront create-invalidation --distribution-id E1234567890 --paths "/*"Multi-Stage Deployment
Section titled “Multi-Stage Deployment”name: Multi-Stage Deployment
on: push: branches: [main, develop] pull_request: branches: [main]
jobs: determine-environment: runs-on: ubuntu-latest outputs: environment: ${{ steps.env.outputs.environment }} keys: ${{ steps.env.outputs.keys }} steps: - id: env run: | if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then echo "environment=production" >> $GITHUB_OUTPUT echo "keys=AWS_PROD_DEPLOY,GCP_PROD_DEPLOY" >> $GITHUB_OUTPUT elif [[ "${{ github.ref }}" == "refs/heads/develop" ]]; then echo "environment=staging" >> $GITHUB_OUTPUT echo "keys=AWS_STAGING_DEPLOY" >> $GITHUB_OUTPUT else echo "environment=preview" >> $GITHUB_OUTPUT echo "keys=AWS_PREVIEW_DEPLOY" >> $GITHUB_OUTPUT fi
deploy: needs: determine-environment runs-on: ubuntu-latest environment: ${{ needs.determine-environment.outputs.environment }} permissions: id-token: write contents: read
steps: - uses: actions/checkout@v4
- name: Setup Voidkey uses: voidkey/setup-voidkey@v1 with: version: 'latest'
- name: Get credentials for ${{ needs.determine-environment.outputs.environment }} run: | GITHUB_TOKEN=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://github.com/myorg" | jq -r .value)
voidkey mint \ --keys ${{ needs.determine-environment.outputs.keys }} \ --broker-url ${{ vars.VOIDKEY_BROKER_URL }} \ --token "$GITHUB_TOKEN" \ --output dotenv > .env.credentials
source .env.credentials
- name: Deploy run: | source .env.credentials ./scripts/deploy.sh ${{ needs.determine-environment.outputs.environment }}Matrix Deployments
Section titled “Matrix Deployments”name: Multi-Cloud Deployment
on: workflow_dispatch: inputs: environment: description: 'Environment to deploy to' required: true default: 'staging' type: choice options: - staging - production
jobs: deploy: runs-on: ubuntu-latest permissions: id-token: write contents: read
strategy: matrix: provider: - name: aws key: AWS_DEPLOY region: us-east-1 - name: gcp key: GCP_DEPLOY region: us-central1 - name: azure key: AZURE_DEPLOY region: eastus
steps: - uses: actions/checkout@v4
- name: Get ${{ matrix.provider.name }} credentials run: | GITHUB_TOKEN=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://github.com/myorg" | jq -r .value)
voidkey mint \ --keys ${{ matrix.provider.key }}_${{ github.event.inputs.environment | upper }} \ --broker-url ${{ vars.VOIDKEY_BROKER_URL }} \ --token "$GITHUB_TOKEN" \ --output json > credentials.json
- name: Deploy to ${{ matrix.provider.name }} run: | ./scripts/deploy-${{ matrix.provider.name }}.sh \ ${{ github.event.inputs.environment }} \ ${{ matrix.provider.region }} \ credentials.jsonGitLab CI Integration
Section titled “GitLab CI Integration”Basic Pipeline
Section titled “Basic Pipeline”stages: - build - deploy
variables: VOIDKEY_BROKER_URL: "https://voidkey.company.com"
before_script: - apt-get update -y && apt-get install -y curl jq - curl -L -o voidkey https://github.com/voidkey-oss/voidkey/releases/latest/download/voidkey-linux-amd64 - chmod +x voidkey && mv voidkey /usr/local/bin/
build: stage: build script: - npm ci - npm run build artifacts: paths: - dist/ expire_in: 1 hour
deploy-staging: stage: deploy only: - develop id_tokens: GITLAB_OIDC_TOKEN: aud: https://gitlab.com/myorg script: - | voidkey mint \ --keys AWS_STAGING_DEPLOY \ --broker-url $VOIDKEY_BROKER_URL \ --token $GITLAB_OIDC_TOKEN \ --output dotenv > .env - source .env - aws s3 sync dist/ s3://staging-bucket/
deploy-production: stage: deploy only: - main when: manual id_tokens: GITLAB_OIDC_TOKEN: aud: https://gitlab.com/myorg script: - | voidkey mint \ --keys AWS_PROD_DEPLOY,GCP_PROD_DEPLOY \ --broker-url $VOIDKEY_BROKER_URL \ --token $GITLAB_OIDC_TOKEN \ --output json > credentials.json - python scripts/multi-cloud-deploy.py credentials.jsonGitLab with Kubernetes
Section titled “GitLab with Kubernetes”deploy-k8s: stage: deploy image: kubectl:latest id_tokens: GITLAB_OIDC_TOKEN: aud: https://gitlab.com/myorg script: - | # Get GCP credentials for GKE access voidkey mint \ --keys GCP_K8S_DEPLOY \ --broker-url $VOIDKEY_BROKER_URL \ --token $GITLAB_OIDC_TOKEN \ --output json > gcp-creds.json
- | # Authenticate with GCP gcloud auth activate-service-account --key-file=gcp-creds.json gcloud container clusters get-credentials production-cluster \ --zone us-central1-a --project my-project
- | # Deploy to Kubernetes kubectl set image deployment/myapp \ myapp=gcr.io/my-project/myapp:$CI_COMMIT_SHA kubectl rollout status deployment/myappAzure DevOps Integration
Section titled “Azure DevOps Integration”Azure Pipelines
Section titled “Azure Pipelines”trigger: branches: include: - main - develop
pool: vmImage: 'ubuntu-latest'
variables: VOIDKEY_BROKER_URL: 'https://voidkey.company.com'
jobs:- job: Deploy steps: - checkout: self
- task: AzureCLI@2 displayName: 'Install Voidkey CLI' inputs: azureSubscription: 'service-connection' scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | curl -L -o voidkey https://github.com/voidkey-oss/voidkey/releases/latest/download/voidkey-linux-amd64 chmod +x voidkey sudo mv voidkey /usr/local/bin/
- task: AzureCLI@2 displayName: 'Get Azure credentials via Voidkey' inputs: azureSubscription: 'service-connection' scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | # Get Azure DevOps OIDC token ADOS_TOKEN=$(curl -H "Authorization: Bearer $SYSTEM_ACCESSTOKEN" \ "$SYSTEM_TEAMFOUNDATIONSERVERURI$SYSTEM_TEAMPROJECT/_apis/distributedtask/hubs/build/plans/$SYSTEM_PLANID/jobs/$SYSTEM_JOBID/oidctoken?serviceConnectionId=$SERVICE_CONNECTION_ID&api-version=7.1-preview.1" \ | jq -r .oidcToken)
# Mint Azure credentials voidkey mint \ --keys AZURE_PROD_DEPLOY \ --broker-url $(VOIDKEY_BROKER_URL) \ --token "$ADOS_TOKEN" \ --output env >> $GITHUB_ENV env: SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- task: AzureCLI@2 displayName: 'Deploy to Azure' inputs: azureSubscription: 'service-connection' scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | source .env az storage blob upload-batch \ --source ./dist \ --destination '$web' \ --account-name mystorageaccountJenkins Integration
Section titled “Jenkins Integration”Declarative Pipeline
Section titled “Declarative Pipeline”// Jenkinsfilepipeline { agent any
environment { VOIDKEY_BROKER_URL = 'https://voidkey.company.com' VOIDKEY_CLI_VERSION = 'latest' }
stages { stage('Setup') { steps { script { // Install Voidkey CLI sh ''' curl -L -o voidkey https://github.com/voidkey-oss/voidkey/releases/latest/download/voidkey-linux-amd64 chmod +x voidkey sudo mv voidkey /usr/local/bin/ ''' } } }
stage('Get Credentials') { steps { script { // Get Jenkins OIDC token (requires OIDC plugin) def token = sh( script: 'echo $OIDC_TOKEN', returnStdout: true ).trim()
// Determine keys based on branch def keys = env.BRANCH_NAME == 'main' ? 'AWS_PROD_DEPLOY' : 'AWS_STAGING_DEPLOY'
// Mint credentials sh """ voidkey mint \\ --keys ${keys} \\ --broker-url ${VOIDKEY_BROKER_URL} \\ --token "${token}" \\ --output dotenv > .env.credentials """
// Load credentials into environment def props = readProperties file: '.env.credentials' props.each { key, value -> env[key] = value } } } }
stage('Deploy') { steps { script { if (env.BRANCH_NAME == 'main') { sh './scripts/deploy-production.sh' } else { sh './scripts/deploy-staging.sh' } } } } }
post { always { // Clean up credentials sh 'rm -f .env.credentials' } }}Scripted Pipeline with Parallel Deployment
Section titled “Scripted Pipeline with Parallel Deployment”// Jenkinsfilenode { def brokerUrl = 'https://voidkey.company.com'
stage('Setup') { checkout scm
sh ''' curl -L -o voidkey https://github.com/voidkey-oss/voidkey/releases/latest/download/voidkey-linux-amd64 chmod +x voidkey sudo mv voidkey /usr/local/bin/ ''' }
stage('Parallel Deploy') { parallel( 'AWS': { script { def token = sh(script: 'echo $OIDC_TOKEN', returnStdout: true).trim()
sh """ voidkey mint \\ --keys AWS_DEPLOY \\ --broker-url ${brokerUrl} \\ --token "${token}" \\ --output env > aws.env """
sh ''' source aws.env aws s3 sync dist/ s3://my-bucket/ ''' } }, 'GCP': { script { def token = sh(script: 'echo $OIDC_TOKEN', returnStdout: true).trim()
sh """ voidkey mint \\ --keys GCP_DEPLOY \\ --broker-url ${brokerUrl} \\ --token "${token}" \\ --output json > gcp-creds.json """
sh ''' gcloud auth activate-service-account --key-file=gcp-creds.json gsutil -m rsync -r -d dist/ gs://my-gcp-bucket/ ''' } } ) }}CircleCI Integration
Section titled “CircleCI Integration”Basic Configuration
Section titled “Basic Configuration”version: 2.1
executors: default: docker: - image: cimg/node:18.17
jobs: deploy: executor: default steps: - checkout
- run: name: Install Voidkey CLI command: | curl -L -o voidkey https://github.com/voidkey-oss/voidkey/releases/latest/download/voidkey-linux-amd64 chmod +x voidkey sudo mv voidkey /usr/local/bin/
- run: name: Get temporary credentials command: | # Get CircleCI OIDC token CIRCLECI_TOKEN=$(curl -H "Circle-Token: $CIRCLE_TOKEN" \ "https://circleci.com/api/v2/project/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/oidc-token" \ | jq -r .token)
# Mint AWS credentials voidkey mint \ --keys AWS_DEPLOY \ --broker-url $VOIDKEY_BROKER_URL \ --token "$CIRCLECI_TOKEN" \ --output dotenv > .env.credentials
cat .env.credentials >> $BASH_ENV
- run: name: Deploy to AWS command: | source $BASH_ENV aws s3 sync dist/ s3://my-production-bucket/
workflows: deploy: jobs: - deploy: filters: branches: only: main context: productionCustom CI/CD Integration
Section titled “Custom CI/CD Integration”Generic OIDC Integration
Section titled “Generic OIDC Integration”#!/bin/bashset -e
# ConfigurationVOIDKEY_BROKER_URL="${VOIDKEY_BROKER_URL:-https://voidkey.company.com}"ENVIRONMENT="${CI_ENVIRONMENT:-staging}"
# Function to get OIDC token (implement based on your CI/CD platform)get_oidc_token() { case "$CI_PLATFORM" in "github") curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://github.com/myorg" | jq -r .value ;; "gitlab") echo "$CI_JOB_JWT_V2" ;; "jenkins") echo "$OIDC_TOKEN" ;; *) echo "Unsupported CI platform: $CI_PLATFORM" >&2 exit 1 ;; esac}
# Get deployment keys based on environmentget_deployment_keys() { case "$ENVIRONMENT" in "production") echo "AWS_PROD_DEPLOY,GCP_PROD_DEPLOY" ;; "staging") echo "AWS_STAGING_DEPLOY" ;; "preview") echo "AWS_PREVIEW_DEPLOY" ;; *) echo "Unknown environment: $ENVIRONMENT" >&2 exit 1 ;; esac}
# Main deployment logicmain() { echo "Starting deployment to $ENVIRONMENT..."
# Get OIDC token echo "Getting OIDC token..." OIDC_TOKEN=$(get_oidc_token)
if [ -z "$OIDC_TOKEN" ] || [ "$OIDC_TOKEN" = "null" ]; then echo "Failed to get OIDC token" >&2 exit 1 fi
# Get deployment keys KEYS=$(get_deployment_keys) echo "Using keys: $KEYS"
# Mint credentials echo "Minting credentials via Voidkey..." voidkey mint \ --keys "$KEYS" \ --broker-url "$VOIDKEY_BROKER_URL" \ --token "$OIDC_TOKEN" \ --output json > credentials.json
# Verify credentials were obtained if [ ! -s credentials.json ]; then echo "Failed to obtain credentials" >&2 exit 1 fi
echo "Credentials obtained successfully"
# Deploy based on available credentials if jq -e '.AWS_PROD_DEPLOY' credentials.json >/dev/null; then echo "Deploying to AWS Production..." deploy_to_aws_prod fi
if jq -e '.AWS_STAGING_DEPLOY' credentials.json >/dev/null; then echo "Deploying to AWS Staging..." deploy_to_aws_staging fi
if jq -e '.GCP_PROD_DEPLOY' credentials.json >/dev/null; then echo "Deploying to GCP Production..." deploy_to_gcp_prod fi
echo "Deployment completed successfully!"}
# AWS Production deploymentdeploy_to_aws_prod() { # Extract AWS credentials export AWS_ACCESS_KEY_ID=$(jq -r '.AWS_PROD_DEPLOY.AWS_ACCESS_KEY_ID' credentials.json) export AWS_SECRET_ACCESS_KEY=$(jq -r '.AWS_PROD_DEPLOY.AWS_SECRET_ACCESS_KEY' credentials.json) export AWS_SESSION_TOKEN=$(jq -r '.AWS_PROD_DEPLOY.AWS_SESSION_TOKEN' credentials.json)
# Deploy aws s3 sync dist/ s3://production-bucket/ aws cloudfront create-invalidation --distribution-id E1234567890 --paths "/*"}
# AWS Staging deploymentdeploy_to_aws_staging() { # Extract AWS credentials export AWS_ACCESS_KEY_ID=$(jq -r '.AWS_STAGING_DEPLOY.AWS_ACCESS_KEY_ID' credentials.json) export AWS_SECRET_ACCESS_KEY=$(jq -r '.AWS_STAGING_DEPLOY.AWS_SECRET_ACCESS_KEY' credentials.json) export AWS_SESSION_TOKEN=$(jq -r '.AWS_STAGING_DEPLOY.AWS_SESSION_TOKEN' credentials.json)
# Deploy aws s3 sync dist/ s3://staging-bucket/}
# GCP Production deploymentdeploy_to_gcp_prod() { # Extract GCP service account key jq -r '.GCP_PROD_DEPLOY.SERVICE_ACCOUNT_KEY' credentials.json > gcp-key.json
# Authenticate gcloud auth activate-service-account --key-file=gcp-key.json
# Deploy gsutil -m rsync -r -d dist/ gs://production-gcp-bucket/}
# Cleanup functioncleanup() { echo "Cleaning up..." rm -f credentials.json gcp-key.json unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN}
# Set trap for cleanuptrap cleanup EXIT
# Run main functionmain "$@"Best Practices
Section titled “Best Practices”Security Considerations
Section titled “Security Considerations”-
Token Handling
Terminal window # Never log tokensset +x # Disable command echoOIDC_TOKEN=$(get_token)set -x # Re-enable if needed# Use secure temporary filesCREDS_FILE=$(mktemp)trap "rm -f $CREDS_FILE" EXIT -
Credential Cleanup
Terminal window # Always clean up credentialscleanup_credentials() {rm -f .env.credentials credentials.jsonunset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN}trap cleanup_credentials EXIT -
Error Handling
Terminal window # Fail fast on errorsset -euo pipefail# Check for required toolscommand -v voidkey >/dev/null 2>&1 || {echo "voidkey CLI not found" >&2exit 1}
Performance Optimization
Section titled “Performance Optimization”-
Credential Caching
Terminal window # Cache credentials for job durationif [ ! -f /tmp/voidkey-cache ]; thenvoidkey mint --keys "$KEYS" --output json > /tmp/voidkey-cachefi -
Parallel Operations
Terminal window # Deploy to multiple environments in paralleldeploy_aws &deploy_gcp &wait # Wait for all background jobs
Monitoring and Logging
Section titled “Monitoring and Logging”-
Audit Logging
Terminal window # Log deployment eventsecho "$(date): Deploying $KEYS to $ENVIRONMENT" >> /var/log/deployments.log -
Metrics Collection
Terminal window # Send deployment metricscurl -X POST https://metrics.company.com/deployments \-d "environment=$ENVIRONMENT&status=success&duration=$DURATION"
Troubleshooting
Section titled “Troubleshooting”Common Issues
Section titled “Common Issues”-
Token Validation Failures
Terminal window # Debug token claimsvoidkey debug-token --token "$OIDC_TOKEN" -
Network Connectivity
Terminal window # Test broker connectivitycurl -f "$VOIDKEY_BROKER_URL/health" || {echo "Cannot reach Voidkey broker" >&2exit 1} -
Permission Issues
Terminal window # Check available keysvoidkey list-keys --token "$OIDC_TOKEN" --broker-url "$VOIDKEY_BROKER_URL"
Next Steps
Section titled “Next Steps”- CLI Reference - Complete CLI command reference
- Configuration Guide - Configure identity mappings
- API Reference - Direct API integration
- Security Model - Security considerations for CI/CD