From b7954dfd8b97dae9ea05b754ea04b6d6619daad8 Mon Sep 17 00:00:00 2001 From: Malte Tammena Date: Sun, 17 Nov 2024 18:07:28 +0100 Subject: [PATCH] feat(state): consolidate, update module, enslave dns entries --- hosts/granodomus-lima/default.nix | 78 +++-------------------- hosts/granodomus-lima/modules/vpn-dns.nix | 43 +++++++++++++ modules/state.nix | 24 ++++++- state.nix | 73 ++++++++++++++------- 4 files changed, 124 insertions(+), 94 deletions(-) create mode 100644 hosts/granodomus-lima/modules/vpn-dns.nix diff --git a/hosts/granodomus-lima/default.nix b/hosts/granodomus-lima/default.nix index 11ed934..9acf784 100644 --- a/hosts/granodomus-lima/default.nix +++ b/hosts/granodomus-lima/default.nix @@ -1,10 +1,8 @@ { - pkgs, lib, config, ... }: let - state = builtins.import ../../state.nix; mkVirtHost = lib.attrsets.recursiveUpdate { forceSSL = true; enableACME = true; @@ -14,6 +12,7 @@ in { ../../hardware/netcup-vps-200-g10.nix ../../modules/nginx-reverse-proxy.nix ./modules/murmur.nix + ./modules/vpn-dns.nix ./topology.nix ]; config = { @@ -32,14 +31,11 @@ in { }; }; - sops.defaultSopsFile = ../../secrets/hosts/granodomus-lima/secrets.yaml; - sops.age.sshKeyPaths = ["/etc/ssh/ssh_host_ed25519_key"]; - services.fail2ban = { enable = true; ignoreIP = let - vpn = state.vpn; - extractIPs = _: config: [config.v4 config.v6]; + vpn = config.state.vpn.machine; + extractIPs = _: config: [config.ipv4 config.ipv6]; in lib.flatten (lib.attrsets.mapAttrsToList extractIPs vpn); }; @@ -48,13 +44,13 @@ in { services.radicaleWithInfcloud.enable = true; services.nginx.virtualHosts = let - services = state.services; + services = config.state.services; removeUnexposed = lib.attrsets.filterAttrs (_: config: config ? "external" && config.external); - createVirtHost = name: config: { + createVirtHost = name: cfg: { name = "${name}.tammena.me"; value = mkVirtHost { locations."/" = { - proxyPass = let ip = state.vpn.${config.host}.v4; in "http://${ip}:${builtins.toString config.port}"; + proxyPass = let ip = config.state.vpn.machine.${cfg.host}.ipv4; in "http://${ip}:${builtins.toString cfg.port}"; proxyWebsockets = true; }; extraConfig = @@ -70,7 +66,7 @@ in { services.nginx.appendConfig = '' stream { upstream ssh { - server ${state.services.git.host}:22; + server ${config.state.services.git.host}:22; } server { listen 22222; @@ -79,66 +75,12 @@ in { } } ''; + networking.firewall.allowedTCPPorts = [22222]; services.qemuGuest.enable = true; - services.bind = { - enable = true; - cacheNetworks = ["any"]; - forwarders = ["100.100.100.100"]; - listenOn = ["any"]; - listenOnIpv6 = ["any"]; - zones."home" = let - granodomus-lima = config.state.vpn.machine.granodomus-lima; - faunus-ater = config.state.vpn.machine.faunus-ater; - point = domain: host: '' - ${domain} AAAA ${host.ipv6} - ${domain} A ${host.ipv4} - ''; - in { - master = true; - # TODO: Fix TTLs - file = pkgs.writeText "home-zone" '' - $TTL 1 - @ IN SOA home. malte.home. ( - 5 ; Serial - 1 ; Refresh - 1 ; Retry - 1 ; Expire - 1) ; Negative Cache TTL - @ NS home. - ${point "home." granodomus-lima} - ${point "cal" granodomus-lima} - ${point "mc" granodomus-lima} - ${point "foto" faunus-ater} - ${point "doc" faunus-ater} - ${point "sheet" faunus-ater} - ${point "media" faunus-ater} - ${point "file" faunus-ater} - ${point "stats" faunus-ater} - ${point "cache" faunus-ater} - ${point "hydra" faunus-ater} - ${point "git" faunus-ater} - ${point "read" faunus-ater} - ${point "note" faunus-ater} - ${point "time" faunus-ater} - ${point "todo" faunus-ater} - ${point "config" faunus-ater} - ${point "listen" faunus-ater} - ${point "eat" faunus-ater} - ${point "sea" faunus-ater} - ${point "s3" faunus-ater} - ${point "bazarr" faunus-ater} - ${point "sonarr" faunus-ater} - ${point "radarr" faunus-ater} - ${point "prowlarr" faunus-ater} - ${point "downloadarr" faunus-ater} - ${point "music" faunus-ater} - ''; - }; - }; - networking.firewall.allowedTCPPorts = [53 22222]; - networking.firewall.allowedUDPPorts = [53]; + sops.defaultSopsFile = ../../secrets/hosts/granodomus-lima/secrets.yaml; + sops.age.sshKeyPaths = ["/etc/ssh/ssh_host_ed25519_key"]; # This value determines the NixOS release from which the default # settings for stateful data, like file locations and database versions diff --git a/hosts/granodomus-lima/modules/vpn-dns.nix b/hosts/granodomus-lima/modules/vpn-dns.nix new file mode 100644 index 0000000..8616aea --- /dev/null +++ b/hosts/granodomus-lima/modules/vpn-dns.nix @@ -0,0 +1,43 @@ +{ + pkgs, + lib, + config, + ... +}: let + thisHost = config.networking.hostName; +in { + services.bind = { + enable = true; + cacheNetworks = ["any"]; + forwarders = ["100.100.100.100"]; + listenOn = ["any"]; + listenOnIpv6 = ["any"]; + zones."home" = let + createEntry = domain: host: '' + ${domain} AAAA ${config.state.vpn.machine.${host}.ipv6} + ${domain} A ${config.state.vpn.machine.${host}.ipv4} + ''; + entries = builtins.concatStringsSep "" (lib.mapAttrsToList createEntry config.state.vpn.cnames); + in { + master = true; + # TODO: Fix TTLs + file = pkgs.writeText "home-zone" '' + $TTL 3600 + @ IN SOA home. malte.home. ( + 2024111701 ; Serial + 3600 ; Refresh + 1800 ; Retry + 604800 ; Expire + 86400) ; Negative Cache TTL + @ NS home. + + home. AAAA ${config.state.vpn.machine.${thisHost}.ipv6} + home. A ${config.state.vpn.machine.${thisHost}.ipv4} + + ${entries} + ''; + }; + }; + networking.firewall.allowedTCPPorts = [53]; + networking.firewall.allowedUDPPorts = [53]; +} diff --git a/modules/state.nix b/modules/state.nix index 4142347..c5197fc 100644 --- a/modules/state.nix +++ b/modules/state.nix @@ -5,7 +5,8 @@ }: let state = builtins.import ../state.nix; ty = { - ipv4 = lib.types.strMatching "[0-9]{1,3}(\\.[0-9]{1,3}){3}"; + ipv4 = lib.types.strMatching "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}"; + ipv6 = lib.types.strMatching "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))"; }; in { options.state = with lib; { @@ -28,7 +29,7 @@ in { description = "VPN IPv4"; }; ipv6 = mkOption { - type = str; + type = ty.ipv6; description = "VPN IPv6"; }; }; @@ -41,6 +42,11 @@ in { description = "VPN internal DNS server address"; readOnly = true; }; + vpn.cnames = mkOption { + type = types.attrsOf types.str; + description = "CNAMES for our internal DNS server"; + readOnly = true; + }; services = mkOption { type = with types; attrsOf (submodule { @@ -78,12 +84,24 @@ in { vpn.machine = if state ? vpn then - builtins.mapAttrs (machine: conf: { + builtins.mapAttrs (_: conf: { ipv4 = conf.v4; ipv6 = conf.v6; }) state.vpn else builtins.throw "No VPN definitions"; + vpn.dns = + if state ? services && state.services ? dns + then + ( + if state ? vpn && state.vpn ? "${state.services.dns.host}" + then state.vpn.${state.services.dns.host}.v4 + else builtins.throw "No VPN entry for DNS host" + ) + else builtins.throw "No DNS service defined!"; + vpn.cnames = (if state ? cnames then state.cnames else {}) // ( + builtins.mapAttrs (_name: value: value.host) config.state.services + ); services = if state ? services then state.services diff --git a/state.nix b/state.nix index b4c3c46..a114fdf 100644 --- a/state.nix +++ b/state.nix @@ -37,24 +37,14 @@ # Information about which services run where # Type: attrsOf { host: str, port: number, external: bool } services = { - git = { + calibre = { host = "faunus-ater"; - port = 10219; + port = 12834; external = true; }; - read = { - host = "faunus-ater"; - port = 10224; - external = true; - }; - foto = { - host = "faunus-ater"; - port = 2343; - external = true; - }; - eat = { - host = "faunus-ater"; - port = 9000; + dns = { + host = "granodomus-lima"; + port = 53; external = true; }; doc = { @@ -62,25 +52,62 @@ port = 28981; external = true; }; - sea = { + eat = { host = "faunus-ater"; - port = 9333; - external = false; + port = 9000; + external = true; }; - s3 = { + foto = { host = "faunus-ater"; - port = 8333; - external = false; + port = 2343; + external = true; + }; + git = { + host = "faunus-ater"; + port = 10219; + external = true; }; music = { host = "faunus-ater"; port = 4533; external = true; }; - calibre = { + read = { host = "faunus-ater"; - port = 12834; + port = 10224; external = true; }; + s3 = { + host = "faunus-ater"; + port = 8333; + external = false; + }; + sea = { + host = "faunus-ater"; + port = 9333; + external = false; + }; + }; + #### CNAMES #### + # CNAMES inside my vpn, service-entries don't have to be repeated here + cnames = { + bazarr = "faunus-ater"; + cache = "faunus-ater"; + cal = "granodomus-lima"; + config = "faunus-ater"; + downloadarr = "faunus-ater"; + file = "faunus-ater"; + hydra = "faunus-ater"; + listen = "faunus-ater"; + mc = "granodomus-lima"; + media = "faunus-ater"; + note = "faunus-ater"; + prowlarr = "faunus-ater"; + radarr = "faunus-ater"; + sheet = "faunus-ater"; + sonarr = "faunus-ater"; + stats = "faunus-ater"; + time = "faunus-ater"; + todo = "faunus-ater"; }; }