up
Some checks failed
Build Test Deploy / docs (push) Has been cancelled
Build Test Deploy / deploy (push) Has been cancelled
Build Test Deploy / build-test (push) Has been cancelled
Build Test Deploy / authority-container (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Build Test Deploy / docs (push) Has been cancelled
Build Test Deploy / deploy (push) Has been cancelled
Build Test Deploy / build-test (push) Has been cancelled
Build Test Deploy / authority-container (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
This commit is contained in:
163
.gitea/workflows/authority-key-rotation.yml
Normal file
163
.gitea/workflows/authority-key-rotation.yml
Normal file
@@ -0,0 +1,163 @@
|
||||
# .gitea/workflows/authority-key-rotation.yml
|
||||
# Manual workflow to push a new Authority signing key using OPS3 tooling
|
||||
|
||||
name: Authority Key Rotation
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
environment:
|
||||
description: 'Target environment name (used to select secrets/vars)'
|
||||
required: true
|
||||
default: 'staging'
|
||||
type: choice
|
||||
options:
|
||||
- staging
|
||||
- production
|
||||
authority_url:
|
||||
description: 'Override Authority URL (leave blank to use env-specific secret)'
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
key_id:
|
||||
description: 'New signing key identifier (kid)'
|
||||
required: true
|
||||
type: string
|
||||
key_path:
|
||||
description: 'Path (as Authority sees it) to the PEM key'
|
||||
required: true
|
||||
type: string
|
||||
source:
|
||||
description: 'Signing key source loader (default: file)'
|
||||
required: false
|
||||
default: 'file'
|
||||
type: string
|
||||
algorithm:
|
||||
description: 'Signing algorithm (default: ES256)'
|
||||
required: false
|
||||
default: 'ES256'
|
||||
type: string
|
||||
provider:
|
||||
description: 'Preferred crypto provider hint'
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
metadata:
|
||||
description: 'Optional key=value metadata entries (comma-separated)'
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
rotate:
|
||||
runs-on: ubuntu-22.04
|
||||
environment: ${{ inputs.environment }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Resolve Authority configuration
|
||||
id: config
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
env_name=${{ inputs.environment }}
|
||||
echo "Environment: $env_name"
|
||||
|
||||
bootstrap_key=""
|
||||
authority_url="${{ inputs.authority_url }}"
|
||||
|
||||
# Helper to prefer secrets over variables and fall back to shared defaults
|
||||
resolve_var() {
|
||||
local name="$1"
|
||||
local default="$2"
|
||||
local value="${{ secrets[name] }}"
|
||||
if [ -z "$value" ]; then value="${{ vars[name] }}"; fi
|
||||
if [ -z "$value" ]; then value="$default"; fi
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
key_name="${env_name^^}_AUTHORITY_BOOTSTRAP_KEY"
|
||||
bootstrap_key="$(resolve_var "$key_name" "")"
|
||||
if [ -z "$bootstrap_key" ]; then
|
||||
bootstrap_key="$(resolve_var "AUTHORITY_BOOTSTRAP_KEY" "")"
|
||||
fi
|
||||
|
||||
if [ -z "$bootstrap_key" ]; then
|
||||
echo "::error::Missing bootstrap key secret (expected $key_name or AUTHORITY_BOOTSTRAP_KEY)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$authority_url" ]; then
|
||||
url_name="${env_name^^}_AUTHORITY_URL"
|
||||
authority_url="$(resolve_var "$url_name" "")"
|
||||
if [ -z "$authority_url" ]; then
|
||||
authority_url="$(resolve_var "AUTHORITY_URL" "")"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$authority_url" ]; then
|
||||
echo "::error::Authority URL not provided and no secret/var found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
key_file="${RUNNER_TEMP}/authority-bootstrap-key"
|
||||
printf '%s\n' "$bootstrap_key" > "$key_file"
|
||||
chmod 600 "$key_file"
|
||||
|
||||
echo "bootstrap-key-file=$key_file" >> "$GITHUB_OUTPUT"
|
||||
echo "authority-url=$authority_url" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Execute key rotation
|
||||
id: rotate
|
||||
shell: bash
|
||||
env:
|
||||
AUTHORITY_BOOTSTRAP_KEY_FILE: ${{ steps.config.outputs['bootstrap-key-file'] }}
|
||||
AUTHORITY_URL: ${{ steps.config.outputs['authority-url'] }}
|
||||
KEY_ID: ${{ inputs.key_id }}
|
||||
KEY_PATH: ${{ inputs.key_path }}
|
||||
KEY_SOURCE: ${{ inputs.source }}
|
||||
KEY_ALGORITHM: ${{ inputs.algorithm }}
|
||||
KEY_PROVIDER: ${{ inputs.provider }}
|
||||
KEY_METADATA: ${{ inputs.metadata }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
bootstrap_key=$(cat "$AUTHORITY_BOOTSTRAP_KEY_FILE")
|
||||
|
||||
metadata_args=()
|
||||
if [ -n "$KEY_METADATA" ]; then
|
||||
IFS=',' read -ra META <<< "$KEY_METADATA"
|
||||
for entry in "${META[@]}"; do
|
||||
trimmed="$(echo "$entry" | xargs)"
|
||||
[ -z "$trimmed" ] && continue
|
||||
metadata_args+=(-m "$trimmed")
|
||||
done
|
||||
fi
|
||||
|
||||
provider_args=()
|
||||
if [ -n "$KEY_PROVIDER" ]; then
|
||||
provider_args+=(--provider "$KEY_PROVIDER")
|
||||
fi
|
||||
|
||||
./ops/authority/key-rotation.sh \
|
||||
--authority-url "$AUTHORITY_URL" \
|
||||
--api-key "$bootstrap_key" \
|
||||
--key-id "$KEY_ID" \
|
||||
--key-path "$KEY_PATH" \
|
||||
--source "$KEY_SOURCE" \
|
||||
--algorithm "$KEY_ALGORITHM" \
|
||||
"${provider_args[@]}" \
|
||||
"${metadata_args[@]}"
|
||||
|
||||
- name: JWKS summary
|
||||
run: |
|
||||
echo "✅ Rotation complete"
|
||||
echo "Environment: ${{ inputs.environment }}"
|
||||
echo "Authority: ${{ steps.config.outputs['authority-url'] }}"
|
||||
echo "Key ID: ${{ inputs.key_id }}"
|
||||
echo "Key Path: ${{ inputs.key_path }}"
|
||||
echo "Source: ${{ inputs.source }}"
|
||||
echo "Algorithm: ${{ inputs.algorithm }}"
|
||||
Reference in New Issue
Block a user