82 lines
3.9 KiB
Nix
82 lines
3.9 KiB
Nix
# In-container network plumbing: DHCP on the bridge veth, resolvconf
|
|
# taken out of the loop, and the oneshot that points resolv.conf at
|
|
# the hive bridge resolver.
|
|
{
|
|
pkgs,
|
|
lib,
|
|
...
|
|
}:
|
|
{
|
|
# Take resolvconf + dhcpcd out of the /etc/resolv.conf loop so the
|
|
# bridge resolver the oneshot below writes actually sticks. At their
|
|
# NixOS defaults, resolvconf regenerates resolv.conf from host-tracking
|
|
# *after* the oneshot has pointed it at the bridge (dhcpcd re-triggers
|
|
# that when the veth comes up under isolation) — silently clobbering the
|
|
# bridge nameserver back to the host resolver, which isn't authoritative
|
|
# for the hive's own zones, so `forge.<domain>` stops resolving. We
|
|
# disable resolvconf and tell dhcpcd not to touch resolv.conf (without
|
|
# disabling dhcpcd itself, so the veth still gets its address); then
|
|
# the hyperhive-isolated-dns oneshot owns resolv.conf. (Same "take
|
|
# resolvconf out of the loop" approach the matrix container uses.)
|
|
# All agent containers receive their bridge IP via DHCP from the hive
|
|
# dnsmasq pool (see nix/host-modules/hive-gateway.nix). useDHCP runs dhcpcd
|
|
# on every interface (just eth0 in practice — the nspawn bridge veth).
|
|
config = {
|
|
networking.useDHCP = true;
|
|
networking.resolvconf.enable = false;
|
|
networking.dhcpcd.extraConfig = "nohook resolv.conf";
|
|
|
|
# Point resolv.conf at the hive bridge resolver when the container is
|
|
# network-isolated. nixos-container copies the *host's* /etc/resolv.conf
|
|
# into the container at every start — but the host resolver (e.g.
|
|
# 127.0.0.53) is unreachable from a private netns and isn't
|
|
# authoritative for the hive's own zones (forge.<domain> etc.). The
|
|
# bridge dnsmasq (gateway IP) is. hive-priv drops the marker
|
|
# `/etc/hyperhive-bridge-dns` (containing the gateway IP) since
|
|
# isolation is always on; the oneshot reads it and rewrites
|
|
# resolv.conf on every boot. Ordered before the first DNS consumer
|
|
# (tea-login) and the network targets so name resolution works for
|
|
# the very first turn.
|
|
systemd.services.hyperhive-isolated-dns = {
|
|
description = "point resolv.conf at the hive bridge resolver (isolated containers)";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "local-fs.target" ];
|
|
# Ordered before every network consumer that does DNS on first
|
|
# boot. `hive-agent` (the harness) is the load-bearing one: its
|
|
# first-turn api.anthropic.com lookup must not race the resolv.conf
|
|
# rewrite (it only declares `after network.target`, so without this
|
|
# edge the harness can start before we've fixed resolv.conf and the
|
|
# first turn errors — self-heals next turn, but better not to flap).
|
|
# `hive-matrix-daemon` likewise syncs over the network; the `before`
|
|
# is a harmless no-op when matrix is disabled (the unit is absent).
|
|
before = [
|
|
"network-online.target"
|
|
"tea-login.service"
|
|
"hive-agent.service"
|
|
"hive-matrix-daemon.service"
|
|
];
|
|
unitConfig.ConditionPathExists = "/etc/hyperhive-bridge-dns";
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
# Pin the journal identity; without it systemd derives it from the
|
|
# generated `script` store-path wrapper (an opaque `<hash>-…-start`).
|
|
SyslogIdentifier = "hyperhive-isolated-dns";
|
|
};
|
|
path = [ pkgs.coreutils ];
|
|
script = ''
|
|
set -eu
|
|
gw=$(tr -d '[:space:]' < /etc/hyperhive-bridge-dns)
|
|
if [ -z "$gw" ]; then
|
|
echo "hyperhive-isolated-dns: empty marker; leaving resolv.conf as-is"
|
|
exit 0
|
|
fi
|
|
# resolv.conf is a regular file copied from the host by
|
|
# nixos-container; replace it (rm first in case it's a symlink).
|
|
rm -f /etc/resolv.conf
|
|
printf 'nameserver %s\n' "$gw" > /etc/resolv.conf
|
|
echo "hyperhive-isolated-dns: resolv.conf -> nameserver $gw"
|
|
'';
|
|
};
|
|
};
|
|
}
|