Files
git.stella-ops.org/.gitea/workflows/authority-key-rotation.yml
master 607e72e2a1
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
up
2025-10-12 20:37:18 +03:00

164 lines
5.2 KiB
YAML

# .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 }}"