{ config, pkgs, lib, self, ... }: let mainServers = [ "server1" "server2" ]; pillar = self.lib.saltPillarFor "*"; renameAttr = from: to: attrset: builtins.foldl' (result: name: if name == from then result // { "${to}" = attrset.${name}; } else result // { "${name}" = attrset.${name}; } ) {} (builtins.attrNames attrset); # HACK: `type = "phys"` works but once an LXC container is stopped # the VLAN interface is not moved back. forceVeth = interface: interface // { type = "veth"; }; netHasDHCP = net: net == "pub" || net == "serv" || builtins.match "priv[[:digit:]]+" net != null; in { options.salt-pillar = lib.mkOption {}; config.salt-pillar = pillar; config.site.net = lib.mkMerge ([ (builtins.mapAttrs (_: vlan: { vlan = vlan; }) pillar.vlans) (builtins.mapAttrs (_: subnet4: { inherit subnet4; }) pillar.subnets-inet) (builtins.mapAttrs (_: hosts4: { inherit hosts4; }) pillar.hosts-inet) (builtins.mapAttrs (net: dhcpData: { dhcp = { inherit (dhcpData) start end time max-time; server = if netHasDHCP net then "${net}-gw" else null; fixed-hosts = if dhcpData ? fixed-hosts then dhcpData.fixed-hosts else {}; router = dhcpData.host-opts.routers; }; domainName = dhcpData.string-opts.domain-name; }) pillar.dhcp) { core.ospf.secret = pillar.ospf.secret; pub.dynamicDomain = true; c3d2.dynamicDomain = true; c3d2.dhcp = { server = "c3d2-gw3"; router = "c3d2-anon"; start = "172.22.99.100"; end = "172.22.99.199"; fixed-hosts = { "astron.hq.c3d2.de" = "aa:00:5b:08:f0:5b"; "astrom.hq.c3d2.de" = "aa:00:5b:08:f0:5c"; "www1.hq.c3d2.de" = "aa:00:13:8b:03:47"; "dn42.hq.c3d2.de" = "aa:00:42:7a:32:46"; "icq.hq.c3d2.de" = "aa:00:30:f6:27:89"; "jabber1.hq.c3d2.de" = "aa:00:0b:19:8f:14"; "jabber2.hq.c3d2.de" = "aa:00:3d:6a:23:b8"; "wiefelspuetz.hq.c3d2.de" = "aa:00:7f:01:8a:d0"; "git.hq.c3d2.de" = "aa:00:47:d8:57:10"; "fernandopoo.hq.c3d2.de" = "aa:00:f7:52:85:27"; "moleflap.hq.c3d2.de" = "aa:00:0d:b1:6c:67"; "wormhole.hq.c3d2.de" = "00:23:c3:d2:00:76"; "sharing.hq.c3d2.de" = "00:23:c3:d2:75:18"; "drucker.hq.c3d2.de" = "00:23:c3:d2:12:0f"; "knot.hq.c3d2.de" = "52:54:cf:fd:ce:3f"; "bender.hq.c3de.de" = "00:23:df:7e:c8:0a"; "sofafon.hq.c3d2.de" = "b8:27:eb:23:8d:01"; "schalter.hq.c3d2.de" = "b8:27:eb:4c:be:ff"; "beere.hq.c3d2.de" = "b8:27:eb:ac:65:d2"; "ledball1.hq.c3d2.de" = "b8:27:eb:53:0b:27"; "cider.hq.c3d2.de" = "00:0d:93:75:ee:fa"; "semanta.hq.c3d2.de" = "00:ff:e4:bb:ea:2a"; "leviathan.hq.c3d2.de" = "00:ff:08:31:db:e5"; "beere2.hq.c3d2.de" = "b8:27:eb:53:0b:27"; "feile.hq.c3d2.de" = "aa:00:5b:12:c1:f7"; "matemat.hq.c3d2.de" = "a2:1b:7c:e8:19:72"; "172.22.99.98" = "08:00:27:aa:90:e2"; "172.22.99.96" = "08:00:27:bb:8c:b3"; "batman.hq.c3d2.de" = "5c:cf:7f:c0:05:28"; "monit.hq.c3d2.de" = "00:23:ae:94:e7:19"; "storage2.hq.c3d2.de" = "42:5e:0f:4e:f3:cc"; "server2.hq.c3d2.de" = "d0:67:e5:f3:57:10"; "server3.hq.c3d2.de" = "e4:1f:13:2e:4f:c0"; "server4.hq.c3d2.de" = "00:9c:02:a9:26:01"; "minecraft.hq.c3d2.de" = "4a:57:d3:64:fe:e9"; "ustriper.hq.c3d2.de" = "aa:bb:95:33:bb:aa"; "lisbeth.hq.c3d2.de" = "b8:27:eb:a5:ee:5c"; "ruststripe1.hq.c3d2.de" = "06:32:0e:39:21:69"; "fhem.hq.c3d2.de" = "b8:27:eb:9e:8b:db"; "glotzbert.hq.c3d2.de" = "ec:a8:6b:fe:b4:cb"; "pulsebert.hq.c3d2.de" = "b8:27:eb:16:31:61"; "dacbert.hq.c3d2.de" = "dc:a6:32:31:b6:32"; "public-access-proxy.hq.c3d2.de" = "12:24:5f:bd:9b:e7"; "marenz-build.hq.c3d2.de" = "44:1e:a1:59:2e:e8"; "ledbeere.hq.c3d2.de" = "b8:27:eb:60:99:59"; }; time = 86400; max-time = 30 * 86400; }; } # net priv* settings ( builtins.mapAttrs (netName: _: { dynamicDomain = true; }) ( lib.filterAttrs (netName: _: builtins.match "priv[[:digit:]]+" netName != null ) pillar.hosts-inet ) ) ] ++ ( map (ctx: builtins.mapAttrs (_: subnet: { subnets6.${ctx} = subnet; }) pillar.subnets-inet6.${ctx} ) (builtins.attrNames pillar.subnets-inet6) ) ++ ( map (ctx: builtins.mapAttrs (_: subnet: { hosts6.${ctx} = subnet; }) pillar.hosts-inet6.${ctx} ) (builtins.attrNames pillar.hosts-inet6) )); config.site.hosts = lib.mkMerge ( [ { # Static definitions mgmt-gw.firewall.enable = true; priv13-gw.firewall.enable = true; dns.services.dns.enable = true; dnscache = { role = "container"; interfaces.serv = { gw4 = "serv-gw"; gw6 = "serv-gw"; type = "veth"; }; services.dnscache.enable = true; }; upstream1.interfaces.up1.upstream = { provider = "vodafone"; staticIpv4Address = "24.134.104.53"; noNat.subnets6 = [ "2a02:8106:208:5200::/56" ]; }; upstream2.interfaces.up2.upstream = { provider = "vodafone"; noNat.subnets6 = [ "2a02:8106:208:e900::/56" ]; }; upstream3.interfaces.up3.upstream.provider = "starlink"; upstream4.interfaces.up4-pppoe = { type = "pppoe"; upstream = { provider = "dsi"; link = "up4"; staticIpv4Address = "81.201.149.152"; upBandwidth = 98000; noNat.subnets6 = [ "2a00:8180:2000:37::1/128" "2a00:8180:2c00:200::/56" ]; }; }; upstream1.ospf.upstreamInstance = 3; upstream2.ospf.upstreamInstance = 4; anon1.ospf.upstreamInstance = 5; freifunk.ospf.upstreamInstance = 6; upstream3.ospf.upstreamInstance = 7; upstream4.ospf.upstreamInstance = 8; c3d2-gw1.ospf.allowedUpstreams = [ "upstream3" "upstream4" "upstream1" "anon1" "freifunk" ]; c3d2-gw2.ospf.allowedUpstreams = [ "upstream1" "upstream3" "upstream4" "anon1" "freifunk" ]; c3d2-gw3.ospf.allowedUpstreams = [ "upstream4" "upstream3" "upstream1" "anon1" "freifunk" ]; serv-gw.ospf.allowedUpstreams = [ "upstream4" "upstream1" "upstream3" "anon1" "freifunk" ]; cls-gw.ospf.allowedUpstreams = [ "upstream4" "upstream1" "upstream3" "anon1" "freifunk" ]; mgmt-gw.ospf.allowedUpstreams = [ "upstream4" "upstream1" "upstream3" "anon1" "freifunk" ]; bgp.ospf.allowedUpstreams = [ "upstream4" "upstream1" "upstream3" "anon1" "freifunk" ]; anon1.ospf.allowedUpstreams = [ "upstream1" "upstream3" "upstream4" "freifunk" ]; priv17-gw-up3.ospf.allowedUpstreams = [ "upstream3" "upstream4" "upstream1" "anon1" "freifunk" ]; pub-gw.ospf.allowedUpstreams = [ "anon1" "freifunk" ]; c3d2-anon.ospf.allowedUpstreams = [ "anon1" "freifunk" ]; upstream4.forwardPorts = [ { destination = config.site.net.serv.hosts4.public-access-proxy; proto = "tcp"; sourcePort = 80; } { destination = config.site.net.serv.hosts4.public-access-proxy; proto = "tcp"; sourcePort = 443; } { destination = config.site.net.serv.hosts4.bind; proto = "tcp"; sourcePort = 53; reflect = false; } { destination = config.site.net.serv.hosts4.bind; proto = "udp"; sourcePort = 53; reflect = false; } { destination = config.site.net.c3d2.hosts4.dn42; proto = "udp"; sourcePort = 2325; } { destination = config.site.net.c3d2.hosts4.dn42; proto = "udp"; sourcePort = 2399; } { destination = config.site.net.c3d2.hosts4.dn42; proto = "udp"; sourcePort = 2327; } { destination = config.site.net.c3d2.hosts4.dn42; proto = "udp"; sourcePort = 2338; } { destination = config.site.net.c3d2.hosts4.dn42; proto = "udp"; sourcePort = 2339; } { destination = config.site.net.c3d2.hosts4.dn42; proto = "udp"; sourcePort = 40533; } { destination = config.site.net.c3d2.hosts4.dn42; proto = "udp"; sourcePort = 61699; } { destination = "${config.site.net.serv.hosts4.leonos}:22"; proto = "tcp"; sourcePort = 2223; } { destination = config.site.net.serv.hosts4.minetest; proto = "udp"; sourcePort = 30000; } { destination = "172.22.99.175:22"; proto = "tcp"; sourcePort = 2224; } { # Gitea ssh destination = config.site.net.serv.hosts4.gitea; proto = "tcp"; sourcePort = 22; } { # Jabber C2S destination = config.site.net.serv.hosts4.jabber; proto = "tcp"; sourcePort = 5222; } { # Jabber C2S+SSL destination = config.site.net.serv.hosts4.jabber; proto = "tcp"; sourcePort = 5223; } { # Jabber S2S destination = config.site.net.serv.hosts4.jabber; proto = "tcp"; sourcePort = 5269; } { # Jabber TURN destination = config.site.net.serv.hosts4.jabber; proto = "tcp"; sourcePort = 3478; } { # Jabber TURN destination = config.site.net.serv.hosts4.jabber; proto = "tcp"; sourcePort = 3479; } { # Jabber TURN destination = config.site.net.serv.hosts4.jabber; proto = "udp"; sourcePort = 3478; } { # Jabber TURN destination = config.site.net.serv.hosts4.jabber; proto = "udp"; sourcePort = 3479; } { destination = "${config.site.net.serv.hosts4.vps1}:22"; proto = "tcp"; sourcePort = 2225; } ] ++ map (port: { destination = config.site.net.serv.hosts4.mail; proto = "tcp"; sourcePort = port; }) [ 25 465 587 110 143 993 995 ]; } # host priv*-gw settings ( builtins.mapAttrs (hostName: _: { ospf.allowedUpstreams = [ "upstream4" "upstream3" "upstream1" "anon1" "freifunk" ]; }) ( lib.filterAttrs (hostName: _: builtins.match "priv[[:digit:]]+-gw" hostName != null ) pillar.containers ) ) (builtins.foldl' (result: hostName: result // { "${hostName}" = { role = "server"; interfaces = builtins.mapAttrs (net: _: { type = "phys"; } // lib.optionalAttrs (net == "cluster") { gw4 = "cls-gw"; gw6 = "cls-gw"; }) ( lib.filterAttrs (_: hosts: hosts ? ${hostName}) ( pillar.hosts-inet // ( builtins.foldl' (result: hosts: result // hosts) {} (builtins.attrValues pillar.hosts-inet6) ) ) ); }; }) {} mainServers) (builtins.mapAttrs (_: switch: { inherit (switch) model location password; role = "switch"; }) pillar.switches) (builtins.mapAttrs (_: ap: { inherit (ap) model location password; role = "ap"; }) pillar.cpe) (builtins.mapAttrs (name: container: let ctPillar = self.lib.saltPillarFor name; in { role = "container"; interfaces = builtins.mapAttrs (net: interface: renameAttr "gw" "gw4" (forceVeth interface) // ( if ctPillar ? upstream && ctPillar.upstream.interface == net then { upstream.upBandwidth = ctPillar.upstream.up-bandwidth; } else {} ) ) container.interfaces; wireguard = lib.optionalAttrs (ctPillar ? wireguard-instances) ( builtins.mapAttrs (net: wgData: { inherit (builtins.head wgData.peers) endpoint; publicKey = (builtins.head wgData.peers).public_key; privateKey = wgData.private_key; addresses = builtins.filter builtins.isString ( builtins.split "[, ]+" wgData.addr ); upBandwidth = ctPillar.upstream.up-bandwidth; }) ctPillar.wireguard-instances); ospf = let ospfConf = ctPillar.ospf; in lib.optionalAttrs (ctPillar ? ospf && ospfConf ? stubnets-inet) { stubNets4 = ospfConf.stubnets-inet; } // lib.optionalAttrs (ctPillar ? ospf && ospfConf ? stubnets-inet6) { stubNets6 = ospfConf.stubnets-inet6; }; bgp = if ctPillar ? bgp then let bgpConf = ctPillar.bgp; in { inherit (bgpConf) asn; peers = bgpConf.peers-inet // bgpConf.peers-inet6; } else null; forwardPorts = if ctPillar ? port-forwarding then map ({ proto, port, to }: { proto = proto; sourcePort = port; destination = to; }) ctPillar.port-forwarding else []; }) pillar.containers) ] ++ (map (net: builtins.mapAttrs (_: addr4: { }) pillar.hosts-inet.${net} ) (builtins.attrNames pillar.hosts-inet)) ++ (builtins.concatMap (ctx: map (net: builtins.mapAttrs (_: addr6: { }) pillar.hosts-inet6.${ctx}.${net} ) (builtins.attrNames pillar.hosts-inet6.${ctx}) ) (builtins.attrNames pillar.hosts-inet6)) ); }