Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2a96490d3 | ||
|
|
806d0e4a61 | ||
|
|
c97120f016 |
4 changed files with 53 additions and 17 deletions
|
|
@ -200,19 +200,21 @@ dashboard reach by design — the surface is privileged (approve /
|
|||
deny / destroy) and must not be exposed without a real reverse
|
||||
proxy in front.
|
||||
|
||||
## `HIVE_FORGE_URL`: loopback for in-cluster, sub-domain for the operator
|
||||
## `HIVE_FORGE_URL`: domain via gateway for isolated agents, loopback for shared-netns
|
||||
|
||||
Agents poll `HIVE_FORGE_URL` for Forgejo notifications + run all
|
||||
`hive-forge` calls against it. `hive-c0re.nix` pins this to
|
||||
`http://127.0.0.1:<forge.httpPort>` for the in-cluster path: every
|
||||
agent container shares the host's network namespace, so loopback
|
||||
reaches the forge container directly with no DNS lookup needed.
|
||||
`hive-forge` calls against it. `hive-c0re.nix` sets this based on the
|
||||
network isolation mode:
|
||||
|
||||
The sub-domain default (`forge.<hive-domain>`) is for **operator
|
||||
browsers + cross-host clients**, not in-cluster traffic. Using the
|
||||
sub-domain URL inside agent containers would fail every `hive-forge`
|
||||
invocation with "Name or service not known" — the agent's nspawn
|
||||
doesn't have DNS for the external hostname.
|
||||
- **`network.isolateContainers = true`**: agents run in private netns and
|
||||
get the bridge dnsmasq as their resolver. `HIVE_FORGE_URL` is set to
|
||||
`http://<forge.domain>` (default `forge.<hive-domain>`). Agents resolve
|
||||
the hostname via dnsmasq → bridge IP, then reach nginx on port 80 (bridge
|
||||
firewall opens 80+443 when isolation is on). nginx proxies to forgejo — the
|
||||
same path an operator browser takes, no raw port exposure needed.
|
||||
- **`network.isolateContainers = false`** (default): agents share the host's
|
||||
network namespace, so loopback reaches forgejo directly. `HIVE_FORGE_URL`
|
||||
is `http://127.0.0.1:<forge.httpPort>`.
|
||||
|
||||
## hive-forge container shape
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,11 @@ agent containers.
|
|||
interface only. Other interfaces stay closed. The hive resolver
|
||||
isn't an external-facing service.
|
||||
|
||||
When `isolateContainers = true`, `allowedTCPPorts` is extended with
|
||||
`[ 80 443 ]` so isolated agents can reach nginx (gateway container,
|
||||
shared host netns) for the forge sub-domain, per-agent UI proxies,
|
||||
and any other HTTP services.
|
||||
|
||||
## Container isolation
|
||||
|
||||
`services.hyperhive.network.isolateContainers` (default `false`) flips
|
||||
|
|
@ -108,6 +113,8 @@ agent containers from shared host netns to private netns. Set only after
|
|||
| IP forwarding | `boot.kernel.sysctl."net.ipv4.ip_forward" = 1` |
|
||||
| Internet NAT | `networking.nat { enable = true; internalInterfaces = [ bridgeName ]; }` — MASQUERADE on packets leaving via any external NIC |
|
||||
| Loopback DROP | `networking.firewall.extraInputRules` — drops bridge-subnet → `127.0.0.0/8` traffic; defence-in-depth against routing table leaks |
|
||||
| Gateway access | `networking.firewall.interfaces.<bridge>.allowedTCPPorts = [ 80 443 ]` — lets isolated agents reach nginx on the host (shared netns) |
|
||||
| Forge URL | `HIVE_FORGE_URL` flips from `http://127.0.0.1:3000` to `http://forge.<domain>` — agents resolve via dnsmasq, nginx proxies to forgejo |
|
||||
| c0re signal | `HIVE_NETWORK_ISOLATION=1`, `HIVE_NETWORK_BRIDGE`, `HIVE_NETWORK_SUBNET` in `systemd.services.hive-c0re.environment` |
|
||||
|
||||
`HIVE_NETWORK_SUBNET` is the host-side bridge IP + prefix (e.g.
|
||||
|
|
|
|||
|
|
@ -557,12 +557,19 @@ in
|
|||
HYPERHIVE_SWARM_NAME = config.services.hyperhive.swarmName;
|
||||
}
|
||||
// lib.optionalAttrs config.services.hyperhive.forge.enable {
|
||||
# Loopback for in-cluster calls (agents share host netns;
|
||||
# external `forge.<hive>` sub-domain isn't DNS-resolvable
|
||||
# from inside nspawn). See
|
||||
# `docs/gateway.md::HIVE_FORGE_URL: loopback for in-cluster,
|
||||
# sub-domain for the operator`.
|
||||
HIVE_FORGE_URL = "http://127.0.0.1:${toString config.services.hyperhive.forge.httpPort}";
|
||||
# In-cluster forge URL.
|
||||
# - Isolated (private netns): containers resolve `forge.<domain>` via
|
||||
# the bridge dnsmasq and reach nginx on port 80. No raw forge port
|
||||
# needed — nginx proxies to forgejo as it does for the operator.
|
||||
# - Shared netns: host loopback is reachable, use direct port.
|
||||
# See `docs/gateway.md::HIVE_FORGE_URL`.
|
||||
HIVE_FORGE_URL =
|
||||
if
|
||||
config.services.hyperhive.network.enable && config.services.hyperhive.network.isolateContainers
|
||||
then
|
||||
"http://${config.services.hyperhive.forge.domain}"
|
||||
else
|
||||
"http://127.0.0.1:${toString config.services.hyperhive.forge.httpPort}";
|
||||
}
|
||||
// lib.optionalAttrs config.services.hyperhive.matrix.gui.enable {
|
||||
# Availability flags read by the dashboard's `/api/state`.
|
||||
|
|
|
|||
|
|
@ -196,6 +196,18 @@ in
|
|||
resolver must be running before isolation is flipped on).
|
||||
'';
|
||||
}
|
||||
{
|
||||
assertion =
|
||||
!config.services.hyperhive.forge.enable || config.services.hyperhive.gateway.enable;
|
||||
message = ''
|
||||
services.hyperhive.network.isolateContainers = true with
|
||||
services.hyperhive.forge.enable = true requires
|
||||
services.hyperhive.gateway.enable = true — isolated agents
|
||||
reach the forge via `http://forge.<domain>` which nginx (in
|
||||
the gateway container) proxies to forgejo. Without the gateway
|
||||
there is nothing listening on port 80 to serve that hostname.
|
||||
'';
|
||||
}
|
||||
];
|
||||
})
|
||||
|
||||
|
|
@ -215,6 +227,14 @@ in
|
|||
ip saddr ${cfg.bridgeIp}/${toString cfg.bridgePrefixLength} ip daddr 127.0.0.0/8 drop
|
||||
'';
|
||||
|
||||
# Allow isolated agents to reach the gateway (nginx on the host, shared
|
||||
# netns). Port 80 covers `http://forge.<domain>`, per-agent UI proxies,
|
||||
# and any other HTTP services the gateway fronts. Port 443 for HTTPS.
|
||||
networking.firewall.interfaces.${cfg.bridgeName}.allowedTCPPorts = [
|
||||
80
|
||||
443
|
||||
];
|
||||
|
||||
# Tells hive-c0re to pass PRIVATE_NETWORK + bridge settings to each
|
||||
# container. HIVE_NETWORK_SUBNET is host-bridge IP/prefix, not canonical
|
||||
# network address — the Rust side normalises before subnet arithmetic.
|
||||
|
|
|
|||
Loading…
Reference in a new issue