infrastructure/nixos-modules/qois/loadbalancer/default.nix

169 lines
4.3 KiB
Nix

{
config,
pkgs,
lib,
...
}:
with lib;
let
# We assume that all static pages are hosted on lindberg-webapps
staticPages = pipe config.qois.static-page.pages [
(mapAttrsToList (name: { domain, domainAliases, ... }: [ domain ] ++ domainAliases))
flatten
(map (name: {
inherit name;
value = "lindberg-webapps";
}))
listToAttrs
];
defaultDomains = staticPages // {
"cloud.qo.is" = "lindberg-nextcloud";
"build.qo.is" = "lindberg-build";
"gitlab-runner.qo.is" = "lindberg-build";
"nixpkgs-cache.qo.is" = "lindberg-build";
"attic.qo.is" = "lindberg-build";
"vault.qo.is" = "lindberg-webapps";
"git.qo.is" = "lindberg-webapps";
"kokus.raphael.li" = "lindberg-rzimmermann";
"auth.raphael.li" = "lindberg-rzimmermann";
"toolia.raphael.li" = "lindberg-rzimmermann";
"ha.raphael.li" = "lindberg-rzimmermann";
"www.raphael.li" = "lindberg-rzimmermann";
"vpn.qo.is" = "cyprianspitz-headscale";
};
getBackplaneIp = hostname: config.qois.meta.network.virtual.backplane.hosts.${hostname}.v4.ip;
defaultHostmap =
lib.pipe
[
"lindberg-nextcloud"
"lindberg-build"
"lindberg-webapps"
]
[
(map (name: {
inherit name;
value = getBackplaneIp name;
}))
lib.listToAttrs
];
defaultExtraConfig =
let
headscalePort = toString 46084;
rzimmermannIp = "10.247.0.113";
in
''
# lindberg-rzimmermann (uses send-proxy-v2)
backend lindberg-rzimmermann-https
mode tcp
server s1 ${rzimmermannIp}:443 send-proxy-v2
backend lindberg-rzimmermann-http
mode http
server s1 ${rzimmermannIp}:80
# cyprianspitz headscale
backend cyprianspitz-headscale-http
mode http
server s1 ${getBackplaneIp "cyprianspitz"}:${headscalePort}
backend cyprianspitz-headscale-https
mode tcp
server s1 ${getBackplaneIp "cyprianspitz"}:${headscalePort}
'';
cfg = config.qois.loadbalancer;
in
{
options.qois.loadbalancer = {
enable = mkEnableOption "Enable services http+s loadbalancing";
domains = mkOption {
description = "Domain to hostname mappings";
type = with lib.types; attrsOf str;
default = defaultDomains;
};
hostmap = mkOption {
description = "Hostname to IP mappings for TLS-TCP and http forwarding";
type = with lib.types; attrsOf str;
default = defaultHostmap;
};
extraConfig = mkOption {
description = "Additional haproxy mapping configs. Amended to services.haproxy.config. Make sure indentations are correct.";
type = types.nullOr types.lines;
default = defaultExtraConfig;
};
};
config =
with lib;
mkIf cfg.enable {
networking.firewall.allowedTCPPorts = [
80
443
];
services.haproxy =
let
domainMappingFile = pipe cfg.domains [
(mapAttrsToList (host: backend: "${host} ${backend}"))
concatLines
(pkgs.writeText "haproxy_backend_map")
];
genHttpBackend = hostName: ip: ''
# Mapping for ${hostName}
backend ${hostName}-https
mode tcp
server s1 ${ip}:443
backend ${hostName}-http
mode http
server s1 ${ip}:80
'';
httpBackends = pipe cfg.hostmap [
(mapAttrsToList genHttpBackend)
concatLines
];
in
{
enable = true;
config = ''
defaults
mode http
retries 3
maxconn 2000
timeout connect 5000
timeout client 50000
timeout server 50000
frontend http
mode http
bind *:80
use_backend %[req.hdr(host),lower,map_dom(${domainMappingFile})]-http
frontend https
bind *:443
mode tcp
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
use_backend %[req.ssl_sni,lower,map_dom(${domainMappingFile})]-https
## Generated Backends:
${httpBackends}
## extraConfig
${cfg.extraConfig}
'';
};
};
}