containers reach network-online only with a dhcp lease, and bao users wait for it
This commit is contained in:
parent
04f8683f99
commit
81a57a9f81
10 changed files with 77 additions and 3 deletions
|
|
@ -56,6 +56,9 @@ The flows, end to end:
|
||||||
|
|
||||||
- **DHCP** — agent `dhcpcd` broadcasts on `eth0` → veth → bridge →
|
- **DHCP** — agent `dhcpcd` broadcasts on `eth0` → veth → bridge →
|
||||||
host firewall (udp 67 hole) → dnsmasq pool → lease + router option.
|
host firewall (udp 67 hole) → dnsmasq pool → lease + router option.
|
||||||
|
`dhcpcd` runs with `noipv4ll` and waits for an IPv4 address, so
|
||||||
|
`network-online.target` means a lease, never a 169.254.x fallback; units
|
||||||
|
that look up a swarm name at start order after it.
|
||||||
- **DNS** — agents and the service containers query `bridgeIp:53`; dnsmasq
|
- **DNS** — agents and the service containers query `bridgeIp:53`; dnsmasq
|
||||||
answers hive zones authoritatively with the bridge IP, everything else
|
answers hive zones authoritatively with the bridge IP, everything else
|
||||||
forwards to the host's resolvers (see _Resolver behaviour_ below). Each
|
forwards to the host's resolvers (see _Resolver behaviour_ below). Each
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,8 @@ in
|
||||||
config = lib.mkIf configured {
|
config = lib.mkIf configured {
|
||||||
systemd.services.${unitName} = {
|
systemd.services.${unitName} = {
|
||||||
description = "keep this agent's swarm secret store identity current, and prove it works";
|
description = "keep this agent's swarm secret store identity current, and prove it works";
|
||||||
after = [ "network.target" ];
|
after = [ "network-online.target" ];
|
||||||
|
wants = [ "network-online.target" ];
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
path = [
|
path = [
|
||||||
pkgs.openbao
|
pkgs.openbao
|
||||||
|
|
|
||||||
|
|
@ -85,9 +85,10 @@ in
|
||||||
${unitName} = {
|
${unitName} = {
|
||||||
description = "fetch the swarm root certificate from the secret store";
|
description = "fetch the swarm root certificate from the secret store";
|
||||||
after = [
|
after = [
|
||||||
"network.target"
|
"network-online.target"
|
||||||
"hive-agent-bao-identity.service"
|
"hive-agent-bao-identity.service"
|
||||||
];
|
];
|
||||||
|
wants = [ "network-online.target" ];
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
path = [
|
path = [
|
||||||
pkgs.coreutils
|
pkgs.coreutils
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,14 @@
|
||||||
config = {
|
config = {
|
||||||
networking.useDHCP = true;
|
networking.useDHCP = true;
|
||||||
networking.resolvconf.enable = false;
|
networking.resolvconf.enable = false;
|
||||||
networking.dhcpcd.extraConfig = "nohook resolv.conf";
|
# `noipv4ll` and `wait = "ipv4"`: dhcpcd counts an IPv4LL address as an
|
||||||
|
# address, so by default `network-online.target` is reached on 169.254.x
|
||||||
|
# before the lease arrives. With both set it waits for the lease.
|
||||||
|
networking.dhcpcd.extraConfig = ''
|
||||||
|
nohook resolv.conf
|
||||||
|
noipv4ll
|
||||||
|
'';
|
||||||
|
networking.dhcpcd.wait = "ipv4";
|
||||||
|
|
||||||
# A unicast DHCP renewal reply otherwise reaches dhcpcd only by
|
# A unicast DHCP renewal reply otherwise reaches dhcpcd only by
|
||||||
# matching the firewall's ESTABLISHED,RELATED conntrack rule against
|
# matching the firewall's ESTABLISHED,RELATED conntrack rule against
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,13 @@ in
|
||||||
(lib.mkIf (!cfg.privateNetwork) {
|
(lib.mkIf (!cfg.privateNetwork) {
|
||||||
networking.firewall.enable = false;
|
networking.firewall.enable = false;
|
||||||
})
|
})
|
||||||
|
# A private-netns container with an interface takes its address from the
|
||||||
|
# hive's DHCP. dhcpcd counts an IPv4LL address as an address, so without
|
||||||
|
# these `network-online.target` is reached on 169.254.x before the lease.
|
||||||
|
(lib.mkIf cfg.privateNetwork {
|
||||||
|
networking.dhcpcd.extraConfig = "noipv4ll";
|
||||||
|
networking.dhcpcd.wait = "ipv4";
|
||||||
|
})
|
||||||
(lib.mkIf cfg.writesOwnResolvConf {
|
(lib.mkIf cfg.writesOwnResolvConf {
|
||||||
networking.resolvconf.enable = lib.mkForce false;
|
networking.resolvconf.enable = lib.mkForce false;
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,8 @@ in
|
||||||
# (../host-modules/lib/store-retry.nix), and a collector that restarts until the file lands.
|
# (../host-modules/lib/store-retry.nix), and a collector that restarts until the file lands.
|
||||||
systemd.services.${fetchUnit} = lib.mkIf fetchesHere {
|
systemd.services.${fetchUnit} = lib.mkIf fetchesHere {
|
||||||
description = "fetch this container's journal forwarder OIDC client secret from the swarm secret store";
|
description = "fetch this container's journal forwarder OIDC client secret from the swarm secret store";
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
wants = [ "network-online.target" ];
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
before = [ "opentelemetry-collector.service" ];
|
before = [ "opentelemetry-collector.service" ];
|
||||||
path = [
|
path = [
|
||||||
|
|
|
||||||
|
|
@ -1066,6 +1066,8 @@ in
|
||||||
# directory: neither unit's stop may remove it from under the other.
|
# directory: neither unit's stop may remove it from under the other.
|
||||||
systemd.services.swarm-matrix-appservice-render = lib.mkIf ctlActive {
|
systemd.services.swarm-matrix-appservice-render = lib.mkIf ctlActive {
|
||||||
description = "render the swarm's appservice registration";
|
description = "render the swarm's appservice registration";
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
wants = [ "network-online.target" ];
|
||||||
before = [ "tuwunel.service" ];
|
before = [ "tuwunel.service" ];
|
||||||
wantedBy = [ "tuwunel.service" ];
|
wantedBy = [ "tuwunel.service" ];
|
||||||
inherit (storeRetry) startLimitBurst startLimitIntervalSec;
|
inherit (storeRetry) startLimitBurst startLimitIntervalSec;
|
||||||
|
|
|
||||||
|
|
@ -223,6 +223,30 @@ let
|
||||||
in
|
in
|
||||||
lib.hasInfix "bao kv get -format=json ${arg} >\"$raw\"" (agentBaoIdentity m).script;
|
lib.hasInfix "bao kv get -format=json ${arg} >\"$raw\"" (agentBaoIdentity m).script;
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
# dhcpcd without IPv4LL waiting for IPv4 makes `network-online` a lease,
|
||||||
|
# and the two units that dial the store at start order after it. The
|
||||||
|
# trust bundle's order after the identity check and `nohook` are the
|
||||||
|
# controls that the lookups find something.
|
||||||
|
name = "an agent reaches network-online only with a lease, and its store identity and trust bundle wait for it";
|
||||||
|
ok =
|
||||||
|
let
|
||||||
|
d = agentBao.networking.dhcpcd;
|
||||||
|
after = n: agentBao.systemd.services.${n}.after;
|
||||||
|
wants = n: agentBao.systemd.services.${n}.wants;
|
||||||
|
units = [
|
||||||
|
"hive-agent-bao-identity"
|
||||||
|
"hive-agent-trust-bundle"
|
||||||
|
];
|
||||||
|
in
|
||||||
|
lib.hasInfix "noipv4ll" d.extraConfig
|
||||||
|
&& lib.hasInfix "nohook resolv.conf" d.extraConfig
|
||||||
|
&& d.wait == "ipv4"
|
||||||
|
&& lib.elem "hive-agent-bao-identity.service" (after "hive-agent-trust-bundle")
|
||||||
|
&& lib.all (
|
||||||
|
n: lib.elem "network-online.target" (after n) && lib.elem "network-online.target" (wants n)
|
||||||
|
) units;
|
||||||
|
}
|
||||||
{
|
{
|
||||||
# The absence arm, and what makes the four above able to fail. An agent
|
# The absence arm, and what makes the four above able to fail. An agent
|
||||||
# whose swarm never minted an identity has nothing to log in with, and a
|
# whose swarm never minted an identity has nothing to log in with, and a
|
||||||
|
|
|
||||||
|
|
@ -276,6 +276,8 @@ let
|
||||||
in
|
in
|
||||||
lib.elem "tuwunel.service" r.before
|
lib.elem "tuwunel.service" r.before
|
||||||
&& lib.elem "tuwunel.service" r.wantedBy
|
&& lib.elem "tuwunel.service" r.wantedBy
|
||||||
|
&& lib.elem "network-online.target" r.after
|
||||||
|
&& lib.elem "network-online.target" r.wants
|
||||||
&& !(lib.elem "tuwunel.service" (r.requiredBy or [ ]))
|
&& !(lib.elem "tuwunel.service" (r.requiredBy or [ ]))
|
||||||
&&
|
&&
|
||||||
baoWithMatrix.containers.hive-matrix.config.systemd.services.tuwunel.serviceConfig.Restart
|
baoWithMatrix.containers.hive-matrix.config.systemd.services.tuwunel.serviceConfig.Restart
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,31 @@ let
|
||||||
&& !(sc.DynamicUser or false)
|
&& !(sc.DynamicUser or false)
|
||||||
&& swarmServicesHere.services.hyperhive.deploy.nats.enable;
|
&& swarmServicesHere.services.hyperhive.deploy.nats.enable;
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
# dhcpcd without IPv4LL, waiting for IPv4: `network-online` is a lease.
|
||||||
|
# The router sets no `nohook` itself and authelia does; `nohook` is the
|
||||||
|
# control that the lookup finds `extraConfig`. The queue shares the host
|
||||||
|
# netns and keeps the default.
|
||||||
|
name = "private-netns service containers wait for a DHCP lease and the forwarder fetch waits for it";
|
||||||
|
ok =
|
||||||
|
let
|
||||||
|
cfgOf = name: swarmServicesHere.containers.${name}.config;
|
||||||
|
leased =
|
||||||
|
name:
|
||||||
|
let
|
||||||
|
d = (cfgOf name).networking.dhcpcd;
|
||||||
|
in
|
||||||
|
lib.hasInfix "noipv4ll" d.extraConfig && d.wait == "ipv4";
|
||||||
|
fetch = (cfgOf "swarm-builder").systemd.services.swarm-journal-forwarder-oidc;
|
||||||
|
in
|
||||||
|
leased "swarm-notify-router"
|
||||||
|
&& leased "swarm-authelia"
|
||||||
|
&& lib.hasInfix "nohook resolv.conf" (cfgOf "swarm-authelia").networking.dhcpcd.extraConfig
|
||||||
|
&& !(lib.hasInfix "noipv4ll" (cfgOf "swarm-nats").networking.dhcpcd.extraConfig)
|
||||||
|
&& (cfgOf "swarm-nats").networking.dhcpcd.wait == "any"
|
||||||
|
&& lib.elem "network-online.target" fetch.after
|
||||||
|
&& lib.elem "network-online.target" fetch.wants;
|
||||||
|
}
|
||||||
{
|
{
|
||||||
name = "the notification router stays off where the shared services are hosted without the store";
|
name = "the notification router stays off where the shared services are hosted without the store";
|
||||||
ok =
|
ok =
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue