feat(webhook): authenticated webhook runner with allowlists, signature verification, idempotent logging

- Rewrite scripts/gitea_webhook_handler.py as HTTP server with HMAC-SHA256 auth
- Add config/webhook.yaml defining allowed repos/events/branches/actions
- Implement dispatch_push calling ansible/scripts/deploy_on_webhook.sh safely
- SQLite logging table with delivery_id dedup for replay safety
- Add tests/test_gitea_webhook_handler.py covering push/PR/signature/idempotency
- Add docs/webhook-deployment.md with security model, ops, and #288 alignment

Closes #436
This commit is contained in:
Alexander Payne
2026-04-30 10:03:57 -04:00
parent ba4220d5ed
commit 54a6def7e8
4 changed files with 859 additions and 62 deletions

61
config/webhook.yaml Normal file
View File

@@ -0,0 +1,61 @@
# Webhook Handler Configuration
# This file defines the allowlists for the authenticated webhook runner.
# Secrets MUST be provided via environment variables — never hardcoded.
# ---------------------------------------------------------------------------
# AUTHENTICATION
# ---------------------------------------------------------------------------
# Gitea sends X-Gitea-Signature header (HMAC-SHA256). The secret must
# match the webhook secret configured in Gitea.
#
# Set in environment: GITEA_WEBHOOK_SECRET
# Example: export GITEA_WEBHOOK_SECRET=$(cat ~/.config/gitea/webhook-secret)
#
# NEVER commit the actual secret. This file documents the key name only.
webhook_secret_env: "GITEA_WEBHOOK_SECRET"
# ---------------------------------------------------------------------------
# ALLOWLISTS — explicit, deny-by-default
# ---------------------------------------------------------------------------
# Only these repositories will trigger actions
allowed_repos:
- "timmy-config"
# Add other Timmy_Foundation repos as needed
# Only these event types are processed
allowed_events:
- "push"
- "pull_request"
# Note: issue events accepted but no action configured yet
# Only these branches are deployment targets
allowed_branches:
- "refs/heads/main"
- "refs/heads/master"
# PR actions that are allowed (push to main is the deploy trigger)
allowed_pr_actions:
- "opened"
- "synchronized"
- "reopened"
- "closed" # merged PRs also trigger push event
# ---------------------------------------------------------------------------
# OPERATIONAL
# ---------------------------------------------------------------------------
# Require valid signature? Set false only for local testing.
require_signature: true
# Where deployment logs are written
log_dir: "logs"
# Path to the ansible deploy script (called on main-branch push)
deploy_script: "ansible/scripts/deploy_on_webhook.sh"
# ---------------------------------------------------------------------------
# DEPLOYMENT NOTES
# - The server runs continuously. Use systemd or cron @reboot.
# - Align webhook creation with inf