fix(forgejo): admit the PR loop's LAN webhooks (ALLOWED_HOST_LIST = private) #58

Merged
pit merged 1 commit from hermes/57-fix-local-webhooks into main 2026-10-10 00:04:18 +00:00
Owner

Summary

The [webhook] section was absent from the templated app.ini, so ALLOWED_HOST_LIST sat at its effective external default — which admits no RFC 1918 address — and the PR loop's deliveries to the n8n Guest and the Hermes host were refused before they left the instance. Template it from one shared variable:

 [service]
 DISABLE_REGISTRATION = true
 
+[webhook]
+ALLOWED_HOST_LIST = private
+
 [actions]
 ENABLED = true
  • ansible/group_vars/forgejo/vars.yml — forgejo_webhook_allowed_host_list: private. One value, so PROD and PRE stay in step: the loop's consumers are the same on both.
  • docs/adr/0006-webhook-lan-delivery.md — the deliberate decision the issue asked for: private (the LAN as a network class) over naming the two consumer hosts, with the rejected alternative and what it costs.

Evidence

Rehearsed on PRE (make pre, PLAY RECAP ok=43 changed=29 failed=0). A throwaway repo webhook pointed at a listener on the LAN, Test delivery fired — the instance's own delivery record and the listener agree:

  • Before: the effective default external, same hook and same listener —
    is_succeed=f, response Delivery: Post "http://10.12.0.214:8080/": ... webhook can only call allowed HTTP servers (check your webhook.ALLOWED_HOST_LIST setting), deny '10.12.0.214(10.12.0.214:8080)' — the delivery never left the instance.
    After: private —
    is_succeed=t, response {"status":200,"body":"ignored\n"}, and the listener logged POST / event=push len=6153.

Negative control — the allowlist is still a fence, not an open door: a target of http://8.8.8.8/ is refused deny '8.8.8.8(8.8.8.8:80)'.

ansible-playbook --syntax-check site.yml and make fmt are clean.

Not rehearsed: the loop's own end-to-end check (a throwaway delivery appearing in n8n as an execution answering 200 ignored) — the n8n Guest and homelab-blueprint are not reachable from here, so the generic LAN-listener variant stands in for it.

Merge Danger

Door: two-way

The service config is templated, so the previous commit is the previous state: git revert this commit and re-run the service playbook, and the effective default returns.

Blast Radius: instance-wide

ALLOWED_HOST_LIST governs every webhook on the instance, not only the loop's, so any webhook may now POST to any RFC 1918 / RFC 4193 / RFC 6598 address. The counterpart: public-internet targets are now refused — private replaces the effective external default rather than adding to it, so a future webhook to a public host must name external explicitly. Reasoning and costs in ADR 0006.

Closes #57

## Summary The `[webhook]` section was absent from the templated `app.ini`, so `ALLOWED_HOST_LIST` sat at its effective `external` default — which admits no RFC 1918 address — and the PR loop's deliveries to the n8n Guest and the Hermes host were refused before they left the instance. Template it from one shared variable: ```diff [service] DISABLE_REGISTRATION = true +[webhook] +ALLOWED_HOST_LIST = private + [actions] ENABLED = true ``` - `ansible/group_vars/forgejo/vars.yml` — `forgejo_webhook_allowed_host_list: private`. One value, so PROD and PRE stay in step: the loop's consumers are the same on both. - `docs/adr/0006-webhook-lan-delivery.md` — the deliberate decision the issue asked for: `private` (the LAN as a network class) over naming the two consumer hosts, with the rejected alternative and what it costs. ## Evidence Rehearsed on PRE (`make pre`, PLAY RECAP `ok=43 changed=29 failed=0`). A throwaway repo webhook pointed at a listener on the LAN, *Test delivery* fired — the instance's own delivery record and the listener agree: - **Before:** the effective default `external`, same hook and same listener — `is_succeed=f`, response `Delivery: Post "http://10.12.0.214:8080/": ... webhook can only call allowed HTTP servers (check your webhook.ALLOWED_HOST_LIST setting), deny '10.12.0.214(10.12.0.214:8080)'` — the delivery never left the instance. **After:** `private` — `is_succeed=t`, response `{"status":200,"body":"ignored\n"}`, and the listener logged `POST / event=push len=6153`. Negative control — the allowlist is still a fence, not an open door: a target of `http://8.8.8.8/` is refused `deny '8.8.8.8(8.8.8.8:80)'`. `ansible-playbook --syntax-check site.yml` and `make fmt` are clean. Not rehearsed: the loop's own end-to-end check (a throwaway delivery appearing in n8n as an execution answering `200 ignored`) — the n8n Guest and homelab-blueprint are not reachable from here, so the generic LAN-listener variant stands in for it. ## Merge Danger **Door:** two-way The service config is templated, so the previous commit is the previous state: `git revert` this commit and re-run the service playbook, and the effective default returns. **Blast Radius:** instance-wide `ALLOWED_HOST_LIST` governs every webhook on the instance, not only the loop's, so any webhook may now POST to any RFC 1918 / RFC 4193 / RFC 6598 address. The counterpart: public-internet targets are now refused — `private` replaces the effective `external` default rather than adding to it, so a future webhook to a public host must name `external` explicitly. Reasoning and costs in ADR 0006. Closes #57
The [webhook] section was absent from the templated app.ini, so
ALLOWED_HOST_LIST sat at its effective `external` default — which admits no
RFC1918 address — and the PR loop's deliveries to the n8n Guest and the Hermes
host were refused before they left the instance.

Template the section from forgejo_webhook_allowed_host_list, set once in
group_vars so PROD and PRE share the one value: the consumers are the same on
both. The value is `private` (the LAN as a class) rather than the two consumer
addresses — why, and what it costs, is ADR 0006.

Closes #57
pit merged commit 53a4883c89 into main 2026-10-10 00:04:18 +00:00
pit deleted branch hermes/57-fix-local-webhooks 2026-10-10 00:04:19 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
olympus/infra-forge!58
No description provided.