From cada70485638e72c03ab67f5699f342dfd0782cf Mon Sep 17 00:00:00 2001 From: damocles Date: Tue, 14 Jul 2026 20:07:27 +0200 Subject: [PATCH 1/4] fix(#2417): allow gateway host in forgejo webhook ssrf list so config-pr webhook delivers --- nix/host-modules/hive-forge/default.nix | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/nix/host-modules/hive-forge/default.nix b/nix/host-modules/hive-forge/default.nix index f79b0367..12f96e3b 100644 --- a/nix/host-modules/hive-forge/default.nix +++ b/nix/host-modules/hive-forge/default.nix @@ -418,6 +418,15 @@ in # disallowed hosts"). Allow loopback + RFC-1918 sources # so an in-hive mirror of the hyperhive repo works. migrations.ALLOW_LOCALNETWORKS = true; + # Webhook deliveries target the gateway + # (`https:///webhook/*`), which resolves to a + # private (RFC-1918) gateway IP. Forgejo's webhook SSRF guard + # denies private hosts by default, so the config-PR + knowledge + # webhooks never actually deliver — only the 5-min poll fallback + # catches config PRs. Allow the gateway host explicitly; scoping + # to the single hostname keeps the SSRF surface tighter than the + # broad `private` builtin. + webhook.ALLOWED_HOST_LIST = hyperhiveDomain; log.LEVEL = "Warn"; ui = { DEFAULT_THEME = "catppuccin-vibec0re"; From 00c9a15ae64fed412b9545d7fe6903720c285e90 Mon Sep 17 00:00:00 2001 From: damocles Date: Tue, 14 Jul 2026 20:16:12 +0200 Subject: [PATCH 2/4] fix(#2417): make webhook allow-list additive via forge.webhookAllowedHosts option --- nix/host-modules/hive-forge/default.nix | 33 ++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/nix/host-modules/hive-forge/default.nix b/nix/host-modules/hive-forge/default.nix index 12f96e3b..6c7b9536 100644 --- a/nix/host-modules/hive-forge/default.nix +++ b/nix/host-modules/hive-forge/default.nix @@ -196,6 +196,28 @@ in ''; }; + webhookAllowedHosts = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + example = [ + "ci.example.com" + "hooks.example.org" + ]; + description = '' + Extra hosts to add to forgejo's webhook SSRF allow-list + (`[webhook] ALLOWED_HOST_LIST`). The hive gateway host + (`services.hyperhive.domain`) is ALWAYS included — the config-PR + and knowledge webhooks target it, and it resolves to a private + gateway IP that forgejo's default guard would otherwise deny. + Add entries here to additionally allow webhooks from project + repos to other hosts (a user's own CI, external services, etc.). + + Entries are forgejo hostmatcher patterns: hostnames, globs, + IPs/CIDRs, or the builtins `loopback` / `private` / `external` + / `*`. + ''; + }; + openFirewall = lib.mkOption { type = lib.types.bool; default = false; @@ -423,10 +445,13 @@ in # private (RFC-1918) gateway IP. Forgejo's webhook SSRF guard # denies private hosts by default, so the config-PR + knowledge # webhooks never actually deliver — only the 5-min poll fallback - # catches config PRs. Allow the gateway host explicitly; scoping - # to the single hostname keeps the SSRF surface tighter than the - # broad `private` builtin. - webhook.ALLOWED_HOST_LIST = hyperhiveDomain; + # catches config PRs. The gateway host is always allowed (scoping + # to that one hostname keeps the SSRF surface tighter than the + # broad `private` builtin); operators extend the list via + # `forge.webhookAllowedHosts` for webhooks on their own repos. + webhook.ALLOWED_HOST_LIST = lib.concatStringsSep "," ( + [ hyperhiveDomain ] ++ cfg.webhookAllowedHosts + ); log.LEVEL = "Warn"; ui = { DEFAULT_THEME = "catppuccin-vibec0re"; From 60e4ea8bb8ec1345f968e0945ce765ea4c620817 Mon Sep 17 00:00:00 2001 From: damocles Date: Tue, 14 Jul 2026 20:19:49 +0200 Subject: [PATCH 3/4] fix(#2417): keep 'external' in webhook allow-list so public webhooks still deliver --- nix/host-modules/hive-forge/default.nix | 42 +++++++++++++++---------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/nix/host-modules/hive-forge/default.nix b/nix/host-modules/hive-forge/default.nix index 6c7b9536..97194f80 100644 --- a/nix/host-modules/hive-forge/default.nix +++ b/nix/host-modules/hive-forge/default.nix @@ -205,12 +205,15 @@ in ]; description = '' Extra hosts to add to forgejo's webhook SSRF allow-list - (`[webhook] ALLOWED_HOST_LIST`). The hive gateway host - (`services.hyperhive.domain`) is ALWAYS included — the config-PR - and knowledge webhooks target it, and it resolves to a private - gateway IP that forgejo's default guard would otherwise deny. - Add entries here to additionally allow webhooks from project - repos to other hosts (a user's own CI, external services, etc.). + (`[webhook] ALLOWED_HOST_LIST`, a strict whitelist). The list + always contains the `external` builtin (forgejo's default — all + public hosts, so webhooks to github/slack/etc. from user repos + keep working) plus the hive gateway host + (`services.hyperhive.domain`), which the config-PR and knowledge + webhooks target and which resolves to a private gateway IP that + `external` alone would deny. Set this to additionally allow + webhooks to other hosts (a user's own CI, internal services, + etc.). Entries are forgejo hostmatcher patterns: hostnames, globs, IPs/CIDRs, or the builtins `loopback` / `private` / `external` @@ -440,17 +443,24 @@ in # disallowed hosts"). Allow loopback + RFC-1918 sources # so an in-hive mirror of the hyperhive repo works. migrations.ALLOW_LOCALNETWORKS = true; - # Webhook deliveries target the gateway - # (`https:///webhook/*`), which resolves to a - # private (RFC-1918) gateway IP. Forgejo's webhook SSRF guard - # denies private hosts by default, so the config-PR + knowledge - # webhooks never actually deliver — only the 5-min poll fallback - # catches config PRs. The gateway host is always allowed (scoping - # to that one hostname keeps the SSRF surface tighter than the - # broad `private` builtin); operators extend the list via - # `forge.webhookAllowedHosts` for webhooks on their own repos. + # `ALLOWED_HOST_LIST` is forgejo's webhook SSRF allow-list, and + # it's a STRICT whitelist (only listed hosts deliver). Its + # default is the `external` builtin: all public unicast IPs are + # allowed, private/loopback denied. We must KEEP `external` so + # user-repo webhooks to public hosts (github, slack, …) keep + # working, and ADD the hive gateway host on top: the config-PR + + # knowledge webhooks target `https:///webhook/*`, + # which resolves to a private (RFC-1918) gateway IP that + # `external` alone would deny (so they'd only ever be caught by + # the 5-min poll fallback). Naming the single gateway host is + # tighter than the broad `private` builtin. Operators append more + # hosts via `forge.webhookAllowedHosts`. webhook.ALLOWED_HOST_LIST = lib.concatStringsSep "," ( - [ hyperhiveDomain ] ++ cfg.webhookAllowedHosts + [ + "external" + hyperhiveDomain + ] + ++ cfg.webhookAllowedHosts ); log.LEVEL = "Warn"; ui = { From 5853ce2c8d8dc6ba90ba44125103a986544524e8 Mon Sep 17 00:00:00 2001 From: damocles Date: Tue, 14 Jul 2026 20:26:02 +0200 Subject: [PATCH 4/4] fix(#2417): hardcode external,gateway in forgejo webhook allowlist, drop option --- nix/host-modules/hive-forge/default.nix | 36 ++----------------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/nix/host-modules/hive-forge/default.nix b/nix/host-modules/hive-forge/default.nix index 97194f80..11c60eeb 100644 --- a/nix/host-modules/hive-forge/default.nix +++ b/nix/host-modules/hive-forge/default.nix @@ -196,31 +196,6 @@ in ''; }; - webhookAllowedHosts = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ ]; - example = [ - "ci.example.com" - "hooks.example.org" - ]; - description = '' - Extra hosts to add to forgejo's webhook SSRF allow-list - (`[webhook] ALLOWED_HOST_LIST`, a strict whitelist). The list - always contains the `external` builtin (forgejo's default — all - public hosts, so webhooks to github/slack/etc. from user repos - keep working) plus the hive gateway host - (`services.hyperhive.domain`), which the config-PR and knowledge - webhooks target and which resolves to a private gateway IP that - `external` alone would deny. Set this to additionally allow - webhooks to other hosts (a user's own CI, internal services, - etc.). - - Entries are forgejo hostmatcher patterns: hostnames, globs, - IPs/CIDRs, or the builtins `loopback` / `private` / `external` - / `*`. - ''; - }; - openFirewall = lib.mkOption { type = lib.types.bool; default = false; @@ -453,15 +428,8 @@ in # which resolves to a private (RFC-1918) gateway IP that # `external` alone would deny (so they'd only ever be caught by # the 5-min poll fallback). Naming the single gateway host is - # tighter than the broad `private` builtin. Operators append more - # hosts via `forge.webhookAllowedHosts`. - webhook.ALLOWED_HOST_LIST = lib.concatStringsSep "," ( - [ - "external" - hyperhiveDomain - ] - ++ cfg.webhookAllowedHosts - ); + # tighter than the broad `private` builtin. + webhook.ALLOWED_HOST_LIST = "external,${hyperhiveDomain}"; log.LEVEL = "Warn"; ui = { DEFAULT_THEME = "catppuccin-vibec0re";