Watch
0
0
Fork
You've already forked hyperhive
0

nats and bao can listen on a routable address, opt-in

This commit is contained in:
atlas 2026-11-09 17:32:03 +01:00 • committed by mara
commit 892675628d
3 changed files with 174 additions and 6 deletions

View file

@ -76,6 +76,9 @@ let
deploy.bao.enable = true;
deploy.bao.bootstrapTokenFile = "/run/secrets/bao-bootstrap.token";
swarm.hives.h2.domain = "h2.example.org";
# The firewall opened with no address to listen on: the combination
# `deploy.bao.openFirewall` refuses.
deploy.bao.openFirewall = true;
};
# The store and the token, with no CA to trust. `mkForce` because the PKI
@ -86,6 +89,10 @@ let
deploy.bao.enable = true;
deploy.bao.bootstrapTokenFile = "/run/secrets/bao-bootstrap.token";
deploy.bao.clientCaFile = lib.mkForce null;
# An off-host listener opened in the firewall with no client CA: the
# combination `deploy.bao.openFirewall` refuses.
deploy.bao.extraListenAddresses = [ "10.0.0.1" ];
deploy.bao.openFirewall = true;
};
# The store plus every reader that used to log in as the hive. One fixture
# rather than one per reader: the claim they are *separate* principals is
@ -1695,6 +1702,32 @@ let
lib.any (a: !a.assertion && names a) baoGranterOddHiveLeafRole.assertions
&& !(lib.any (a: !a.assertion && names a) baoGrantHere.assertions);
}
{
# Control: `baoGrantHere` leaves the option off and fails no assertion
# naming it.
name = "openFirewall without a client CA is refused, naming both options";
ok =
let
names =
a:
lib.hasInfix "services.hyperhive.deploy.bao.openFirewall" a.message
&& lib.hasInfix "services.hyperhive.deploy.bao.clientCaFile" a.message;
in
lib.any (a: !a.assertion && names a) baoGrantNoClientCa.assertions
&& !(lib.any (a: !a.assertion && names a) baoGrantHere.assertions);
}
{
name = "openFirewall without an extra listen address is refused, naming both options";
ok =
let
names =
a:
lib.hasInfix "services.hyperhive.deploy.bao.openFirewall" a.message
&& lib.hasInfix "services.hyperhive.deploy.bao.extraListenAddresses" a.message;
in
lib.any (a: !a.assertion && names a) baoGrantTwoHives.assertions
&& !(lib.any (a: !a.assertion && names a) baoGrantHere.assertions);
}
{
# The granter writes pki roles through `roles/swarm-*` only, so a role
# named otherwise is refused at eval rather than 403'd at deploy.

View file

@ -21,6 +21,7 @@ let
hive
runGroup
baoSettings
bridgePorts
forceServiceConfigs
selfScrapes
;
@ -28,6 +29,7 @@ let
baoTwoAddresses = hive {
deploy.bao.enable = true;
deploy.bao.extraListenAddresses = [ "10.0.0.1" ];
deploy.bao.openFirewall = true;
# Pinned, not incidental: the case counting these listeners is about the
# declared API addresses. ⚠️ Leaving the swarm tier off does not keep the
# METRICS listener out of the count — that one is a function of the
@ -106,6 +108,31 @@ let
in
l.loopback.address == "127.0.0.1:8200" && l.extra-1.address == "10.0.0.1:8200";
}
{
# On: the port is open host-wide and the extra listener demands a client
# certificate, like loopback. Evaluates with no assertion naming the
# option, so the two refusals in bao-grants are not firing here.
name = "openFirewall opens the store port host-wide, and the extra listener requires a client certificate";
ok =
let
l = (baoSettings baoTwoAddresses).listener;
in
lib.elem 8200 baoTwoAddresses.networking.firewall.allowedTCPPorts
&& l.extra-1.tls_require_and_verify_client_cert == true
&& l.extra-1.tls_client_ca_file == l.loopback.tls_client_ca_file
&& !(lib.any (
a: !a.assertion && lib.hasInfix "deploy.bao.openFirewall" a.message
) baoTwoAddresses.assertions);
}
{
# Off is today's config: the port is open on the bridge for agents and on
# no other interface. The bridge arm is the control that the lookup finds
# a port that is there.
name = "without openFirewall the store port is open on the bridge only";
ok =
!(lib.elem 8200 baoOtelElsewhere.networking.firewall.allowedTCPPorts)
&& lib.elem 8200 (bridgePorts baoOtelElsewhere);
}
{
# Control for the case above: these settings are rendered per deployment,
# not constants a passing case could be indifferent to. The metrics and

View file

@ -42,6 +42,7 @@ let
# The queue on a host whose store is elsewhere: no local policy unit.
queueNoStore = hive {
deploy.nats.enable = true;
deploy.nats.openFirewall = true;
};
inherit (shared) natsOnStoreHostReaderMoved;
@ -256,6 +257,12 @@ let
name = "the server requires TLS: no allow_non_tls";
ok = !(natsContainer.services.nats.settings ? allow_non_tls);
}
{
# Reachability is the firewall alone because no listen address is set.
name = "the server sets no listen address, so it binds every address";
ok =
!(natsContainer.services.nats.settings ? host) && !(natsContainer.services.nats.settings ? listen);
}
{
name = "4222 is open on wg-hive when this host is on the mesh, and never host-wide";
ok =
@ -263,6 +270,19 @@ let
&& !(lib.elem 4222 allLocalMesh.networking.firewall.allowedTCPPorts)
&& lib.elem 4222 (bridgePorts allLocalMesh);
}
{
# Off is today's config; the bridge arm is the control that the lookup
# finds a port that is there. On adds the host-wide port and keeps the
# bridge one.
name = "4222 is open host-wide only with openFirewall, and the bridge keeps it either way";
ok =
queueNoStore.services.hyperhive.deploy.nats.openFirewall
&& lib.elem 4222 queueNoStore.networking.firewall.allowedTCPPorts
&& lib.elem 4222 (bridgePorts queueNoStore)
&& !allLocal.services.hyperhive.deploy.nats.openFirewall
&& !(lib.elem 4222 allLocal.networking.firewall.allowedTCPPorts)
&& lib.elem 4222 (bridgePorts allLocal);
}
{
name = "4222 is not opened on wg-hive when this host is not on the mesh";
ok =