gateway: per-vhost public flag, private listen addresses, and IPv6 listeners
This commit is contained in:
parent
74754d1584
commit
fbbdf26a19
1 changed files with 364 additions and 25 deletions
|
|
@ -75,11 +75,43 @@ let
|
|||
};
|
||||
|
||||
# The controller behind a gateway on a non-default TLS port, with the
|
||||
# firewall opened. Its collector URL and its listener are read off it.
|
||||
# firewall opened, a private IPv4 and IPv6 address, and one vhost kept off the
|
||||
# public address. Its collector URL and its listeners are read off it.
|
||||
controllerAltPort = hive {
|
||||
deploy.swarm-controller.enable = true;
|
||||
gateway.httpsPort = 8443;
|
||||
gateway.openFirewall = true;
|
||||
gateway.privateListenAddresses = [
|
||||
"10.100.0.1"
|
||||
"fd00:100::1"
|
||||
];
|
||||
gateway.vhosts."internal.t.local".public = false;
|
||||
};
|
||||
|
||||
# A private address that is the wildcard, beside a vhost that asks to be
|
||||
# private: the listen set is well-formed, the privacy it promises is not.
|
||||
privateWildcard = hive {
|
||||
gateway.privateListenAddresses = [ "0.0.0.0" ];
|
||||
gateway.vhosts."internal.t.local".public = false;
|
||||
};
|
||||
|
||||
# Only its `networking.enableIPv6` default is read.
|
||||
noIPv6 = hiveWith [ { networking.enableIPv6 = false; } ] { };
|
||||
|
||||
gatewayListen = import ../host-modules/hive-gateway/listen.nix { inherit lib; };
|
||||
# nginx fills defaults into each `listen` entry; compare the fields the
|
||||
# gateway sets.
|
||||
listenFields = map (l: {
|
||||
inherit (l) addr port;
|
||||
ssl = l.ssl or false;
|
||||
});
|
||||
httpAt = addr: port: {
|
||||
inherit addr port;
|
||||
ssl = false;
|
||||
};
|
||||
httpsAt = addr: port: {
|
||||
inherit addr port;
|
||||
ssl = true;
|
||||
};
|
||||
|
||||
# A container the operator runs that hyperhive doesn't own, next to one it
|
||||
|
|
@ -2176,6 +2208,120 @@ let
|
|||
&& lib.length (checks "swarm-bao") == 1
|
||||
&& checks "operator-box" == [ ];
|
||||
}
|
||||
{
|
||||
# Presence controls: the IPv6 arm is on in this fixture, and the private
|
||||
# list is empty, so what is absent below is absent by configuration.
|
||||
name = "a gateway with no private addresses listens on both ports of 0.0.0.0 and [::] and nowhere else";
|
||||
ok =
|
||||
let
|
||||
gw = allLocal.services.hyperhive.gateway;
|
||||
l = allLocal.services.nginx.virtualHosts."_".listen;
|
||||
in
|
||||
gw.listenIPv6
|
||||
&& gw.privateListenAddresses == [ ]
|
||||
&&
|
||||
listenFields l == [
|
||||
(httpAt "0.0.0.0" 80)
|
||||
(httpsAt "0.0.0.0" 443)
|
||||
(httpAt "[::]" 80)
|
||||
(httpsAt "[::]" 443)
|
||||
]
|
||||
&& listenFields gw.lib.listen == listenFields l
|
||||
&& gw.lib.privateListen == [ ];
|
||||
}
|
||||
{
|
||||
# `listenIPv6` is the escape for a host whose kernel cannot open an IPv6
|
||||
# socket, and follows `networking.enableIPv6` unless set.
|
||||
name = "the IPv6 listener can be turned off and follows networking.enableIPv6";
|
||||
ok =
|
||||
let
|
||||
off = {
|
||||
port = 80;
|
||||
httpsPort = 443;
|
||||
listenIPv6 = false;
|
||||
privateListenAddresses = [ ];
|
||||
};
|
||||
in
|
||||
listenFields (gatewayListen.public off) == [
|
||||
(httpAt "0.0.0.0" 80)
|
||||
(httpsAt "0.0.0.0" 443)
|
||||
]
|
||||
&& !noIPv6.services.hyperhive.gateway.listenIPv6
|
||||
&& allLocal.services.hyperhive.gateway.listenIPv6;
|
||||
}
|
||||
{
|
||||
# An IPv6 literal is bracketed whichever way the operator wrote it, and
|
||||
# listed once: nginx refuses a duplicate `listen`. The wildcard test is
|
||||
# paired with addresses that are not wildcards.
|
||||
name = "private addresses render IPv6 in brackets, once, and the wildcards are recognised";
|
||||
ok =
|
||||
let
|
||||
cfg = {
|
||||
port = 80;
|
||||
httpsPort = 443;
|
||||
listenIPv6 = true;
|
||||
privateListenAddresses = [
|
||||
"10.100.0.1"
|
||||
"fd00:100::1"
|
||||
"[fd00:100::1]"
|
||||
];
|
||||
};
|
||||
in
|
||||
listenFields (gatewayListen.private cfg) == [
|
||||
(httpAt "10.100.0.1" 80)
|
||||
(httpsAt "10.100.0.1" 443)
|
||||
(httpAt "[fd00:100::1]" 80)
|
||||
(httpsAt "[fd00:100::1]" 443)
|
||||
]
|
||||
&& lib.all gatewayListen.isWildcard [
|
||||
"0.0.0.0"
|
||||
"::"
|
||||
"[::]"
|
||||
"*"
|
||||
]
|
||||
&& !(lib.any gatewayListen.isWildcard cfg.privateListenAddresses);
|
||||
}
|
||||
{
|
||||
# The private vhost binds the private addresses only; the catch-all and
|
||||
# every vhost that stays public bind the wildcards AND the private
|
||||
# addresses, because nginx gives a private address a server set of its
|
||||
# own. The catch-all arm is the presence control for the wildcard check.
|
||||
name = "a vhost with public = false listens on the private addresses only, the catch-all on those and the wildcards";
|
||||
ok =
|
||||
let
|
||||
vh = controllerAltPort.services.nginx.virtualHosts;
|
||||
private = [
|
||||
(httpAt "10.100.0.1" 80)
|
||||
(httpsAt "10.100.0.1" 8443)
|
||||
(httpAt "[fd00:100::1]" 80)
|
||||
(httpsAt "[fd00:100::1]" 8443)
|
||||
];
|
||||
in
|
||||
listenFields vh."internal.t.local".listen == private
|
||||
&& !(lib.any (l: l.addr == "0.0.0.0" || l.addr == "[::]") vh."internal.t.local".listen)
|
||||
&&
|
||||
listenFields vh."_".listen == [
|
||||
(httpAt "0.0.0.0" 80)
|
||||
(httpsAt "0.0.0.0" 8443)
|
||||
(httpAt "[::]" 80)
|
||||
(httpsAt "[::]" 8443)
|
||||
]
|
||||
++ private
|
||||
&& vh."_".default;
|
||||
}
|
||||
{
|
||||
# Both arms: the wildcard refusal fires on the wildcard, and the
|
||||
# "needs an address" refusal stays quiet there because the list is not
|
||||
# empty.
|
||||
name = "a wildcard private address is refused";
|
||||
ok =
|
||||
let
|
||||
fired = lib.filter (a: !a.assertion) privateWildcard.assertions;
|
||||
mentions = s: lib.any (a: lib.hasInfix s a.message) fired;
|
||||
in
|
||||
mentions "privateListenAddresses holds"
|
||||
&& !(mentions "while services.hyperhive.gateway.privateListenAddresses is empty");
|
||||
}
|
||||
]
|
||||
# Starting a unit that stays `active (exited)` is a no-op, so a timer aimed
|
||||
# at one fires and fetches nothing.
|
||||
|
|
|
|||
Loading…
Reference in a new issue