hive-matrix: run the homeserver in its own netns, behind a unix socket
tuwunel bound `0.0.0.0:<httpPort>` in the host netns, so the homeserver
port was open on every host address, and the gateway proxied to it on
host loopback.
The container now sets `privateNetwork = true` and hangs off the hive
bridge with a DHCP lease, the same attach the CI runner, builder and
review classifier use (resolvconf on, `networking.nameservers` the bridge
IP). tuwunel listens only on `/run/hive-matrix/tuwunel.sock`, a host
directory (root:nginx 0770) bind-mounted into the container; its primary
group is nginx's gid and the socket is 0660. The gateway's `/_matrix/`
location proxies to the socket.
The static resolv.conf goes: it existed because resolvconf's host
tracking came up empty in the shared netns.
`swarm.matrix.httpPort` and `{swarm,deploy}.matrix.openFirewall` are
removed with a message: there is no host port to choose or open. The
`.well-known/matrix/*` fallback to `<hive>:<httpPort>` is dropped with
them; it only rendered with `gatewayHost = null`, which the homeserver's
SSO assertion already rejects.
Part of #4815.
This commit is contained in:
parent
e455964cb3
commit
34e142bb5c
11 changed files with 191 additions and 163 deletions
|
|
@ -57,8 +57,8 @@ let
|
|||
swarm.matrix.gatewayHost = "matrix.hive.t.local";
|
||||
};
|
||||
|
||||
# No matrix vhost for this swarm at all: tuwunel stays direct on
|
||||
# `httpPort`, so there is no homeserver URL to hand the controller.
|
||||
# No matrix vhost for this swarm at all, so there is no homeserver URL
|
||||
# to hand the controller.
|
||||
controllerNoGatewayHost = hive {
|
||||
deploy.swarm-controller.enable = true;
|
||||
swarm.matrix.gatewayHost = null;
|
||||
|
|
|
|||
|
|
@ -163,8 +163,8 @@ let
|
|||
lib.filter (l: swarmServiceLinkDomains ? ${l.label}) cfg.services.hyperhive.swarm.controller.links;
|
||||
|
||||
# Every service container at once: the services-only host plus the CI
|
||||
# runner. The CI runner, the review classifier, the builder and the metrics
|
||||
# store are the four containers on a netns of their own.
|
||||
# runner. The CI runner, the review classifier, the builder, the metrics
|
||||
# store and the homeserver are the five containers on a netns of their own.
|
||||
serviceContainersWithCi = hive {
|
||||
deploy.hive-controller.enable = false;
|
||||
deploy.allSwarmServices = true;
|
||||
|
|
@ -522,7 +522,7 @@ let
|
|||
let
|
||||
shared = lib.filter (c: !c.privateNetwork) (lib.attrValues serviceContainers);
|
||||
in
|
||||
lib.length shared == 8
|
||||
lib.length shared == 7
|
||||
&& lib.all (
|
||||
c: !c.config.networking.firewall.enable && !c.config.networking.resolvconf.enable
|
||||
) shared;
|
||||
|
|
@ -575,6 +575,46 @@ let
|
|||
&& unit.serviceConfig.UMask == "0007"
|
||||
&& lib.hasInfix "--unix-socket ${sock}" unit.postStart;
|
||||
}
|
||||
{
|
||||
# On the bridge by DHCP, like the CI runner, and its socket the only
|
||||
# listener the gateway dials. `address` unset is what keeps tuwunel off
|
||||
# TCP. Every vhost location that forwards goes through the socket; the
|
||||
# `/_matrix/` one is named so an empty filter can't pass.
|
||||
name = "the homeserver runs on a netns of its own and the gateway reaches it over its socket";
|
||||
ok =
|
||||
let
|
||||
host = serviceContainersWithCi;
|
||||
net = host.services.hyperhive.network;
|
||||
c = host.containers.hive-matrix;
|
||||
dir = "/run/hive-matrix";
|
||||
sock = "${dir}/tuwunel.sock";
|
||||
locs =
|
||||
host.services.nginx.virtualHosts.${host.services.hyperhive.swarm.matrix.gatewayHost}.locations;
|
||||
forwarded = lib.filter (p: p != null) (map (l: l.proxyPass) (lib.attrValues locs));
|
||||
tuwunel = c.config.services.matrix-tuwunel;
|
||||
unit = c.config.systemd.services.tuwunel;
|
||||
nginxGid = host.ids.gids.nginx;
|
||||
in
|
||||
c.privateNetwork
|
||||
&& c.hostBridge == net.bridgeName
|
||||
&& c.hostAddress == null
|
||||
&& c.localAddress == null
|
||||
&& c.config.networking.interfaces.eth0.useDHCP
|
||||
&& c.config.networking.nameservers == [ net.bridgeIp ]
|
||||
&& c.config.networking.firewall.enable
|
||||
&& c.config.networking.resolvconf.enable
|
||||
&& (tuwunel.settings.global.address or null) == null
|
||||
&& tuwunel.settings.global.unix_socket_path == sock
|
||||
&& tuwunel.settings.global.unix_socket_perms == 660
|
||||
&& locs."/_matrix/".proxyPass == "http://unix:${sock}:"
|
||||
&& forwarded == [ "http://unix:${sock}:" ]
|
||||
&& c.bindMounts.${dir}.hostPath == dir
|
||||
&& builtins.elem "d ${dir} 0770 root ${toString nginxGid} - -" host.systemd.tmpfiles.rules
|
||||
&& c.config.users.groups.gateway-nginx.gid == nginxGid
|
||||
&& tuwunel.group == "gateway-nginx"
|
||||
&& unit.serviceConfig.Group == "gateway-nginx"
|
||||
&& lib.elem dir unit.serviceConfig.ReadWritePaths;
|
||||
}
|
||||
{
|
||||
# The DynamicUser layout keeps the history under /var/lib/private; the
|
||||
# static user's StateDirectory is the plain path. The move runs as its
|
||||
|
|
|
|||
Loading…
Reference in a new issue