diff --git a/SwarselSystems.org b/SwarselSystems.org index 103c9aa..b0d8309 100644 --- a/SwarselSystems.org +++ b/SwarselSystems.org @@ -1215,10 +1215,11 @@ A short overview over each input and what it does: smallpkgs.url = "github:nixos/nixpkgs/08fcb0dcb59df0344652b38ea6326a2d8271baff?narHash=sha256-HXIQzULIG/MEUW2Q/Ss47oE3QrjxvpUX7gUl4Xp6lnc%3D&shallow=1"; nixpkgs-dev.url = "github:Swarsel/nixpkgs/main"; nixpkgs-kernel.url = "github:NixOS/nixpkgs/063f43f2dbdef86376cc29ad646c45c46e93234c?narHash=sha256-6m1Y3/4pVw1RWTsrkAK2VMYSzG4MMIj7sqUy7o8th1o%3D"; #specifically pinned for kernel version - nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-25.05"; + nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-25.11"; nixpkgs-stable24_05.url = "github:NixOS/nixpkgs/nixos-24.05"; nixpkgs-stable24_11.url = "github:NixOS/nixpkgs/nixos-24.11"; nixpkgs-stable25_05.url = "github:NixOS/nixpkgs/nixos-25.05"; + nixpkgs-stable25_11.url = "github:NixOS/nixpkgs/nixos-25.11"; home-manager = { # url = "github:nix-community/home-manager"; @@ -3529,7 +3530,6 @@ This is my main server that I run at home. It handles most tasks that require bi transmission = true; syncthing = true; grafana = true; - emacs = true; freshrss = true; kanidm = true; firefly-iii = true; @@ -3542,6 +3542,8 @@ This is my main server that I run at home. It handles most tasks that require bi opkssh = true; }; + networking.nftables.firewall.zones.untrusted.interfaces = [ "lan" "enp3s0" ]; + } #+end_src @@ -6474,13 +6476,16 @@ in splitPath = path: lib.splitString "." path; forwardedOptions = [ - (splitPath "sops.secrets") - (splitPath "swarselsystems.server.dns") + (splitPath "boot.kernel.sysctl") + (splitPath "networking.nftables.chains.postrouting") (splitPath "services.kanidm.provision.groups") (splitPath "services.kanidm.provision.systems.oauth2") + (splitPath "sops.secrets") + (splitPath "swarselsystems.server.dns") ] + ++ expandOptions (splitPath "networking.nftables.firewall") [ "zones" "rules" ] + ++ expandOptions (splitPath "services.firezone.gateway") [ "enable" "name" "apiUrl" "tokenFile" "package" "logLevel" ] ++ expandOptions (splitPath "services.nginx") [ "upstreams" "virtualHosts" ] - ++ expandOptions (splitPath "services.firezone.gateway") [ "enable" "name" "apiUrl" "tokenFile" ] ; attrsForEachOption = @@ -6522,7 +6527,30 @@ in inherit (lib) mkOption types - ; + ; + + firewallOptions = { + allowedTCPPorts = mkOption { + type = types.listOf types.port; + default = [ ]; + description = "Convenience option to open specific TCP ports for traffic from the network."; + }; + allowedUDPPorts = mkOption { + type = types.listOf types.port; + default = [ ]; + description = "Convenience option to open specific UDP ports for traffic from the network."; + }; + allowedTCPPortRanges = mkOption { + type = lib.types.listOf (lib.types.attrsOf lib.types.port); + default = [ ]; + description = "Convenience option to open specific TCP port ranges for traffic from another node."; + }; + allowedUDPPortRanges = mkOption { + type = lib.types.listOf (lib.types.attrsOf lib.types.port); + default = [ ]; + description = "Convenience option to open specific UDP port ranges for traffic from another node."; + }; + }; networkOptions = netSubmod: { cidrv4 = mkOption { @@ -6552,45 +6580,10 @@ in services to the network. ''; type = types.submodule { - options = { - allowedTCPPorts = mkOption { - type = types.listOf types.port; - default = [ ]; - description = "Convenience option to open specific TCP ports for traffic from the network."; - }; - allowedUDPPorts = mkOption { - type = types.listOf types.port; - default = [ ]; - description = "Convenience option to open specific UDP ports for traffic from the network."; - }; - }; + options = firewallOptions; }; }; - firewallRuleForNode = mkOption { - default = { }; - description = '' - If this is a wireguard network: Allows you to set specific firewall rules just for traffic originating from another network node. - A corresponding rule `-node--to-` will be created to easily expose - services to that node. - ''; - type = types.attrsOf ( - types.submodule { - options = { - allowedTCPPorts = mkOption { - type = types.listOf types.port; - default = [ ]; - description = "Convenience option to open specific TCP ports for traffic from another node."; - }; - allowedUDPPorts = mkOption { - type = types.listOf types.port; - default = [ ]; - description = "Convenience option to open specific UDP ports for traffic from another node."; - }; - }; - } - ); - }; hosts = mkOption { @@ -6653,150 +6646,164 @@ in # if we use the /32 wan address as local address directly, do not use the network address in ipv6 lib.net.cidr.hostCidr (if hostSubmod.config.id == 0 then 1 else hostSubmod.config.id) netSubmod.config.cidrv6; }; + + firewallRuleForNode = mkOption { + default = { }; + description = '' + If this is a wireguard network: Allows you to set specific firewall rules just for traffic originating from another network node. + A corresponding rule `-node--to-` will be created to easily expose + services to that node. + ''; + type = types.attrsOf ( + types.submodule { + options = firewallOptions; + } + ); + }; }; }) ); }; }; in - { - options = { - globals = mkOption { - default = { }; - type = types.submodule { - options = { - root = { - hashedPassword = mkOption { - type = types.str; + { + options = { + globals = mkOption { + default = { }; + type = types.submodule { + options = { + root = { + hashedPassword = mkOption { + type = types.str; + }; }; - }; - user = { - name = mkOption { - type = types.str; + user = { + name = mkOption { + type = types.str; + }; + work = mkOption { + type = types.str; + }; }; - work = mkOption { - type = types.str; + + + services = mkOption { + type = types.attrsOf ( + types.submodule (serviceSubmod: { + options = { + domain = mkOption { + type = types.str; + }; + subDomain = mkOption { + readOnly = true; + type = types.str; + default = lib.swarselsystems.getSubDomain serviceSubmod.config.domain; + }; + baseDomain = mkOption { + readOnly = true; + type = types.str; + default = lib.swarselsystems.getBaseDomain serviceSubmod.config.domain; + }; + proxyAddress4 = mkOption { + type = types.nullOr types.str; + default = null; + }; + proxyAddress6 = mkOption { + type = types.nullOr types.str; + default = null; + }; + isHome = mkOption { + type = types.bool; + default = false; + }; + }; + }) + ); }; - }; + networks = mkOption { + default = { }; + type = types.attrsOf ( + types.submodule (netSubmod: { + options = networkOptions netSubmod // { + vlans = mkOption { + default = { }; + type = types.attrsOf ( + types.submodule (vlanNetSubmod: { + options = networkOptions vlanNetSubmod // { + id = mkOption { + type = types.ints.between 1 4094; + description = "The VLAN id"; + }; - services = mkOption { - type = types.attrsOf ( - types.submodule (serviceSubmod: { - options = { - domain = mkOption { - type = types.str; - }; - subDomain = mkOption { - readOnly = true; - type = types.str; - default = lib.swarselsystems.getSubDomain serviceSubmod.config.domain; - }; - baseDomain = mkOption { - readOnly = true; - type = types.str; - default = lib.swarselsystems.getBaseDomain serviceSubmod.config.domain; - }; - proxyAddress4 = mkOption { - type = types.nullOr types.str; - default = null; - }; - proxyAddress6 = mkOption { - type = types.nullOr types.str; - default = null; - }; - isHome = mkOption { - type = types.bool; - default = false; - }; - }; - }) - ); - }; - - networks = mkOption { - default = { }; - type = types.attrsOf ( - types.submodule (netSubmod: { - options = networkOptions netSubmod // { - vlans = mkOption { - default = { }; - type = types.attrsOf ( - types.submodule (vlanNetSubmod: { - options = networkOptions vlanNetSubmod // { - id = mkOption { - type = types.ints.between 1 4094; - description = "The VLAN id"; + name = mkOption { + description = "The name of this VLAN"; + default = vlanNetSubmod.config._module.args.name; + type = types.str; + }; }; - - name = mkOption { - description = "The name of this VLAN"; - default = vlanNetSubmod.config._module.args.name; - type = types.str; - }; - }; - }) - ); + }) + ); + }; }; - }; - }) - ); - }; - - hosts = mkOption { - type = types.attrsOf ( - types.submodule { - options = { - defaultGateway4 = mkOption { - type = types.nullOr types.net.ipv4; - }; - defaultGateway6 = mkOption { - type = types.nullOr types.net.ipv6; - }; - wanAddress4 = mkOption { - type = types.nullOr types.net.ipv4; - }; - wanAddress6 = mkOption { - type = types.nullOr types.net.ipv6; - }; - isHome = mkOption { - type = types.bool; - }; - }; - } - ); - }; - - domains = { - main = mkOption { - type = types.str; + }) + ); }; - externalDns = mkOption { - type = types.listOf types.str; - description = "List of external dns nameservers"; - }; - }; - general = lib.mkOption { - type = types.submodule { - freeformType = types.unspecified; + hosts = mkOption { + type = types.attrsOf ( + types.submodule { + options = { + defaultGateway4 = mkOption { + type = types.nullOr types.net.ipv4; + }; + defaultGateway6 = mkOption { + type = types.nullOr types.net.ipv6; + }; + wanAddress4 = mkOption { + type = types.nullOr types.net.ipv4; + }; + wanAddress6 = mkOption { + type = types.nullOr types.net.ipv6; + }; + isHome = mkOption { + type = types.bool; + }; + }; + } + ); }; + + domains = { + main = mkOption { + type = types.str; + }; + externalDns = mkOption { + type = types.listOf types.str; + description = "List of external dns nameservers"; + }; + }; + + general = lib.mkOption { + type = types.submodule { + freeformType = types.unspecified; + }; + }; + }; }; + }; + _globalsDefs = mkOption { + type = types.unspecified; + default = options.globals.definitions; + readOnly = true; + internal = true; }; }; - - _globalsDefs = mkOption { - type = types.unspecified; - default = options.globals.definitions; - readOnly = true; - internal = true; - }; - }; - } + } #+end_src **** Expose home-manager sops secrets in NixOS (automatically active) @@ -7862,6 +7869,8 @@ Here I only enable =networkmanager= and a few default networks. The rest of the }; }; + services.resolved.enable = true; + networking = { hostName = config.node.name; hosts = { @@ -7895,9 +7904,11 @@ Here I only enable =networkmanager= and a few default networks. The rest of the ]; }; + networkmanager = { enable = true; wifi.backend = "iwd"; + dns = "systemd-resolved"; plugins = [ # list of plugins: https://search.nixos.org/packages?query=networkmanager- # docs https://networkmanager.dev/docs/vpn/ @@ -9152,6 +9163,27 @@ Auto login for the initial session. } #+end_src +**** Firezone Client + + +#+begin_src nix-ts :tangle modules/nixos/client/firezone-client.nix + { lib, config, ... }: + let + moduleName = "firezone-client"; + inherit (config.swarselsystems) mainUser; + in + { + options.swarselmodules.${moduleName} = lib.mkEnableOption "${moduleName} settings"; + config = lib.mkIf config.swarselmodules.${moduleName} { + services.firezone.gui-client = { + enable = true; + inherit (config.node) name; + allowedUsers = [ mainUser ]; + }; + }; + } +#+end_src + *** Server :PROPERTIES: :CUSTOM_ID: h:e492c24a-83a0-4bcb-a084-706f49318651 @@ -10174,7 +10206,7 @@ In order to define a new wireguard interface, I have to: in { "${ifName}-to-${localZoneName}" = { - inherit (netCfg.firewallRuleForAll) allowedTCPPorts allowedUDPPorts; + inherit (netCfg.firewallRuleForAll) allowedTCPPorts allowedUDPPorts allowedTCPPortRanges allowedUDPPortRanges; from = [ ifName ]; to = [ localZoneName ]; ignoreEmptyRule = true; @@ -10183,8 +10215,8 @@ In order to define a new wireguard interface, I have to: // lib.listToAttrs (map (peer: lib.nameValuePair "${ifName}-node-${peer}-to-${localZoneName}" ( - lib.mkIf (netCfg.firewallRuleForNode ? ${peer}) { - inherit (netCfg.firewallRuleForNode.${peer}) allowedTCPPorts allowedUDPPorts; + lib.mkIf (netCfg.hosts.${config.node.name}.firewallRuleForNode ? ${peer}) { + inherit (netCfg.hosts.${config.node.name}.firewallRuleForNode.${peer}) allowedTCPPorts allowedTCPPortRanges allowedUDPPorts allowedUDPPortRanges; from = [ "${ifName}-node-${peer}" ]; to = [ localZoneName ]; ignoreEmptyRule = true; @@ -10467,7 +10499,7 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin let inherit (config.swarselsystems) sopsFile; - inherit (confLib.gen { name = "kavita"; port = 8080; }) servicePort serviceName serviceUser serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "kavita"; port = 8080; }) servicePort serviceName serviceUser serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; @@ -10476,7 +10508,7 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin calibre ]; - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -10486,17 +10518,26 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin sops.secrets.kavita-token = { inherit sopsFile; owner = serviceUser; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; - + # networking.firewall.allowedTCPPorts = [ servicePort ]; topology.self.services.${serviceName} = { name = "Kavita"; info = "https://${serviceDomain}"; icon = "${self}/files/topology-images/${serviceName}.png"; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = { @@ -10507,7 +10548,7 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin dataDir = "/Vault/data/${serviceName}"; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -10544,13 +10585,13 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin #+begin_src nix-ts :tangle modules/nixos/server/jellyfin.nix { pkgs, lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "jellyfin"; port = 8096; }) servicePort serviceName serviceUser serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "jellyfin"; port = 8096; }) servicePort serviceName serviceUser serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -10574,18 +10615,28 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin topology.self.services.${serviceName}.info = "https://${serviceDomain}"; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = { enable = true; user = serviceUser; - openFirewall = true; # this works only for the default ports + # openFirewall = true; # this works only for the default ports }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -10623,13 +10674,13 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin #+begin_src nix-ts :tangle modules/nixos/server/navidrome.nix { pkgs, config, lib, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "navidrome"; port = 4040; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "navidrome"; port = 4040; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -10660,11 +10711,21 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin enableAllFirmware = lib.mkForce true; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.snapserver = { @@ -10690,7 +10751,7 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin services.${serviceName} = { enable = true; - openFirewall = true; + # openFirewall = true; settings = { LogLevel = "debug"; Address = "0.0.0.0"; @@ -10729,7 +10790,7 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -10979,7 +11040,7 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin { lib, config, pkgs, globals, dns, confLib, ... }: let inherit (config.swarselsystems) sopsFile; - inherit (confLib.gen { name = "matrix"; user = "matrix-synapse"; port = 8008; }) servicePort serviceName serviceUser serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "matrix"; user = "matrix-synapse"; port = 8008; }) servicePort serviceName serviceUser serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; federationPort = 8448; whatsappPort = 29318; @@ -10998,7 +11059,7 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -11038,7 +11099,7 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin }; }; - networking.firewall.allowedTCPPorts = [ servicePort federationPort ]; + # networking.firewall.allowedTCPPorts = [ servicePort federationPort ]; systemd = { timers."restart-bridges" = { @@ -11069,9 +11130,19 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin }; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort federationPort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services = { @@ -11271,7 +11342,7 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin # messages out after a while. - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -11340,7 +11411,7 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin let inherit (config.repo.secrets.local.nextcloud) adminuser; inherit (config.swarselsystems) sopsFile; - inherit (confLib.gen { name = "nextcloud"; port = 80; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "nextcloud"; port = 80; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome dnsServer webProxy; nextcloudVersion = "32"; in @@ -11348,7 +11419,7 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -11388,7 +11459,7 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -11425,13 +11496,13 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin #+begin_src nix-ts :tangle modules/nixos/server/immich.nix { lib, pkgs, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "immich"; port = 3001; }) servicePort serviceName serviceUser serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "immich"; port = 3001; }) servicePort serviceName serviceUser serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -11441,9 +11512,20 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin topology.self.services.${serviceName}.info = "https://${serviceDomain}"; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + # networking.firewall.allowedTCPPorts = [ servicePort ]; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = { @@ -11451,16 +11533,15 @@ This is the configuration to make [[#h:58c7563e-6954-42e6-a622-9d06523e8e24][Hin package = pkgs.immich; host = "0.0.0.0"; port = servicePort; - openFirewall = true; + # openFirewall = true; mediaLocation = "/Vault/Eternor/Immich"; # dataDir environment = { IMMICH_MACHINE_LEARNING_URL = lib.mkForce "http://localhost:3003"; }; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -11513,7 +11594,7 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of =.eml= { lib, pkgs, config, dns, globals, confLib, ... }: let inherit (config.swarselsystems) sopsFile; - inherit (confLib.gen { name = "paperless"; port = 28981; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "paperless"; port = 28981; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; tikaPort = 9998; gotenbergPort = 3002; @@ -11523,7 +11604,7 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of =.eml= options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -11536,11 +11617,21 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of =.eml= kanidm-paperless-client = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services = { @@ -11611,7 +11702,7 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of =.eml= ) ''; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -11845,7 +11936,7 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of =.eml= { lib, config, globals, dns, confLib, ... }: let inherit (config.swarselsystems.syncthing) serviceDomain; - inherit (confLib.gen { name = "syncthing"; port = 8384; }) servicePort serviceName serviceUser serviceGroup serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "syncthing"; port = 8384; }) servicePort serviceName serviceUser serviceGroup serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; specificServiceName = "${serviceName}-${config.node.name}"; @@ -11886,7 +11977,7 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of =.eml= }; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${specificServiceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${specificServiceName}.baseDomain}.subdomainRecords = { "${globals.services.${specificServiceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -11898,11 +11989,27 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of =.eml= users.groups.${serviceGroup} = { }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; - globals.services.${specificServiceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy} = { + allowedTCPPorts = [ servicePort 22000 ]; + allowedUDPPorts = [ 20000 21027 ]; + }; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy} = { + allowedTCPPorts = [ servicePort 20000 ]; + allowedUDPPorts = [ 20000 21027 ]; + }; + }; + }; + services.${specificServiceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = rec { @@ -11912,7 +12019,7 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of =.eml= dataDir = lib.mkDefault "/Vault/data/${serviceName}"; configDir = "${cfg.dataDir}/.config/${serviceName}"; guiAddress = "0.0.0.0:${builtins.toString servicePort}"; - openDefaultPorts = true; # opens ports TCP/UDP 22000 and UDP 21027 for discovery + openDefaultPorts = lib.mkIf (!isProxied) true; # opens ports TCP/UDP 22000 and UDP 21027 for discovery relay.enable = false; settings = { urAccepted = -1; @@ -11959,7 +12066,7 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of =.eml= }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${specificServiceName} = { servers = { @@ -12070,7 +12177,7 @@ This section exposes several metrics that I use to check the health of my server #+begin_src nix-ts :tangle modules/nixos/server/monitoring.nix { lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "grafana"; port = 3000; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "grafana"; port = 3000; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; prometheusPort = 9090; prometheusUser = "prometheus"; @@ -12088,7 +12195,7 @@ This section exposes several metrics that I use to check the health of my server options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -12126,13 +12233,23 @@ This section exposes several metrics that I use to check the health of my server }; }; - networking.firewall.allowedTCPPorts = [ servicePort prometheusPort ]; + # networking.firewall.allowedTCPPorts = [ servicePort prometheusPort ]; topology.self.services.prometheus.info = "https://${serviceDomain}/${prometheusWebRoot}"; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort prometheusPort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort prometheusPort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services = { @@ -12282,7 +12399,7 @@ This section exposes several metrics that I use to check the health of my server }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { "${grafanaUpstream}" = { servers = { @@ -12333,19 +12450,29 @@ This is a WIP Jenkins instance. It is used to automatically build a new system w #+begin_src nix-ts :tangle modules/nixos/server/jenkins.nix { pkgs, lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "jenkins"; port = 8088; }) servicePort serviceName serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "jenkins"; port = 8088; }) servicePort serviceName serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.jenkins = { @@ -12357,7 +12484,7 @@ This is a WIP Jenkins instance. It is used to automatically build a new system w home = "/Vault/apps/${serviceName}"; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -12432,7 +12559,7 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with #+begin_src nix-ts :tangle modules/nixos/server/freshrss.nix { self, lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "freshrss"; port = 80; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "freshrss"; port = 80; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome webProxy dnsServer; inherit (config.swarselsystems) sopsFile; in @@ -12440,7 +12567,7 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -12508,7 +12635,7 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with # config.sops.templates.freshrss-env.path # ]; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -12550,7 +12677,7 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with { lib, config, pkgs, globals, dns, confLib, ... }: let inherit (config.swarselsystems) sopsFile; - inherit (confLib.gen { name = "forgejo"; port = 3004; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "forgejo"; port = 3004; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; kanidmDomain = globals.services.kanidm.domain; in @@ -12558,11 +12685,11 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; users.users.${serviceUser} = { group = serviceGroup; @@ -12575,9 +12702,19 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with kanidm-forgejo-client = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = { @@ -12679,7 +12816,7 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with ''; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -12718,7 +12855,7 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with { self, lib, config, globals, dns, confLib, ... }: let inherit (config.swarselsystems) sopsFile; - inherit (confLib.gen { name = "ankisync"; port = 27701; }) servicePort serviceName serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "ankisync"; port = 27701; }) servicePort serviceName serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; ankiUser = globals.user.name; in @@ -12726,11 +12863,11 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; sops.secrets.anki-pw = { inherit sopsFile; owner = "root"; }; @@ -12740,16 +12877,26 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with info = "https://${serviceDomain}"; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.anki-sync-server = { enable = true; port = servicePort; address = "0.0.0.0"; - openFirewall = true; + # openFirewall = true; users = [ { username = ankiUser; @@ -12758,7 +12905,7 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with ]; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -12811,7 +12958,7 @@ kanidm person credential create-reset-token let certsSopsFile = self + /secrets/repo/certs.yaml; inherit (config.swarselsystems) sopsFile; - inherit (confLib.gen { name = "kanidm"; port = 8300; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "kanidm"; port = 8300; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy homeProxyIf webProxyIf dnsServer; oauth2ProxyDomain = globals.services.oauth2-proxy.domain; immichDomain = globals.services.immich.domain; @@ -12840,16 +12987,18 @@ kanidm person credential create-reset-token options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; - users.users.${serviceUser} = { - group = serviceGroup; - isSystemUser = true; - }; + users = { + users.${serviceUser} = { + group = serviceGroup; + isSystemUser = true; + }; - users.groups.${serviceGroup} = { }; + groups.${serviceGroup} = { }; + }; sops = { secrets = { @@ -12867,13 +13016,23 @@ kanidm person credential create-reset-token }; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + general.idmServer = config.node.name; + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; - globals.general.idmServer = config.node.name; environment.persistence."/persist" = lib.mkIf config.swarselsystems.isImpermanence { files = [ @@ -13190,7 +13349,7 @@ kanidm person credential create-reset-token ${serviceName}.serviceConfig.RestartSec = "30"; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -13228,7 +13387,7 @@ kanidm person credential create-reset-token #+begin_src nix-ts :tangle modules/nixos/server/oauth2-proxy.nix { lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "oauth2-proxy"; port = 3004; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "oauth2-proxy"; port = 3004; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; kanidmDomain = globals.services.kanidm.domain; mainDomain = globals.domains.main; @@ -13347,7 +13506,7 @@ kanidm person credential create-reset-token }; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -13370,11 +13529,21 @@ kanidm person credential create-reset-token }; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; - globals.services.${serviceName} = { + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { domain = serviceDomain; inherit proxyAddress4 proxyAddress6 isHome; + }; }; services = { @@ -13426,7 +13595,7 @@ kanidm person credential create-reset-token }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -13464,7 +13633,7 @@ kanidm person credential create-reset-token #+begin_src nix-ts :tangle modules/nixos/server/firefly-iii.nix { self, lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "firefly-iii"; port = 80; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "firefly-iii"; port = 80; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome dnsServer webProxy; nginxGroup = "nginx"; @@ -13475,7 +13644,7 @@ kanidm person credential create-reset-token options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -13545,7 +13714,7 @@ kanidm person credential create-reset-token }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -13588,7 +13757,7 @@ kanidm person credential create-reset-token #+begin_src nix-ts :tangle modules/nixos/server/koillection.nix { self, lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "koillection"; port = 2282; dir = "/Vault/data/koillection"; }) servicePort serviceName serviceUser serviceDir serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "koillection"; port = 2282; dir = "/Vault/data/koillection"; }) servicePort serviceName serviceUser serviceDir serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; serviceDB = "koillection"; postgresUser = config.systemd.services.postgresql.serviceConfig.User; # postgres @@ -13602,7 +13771,7 @@ kanidm person credential create-reset-token config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; sops.secrets = { @@ -13616,9 +13785,19 @@ kanidm person credential create-reset-token icon = "${self}/files/topology-images/${serviceName}.png"; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort postgresPort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort postgresPort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; virtualisation.oci-containers.containers = { @@ -13662,7 +13841,7 @@ kanidm person credential create-reset-token }; }; - networking.firewall.allowedTCPPorts = [ servicePort postgresPort ]; + # networking.firewall.allowedTCPPorts = [ servicePort postgresPort ]; systemd.services.postgresql.postStart = let @@ -13695,7 +13874,7 @@ kanidm person credential create-reset-token }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -13734,32 +13913,42 @@ kanidm person credential create-reset-token #+begin_src nix-ts :tangle modules/nixos/server/atuin.nix { lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "atuin"; port = 8888; }) servicePort serviceName serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "atuin"; port = 8888; }) servicePort serviceName serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; topology.self.services.${serviceName}.info = "https://${serviceDomain}"; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = { enable = true; host = "0.0.0.0"; port = servicePort; - openFirewall = true; + # openFirewall = true; openRegistration = false; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -13798,8 +13987,7 @@ kanidm person credential create-reset-token #+begin_src nix-ts :tangle modules/nixos/server/radicale.nix { lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "radicale"; port = 8000; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; - # sopsFile = config.node.secretsDir + "/secrets2.yaml"; + inherit (confLib.gen { name = "radicale"; port = 8000; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; inherit (config.swarselsystems) sopsFile; cfg = config.services.${serviceName}; @@ -13808,7 +13996,7 @@ kanidm person credential create-reset-token options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -13833,9 +14021,19 @@ kanidm person credential create-reset-token topology.self.services.${serviceName}.info = "https://${serviceDomain}"; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = { @@ -13887,9 +14085,9 @@ kanidm person credential create-reset-token }; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -13929,7 +14127,7 @@ kanidm person credential create-reset-token #+begin_src nix-ts :tangle modules/nixos/server/croc.nix { self, lib, config, pkgs, dns, globals, confLib, ... }: let - inherit (confLib.gen { name = "croc"; proxy = config.node.name; }) serviceName serviceDomain proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "croc"; proxy = config.node.name; }) serviceName serviceDomain proxyAddress4 proxyAddress6 isHome dnsServer; servicePorts = [ 9009 9010 @@ -13946,7 +14144,7 @@ kanidm person credential create-reset-token options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -14011,7 +14209,7 @@ kanidm person credential create-reset-token #+begin_src nix-ts :tangle modules/nixos/server/microbin.nix { self, lib, config, dns, globals, confLib, ... }: let - inherit (confLib.gen { name = "microbin"; port = 8777; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "microbin"; port = 8777; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; inherit (config.swarselsystems) sopsFile; @@ -14021,7 +14219,7 @@ kanidm person credential create-reset-token options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -14061,9 +14259,19 @@ kanidm person credential create-reset-token icon = "${self}/files/topology-images/${serviceName}.png"; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = { @@ -14110,13 +14318,13 @@ kanidm person credential create-reset-token }; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ { directory = cfg.dataDir; user = serviceUser; group = serviceGroup; mode = "0700"; } ]; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -14157,7 +14365,7 @@ kanidm person credential create-reset-token #+begin_src nix-ts :tangle modules/nixos/server/shlink.nix { self, lib, config, dns, globals, confLib, ... }: let - inherit (confLib.gen { name = "shlink"; port = 8081; dir = "/var/lib/shlink";}) servicePort serviceName serviceDomain serviceDir serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "shlink"; port = 8081; dir = "/var/lib/shlink"; }) servicePort serviceName serviceDomain serviceDir serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; containerRev = "sha256:1a697baca56ab8821783e0ce53eb4fb22e51bb66749ec50581adc0cb6d031d7a"; @@ -14169,7 +14377,7 @@ kanidm person credential create-reset-token }; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -14226,7 +14434,7 @@ kanidm person credential create-reset-token ] ); - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ { directory = serviceDir; } @@ -14239,12 +14447,22 @@ kanidm person credential create-reset-token icon = "${self}/files/topology-images/${serviceName}.png"; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -14280,103 +14498,113 @@ Deployment notes: - finally, disable new user registration in web ui #+begin_src nix-ts :tangle modules/nixos/server/slink.nix - { self, lib, config, dns, globals, confLib, ... }: - let - inherit (confLib.gen { name = "slink"; port = 3000; dir = "/var/lib/slink";}) servicePort serviceName serviceDomain serviceDir serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; +{ self, lib, config, dns, globals, confLib, ... }: +let + inherit (confLib.gen { name = "slink"; port = 3000; dir = "/var/lib/slink"; }) servicePort serviceName serviceDomain serviceDir serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; - containerRev = "sha256:98b9442696f0a8cbc92f0447f54fa4bad227af5dcfd6680545fedab2ed28ddd9"; - in - { - options = { - swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; + containerRev = "sha256:98b9442696f0a8cbc92f0447f54fa4bad227af5dcfd6680545fedab2ed28ddd9"; +in +{ + options = { + swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; + }; + config = lib.mkIf config.swarselmodules.server.${serviceName} { + + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; - config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { - "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; + virtualisation.oci-containers.containers.${serviceName} = { + image = "anirdev/slink@${containerRev}"; + environment = { + "ORIGIN" = "https://${serviceDomain}"; + "TZ" = config.repo.secrets.common.location.timezone; + "STORAGE_PROVIDER" = "local"; + "IMAGE_MAX_SIZE" = "50M"; + "USER_APPROVAL_REQUIRED" = "true"; }; - - virtualisation.oci-containers.containers.${serviceName} = { - image = "anirdev/slink@${containerRev}"; - environment = { - "ORIGIN" = "https://${serviceDomain}"; - "TZ" = config.repo.secrets.common.location.timezone; - "STORAGE_PROVIDER" = "local"; - "IMAGE_MAX_SIZE" = "50M"; - "USER_APPROVAL_REQUIRED" = "true"; - }; - ports = [ "${builtins.toString servicePort}:${builtins.toString servicePort}" ]; - volumes = [ - "${serviceDir}/var/data:/app/var/data" - "${serviceDir}/images:/app/slink/images" - ]; - }; - - systemd.tmpfiles.settings."12-slink" = builtins.listToAttrs ( - map - (path: { - name = "${serviceDir}/${path}"; - value = { - d = { - group = "root"; - user = "root"; - mode = "0750"; - }; - }; - }) [ - "var/data" - "images" - ] - ); - - networking.firewall.allowedTCPPorts = [ servicePort ]; - - environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ - { directory = serviceDir; } + ports = [ "${builtins.toString servicePort}:${builtins.toString servicePort}" ]; + volumes = [ + "${serviceDir}/var/data:/app/var/data" + "${serviceDir}/images:/app/slink/images" ]; + }; - topology.self.services.${serviceName} = { - name = lib.swarselsystems.toCapitalized serviceName; - info = "https://${serviceDomain}"; - icon = "${self}/files/topology-images/shlink.png"; + systemd.tmpfiles.settings."12-slink" = builtins.listToAttrs ( + map + (path: { + name = "${serviceDir}/${path}"; + value = { + d = { + group = "root"; + user = "root"; + mode = "0750"; + }; + }; + }) [ + "var/data" + "images" + ] + ); + + # networking.firewall.allowedTCPPorts = [ servicePort ]; + + environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ + { directory = serviceDir; } + ]; + + topology.self.services.${serviceName} = { + name = lib.swarselsystems.toCapitalized serviceName; + info = "https://${serviceDomain}"; + icon = "${self}/files/topology-images/shlink.png"; + }; + + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; }; - - globals.services.${serviceName} = { + services.${serviceName} = { domain = serviceDomain; inherit proxyAddress4 proxyAddress6 isHome; }; + }; - nodes.${serviceProxy}.services.nginx = { - upstreams = { - ${serviceName} = { - servers = { - "${serviceAddress}:${builtins.toString servicePort}" = { }; - }; + nodes.${webProxy}.services.nginx = { + upstreams = { + ${serviceName} = { + servers = { + "${serviceAddress}:${builtins.toString servicePort}" = { }; }; }; - virtualHosts = { - "${serviceDomain}" = { - useACMEHost = globals.domains.main; + }; + virtualHosts = { + "${serviceDomain}" = { + useACMEHost = globals.domains.main; - forceSSL = true; - acmeRoot = null; - oauth2.enable = true; - oauth2.allowedGroups = [ "slink_access" ]; - locations = { - "/" = { - proxyPass = "http://${serviceName}"; - }; - "/image" = { - proxyPass = "http://${serviceName}"; - setOauth2Headers = false; - bypassAuth = true; - }; + forceSSL = true; + acmeRoot = null; + oauth2.enable = true; + oauth2.allowedGroups = [ "slink_access" ]; + locations = { + "/" = { + proxyPass = "http://${serviceName}"; + }; + "/image" = { + proxyPass = "http://${serviceName}"; + setOauth2Headers = false; + bypassAuth = true; }; }; }; }; }; - } + }; +} #+end_src **** Snipe-IT (currently unused) @@ -14387,7 +14615,7 @@ Deployment notes: #+begin_src nix-ts :tangle modules/nixos/server/snipe-it.nix { lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "snipeit"; port = 80; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "snipeit"; port = 80; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome webProxy dnsServer; # sopsFile = config.node.secretsDir + "/secrets2.yaml"; inherit (config.swarselsystems) sopsFile; @@ -14399,7 +14627,7 @@ Deployment notes: options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -14433,7 +14661,7 @@ Deployment notes: }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -14469,21 +14697,31 @@ Deployment notes: #+begin_src nix-ts :tangle modules/nixos/server/homebox.nix { lib, pkgs, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "homebox"; port = 7745; }) servicePort serviceName serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6 isHome; + inherit (confLib.gen { name = "homebox"; port = 7745; }) servicePort serviceName serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; topology.self.services.${serviceName}.info = "https://${serviceDomain}"; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = { @@ -14498,9 +14736,9 @@ Deployment notes: }; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -14597,7 +14835,7 @@ or 2) use classic path addressing =aws s3 cp s3:/// s3:/// s3:/// s3:/// s3:/// s3:/// s3:/// Identity Providers > Kanidm > Edit=: + - Note the UUIDs in the URLs under "Ensure the OAuth application has the following redirect URLs whitelisted:": + - the first one is =accountId=, the second is the =externalId=, update them in the Kanidm config +- Relay setup: + - =Relays > Relays > Deploy=: + - note FIREZONE_TOKEN and add it as the firezone-relay-token secret +- Gateway setup: + - =Sites > Home > Deploy Gateway=: + - note FIREZONE_TOKEN and add it as the firezone-gateway-token secret +- After initial deployment, setup the user account under =Actors > Add Actor=: + - use OIDC login #+begin_src nix-ts :tangle modules/nixos/server/firezone.nix - { lib, pkgs, config, globals, confLib, dns, nodes, ... }: - let - inherit (confLib.gen { name = "firezone"; dir = "/var/lib/private/firezone"; }) serviceName serviceUser serviceGroup serviceDir serviceAddress serviceDomain proxyAddress4 proxyAddress6 isHome homeProxy webProxy idmServer dnsServer; - inherit (config.swarselsystems) sopsFile; + { lib, pkgs, config, globals, confLib, dns, nodes, ... }: + let + inherit (confLib.gen { name = "firezone"; dir = "/var/lib/private/firezone"; }) serviceName serviceDir serviceAddress serviceDomain proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy homeProxyIf webProxyIf idmServer dnsServer; + inherit (config.swarselsystems) sopsFile; - cfg = config.services.firezone.server; + apiPort = 8081; + webPort = 8080; + relayPort = 3478; + domainPort = 9003; - homeServices = lib.attrNames (lib.filterAttrs (_: serviceCfg: serviceCfg.isHome) globals.services); - homeDomains = map (name: globals.services.${name}.domain) homeServices; - allow = group: resource: { - "${group}@${resource}" = { - inherit group resource; - description = "Allow ${group} access to ${resource}"; - }; - }; - in - { - options = { - swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; - }; - config = lib.mkIf config.swarselmodules.server.${serviceName} { - - nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { - "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; - }; - - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6 isHome; - }; - - sops = { - secrets = { - kanidm-firezone-client = { inherit sopsFile; mode = "0400"; }; - firezone-relay-token = { inherit sopsFile; mode = "0400"; }; - firezone-smtp-password = { inherit sopsFile; mode = "0440"; }; - firezone-adapter-config = { inherit sopsFile; mode = "0440"; }; + homeServices = lib.attrNames (lib.filterAttrs (_: serviceCfg: serviceCfg.isHome) globals.services); + homeDomains = map (name: globals.services.${name}.domain) homeServices; + allow = group: resource: { + "${group}@${resource}" = { + inherit group resource; + description = "Allow ${group} access to ${resource}"; }; }; + in + { + options = { + swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; + }; + config = lib.mkIf config.swarselmodules.server.${serviceName} { - environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ - { directory = serviceDir; mode = "0700"; } - ]; + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; + }; - services.firezone = { - server = { - enable = true; - enableLocalDB = true; - settings = { - LOG_LEVEL = "debug"; - # OUTBOUND_EMAIL_ADAPTER = "Elixir.Swoosh.Adapters.SMTP"; - # OUTBOUND_EMAIL_FROM = config.repo.secrets.local.firezone.mail.from; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy} = { + allowedTCPPorts = [ apiPort webPort domainPort ]; + allowedUDPPorts = [ relayPort ]; + allowedUDPPortRanges = [ + { + from = config.services.firezone.relay.lowestPort; + to = config.services.firezone.relay.highestPort; + } + ]; + }; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy} = { + allowedTCPPorts = [ apiPort webPort domainPort ]; + allowedUDPPorts = [ relayPort ]; + allowedUDPPortRanges = [ + { + from = config.services.firezone.relay.lowestPort; + to = config.services.firezone.relay.highestPort; + } + ]; + }; + }; }; - settingsSecret = { - # OUTBOUND_EMAIL_ADAPTER_OPTS = config.sops.secrets.firezone-adapter-config.path; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; }; + }; - # smtp.configureManually = true; - smtp= { - inherit (config.repo.secrets.local.firezone.mail) from username; - host = globals.services.mailserver.domain; - port = 465; - implicitTls = true; - passwordFile = config.sops.secrets.firezone-smtp-password.path; + sops = { + secrets = { + kanidm-firezone-client = { inherit sopsFile; mode = "0400"; }; + firezone-relay-token = { inherit sopsFile; mode = "0400"; }; + firezone-smtp-password = { inherit sopsFile; mode = "0440"; }; + firezone-adapter-config = { inherit sopsFile; mode = "0440"; }; }; + }; - provision = { + environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ + { directory = serviceDir; mode = "0700"; } + ]; + + services.firezone = { + server = { enable = true; - accounts.main = { - name = "Home"; - relayGroups.relays.name = "Relays"; - gatewayGroups.home.name = "Home"; - actors.admin = { - type = "account_admin_user"; - name = "Admin"; - email = "admin@${globals.domains.main}"; - }; + enableLocalDB = true; - # auth.oidc = - # let - # client_id = "firezone"; - # in - # { - # name = "Kanidm"; - # adapter = "openid_connect"; - # adapter_config = { - # scope = "openid email profile"; - # response_type = "code"; - # inherit client_id; - # discovery_document_uri = "https://${globals.services.kanidm.domain}/oauth2/openid/${client_id}/.well-known/openid-configuration"; - # clientSecretFile = config.sops.secrets.kanidm-firezone-client.path; - # }; - # }; - - resources = - lib.genAttrs homeDomains - (domain: { - type = "dns"; - name = domain; - address = domain; - gatewayGroups = [ "home" ]; - filters = [ - { protocol = "icmp"; } - { - protocol = "tcp"; - ports = [ - 443 - 80 - ]; - } - { - protocol = "udp"; - ports = [ 443 ]; - } - ]; - }) - // { - "home.vlan-services.v4" = { - type = "cidr"; - name = "home.vlan-services.v4"; - address = globals.networks.home-lan.vlans.services.cidrv4; - gatewayGroups = [ "home" ]; - }; - "home.vlan-services.v6" = { - type = "cidr"; - name = "home.vlan-services.v6"; - address = globals.networks.home-lan.vlans.services.cidrv6; - gatewayGroups = [ "home" ]; - }; - }; - - policies = - { } - // allow "everyone" "home.vlan-services.v4" - // allow "everyone" "home.vlan-services.v6" - // lib.mergeAttrsList (map (domain: allow "everyone" domain) homeDomains); + smtp = { + inherit (config.repo.secrets.local.firezone.mail) from username; + host = globals.services.mailserver.domain; + port = 465; + implicitTls = true; + passwordFile = config.sops.secrets.firezone-smtp-password.path; }; - }; - domain = { - settings.ERLANG_DISTRIBUTION_PORT = 9003; - package = pkgs.dev.firezone-server-domain; - }; - api = { - externalUrl = "https://${serviceDomain}/api/"; - port = 8081; - package = pkgs.dev.firezone-server-api; - }; - web = { - externalUrl = "https://${serviceDomain}/"; - port = 8080; - package = pkgs.dev.firezone-server-web; - }; - }; - - # relay = { - # enable = true; - # port = 3478; - # inherit (config.node) name; - # apiUrl = "wss://${serviceDomain}/api/"; - # tokenFile = config.sops.secrets.firezone-relay-token.path; - # publicIpv4 = proxyAddress4; - # publicIpv6 = proxyAddress6; - # openFirewall = true; - # package = pkgs.dev.firezone-relay; - # }; - }; - systemd.services.firezone-initialize = - let - generateSecrets = - let - requiredSecrets = lib.filterAttrs (_: v: v == null) cfg.settingsSecret; - in - '' - mkdir -p secrets - chmod 700 secrets - '' - + lib.concatLines ( - lib.forEach (builtins.attrNames requiredSecrets) (secret: '' - if [[ ! -e secrets/${secret} ]]; then - echo "Generating ${secret}" - # Some secrets like TOKENS_KEY_BASE require a value >=64 bytes. - head -c 64 /dev/urandom | base64 -w 0 > secrets/${secret} - chmod 600 secrets/${secret} - fi - '') - ); - loadSecretEnvironment = - component: - let - relevantSecrets = lib.subtractLists (builtins.attrNames cfg.${component}.settings) ( - builtins.attrNames cfg.settingsSecret - ); - in - lib.concatLines ( - lib.forEach relevantSecrets ( - secret: - ''export ${secret}=$(< ${ - if cfg.settingsSecret.${secret} == null then - "secrets/${secret}" - else - "\"$CREDENTIALS_DIRECTORY/${secret}\"" - })'' - ) - ); - in - { - script = lib.mkForce '' - mkdir -p "$TZDATA_DIR" - - # Generate and load secrets - ${generateSecrets} - ${loadSecretEnvironment "domain"} - - echo "Running migrations" - ${lib.getExe cfg.domain.package} eval "Domain.Release.migrate(manual: true)" - ''; - }; - - - nodes = { - ${homeProxy} = - let - nodeCfg = nodes.${homeProxy}.config; - in - { - sops.secrets.firezone-gateway-token = { inherit sopsFile; mode = "0400"; }; - services.firezone.gateway = { + provision = { enable = true; - inherit (nodeCfg.node) name; - apiUrl = "wss://${globals.services.firezone.domain}/api/"; - tokenFile = nodeCfg.sops.secrets.firezone-gateway-token.path; - package = pkgs.dev.firezone-gateway; + accounts.main = { + name = "Home"; + relayGroups.relays.name = "Relays"; + gatewayGroups.home.name = "Home"; + actors.admin = { + type = "account_admin_user"; + name = "Admin"; + email = "admin@${globals.domains.main}"; + }; + groups.anyone = { + name = "anyone"; + members = [ + "admin" + ]; + }; + + auth.oidc = + let + client_id = "firezone"; + in + { + name = "Kanidm"; + adapter = "openid_connect"; + adapter_config = { + scope = "openid email profile"; + response_type = "code"; + inherit client_id; + discovery_document_uri = "https://${globals.services.kanidm.domain}/oauth2/openid/${client_id}/.well-known/openid-configuration"; + clientSecretFile = config.sops.secrets.kanidm-firezone-client.path; + }; + }; + + resources = + lib.genAttrs homeDomains + (domain: { + type = "dns"; + name = domain; + address = domain; + gatewayGroups = [ "home" ]; + filters = [ + { protocol = "icmp"; } + { + protocol = "tcp"; + ports = [ + 443 + 80 + ]; + } + { + protocol = "udp"; + ports = [ 443 ]; + } + ]; + }) + // { + "home.vlan-services.v4" = { + type = "cidr"; + name = "home.vlan-services.v4"; + address = globals.networks.home-lan.vlans.services.cidrv4; + gatewayGroups = [ "home" ]; + }; + "home.vlan-services.v6" = { + type = "cidr"; + name = "home.vlan-services.v6"; + address = globals.networks.home-lan.vlans.services.cidrv6; + gatewayGroups = [ "home" ]; + }; + }; + + policies = + { } + // allow "everyone" "home.vlan-services.v4" + // allow "anyone" "home.vlan-services.v4" + // allow "everyone" "home.vlan-services.v6" + // allow "anyone" "home.vlan-services.v6" + // lib.mergeAttrsList (map (domain: allow "everyone" domain) homeDomains) + // lib.mergeAttrsList (map (domain: allow "anyone" domain) homeDomains); + }; + }; + + domain = { + settings.ERLANG_DISTRIBUTION_PORT = domainPort; + package = pkgs.dev.firezone-server-domain; + }; + api = { + externalUrl = "https://${serviceDomain}/api/"; + address = "0.0.0.0"; + port = apiPort; + package = pkgs.dev.firezone-server-api; + }; + web = { + externalUrl = "https://${serviceDomain}/"; + address = "0.0.0.0"; + port = webPort; + package = pkgs.dev.firezone-server-web; }; }; - ${idmServer} = - let - nodeCfg = nodes.${idmServer}.config; - in - { - sops.secrets.kanidm-firezone = { inherit (nodeCfg.swarselsystems) sopsFile; owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - services.kanidm.provision = { - groups."firezone.access" = { }; - systems.oauth2.firezone = { - displayName = "Firezone VPN"; - # NOTE: state: both uuids are runtime values - originUrl = [ - "https://${globals.services.firezone.domain}/50e16678-6e95-49e2-b59e-d70d0e658843/sign_in/providers/fc8afaa3-ce60-4073-9cae-81dec9453a2d/handle_callback" - "https://${globals.services.firezone.domain}/50e16678-6e95-49e2-b59e-d70d0e658843/settings/identity_providers/openid_connect/fc8afaa3-ce60-4073-9cae-81dec9453a2d/handle_callback" - ]; - originLanding = "https://${globals.services.firezone.domain}/"; - basicSecretFile = nodeCfg.sops.secrets.kanidm-firezone.path; - preferShortUsername = true; - scopeMaps."firezone.access" = [ - "openid" - "email" - "profile" - ]; + + relay = { + enable = true; + port = relayPort; + inherit (config.node) name; + apiUrl = "wss://${serviceDomain}/api/"; + tokenFile = config.sops.secrets.firezone-relay-token.path; + publicIpv4 = proxyAddress4; + publicIpv6 = proxyAddress6; + openFirewall = lib.mkIf (!isProxied) true; + package = pkgs.dev.firezone-relay; + }; + }; + # systemd.services.firezone-initialize = + # let + # generateSecrets = + # let + # requiredSecrets = lib.filterAttrs (_: v: v == null) cfg.settingsSecret; + # in + # '' + # mkdir -p secrets + # chmod 700 secrets + # '' + # + lib.concatLines ( + # lib.forEach (builtins.attrNames requiredSecrets) (secret: '' + # if [[ ! -e secrets/${secret} ]]; then + # echo "Generating ${secret}" + # # Some secrets like TOKENS_KEY_BASE require a value >=64 bytes. + # head -c 64 /dev/urandom | base64 -w 0 > secrets/${secret} + # chmod 600 secrets/${secret} + # fi + # '') + # ); + # loadSecretEnvironment = + # component: + # let + # relevantSecrets = lib.subtractLists (builtins.attrNames cfg.${component}.settings) ( + # builtins.attrNames cfg.settingsSecret + # ); + # in + # lib.concatLines ( + # lib.forEach relevantSecrets ( + # secret: + # ''export ${secret}=$(< ${ + # if cfg.settingsSecret.${secret} == null then + # "secrets/${secret}" + # else + # "\"$CREDENTIALS_DIRECTORY/${secret}\"" + # })'' + # ) + # ); + # in + # { + # script = lib.mkForce '' + # mkdir -p "$TZDATA_DIR" + + # # Generate and load secrets + # ${generateSecrets} + # ${loadSecretEnvironment "domain"} + + # echo "Running migrations" + # ${lib.getExe cfg.domain.package} eval "Domain.Release.migrate(manual: true)" + # ''; + # }; + + + nodes = { + ${homeProxy} = + let + nodeCfg = nodes.${homeProxy}.config; + nodePkgs = nodes.${homeProxy}.pkgs; + in + { + sops.secrets.firezone-gateway-token = { inherit (nodeCfg.swarselsystems) sopsFile; mode = "0400"; }; + networking.nftables = { + firewall = { + zones.firezone.interfaces = [ "tun-firezone" ]; + rules = { + # masquerade firezone traffic + masquerade-firezone = { + from = [ "firezone" ]; + to = [ "vlan-services" ]; + # masquerade = true; NOTE: custom rule below for ip4 + ip6 + late = true; # Only accept after any rejects have been processed + verdict = "accept"; + }; + # forward firezone traffic + forward-incoming-firezone-traffic = { + from = [ "firezone" ]; + to = [ "vlan-services" ]; + verdict = "accept"; + }; + + # FIXME: is this needed? conntrack should take care of it and we want to masquerade anyway + forward-outgoing-firezone-traffic = { + from = [ "vlan-services" ]; + to = [ "firezone" ]; + verdict = "accept"; + }; + }; + }; + chains.postrouting = { + masquerade-firezone = { + after = [ "hook" ]; + late = true; + rules = + lib.forEach + [ + "firezone" + ] + ( + zone: + lib.concatStringsSep " " [ + "meta protocol { ip, ip6 }" + (lib.head nodeCfg.networking.nftables.firewall.zones.${zone}.ingressExpression) + (lib.head nodeCfg.networking.nftables.firewall.zones.vlan-services.egressExpression) + "masquerade random" + ] + ); + }; + }; }; - }; - }; - ${webProxy} = { - services.nginx = { - upstreams = { - ${serviceName} = { - servers."127.0.0.1:${toString config.services.firezone.server.web.port}" = { }; + boot.kernel.sysctl = { + "net.core.wmem_max" = 16777216; + "net.core.rmem_max" = 134217728; }; - "${serviceName}-api" = { - servers."${serviceAddress}:${toString config.services.firezone.server.api.port}" = { }; + services.firezone.gateway = { + enable = true; + logLevel = "trace"; + inherit (nodeCfg.node) name; + apiUrl = "wss://${globals.services.firezone.domain}/api/"; + tokenFile = nodeCfg.sops.secrets.firezone-gateway-token.path; + package = nodePkgs.stable25_05.firezone-gateway; # newer versions of firezone-gateway are not compatible with server package }; }; - virtualHosts = { - ${serviceDomain} = { - useACMEHost = globals.domains.main; - forceSSL = true; - acmeRoot = null; - locations."/" = { - # The trailing slash is important to strip the location prefix from the request - proxyPass = "http://${serviceName}/"; - proxyWebsockets = true; + ${idmServer} = + let + nodeCfg = nodes.${idmServer}.config; + accountId = "6b3c6ba7-5240-4684-95ce-f40fdae45096"; + externalId = "08d714e9-1ab9-4133-a39d-00e843a960cc"; + in + { + sops.secrets.kanidm-firezone = { inherit (nodeCfg.swarselsystems) sopsFile; owner = "kanidm"; group = "kanidm"; mode = "0440"; }; + services.kanidm.provision = { + groups."firezone.access" = { }; + systems.oauth2.firezone = { + displayName = "Firezone VPN"; + # NOTE: state: both uuids are runtime values + originUrl = [ + "https://${globals.services.firezone.domain}/${accountId}/sign_in/providers/${externalId}/handle_callback" + "https://${globals.services.firezone.domain}/${accountId}/settings/identity_providers/openid_connect/${externalId}/handle_callback" + ]; + originLanding = "https://${globals.services.firezone.domain}/"; + basicSecretFile = nodeCfg.sops.secrets.kanidm-firezone.path; + preferShortUsername = true; + scopeMaps."firezone.access" = [ + "openid" + "email" + "profile" + ]; }; - locations."/api/" = { - # The trailing slash is important to strip the location prefix from the request - proxyPass = "http://${serviceName}-api/"; - proxyWebsockets = true; + + }; + }; + ${webProxy} = { + services.nginx = { + upstreams = { + ${serviceName} = { + servers."${serviceAddress}:${builtins.toString webPort}" = { }; + }; + "${serviceName}-api" = { + servers."${serviceAddress}:${builtins.toString apiPort}" = { }; + }; + }; + virtualHosts = { + ${serviceDomain} = { + useACMEHost = globals.domains.main; + forceSSL = true; + acmeRoot = null; + locations."/" = { + # The trailing slash is important to strip the location prefix from the request + proxyPass = "http://${serviceName}/"; + proxyWebsockets = true; + }; + locations."/api/" = { + # The trailing slash is important to strip the location prefix from the request + proxyPass = "http://${serviceName}-api/"; + proxyWebsockets = true; + }; }; }; }; }; }; - }; - }; - } + }; + } #+end_src *** Darwin :PROPERTIES: @@ -17537,7 +17909,7 @@ This holds packages that I can use as provided, or with small modifications (as # element-desktop nicotine-plus - stable.transmission_3 + stable25_05.transmission_3 mktorrent hugo @@ -21303,6 +21675,40 @@ Sets up a systemd user service for anki that does not stall the shutdown process } #+end_src +***** firezone service for tray + +#+begin_src nix-ts :tangle modules/home/common/firezone-tray.nix + { lib, config, pkgs, ... }: + { + options.swarselmodules.firezone-tray = lib.mkEnableOption "enable firezone applet for tray"; + config = lib.mkIf config.swarselmodules.firezone-tray { + + systemd.user.services.firezone-applet = { + Unit = { + Description = "Firezone applet"; + Requires = [ + "tray.target" + ]; + After = [ + "graphical-session.target" + "tray.target" + ]; + PartOf = [ "graphical-session.target" ]; + }; + + Install = { + WantedBy = [ "graphical-session.target" ]; + }; + + Service = { + ExecStart = "${pkgs.firezone-gui-client}/bin/firezone-client-gui"; + }; + }; + }; + + } +#+end_src + ***** syncthing service for tray :PROPERTIES: :CUSTOM_ID: h:5e7c606f-628a-4849-94e9-359d7b75f228 @@ -24328,6 +24734,9 @@ In short, the options defined here are passed to the modules systems using =_mod proxyAddress6 = globals.hosts.${proxy}.wanAddress6 or null; inherit (globals.hosts.${config.node.name}) isHome; inherit (globals.general) homeProxy webProxy dnsServer idmServer; + webProxyIf = "${webProxy}-wgProxy"; + homeProxyIf = "home-wgHome"; + isProxied = config.node.name != webProxy; }; mkMicrovm = if config.swarselsystems.withMicroVMs then (guestName: { @@ -26604,6 +27013,7 @@ Modules that need to be loaded on the NixOS level. Note that these will not be a btrfs = lib.mkDefault true; distrobox = lib.mkDefault true; env = lib.mkDefault true; + firezone-client = lib.mkDefault true; general = lib.mkDefault true; gnome-keyring = lib.mkDefault true; gvfs = lib.mkDefault true; @@ -26895,6 +27305,7 @@ This holds modules that are to be used on most hosts. These are also the most im env = lib.mkDefault true; eza = lib.mkDefault true; firefox = lib.mkDefault true; + firezone-tray = lib.mkDefault true; fuzzel = lib.mkDefault true; gammastep = lib.mkDefault true; general = lib.mkDefault true; @@ -30621,24 +31032,27 @@ These general steps are needed when setting up a new machine and do not fit into These are current deviations from the standard settings that I take while some things are broken upstream #+begin_src markdown :noweb-ref fixes :exports both :results html -- 202501102: - - flake: - - emacs-overlay: - - : version pinned because emacsclient is currently broken on latest - - niri-flake: - - currently not using the sugared version of screenshot-[,window], as it is currently broken - - home-manager: - - emacs-tramp: - - using stable version in extraPackages (broken in unstable) - - :ensure nil in emacs tramp settings to use package in extraPackages - - emacs-calfwL - - pinned to version not in nixpkgs (is in latest emacs-overlay, but that is broken) - - vesktop: - - running stable version (broken in unstable) - - batgrep: - - running stable version (broken in unstable) - - swayosd: - - pinned to version not in nixpkgs (fixes https://github.com/ErikReider/SwayOSD/issues/175) + - 20260101: + - nixos: + - firezone-gateway does not work in newer versions with firezone-server (currently working version v1.4.8) + - 20251102: + - flake: + - emacs-overlay: + - : version pinned because emacsclient is currently broken on latest + - niri-flake: + - currently not using the sugared version of screenshot-[,window], as it is currently broken + - home-manager: + - emacs-tramp: + - using stable version in extraPackages (broken in unstable) + - :ensure nil in emacs tramp settings to use package in extraPackages + - emacs-calfwL + - pinned to version not in nixpkgs (is in latest emacs-overlay, but that is broken) + - vesktop: + - running stable version (broken in unstable) + - batgrep: + - running stable version (broken in unstable) + - swayosd: + - pinned to version not in nixpkgs (fixes https://github.com/ErikReider/SwayOSD/issues/175) #+end_src * Appendix B: Supplementary Files diff --git a/flake.lock b/flake.lock index e2f655d..44acb12 100644 --- a/flake.lock +++ b/flake.lock @@ -1600,11 +1600,11 @@ }, "nixpkgs-dev": { "locked": { - "lastModified": 1763648956, - "narHash": "sha256-JBATYs0HPlATioA2kYFwUAsnzWv9Bd2tXqeCOr/ix6I=", + "lastModified": 1767131767, + "narHash": "sha256-APHjXWyLmNKFNXoVU7Z82L8zUeSpR1/owKFryitWSsg=", "owner": "Swarsel", "repo": "nixpkgs", - "rev": "230b56741730ede84e7e488d11cb34044f5b54c7", + "rev": "449fa265ea9c67c1ea9b1c3d8121959e2ce348d3", "type": "github" }, "original": { @@ -1781,6 +1781,22 @@ "type": "github" } }, + "nixpkgs-stable25_11": { + "locked": { + "lastModified": 1767047869, + "narHash": "sha256-tzYsEzXEVa7op1LTnrLSiPGrcCY6948iD0EcNLWcmzo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "89dbf01df72eb5ebe3b24a86334b12c27d68016a", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs-stable_2": { "locked": { "lastModified": 1763622513, @@ -1799,16 +1815,16 @@ }, "nixpkgs-stable_3": { "locked": { - "lastModified": 1763622513, - "narHash": "sha256-1jQnuyu82FpiSxowrF/iFK6Toh9BYprfDqfs4BB+19M=", + "lastModified": 1767047869, + "narHash": "sha256-tzYsEzXEVa7op1LTnrLSiPGrcCY6948iD0EcNLWcmzo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c58bc7f5459328e4afac201c5c4feb7c818d604b", + "rev": "89dbf01df72eb5ebe3b24a86334b12c27d68016a", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-25.05", + "ref": "nixos-25.11", "repo": "nixpkgs", "type": "github" } @@ -2653,6 +2669,7 @@ "nixpkgs-stable24_05": "nixpkgs-stable24_05", "nixpkgs-stable24_11": "nixpkgs-stable24_11", "nixpkgs-stable25_05": "nixpkgs-stable25_05", + "nixpkgs-stable25_11": "nixpkgs-stable25_11", "nswitch-rcm-nix": "nswitch-rcm-nix", "nur": "nur", "pre-commit-hooks": "pre-commit-hooks_3", diff --git a/flake.nix b/flake.nix index 2b2ecc5..277493e 100644 --- a/flake.nix +++ b/flake.nix @@ -28,10 +28,11 @@ smallpkgs.url = "github:nixos/nixpkgs/08fcb0dcb59df0344652b38ea6326a2d8271baff?narHash=sha256-HXIQzULIG/MEUW2Q/Ss47oE3QrjxvpUX7gUl4Xp6lnc%3D&shallow=1"; nixpkgs-dev.url = "github:Swarsel/nixpkgs/main"; nixpkgs-kernel.url = "github:NixOS/nixpkgs/063f43f2dbdef86376cc29ad646c45c46e93234c?narHash=sha256-6m1Y3/4pVw1RWTsrkAK2VMYSzG4MMIj7sqUy7o8th1o%3D"; #specifically pinned for kernel version - nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-25.05"; + nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-25.11"; nixpkgs-stable24_05.url = "github:NixOS/nixpkgs/nixos-24.05"; nixpkgs-stable24_11.url = "github:NixOS/nixpkgs/nixos-24.11"; nixpkgs-stable25_05.url = "github:NixOS/nixpkgs/nixos-25.05"; + nixpkgs-stable25_11.url = "github:NixOS/nixpkgs/nixos-25.11"; home-manager = { # url = "github:nix-community/home-manager"; diff --git a/hosts/nixos/aarch64-linux/belchsfactory/default.nix b/hosts/nixos/aarch64-linux/belchsfactory/default.nix index a5c764f..f024b3b 100644 --- a/hosts/nixos/aarch64-linux/belchsfactory/default.nix +++ b/hosts/nixos/aarch64-linux/belchsfactory/default.nix @@ -62,7 +62,6 @@ attic = true; garage = true; hydra = false; - dns-hostrecord = true; }; } diff --git a/hosts/nixos/aarch64-linux/liliputsteps/default.nix b/hosts/nixos/aarch64-linux/liliputsteps/default.nix index 3fc716c..4a4ead2 100644 --- a/hosts/nixos/aarch64-linux/liliputsteps/default.nix +++ b/hosts/nixos/aarch64-linux/liliputsteps/default.nix @@ -33,7 +33,6 @@ swarselmodules.server = { bastion = true; - dns-hostrecord = true; # ssh = false; }; diff --git a/hosts/nixos/aarch64-linux/moonside/default.nix b/hosts/nixos/aarch64-linux/moonside/default.nix index 377a151..bfbd912 100644 --- a/hosts/nixos/aarch64-linux/moonside/default.nix +++ b/hosts/nixos/aarch64-linux/moonside/default.nix @@ -108,6 +108,5 @@ in minecraft = true; restic = true; diskEncryption = lib.mkForce false; - dns-hostrecord = true; }; } diff --git a/hosts/nixos/aarch64-linux/stoicclub/default.nix b/hosts/nixos/aarch64-linux/stoicclub/default.nix index 1a84d39..01b40d4 100644 --- a/hosts/nixos/aarch64-linux/stoicclub/default.nix +++ b/hosts/nixos/aarch64-linux/stoicclub/default.nix @@ -1,4 +1,4 @@ -{ self, lib, minimal, ... }: +{ self, config, lib, minimal, ... }: { imports = [ ./hardware-configuration.nix @@ -25,6 +25,8 @@ isCloud = true; isBastionTarget = true; }; + + globals.general.dnsServer = config.node.name; } // lib.optionalAttrs (!minimal) { swarselprofiles = { server = true; @@ -32,6 +34,7 @@ swarselmodules.server = { nsd = true; - dns-hostrecord = true; }; + + networking.nftables.firewall.zones.untrusted.interfaces = [ "lan" ]; } diff --git a/hosts/nixos/aarch64-linux/twothreetunnel/default.nix b/hosts/nixos/aarch64-linux/twothreetunnel/default.nix index e4af5a3..0b33db2 100644 --- a/hosts/nixos/aarch64-linux/twothreetunnel/default.nix +++ b/hosts/nixos/aarch64-linux/twothreetunnel/default.nix @@ -1,4 +1,4 @@ -{ self, lib, minimal, ... }: +{ self, config, lib, minimal, ... }: { imports = [ ./hardware-configuration.nix @@ -12,6 +12,8 @@ icon = "devices.cloud-server"; }; + globals.general.webProxy = config.node.name; + swarselsystems = { flakePath = "/root/.dotfiles"; info = "VM.Standard.A1.Flex, 2 vCPUs, 8GB RAM"; @@ -27,7 +29,6 @@ server = { wireguard.interfaces = { wgProxy = { - # ifName = "wg"; isServer = true; peers = [ "moonside" @@ -47,8 +48,16 @@ swarselmodules.server = { nginx = true; oauth2-proxy = true; - dns-hostrecord = true; wireguard = true; + firezone = true; + }; + + networking.nftables = { + firewall.zones.untrusted.interfaces = [ "lan" ]; + chains.forward.dnat = { + after = [ "conntrack" ]; + rules = [ "ct status dnat accept" ]; + }; }; } diff --git a/hosts/nixos/aarch64-linux/twothreetunnel/secrets/pii.nix.enc b/hosts/nixos/aarch64-linux/twothreetunnel/secrets/pii.nix.enc index edcccff..7a21c2d 100644 --- a/hosts/nixos/aarch64-linux/twothreetunnel/secrets/pii.nix.enc +++ b/hosts/nixos/aarch64-linux/twothreetunnel/secrets/pii.nix.enc @@ -1,5 +1,5 @@ { - "data": "ENC[AES256_GCM,data:UKXEKxP1SDqQWktd3eQzkoqsk6k3m9Rj+JNk3xmdZmp5p+pXnY+uDltSIL2PTsOy7wtf4gp16jze1PhHvYojuN2nnou/D1KJALPHBgGiR8CgBlbX5nrCbtHrs4SZq+M7QihRV8lsG8gU0aIm0lDO83cJ0boUfnZdexDPjcuhYJj5nmgOG1bV60LOJNg7yn//hlVhovrf7ygXOk9HirDMmK9MVkKw5utD7iE4Cm7txrK1z9rQLJYM3kzwsWJAGkIc/IbI4Css10ScNK9VMKU4B596Dv2eCHvSIUJ8Y8AJrE/1+jp8XQW4aUMcFsbKpwjL2mOm0DSFupr/D60vJ1j5ovEIT4Vt51H4cpcBduBUCHoRZ1S/fZePxYaPunEI9lJVSQeANGevqXmvd8SSpO8YFN2S06CsFcx8hadQpq79uD7hm4tZzNUFOm2fytY9WMl0YWlSM4g3U30tKVVo+RMmm43oMaStOiyXUyohXjKY5QJqI+rJRRifKUhfze4Z4aGn,iv:nOU57gwkc3hld/+IqqHYtEiJYXzOFwTaG6cNEl7ZNHk=,tag:kRp580c9haQSQmOw2hBvrw==,type:str]", + "data": "ENC[AES256_GCM,data:mQPfK2Dh2ACae0a+1GRHY/CV0JpHH8JO+td+RR17UXyq5v/OF6YDfS7loIpQvImEAs6AvIzIdyq0848Fh/34kh/K2ZAq4AknW9jQx5YyP4nbk8/q1/dk+95c0u98WnN6mw3BFHHesKYCfGy82GMnu00Ffxu7WSYzTKxq6yvROS7ugefRjsoMsuJcEeHmoIBgEIjXntGT4DxJjw4RhWPm+unSmce9SXfqbAuuizHm/S5URYvicIzalSITlfFBrpKWNxNe9fC2etDb/fB+uMpG28rmB98ov1W0X/W3JOUhASXVhB+YCau8XdIRPopEnkR4Wm1HD+exJ3CToJMgrdmv5Cj9rJoFI0jvApRpjBix5qDrTsbn3iWbv/QYuCnL8ulXY7nYtkmFjFCG3fLZ5G+6EVE+bZnh2V8KYAVM9moehNJ9Or4kGST5JWnIizFvAeeYef0xZtBMwv36Yc1JNAh3zlHP26lcXew+Ulxxcv07RmmV52jZMfWweyg4nXNumrbmy/GwingIhqN8wHrOD3Tu0HlvqmX5C5YRZg5iVU4lnAjKJc6XRn7B1GQzKeyE9HKagkrULQKGmqDlqEvEAp/9eW+rTR2Yho1QStK7J2RXFnWwpE4PH3cIfHWtwIv67yw+QWqj+lXMztHaX3RIRXGLyqnWtaLjMG+IIYytzaBt,iv:djDts0mzoVU6Cvf8KJb01CkHO+OrnIJyMhTfgJ8lZEE=,tag:JiZ2t5cBfSAKG0b1wAZCZA==,type:str]", "sops": { "age": [ { @@ -7,8 +7,8 @@ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqdEhDamZTRUhQZFNDTTl4\nVVVNNGZXa2h2THVzY0JWMjE2WjNJT0ZoblV3ClYzeEt4c0dWRzlISnN3NGthR21M\nTEtDQ011dFdhRVdPWlpweS9ma0N3dmsKLS0tIHFPQzQ5VzkyODZyY1JpcE4xR2Nl\nY2MrSERXTWkvNVZCR2xHUGh4ZXMvYTgK7pxPjnh3idl4QzBkR6LHyRskgqA3apS2\nkbg7As6wlEs34TAO8reyZknKTUd3Xif1v9RXiTcu1sEKHqkcqEoDog==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2025-12-22T01:24:25Z", - "mac": "ENC[AES256_GCM,data:NtGHAadNGMfyCOqiaE/XRZqu4CnQ1IujgI3/IraY6E3luqzFVxJk/CgWD2rjbhLmaL7hd3Tay2LjL5uFxzM7kAE9QaaZtcxYKbudhznUdi/UEZ2ZtqyXqafXfCjEVbETaTAP3YGmQwJ/kAMj+FZp9yx7d6B8SVqWu1PatJGsOIA=,iv:OW6Xsr2MmEJq70TnEIJFgwLi3iMmKFV2Fy05a5G6Ibw=,tag:8KtNH6tEj/rQoht7FRDN3Q==,type:str]", + "lastmodified": "2025-12-30T14:45:29Z", + "mac": "ENC[AES256_GCM,data:/hfp7IopUWZSMequVWcpMup9lM/e5G3Qda+8zz8ecPMdMrbUqpzi43QAbiTvMC1Wa2DKWFOsZPilClJQfG0MMEYD4GWehd2C5psK5HOxS3h9pjE/AjctaCwu8RB71paK940W6NY8sCjOi+zm+Az4KDwkOl0R3ApaUMofV4hsg6M=,iv:d5Zy4HXtoSfRN4E0FHjT2vIWMY8k3G422ygVAZ7gXrc=,tag:a6UZVjb9kTj+8FZG1FIyrg==,type:str]", "pgp": [ { "created_at": "2025-12-01T23:06:36Z", diff --git a/hosts/nixos/aarch64-linux/twothreetunnel/secrets/secrets.yaml b/hosts/nixos/aarch64-linux/twothreetunnel/secrets/secrets.yaml index 858d631..5e9fd94 100644 --- a/hosts/nixos/aarch64-linux/twothreetunnel/secrets/secrets.yaml +++ b/hosts/nixos/aarch64-linux/twothreetunnel/secrets/secrets.yaml @@ -7,6 +7,15 @@ wireguard-private-key: ENC[AES256_GCM,data:m8fL4Y5TusV4imzcVqTmJZB0rlb+ndoH/Bl7K #ENC[AES256_GCM,data:IpoTYZX4KGjPA+hZ,iv:Hd1V9//M1f/10HQ7ZEEA9ZtuO8EBtY1kn3n28krYxpg=,tag:We6WirbRgSH1qOjC4g7spg==,type:comment] oauth2-cookie-secret: ENC[AES256_GCM,data:ZN44Kdai0hUgx0GduynlyMHDnZpdnp1SPAGEaNaNFHGMhM9Q5HPzotiNXQM=,iv:vsYhWriY5G4KLiJ12MLm26B7aBzCL5GAr+S15klH4Bc=,tag:t+MsS0Wgo5papvoeK1nk+g==,type:str] kanidm-oauth2-proxy-client: ENC[AES256_GCM,data:a90dn//LD6tvDYGSNT2neorQRfo0puo7GA==,iv:a/R6xlwGdrwJNc7qBoo0Zmlh7GkZ1+uU+RzOxRE+okc=,tag:3WpAVThFLXZFsCIl5xM0IQ==,type:str] +#ENC[AES256_GCM,data:vm48D/CiRtw=,iv:7Vs8SfqqGEEU64ZqF3uvFIG7DnUfOT3kGqodiIbCwjQ=,tag:hdNZZUMTLIrAGydGSFfP5Q==,type:comment] +kanidm-firezone-client: ENC[AES256_GCM,data:YD1lkGkg+HxqHrGsbIz2GRq/VMIJqOD+VQ==,iv:AJa/sVAC0s4hdfvQYf+/NaYTJaxO0fdwzNmmD7S+kc8=,tag:JSU6aX8kYbr70+YYwRV56Q==,type:str] +#ENC[AES256_GCM,data:XS4Kqba//4tVSj8AzyLY19Milwl0w7UkTM48t8m/wyB/P8TgDerxJwOGJvz3uLZJX/EO0/4rKminMYSoMybRnNn4TVv9pa9uV3JEkUsGkFk2abMfBriAQjQgziwLbDZQJmnJs46YD5s+sYELN4MJtwFNg6NzEDATDMWuE4+loyxoqgF/lzG3OFGkDl1R2JkCIOU6NGRqTn8a4XpX+p8U5QrY2V4iBCXajGXrcqLfINYW508feq1TAUZazaNdA+RC2SMvq6Diy8mysP1p/5mGUpIATjmoDqN74Yc5uZAwaenI6jIsfcE4JP5lFy7dHWOfTQS/9MCsEsRN2LWuP0ivaKOgF79ykd4Tb19EACdhpkip8XV0hKHJMuyEr6zJ23dUNtBE,iv:lpA1sk5y4tSk6iXAjArtF4piJW5af3+tIwMos1BpPEU=,tag:479ZIsnwkSSFq+C2a0jHzQ==,type:comment] +firezone-relay-token: ENC[AES256_GCM,data:QLQ444ocvL1yjXXslo6YzdPUasdt58Qztf6yv4UHh0AZtMVuOcDmUUXdI9Qz0i0J34zGbtcPw/Ac9CzxnF5sRj9v1D6RkfHf642vo2JxcnG+LExHzUFNEhTAXqgLvfdQhi89hQTjSfc/+ryDyf16tTJklX40VitqYLtTEW9CHSHhKrVr7Gx9u5qw1+j0voQbJEs/ojBwsnzNQ4Z7FJgWLBw9FMOQg9sap28m6fBFJNnUGaK2vIUQ1qPXQWyX1YTh6xd0nq/jyB9ctqQczYftgd+wkaEiyMjQJkNk22W/6P1M3biV4L52H7WVVhptB8yWa7TZUXD6GFi3cMTXhn0NhM5FsCJhXeGcnzNmBs8=,iv:RdVXYof5cSMM0WTAoh8SO3jTWyR+XTNmK0U4ezHu76g=,tag:nSw7ykFPYuHq/klTwlNpSQ==,type:str] +firezone-smtp-password: ENC[AES256_GCM,data:WLj+kcidIMQIP6gPuuIrujA+fHypUpGUFg==,iv:kg96vVaGund6HcXoJltIma9ecv6tK9AxZJf8n62+9aE=,tag:g54wHPhD4qnHlKZQd+MPZw==,type:str] +#ENC[AES256_GCM,data:aBNmUs9ZW+h5fDMVKdW3WQebJ8zmbHuYmNK9slZx5tZONTfnfnFRYjbzyqFTBKfC0bYjzLYL8AxXiEiPmBo2yLgbXtsOrVMoML3hD9Oi9T/7++BUBpbBQ31cC/EtnALumpes7+hO3DULm5tzWYc9qIz3yB9/gQzuKCqFOB6TCt/PwAKrVKNbcOihx/5xh04s6WyqfSUjWOOcHSY/ng2G7NeYRInLe6TgM6gGQGe2DjXCmNvgxJV2Mh78IWs3yA3aJ9VtrgF5R0PGoqHHZ8GfRZfYn7MBSW2dHztb0oLWux6bnO61Wnm8iDdR7xguQkNXPO0XXIIIO6AOL9duThXYjwQmieqYEEu1BmrvaQ4/tslLHX77axQCm1miwmZP9DoKor3yAziCBMa/pbU5JFlft4QZ2QGY7EreDfBVoDcPjCgA+gXuvq1VozPTiRH+y1hiulGlbGL0TmA=,iv:nsXYOxnWGceyB0aiv0Db7H+oD4hagzwQi96h4mGWD+o=,tag:n4p5Aoh7lYvCRDWRcc9tbQ==,type:comment] +firezone-adapter-config: ENC[AES256_GCM,data:CPY6DPFJ0OZRJqY0u05rAoc9gfCvHY8fFXkSyKvC+VdjNkC4LwjSJkaBU7aBAyIVsLrLz7cS52fcFfwdnAp/6V7BUDE2qpRdpwuN0ZuTMrnFnmLIi0jy4JXcU5niiClSfulgRfY9Dw9f8oHdYiu+uziVhDdjThx61tNyW+OVMNsKv2avWKqotM/fhBf59hJDS0NwaFi10X4X9Z0Oljd9mHQw+LDJkSTX0dk=,iv:IRn5awskI2mZCzQka6VFvCaNnYATvj6yMH9UWs4vJus=,tag:3gbxkbfwS2mNLkVK9KmTUw==,type:str] +#ENC[AES256_GCM,data:xZvu7VeZ8IVeiR94gfJR1BB34V1z8ou+YKRrIxlK+qJ8idgzEKXRiWCcdwC345UNIEuVShI8CT7+Bno9c2bllkkKwW4RhSEnMOYo3g+iouKB3p2iwRBX+OEZuWbpoZGDr1KpHLP+ypiTekNOAZgx4EmxQWFL78bBMswoPn/Tv5ahN1Gha75A9iO7nNQgjRIn62s4l+U1cMXDBBKUCIwcfg==,iv:V7G6wGFjSoKNGNuwW4i2U8+zKI8AQm+ATbSLls7688s=,tag:jQqxbMGaJ96fHvPj5Y0CTw==,type:comment] +#ENC[AES256_GCM,data:td0zw1WORHtMvBO7IK06Of1PoG1QTMiDeJ8KSa4LpLrIgOPTdIg9TkU7UYPNxFD1bVGpU708Rs8Skmyz0v4y9S9H6PM9+4fVij5GN6uaLH/pfMXzaArD8SHbppYQGgpVqsq4kJ+sk02yAjvEM4BBfTpOEPgnu1CSmwlyjw0ysrCwq5YLOYqAQa9rT9uiVCL3FYWuuUzh7SPuRaZouGX2m/MdtQ==,iv:uetwzIK53P3ja94Jw/QDnrel61ducf907mZwB1yy6cQ=,tag:89IjmIvEQs7ayBmuvw3RFQ==,type:comment] sops: age: - recipient: age1g7atkxdlt4ymeh7v7aa2yzr2hq2qkvzrc4r49ugttm3n582ymv9qrmpk8d @@ -18,8 +27,8 @@ sops: NmVFamgzKzRlV2oxS0x0UCsrc240eEEKByZ5WYf+QO8T43VLfO2ym4x7TQltS1nS ckgZLorWZBWQg2vAwQktxQ0WTcjhM6tktZ7zgCIzKBLbQXtSt7VG9Q== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-12-04T19:12:20Z" - mac: ENC[AES256_GCM,data:WAAHE40CAJgdT1tMYBBuFeSqaziHOvpUKrBlfycHvpXhPZ4Oa8sDElpc1lxp1VY2AVeLkqeAB7bH/HVQYGVJhxbfSVHAXm0kCQTT7yNLW8x7RK8RlwzGq9jDfng5UoA46kP2GWyGbicnaUzaH6gnF0Os3rqAMMhTg8pme7pUVwg=,iv:vJ+XFfGMwmIlgJ9ZSu/+kow4dhVsY5aeB0jPjL4TIpk=,tag:mBZzL2JGFPwIx8hNM09hEw==,type:str] + lastmodified: "2025-12-31T22:00:22Z" + mac: ENC[AES256_GCM,data:wGGou+Jx0BV3fMI8gF3HL6VW05lz4CSBvjQF8WSbIHoykor4uthR0TN4ndanU3ZPjhU+NRNxIxTs2cFGJOH4YMIG6bGH0WIoFIfw3xkSIT/zAmfK33P7AUV8/vA45TZli5VHf6S/4CUqXfN91qezrMUiUVr+AEeqa/hbOMBO3j8=,iv:TRc4ci8KRF3ZHuqtafqP0AaRMHMlqnhB1psGbuL4zms=,tag:aTFxdF5qpkGEYvwwj7Q4SQ==,type:str] pgp: - created_at: "2025-12-01T23:06:35Z" enc: |- diff --git a/hosts/nixos/x86_64-linux/eagleland/default.nix b/hosts/nixos/x86_64-linux/eagleland/default.nix index 92e85ec..dea0095 100644 --- a/hosts/nixos/x86_64-linux/eagleland/default.nix +++ b/hosts/nixos/x86_64-linux/eagleland/default.nix @@ -40,7 +40,6 @@ swarselmodules.server = { mailserver = true; - dns-hostrecord = true; postgresql = true; nginx = true; wireguard = true; @@ -50,4 +49,6 @@ server = true; }; + networking.nftables.firewall.zones.untrusted.interfaces = [ "wan" ]; + } diff --git a/hosts/nixos/x86_64-linux/eagleland/secrets/pii.nix.enc b/hosts/nixos/x86_64-linux/eagleland/secrets/pii.nix.enc index 091f446..ab88732 100644 --- a/hosts/nixos/x86_64-linux/eagleland/secrets/pii.nix.enc +++ b/hosts/nixos/x86_64-linux/eagleland/secrets/pii.nix.enc @@ -1,5 +1,5 @@ { - "data": "ENC[AES256_GCM,data:GB11Medb866IeeAeZGNRWO7ckMoNO1DACgP3bLgKhJ2ZYZa+Xkl5FUR6sZTBHJInSODpnOIMZWcV+ZjyRbQHJvvYxt1wJxcDC9fBVPnhoJATbQrV0zQ7XQBlfYyUhzvQ0w4lB+G5sbWeBkVQDwGhNoMxLaPt52Vg3uW0Vq42VIZZkpeWaJtGVDXoMIHCTIz2K/sC7JafRoE25a74i5XSrd2hxvvRFDQA1PFzH6JGguRqOXroqS1M1QNF3DEugsNhl3nwgc9bjLeSzvGdc8cCmUsS4LDITjPBJb4F/2mWqC/o1qUA4rEbgM/jTve3NInptgIUQiyPkHxZCtlU6MK5v5K0idI8njSolvqofy8j85YccsPPpOoQ06NPAUMa9hN0Jv+DpnyjUJAuf74JFzFP4kEMh+a9/6nVB/8cMeQATwt5b9Lbvr7zIZA5PMt1wOVDwAosZigMm6oUpDyYOjSlCvOgvEuojHDR7V6HHYd7mzg8AnIQyHa8iWR/A8/h+hzru5elb6H6rTwKaGW+jisDxRrsBsqxOWQrMrHZyhK3ppTc+IIDdIpKfe1QfVGnhH/cWXLE7YlG80fh48PKwp55azKfb76fDxQHJ4mx3PMHf1dF1bM/D6zGvsMcgIjZTO7xoVTZUZ/j7jiKHUpZnkciGfD9mJZKNGP80DMSgVDhVDUVBrZVCVwqbfksUob6bwU5nT5MSrlmY9Wj70uOl+VGLAaD0WF4T01qB/FBxoFBoYQJ+EIG,iv:5ZEu/YUvgNNgmxx9p/zurljFHRVRuKErhGhZpv/9XVk=,tag:ZFpqY1ewgJ8BLg9tnQc35w==,type:str]", + "data": "ENC[AES256_GCM,data:sfFILq+nY2tqP2aHjJZqUZMdk+qQXSsL72ubGsu/U5G8ULqXcq73d69Op1M8ARfLgOrzoBfzyjcxFMUZiVxoSHevEtqoRBq0Yol6S+3m8vXbW6k/4tj7vRTCsnuxEbGgX2MCbpfb8GChkX2xy5ZKdOWTyXNGWIIAOkHj9NAfnKz2ppY8tFPcQseOtfbI7o/MiFtaMzI6HTpbgBqhifJHLvJyIjsFH1ZecyYjYtL6682isMGZwywUdzaE7dH4m8+sFztHiliJaCM/gID+Kl4GsWlIqZuJmCi3Ac6czCCnLf2fXk53YLXawjwkQmNWjSkOVYI5yybonySSzmyjCfB15487E/ScNlG/Cc0GesoaxkJQpgys3rjuyIUwBKhfHa0qsEd5XkUFKemlB3uQTNfst6CQ1WzZYagIGwTM4zB8HjsjG2hRX6Jck7kS+5eQAoxToe5Z/bDGworUYWRhm5To7bbWn6w2AZ0FjWsb/h2lGy3rgCpjtaaKLAcG7kzbXyUW3crjLg0NR7REKYQf/ZLDGs7a0zYiDfGHyh9+krNiZ7c4dAlCh0lwUJuWSLP3VBPlcLqvSoOg8rRvoJwmIwh9rCCuKxhhGHwwL4SiE10Gw+5rajHNfj+ZnjvVXmFdpZRQW73FF8ThVexKu0qtCmzGik8R7wIhI5AW3Pj1wBURftl+jd3vRZkU9isCE3CZit0L536ZJwawoAZ0eU5tYkRjI/7iHXAbyLEILspSdrHDneRiVLh/IYXv6BEtlZFXwQMtPRPAwx9F8JG0zG9iX4Yy,iv:js4R7cAoIFGCgURc2WyiqRwfqLLBKNWCEEAlsRYdUeA=,tag:NZD44GRRgt7B7U2oDBDjyg==,type:str]", "sops": { "age": [ { @@ -7,8 +7,8 @@ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJR1ZPZFUxRTh0QjB6UDJ4\nOFd2c2lFejhHck5UdUxVbmFFbVRYNEJaSzJZCkNxbndVVThObDkxUmx2WW9ESzhh\na2o0LzFCbWdJVlRIV00rTVUwTktoek0KLS0tIC9qalVvZmpGQXZsV3RIYWRPbmRY\nam80NkRkT2l0ak8wV3pTSW9kSC9nZ3cKCH8eEMmku6WMliEDdAiW2Lk1jAGH9SoP\nWQ5Y6e90jEnp8XbGE7KYiG+jy5fHSc6Y5/YyMmi/b9bF9AhmRT6rdw==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2025-12-05T09:21:59Z", - "mac": "ENC[AES256_GCM,data:S0o8zMcZ7cVmhuQ+FyC73T2USIEGryy3v61xXafd63pymEjJiOwgLZk0+nQQii+qKzwFcXNJIOjEjWyHhprcq+2hha79unEH6nfxAFjqyKdhLzFzmP73ML0vB7Fbzl5mEDyc++v2bsH/6J8UakXCkhRTUSjyuotxIChjU0YjTKM=,iv:dzGQH3HyF3lTWYhU6Mv81WcXilYVBMc++ZK5nPSPBVw=,tag:dnsxa1XnUNdZd7XIxmTgWQ==,type:str]", + "lastmodified": "2025-12-25T00:58:02Z", + "mac": "ENC[AES256_GCM,data:AVZqvJDOcRyUKkxxN3QkxFDiPgB7R/yI5cSGrsgZS/T+rcyi9db9fYhS60c7egLpYmO1ieBk59wwykCAP5TdTQoPXm/+O24MCXquEYuY9CR4YjYno/dBnbCWtKvIB7vs/yIyVfKBW4VQYSbnH/LpBSB6RJ0ivLU9S8hrmrgTkDw=,iv:pSbmaXMW7hqxxTNS7n9vDlVlO7zE3rqHnDAP0XaC5xw=,tag:jH1qSjGWX8bwKSk/MFmDQw==,type:str]", "pgp": [ { "created_at": "2025-11-23T15:25:41Z", diff --git a/hosts/nixos/x86_64-linux/eagleland/secrets/secrets.yaml b/hosts/nixos/x86_64-linux/eagleland/secrets/secrets.yaml index d1bd120..b7496b0 100644 --- a/hosts/nixos/x86_64-linux/eagleland/secrets/secrets.yaml +++ b/hosts/nixos/x86_64-linux/eagleland/secrets/secrets.yaml @@ -4,7 +4,8 @@ wireguard-private-key: ENC[AES256_GCM,data:grHYayd0/og7SZhnkemUE9NySA8M2Pev5C/Gg acme-dns-token: ENC[AES256_GCM,data:5U/74jeGpQH39kyjuVwLU3WBYk5MrCMZSFouRFRVbB5FhOkiJtqYBA==,iv:f1TgdiVVbAB+580AtQAe8mCXU0WuS9JX7AWukKbDYj4=,tag:Ut0tbtiNcV/NxfStyZA9XA==,type:str] #ENC[AES256_GCM,data:dZiEtGPKsbsd9g==,iv:lNgXQHx/w7pm3EUTBwyFnqv2j0T7zQ59nFLom8F0hQ8=,tag:1cF89QMfjipYZgfl08qSOA==,type:comment] user1-hashed-pw: ENC[AES256_GCM,data:uPyDpGOVIqE6cCyvhXIM6v8sTqEx9dV96oqMYS7fRMLiR0kYlCmgNBEeDFmTNRskqwW/WGXrOBn555ZH,iv:KbHW2mOGzOw4t9aOrKLOIobkUNLWj69dk7fFuy1x3aQ=,tag:51+qAavIiM6K256MkhBaZw==,type:str] -user2-hashed-pw: ENC[AES256_GCM,data:+BES2HwH+Jj6wl7MVzsdmPGxp6AuiPLx+XuOpJClksm9SlbAyqATAHeNokAHmj7yLS79rJF5C3YBBtT4,iv:bSX0PLcriKal3eir24DTyePfropgVhh83U0JdR6/2Cs=,tag:TiSKjApnJg3di+77vV9l6Q==,type:str] +#ENC[AES256_GCM,data:brmNZZpgXixukd/wVGB+aedAR69Lw97B/vJIJndX6gSZXmv85ioXOE+INhdXFzCjUA2FDZlWOVmBLbtWSsgF9bqV/4WTBOwk8Cy4fInU,iv:x1aYveoBXS48OodS+4MtW74oUdCS9EFdaFZBgpmmfSU=,tag:FlGm89rFi5ZLoRq8Uxnpbg==,type:comment] +user2-hashed-pw: ENC[AES256_GCM,data:B2gK16sr8GqnngSyhG3vdGb9x8M3j0A/KDF6Vak+ZHO8hOsFAriKHnHEyvcJCE9p6oi+9cqPzcbL6VT7gYQf3KJrid+Ejzl4EQ==,iv:PVG04/i7xAokvcjcedXOEYuTwfdt0Jofev0Eit9kD+8=,tag:zCV4JPQHRArqW48lkhCzfw==,type:str] user3-hashed-pw: ENC[AES256_GCM,data:sr7jv7PppT5Ub8VsvipXdZZWTZ31GFscmZ/CcHzYE4vsfIYYHpFElHGMjlbcTSLjyqfVOcXAKNvabcoO,iv:C22sZLrUUc3G80yyYr1snuwqtAa8USZd8FRtua5hllw=,tag:lu0hPo24CXNI2kE7C8g3Eg==,type:str] sops: age: @@ -17,8 +18,8 @@ sops: SmZrb2xuVW5VVjM0b244U0lkVmlkVGcKin/6A8ONfW72fbQmvJWiNCzAZfGUtxCI WV0DaPvO7sO5y7q37QxVUOxgJgF0WpKiNel4Y9E06xbl3TK6jXk2MA== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-12-03T16:14:20Z" - mac: ENC[AES256_GCM,data:PrF5wUKzsDuJUCdAvJFKQ/ILxTNyrsHK/tJnN7tM+46gAIsQge2Nzcq7/sRCjBBy5/c0Gyv2XJqlWrO2hNHHkdEqM2NCMosxIhNknjE2znYz5giMRbmNqxR8MLbohXHZ3pgUIKKQnBqPic0T6xEgPuGnt6j0Dwb8rmy4xUByhFQ=,iv:g+iOSoOn+zydHyA56+lqAo7wTXgZ05K1C4H8DNj3MrA=,tag:Zj6a2GESBA755uVHtEpTMQ==,type:str] + lastmodified: "2025-12-25T01:03:31Z" + mac: ENC[AES256_GCM,data:phjkITBZVZ9Mk0y1FL2dZNgrxyIPbLIXmoTYSlRdHslHg0+hBViLnXAvS0QN/HvsvAldzH8THyACQrXDZQSFBHljIy2wqZr5bu7ByIlRc8FhwNePXNOUs7HH7bQISvFuDWrXl2KQn8OirfJjpIpwQIi5d44pa4Fs1+tpWAg+OiI=,iv:k7brMvP64XV5eNYdm1OJqpjEJ3xEhhfOqErBIG7xMNs=,tag:EhXT3gZrZg2QkYzVCUQKlw==,type:str] pgp: - created_at: "2025-11-24T12:05:01Z" enc: |- diff --git a/hosts/nixos/x86_64-linux/hintbooth/default.nix b/hosts/nixos/x86_64-linux/hintbooth/default.nix index b931b70..3e3e344 100644 --- a/hosts/nixos/x86_64-linux/hintbooth/default.nix +++ b/hosts/nixos/x86_64-linux/hintbooth/default.nix @@ -20,6 +20,8 @@ }; }; + globals.general.homeProxy = config.node.name; + swarselsystems = { info = "HUNSN RM02, 8GB RAM"; flakePath = "/root/.dotfiles"; diff --git a/hosts/nixos/x86_64-linux/hintbooth/secrets/secrets.yaml b/hosts/nixos/x86_64-linux/hintbooth/secrets/secrets.yaml index 402209f..3f43694 100644 --- a/hosts/nixos/x86_64-linux/hintbooth/secrets/secrets.yaml +++ b/hosts/nixos/x86_64-linux/hintbooth/secrets/secrets.yaml @@ -1,4 +1,7 @@ wireguard-private-key: ENC[AES256_GCM,data:DBCK92h8mGxDshB5OIEbyUENc6a4jmvzKPvljUn50AM1I5vBm/bSTDRStIM=,iv:K/OiPnAlXNt3RqBiBiiZqIY8vqsIw0kmKE+aeeVhr+Q=,tag:eloCJ7yjI2tpHMxwNxZDDw==,type:str] +#ENC[AES256_GCM,data:3lP1BqtvBwyeOvq4K5HTaQ==,iv:j1xenUUIkyJDaeLlX7LGhjFdhNlfTXF6r6v2+XbJlOU=,tag:TsGKu6VfF6D8I2p4kb63/A==,type:comment] +#ENC[AES256_GCM,data:LItVBIEQVz0x8ZARRlMVRPa0vdEe1Kv0CZaEnauUWw3P+NZv6WZkXw0SjuW+k9oqlDOTPR6gQ0Aa4GoX51NRFFmtlCVU0YL/RmdfrC6nkSea2S5btXCG4pptSusmQx42Rn+RfttcLDIXBAOIDSA/kKiBYvDhsZe0XOHAzj7jTAshSeGlccEOUIs8SctS8b13OAiSs4ceuMRPz6J45f6RVKG6COgiUEav5U6RFa1ZOLv8A/EFsqOsEZ45aYqngLM0/7gZ5Wqwpft8a+7dLRmakUjTOxH+wtVn6CV7wItUJAoz6BjLR/jtDr9EUm/QesZSHhuxs3eu0iXPXzaQgUt5Qz2knxSvzsEKYUx5bPsNBSb4uWgG3b/vKzPUKKYP5CrOwvPxsqI=,iv:z1YrJmuMaiiQpAc8ajoa7A1GH5Z2D2holm3lBCiBqOU=,tag:ghl+1BN9Tyxpwr9KXre5jw==,type:comment] +firezone-gateway-token: ENC[AES256_GCM,data:3vFtknbuAKk4syzNMDBWZegqyjDQWWPYXVJOs40cnEgAYnOWF2svt4mg3ueRH6b3j5E0Mrkv1PJIch5yxu9FYjfcx+jlsrqneJQrHGX3LDcW5JFOwP6H4nb2Oo8Q8BtpbpOdxAdUeFoLjRSFYy3DGzDatLG9CN3AinhIuxrTGM9Dfxvfn5ahkZ/LPLNRsKj6822C6dxSISW5QSGz+I2woyKzVd9hYoyeHzj5PB2WeaP4ty6bdQRwtA22i15ODpjMDt+AwPL9Wv+tzcv8StDpawbLrJ+0vAh8uRrIjka/W731WkAIWsgMr4mDt0dw99VgJ3mixbXEOdQRidVCeDTXwb9N17RQr5Z5pcjWqGU=,iv:+zbkWWlR0FAFIFB73TXuUwhyuhiVzaEhPeYBkJXfbmY=,tag:8NZbeFLv0FiRDVZJtmLmgQ==,type:str] sops: age: - recipient: age1wmx8y2hs83j2u5srdnfxljrzxm8jtxl6fr0mq7xf2ldxyglpzf2qq89rpx @@ -10,8 +13,8 @@ sops: YWlkK0xrclpXYTkxUXFiNGMxU1NnMGcKCZzLfTPjeeGxyD43dOGDYsQVsw24cyHI jz0B9VV07p33OP448eLyLgwpVFaNG0q+hXPH+0fb3V3foBT2QSeuPA== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-12-22T08:58:44Z" - mac: ENC[AES256_GCM,data:GnnNiw5DwXDCXEWqMa6eGYVNK4GyNvoNf9WK5wYE+uT8nolKD/pFEjqt++vHHlmEbPePhErAAu2vr7QGH/p8c+oEOEjiLJicMxJ72Bx8+5RLe4WuKO3GLTizgCy2f9Fr3gDWaKG8W9XF6xVzwPzzguRpfo1F0fmrPW6/EiGJDJ0=,iv:DWclKhUVp9UYc0F1J1k5+Y80dPK/RXoPDmylYlbmtiE=,tag:VgBHoVWPhOIwn7vuDwxKSw==,type:str] + lastmodified: "2025-12-31T22:06:39Z" + mac: ENC[AES256_GCM,data:BXX6xL5AJ9Ar4le429W86bkCRQkPWiYbJxd+xvp3xfy/T0MptAMsOB7K7dJrtokdXBKK3iPxapgPZCVCSBT49Sj9X2e7wWCJq+olcNTmojMZBtgsDjHgg2rbl8jY7mKeAlGRiImc5iIengJP0cwxF2zplUkZeQmJzXE0+4P8R6c=,iv:63xUQfIl2gpDONSJUrADsRxeSFtBs3h8e8LQs8eQxEE=,tag:vQgKvv8AuW+oEh7dimPhPg==,type:str] pgp: - created_at: "2025-12-22T08:56:58Z" enc: |- diff --git a/hosts/nixos/x86_64-linux/pyramid/default.nix b/hosts/nixos/x86_64-linux/pyramid/default.nix index cf5bb50..5f662f8 100644 --- a/hosts/nixos/x86_64-linux/pyramid/default.nix +++ b/hosts/nixos/x86_64-linux/pyramid/default.nix @@ -77,4 +77,9 @@ in swarselprofiles = { personal = true; }; + + networking.nftables = { + enable = lib.mkForce false; + firewall.enable = lib.mkForce false; + }; } diff --git a/hosts/nixos/x86_64-linux/winters/default.nix b/hosts/nixos/x86_64-linux/winters/default.nix index e449875..557cae0 100644 --- a/hosts/nixos/x86_64-linux/winters/default.nix +++ b/hosts/nixos/x86_64-linux/winters/default.nix @@ -91,7 +91,6 @@ transmission = true; syncthing = true; grafana = true; - emacs = true; freshrss = true; kanidm = true; firefly-iii = true; @@ -104,4 +103,6 @@ opkssh = true; }; + networking.nftables.firewall.zones.untrusted.interfaces = [ "lan" "enp3s0" ]; + } diff --git a/hosts/nixos/x86_64-linux/winters/secrets/pii.nix.enc b/hosts/nixos/x86_64-linux/winters/secrets/pii.nix.enc index 80471e3..8256be6 100644 --- a/hosts/nixos/x86_64-linux/winters/secrets/pii.nix.enc +++ b/hosts/nixos/x86_64-linux/winters/secrets/pii.nix.enc @@ -1,5 +1,5 @@ { - "data": "ENC[AES256_GCM,data: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,iv:tRfCSNz1Jm1qQFXt7gVEmd8VxWsqYivXtF/u+J+mnpk=,tag:3V6uLwgc0/XZvk4en2KfIw==,type:str]", + "data": "ENC[AES256_GCM,data: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,iv:Wc2rOI9aWgchjFGCl5R8d6E6GmYEKCIkIAZlUIlbE/4=,tag:3p301gA9WrBjwcpXs68ayg==,type:str]", "sops": { "age": [ { @@ -7,8 +7,8 @@ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3UFZTaXFNdjF2UmRFd3VL\nY2pZZ3ZaRkhZSjdVUjIraHV5ZlNaNGtwM3k0CkZ4OVRFcmR3MFBDcmdsbWFId3Iy\nVzQyUGI1eG44d3JFL2NvZEg4NnduT2cKLS0tIEdhOEZETk9nRTlVbmJ5UW9GalVx\nS00yaUpJZVFVNThFei8yRzJYejRkYk0Kf6Z8WnG8phRtFIUWIPys3PW0OImhAcF+\nUFLuL4Qr7zWaeItCRieYCs1yBn7KbUJHZNkJcvnkYW50NYvlEa8wBw==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2025-12-05T10:12:13Z", - "mac": "ENC[AES256_GCM,data:2uJJUnYNM7kNysGtiwmlctwjrE2ZAropTNOcph4K51VUr48UZcwYQTPpdJEqEIGiNq4pcT1W5h/ghYFUAZdZdleOKrh+tLnQ5LIib/A9WGkW44m3i6dCVlTXRt+MhrVfJXffRTMM101JoMCq8V00juuFYcDxNhI3uvKqwxXSbyo=,iv:hjMu3oSlc9gKi8cO0RX4leht40PUthldYpLwZKdX4Xw=,tag:n08RFXUHkXyUgE5jB0KZxw==,type:str]", + "lastmodified": "2025-12-31T12:22:14Z", + "mac": "ENC[AES256_GCM,data:H17OWQwkZaugzTuMM8kBPZLYZs/poaLJt8osoY/gzC2CMpWXUtWpwgJ83CO7GkiPrPN2SZtEiaADP3PvZZqVcV5rDJNhdELmdvZfB14RQDUD9rYfnIX5uuzMMII+kguTkk+Zd1IRWv+MN9y4cdhys0lYJ7Nw1RyEMP9Bxd1zvcA=,iv:01Yode7T/2pUP1dFLyIUoDIfeWRWKf1Qq7pHvUDKQJQ=,tag:Ldxq5cRB2iexLhIrBfqGVQ==,type:str]", "pgp": [ { "created_at": "2025-12-02T14:59:33Z", diff --git a/modules/home/common/firezone-tray.nix b/modules/home/common/firezone-tray.nix new file mode 100644 index 0000000..042e690 --- /dev/null +++ b/modules/home/common/firezone-tray.nix @@ -0,0 +1,29 @@ +{ lib, config, pkgs, ... }: +{ + options.swarselmodules.firezone-tray = lib.mkEnableOption "enable firezone applet for tray"; + config = lib.mkIf config.swarselmodules.firezone-tray { + + systemd.user.services.firezone-applet = { + Unit = { + Description = "Firezone applet"; + Requires = [ + "tray.target" + ]; + After = [ + "graphical-session.target" + "tray.target" + ]; + PartOf = [ "graphical-session.target" ]; + }; + + Install = { + WantedBy = [ "graphical-session.target" ]; + }; + + Service = { + ExecStart = "${pkgs.firezone-gui-client}/bin/firezone-client-gui"; + }; + }; + }; + +} diff --git a/modules/home/common/packages.nix b/modules/home/common/packages.nix index e9dbb00..728f6c4 100644 --- a/modules/home/common/packages.nix +++ b/modules/home/common/packages.nix @@ -61,7 +61,7 @@ nix-visualize nix-init nix-inspect - nixpkgs-review + (nixpkgs-review.override { nix = config.nix.package; }) manix # shellscripts @@ -90,7 +90,7 @@ # element-desktop nicotine-plus - stable.transmission_3 + stable25_05.transmission_3 mktorrent hugo diff --git a/modules/nixos/client/firezone-client.nix b/modules/nixos/client/firezone-client.nix new file mode 100644 index 0000000..922a038 --- /dev/null +++ b/modules/nixos/client/firezone-client.nix @@ -0,0 +1,15 @@ +{ lib, config, ... }: +let + moduleName = "firezone-client"; + inherit (config.swarselsystems) mainUser; +in +{ + options.swarselmodules.${moduleName} = lib.mkEnableOption "${moduleName} settings"; + config = lib.mkIf config.swarselmodules.${moduleName} { + services.firezone.gui-client = { + enable = true; + inherit (config.node) name; + allowedUsers = [ mainUser ]; + }; + }; +} diff --git a/modules/nixos/client/network.nix b/modules/nixos/client/network.nix index c47d91d..e33b147 100644 --- a/modules/nixos/client/network.nix +++ b/modules/nixos/client/network.nix @@ -47,8 +47,10 @@ in }; }; + services.resolved.enable = true; + networking = { - inherit (config.swarselsystems) hostName; + hostName = config.node.name; hosts = { "${globals.networks.home-lan.hosts.winters.ipv4}" = [ globals.services.transmission.domain ]; }; @@ -80,9 +82,11 @@ in ]; }; + networkmanager = { enable = true; wifi.backend = "iwd"; + dns = "systemd-resolved"; plugins = [ # list of plugins: https://search.nixos.org/packages?query=networkmanager- # docs https://networkmanager.dev/docs/vpn/ diff --git a/modules/nixos/common/globals.nix b/modules/nixos/common/globals.nix index aa38e6b..4466f77 100644 --- a/modules/nixos/common/globals.nix +++ b/modules/nixos/common/globals.nix @@ -5,6 +5,29 @@ let types ; + firewallOptions = { + allowedTCPPorts = mkOption { + type = types.listOf types.port; + default = [ ]; + description = "Convenience option to open specific TCP ports for traffic from the network."; + }; + allowedUDPPorts = mkOption { + type = types.listOf types.port; + default = [ ]; + description = "Convenience option to open specific UDP ports for traffic from the network."; + }; + allowedTCPPortRanges = mkOption { + type = lib.types.listOf (lib.types.attrsOf lib.types.port); + default = [ ]; + description = "Convenience option to open specific TCP port ranges for traffic from another node."; + }; + allowedUDPPortRanges = mkOption { + type = lib.types.listOf (lib.types.attrsOf lib.types.port); + default = [ ]; + description = "Convenience option to open specific UDP port ranges for traffic from another node."; + }; + }; + networkOptions = netSubmod: { cidrv4 = mkOption { type = types.nullOr types.net.cidrv4; @@ -25,6 +48,20 @@ let default = null; }; + firewallRuleForAll = mkOption { + default = { }; + description = '' + If this is a wireguard network: Allows you to set specific firewall rules for traffic originating from any participant in this + wireguard network. A corresponding rule `-to-` will be created to easily expose + services to the network. + ''; + type = types.submodule { + options = firewallOptions; + }; + }; + + + hosts = mkOption { default = { }; type = types.attrsOf ( @@ -85,6 +122,20 @@ let # if we use the /32 wan address as local address directly, do not use the network address in ipv6 lib.net.cidr.hostCidr (if hostSubmod.config.id == 0 then 1 else hostSubmod.config.id) netSubmod.config.cidrv6; }; + + firewallRuleForNode = mkOption { + default = { }; + description = '' + If this is a wireguard network: Allows you to set specific firewall rules just for traffic originating from another network node. + A corresponding rule `-node--to-` will be created to easily expose + services to that node. + ''; + type = types.attrsOf ( + types.submodule { + options = firewallOptions; + } + ); + }; }; }) ); @@ -210,12 +261,14 @@ in }; }; - + general = lib.mkOption { + type = types.submodule { + freeformType = types.unspecified; + }; + }; }; - - }; }; diff --git a/modules/nixos/common/nodes.nix b/modules/nixos/common/nodes.nix index fe667aa..236354d 100644 --- a/modules/nixos/common/nodes.nix +++ b/modules/nixos/common/nodes.nix @@ -1,5 +1,5 @@ # adapted from https://github.com/oddlama/nix-config/blob/main/modules/distributed-config.nix -{ config, lib, outputs, ... }: +{ config, lib, nodes, ... }: let nodeName = config.node.name; mkForwardedOption = @@ -23,23 +23,21 @@ let ''; }; + expandOptions = basePath: optionNames: map (option: basePath ++ [ option ]) optionNames; + splitPath = path: lib.splitString "." path; + forwardedOptions = [ - [ - "services" - "nginx" - "upstreams" - ] - [ - "services" - "nginx" - "virtualHosts" - ] - [ - "swarselsystems" - "server" - "dns" - ] - ]; + (splitPath "boot.kernel.sysctl") + (splitPath "networking.nftables.chains.postrouting") + (splitPath "services.kanidm.provision.groups") + (splitPath "services.kanidm.provision.systems.oauth2") + (splitPath "sops.secrets") + (splitPath "swarselsystems.server.dns") + ] + ++ expandOptions (splitPath "networking.nftables.firewall") [ "zones" "rules" ] + ++ expandOptions (splitPath "services.firezone.gateway") [ "enable" "name" "apiUrl" "tokenFile" "package" "logLevel" ] + ++ expandOptions (splitPath "services.nginx") [ "upstreams" "virtualHosts" ] + ; attrsForEachOption = f: lib.foldl' (acc: path: lib.recursiveUpdate acc (lib.setAttrByPath path (f path))) { } forwardedOptions; @@ -60,10 +58,10 @@ in getConfig = path: otherNode: let - cfg = outputs.nixosConfigurations.${otherNode}.config.nodes.${nodeName} or null; + cfg = nodes.${otherNode}.config.nodes.${nodeName} or null; in lib.optionals (cfg != null) (lib.getAttrFromPath path cfg); - mergeConfigFromOthers = path: lib.mkMerge (lib.concatMap (getConfig path) (lib.attrNames outputs.nixosConfigurations)); + mergeConfigFromOthers = path: lib.mkMerge (lib.concatMap (getConfig path) (lib.attrNames nodes)); in attrsForEachOption mergeConfigFromOthers; } diff --git a/modules/nixos/server/ankisync.nix b/modules/nixos/server/ankisync.nix index cc67228..b6f2158 100644 --- a/modules/nixos/server/ankisync.nix +++ b/modules/nixos/server/ankisync.nix @@ -1,7 +1,7 @@ { self, lib, config, globals, dns, confLib, ... }: let inherit (config.swarselsystems) sopsFile; - inherit (confLib.gen { name = "ankisync"; port = 27701; }) servicePort serviceName serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "ankisync"; port = 27701; }) servicePort serviceName serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; ankiUser = globals.user.name; in @@ -9,11 +9,11 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; sops.secrets.anki-pw = { inherit sopsFile; owner = "root"; }; @@ -23,16 +23,26 @@ in info = "https://${serviceDomain}"; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.anki-sync-server = { enable = true; port = servicePort; address = "0.0.0.0"; - openFirewall = true; + # openFirewall = true; users = [ { username = ankiUser; @@ -41,7 +51,7 @@ in ]; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/attic.nix b/modules/nixos/server/attic.nix index 5c1f933..e278ed6 100644 --- a/modules/nixos/server/attic.nix +++ b/modules/nixos/server/attic.nix @@ -1,6 +1,6 @@ { lib, config, pkgs, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "attic"; port = 8091; }) serviceName serviceDir servicePort serviceAddress serviceDomain serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "attic"; port = 8091; }) serviceName serviceDir servicePort serviceAddress serviceDomain proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; inherit (config.swarselsystems) mainUser isPublic sopsFile; serviceDB = "atticd"; in @@ -10,13 +10,23 @@ in }; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; sops = lib.mkIf (!isPublic) { @@ -36,7 +46,7 @@ in }; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; services.atticd = { enable = true; @@ -122,7 +132,7 @@ in after = [ "garage.service" ]; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/atuin.nix b/modules/nixos/server/atuin.nix index fd6e980..5c72023 100644 --- a/modules/nixos/server/atuin.nix +++ b/modules/nixos/server/atuin.nix @@ -1,31 +1,41 @@ { lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "atuin"; port = 8888; }) servicePort serviceName serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "atuin"; port = 8888; }) servicePort serviceName serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; topology.self.services.${serviceName}.info = "https://${serviceDomain}"; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = { enable = true; host = "0.0.0.0"; port = servicePort; - openFirewall = true; + # openFirewall = true; openRegistration = false; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/bastion.nix b/modules/nixos/server/bastion.nix index 9961997..7503576 100644 --- a/modules/nixos/server/bastion.nix +++ b/modules/nixos/server/bastion.nix @@ -1,7 +1,7 @@ -{ self, lib, config, ... }: +{ self, lib, config, withHomeManager, ... }: { options.swarselmodules.server.bastion = lib.mkEnableOption "enable bastion on server"; - config = lib.mkIf config.swarselmodules.server.bastion { + config = lib.mkIf config.swarselmodules.server.bastion ({ users = { groups = { @@ -50,6 +50,7 @@ } ]; }; + } // lib.optionalAttrs withHomeManager { home-manager.users.jump.config = { home.stateVersion = lib.mkDefault "23.05"; @@ -63,5 +64,5 @@ } // config.repo.secrets.local.ssh.hosts; }; }; - }; + }); } diff --git a/modules/nixos/server/croc.nix b/modules/nixos/server/croc.nix index 51bcd96..ae687c6 100644 --- a/modules/nixos/server/croc.nix +++ b/modules/nixos/server/croc.nix @@ -1,6 +1,6 @@ { self, lib, config, pkgs, dns, globals, confLib, ... }: let - inherit (confLib.gen { name = "croc"; proxy = config.node.name; }) serviceName serviceDomain proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "croc"; proxy = config.node.name; }) serviceName serviceDomain proxyAddress4 proxyAddress6 isHome dnsServer; servicePorts = [ 9009 9010 @@ -17,7 +17,7 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -44,7 +44,7 @@ in globals.services.${serviceName} = { domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + inherit proxyAddress4 proxyAddress6 isHome; }; services.${serviceName} = { diff --git a/modules/nixos/server/dns-hostrecord.nix b/modules/nixos/server/dns-hostrecord.nix index b0feaf1..e71e452 100644 --- a/modules/nixos/server/dns-hostrecord.nix +++ b/modules/nixos/server/dns-hostrecord.nix @@ -3,8 +3,8 @@ let inherit (confLib.gen { name = "dns-hostrecord"; proxy = config.node.name; }) serviceName proxyAddress4 proxyAddress6; in { - options. swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; - config = lib.mkIf config.swarselmodules.server.${serviceName} { + options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; + config = lib.mkIf (config.swarselmodules.server.${serviceName} && config.swarselsystems.isCloud) { nodes.stoicclub.swarselsystems.server.dns.${globals.domains.main}.subdomainRecords = { "server.${config.node.name}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; diff --git a/modules/nixos/server/firefly-iii.nix b/modules/nixos/server/firefly-iii.nix index 9a51a94..75f0553 100644 --- a/modules/nixos/server/firefly-iii.nix +++ b/modules/nixos/server/firefly-iii.nix @@ -1,6 +1,6 @@ { self, lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "firefly-iii"; port = 80; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "firefly-iii"; port = 80; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome dnsServer webProxy; nginxGroup = "nginx"; @@ -11,7 +11,7 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -38,7 +38,7 @@ in globals.services.${serviceName} = { domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + inherit proxyAddress4 proxyAddress6 isHome; }; services = { @@ -81,7 +81,7 @@ in }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/firezone.nix b/modules/nixos/server/firezone.nix new file mode 100644 index 0000000..eb0f546 --- /dev/null +++ b/modules/nixos/server/firezone.nix @@ -0,0 +1,385 @@ +{ lib, pkgs, config, globals, confLib, dns, nodes, ... }: +let + inherit (confLib.gen { name = "firezone"; dir = "/var/lib/private/firezone"; }) serviceName serviceDir serviceAddress serviceDomain proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy homeProxyIf webProxyIf idmServer dnsServer; + inherit (config.swarselsystems) sopsFile; + + apiPort = 8081; + webPort = 8080; + relayPort = 3478; + domainPort = 9003; + + homeServices = lib.attrNames (lib.filterAttrs (_: serviceCfg: serviceCfg.isHome) globals.services); + homeDomains = map (name: globals.services.${name}.domain) homeServices; + allow = group: resource: { + "${group}@${resource}" = { + inherit group resource; + description = "Allow ${group} access to ${resource}"; + }; + }; +in +{ + options = { + swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; + }; + config = lib.mkIf config.swarselmodules.server.${serviceName} { + + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; + }; + + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy} = { + allowedTCPPorts = [ apiPort webPort domainPort ]; + allowedUDPPorts = [ relayPort ]; + allowedUDPPortRanges = [ + { + from = config.services.firezone.relay.lowestPort; + to = config.services.firezone.relay.highestPort; + } + ]; + }; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy} = { + allowedTCPPorts = [ apiPort webPort domainPort ]; + allowedUDPPorts = [ relayPort ]; + allowedUDPPortRanges = [ + { + from = config.services.firezone.relay.lowestPort; + to = config.services.firezone.relay.highestPort; + } + ]; + }; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; + }; + + sops = { + secrets = { + kanidm-firezone-client = { inherit sopsFile; mode = "0400"; }; + firezone-relay-token = { inherit sopsFile; mode = "0400"; }; + firezone-smtp-password = { inherit sopsFile; mode = "0440"; }; + firezone-adapter-config = { inherit sopsFile; mode = "0440"; }; + }; + }; + + environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ + { directory = serviceDir; mode = "0700"; } + ]; + + services.firezone = { + server = { + enable = true; + enableLocalDB = true; + + smtp = { + inherit (config.repo.secrets.local.firezone.mail) from username; + host = globals.services.mailserver.domain; + port = 465; + implicitTls = true; + passwordFile = config.sops.secrets.firezone-smtp-password.path; + }; + + provision = { + enable = true; + accounts.main = { + name = "Home"; + relayGroups.relays.name = "Relays"; + gatewayGroups.home.name = "Home"; + actors.admin = { + type = "account_admin_user"; + name = "Admin"; + email = "admin@${globals.domains.main}"; + }; + groups.anyone = { + name = "anyone"; + members = [ + "admin" + ]; + }; + + auth.oidc = + let + client_id = "firezone"; + in + { + name = "Kanidm"; + adapter = "openid_connect"; + adapter_config = { + scope = "openid email profile"; + response_type = "code"; + inherit client_id; + discovery_document_uri = "https://${globals.services.kanidm.domain}/oauth2/openid/${client_id}/.well-known/openid-configuration"; + clientSecretFile = config.sops.secrets.kanidm-firezone-client.path; + }; + }; + + resources = + lib.genAttrs homeDomains + (domain: { + type = "dns"; + name = domain; + address = domain; + gatewayGroups = [ "home" ]; + filters = [ + { protocol = "icmp"; } + { + protocol = "tcp"; + ports = [ + 443 + 80 + ]; + } + { + protocol = "udp"; + ports = [ 443 ]; + } + ]; + }) + // { + "home.vlan-services.v4" = { + type = "cidr"; + name = "home.vlan-services.v4"; + address = globals.networks.home-lan.vlans.services.cidrv4; + gatewayGroups = [ "home" ]; + }; + "home.vlan-services.v6" = { + type = "cidr"; + name = "home.vlan-services.v6"; + address = globals.networks.home-lan.vlans.services.cidrv6; + gatewayGroups = [ "home" ]; + }; + }; + + policies = + { } + // allow "everyone" "home.vlan-services.v4" + // allow "anyone" "home.vlan-services.v4" + // allow "everyone" "home.vlan-services.v6" + // allow "anyone" "home.vlan-services.v6" + // lib.mergeAttrsList (map (domain: allow "everyone" domain) homeDomains) + // lib.mergeAttrsList (map (domain: allow "anyone" domain) homeDomains); + }; + }; + + domain = { + settings.ERLANG_DISTRIBUTION_PORT = domainPort; + package = pkgs.dev.firezone-server-domain; + }; + api = { + externalUrl = "https://${serviceDomain}/api/"; + address = "0.0.0.0"; + port = apiPort; + package = pkgs.dev.firezone-server-api; + }; + web = { + externalUrl = "https://${serviceDomain}/"; + address = "0.0.0.0"; + port = webPort; + package = pkgs.dev.firezone-server-web; + }; + }; + + relay = { + enable = true; + port = relayPort; + inherit (config.node) name; + apiUrl = "wss://${serviceDomain}/api/"; + tokenFile = config.sops.secrets.firezone-relay-token.path; + publicIpv4 = proxyAddress4; + publicIpv6 = proxyAddress6; + openFirewall = lib.mkIf (!isProxied) true; + package = pkgs.dev.firezone-relay; + }; + }; + # systemd.services.firezone-initialize = + # let + # generateSecrets = + # let + # requiredSecrets = lib.filterAttrs (_: v: v == null) cfg.settingsSecret; + # in + # '' + # mkdir -p secrets + # chmod 700 secrets + # '' + # + lib.concatLines ( + # lib.forEach (builtins.attrNames requiredSecrets) (secret: '' + # if [[ ! -e secrets/${secret} ]]; then + # echo "Generating ${secret}" + # # Some secrets like TOKENS_KEY_BASE require a value >=64 bytes. + # head -c 64 /dev/urandom | base64 -w 0 > secrets/${secret} + # chmod 600 secrets/${secret} + # fi + # '') + # ); + # loadSecretEnvironment = + # component: + # let + # relevantSecrets = lib.subtractLists (builtins.attrNames cfg.${component}.settings) ( + # builtins.attrNames cfg.settingsSecret + # ); + # in + # lib.concatLines ( + # lib.forEach relevantSecrets ( + # secret: + # ''export ${secret}=$(< ${ + # if cfg.settingsSecret.${secret} == null then + # "secrets/${secret}" + # else + # "\"$CREDENTIALS_DIRECTORY/${secret}\"" + # })'' + # ) + # ); + # in + # { + # script = lib.mkForce '' + # mkdir -p "$TZDATA_DIR" + + # # Generate and load secrets + # ${generateSecrets} + # ${loadSecretEnvironment "domain"} + + # echo "Running migrations" + # ${lib.getExe cfg.domain.package} eval "Domain.Release.migrate(manual: true)" + # ''; + # }; + + + nodes = { + ${homeProxy} = + let + nodeCfg = nodes.${homeProxy}.config; + nodePkgs = nodes.${homeProxy}.pkgs; + in + { + sops.secrets.firezone-gateway-token = { inherit (nodeCfg.swarselsystems) sopsFile; mode = "0400"; }; + networking.nftables = { + firewall = { + zones.firezone.interfaces = [ "tun-firezone" ]; + rules = { + # masquerade firezone traffic + masquerade-firezone = { + from = [ "firezone" ]; + to = [ "vlan-services" ]; + # masquerade = true; NOTE: custom rule below for ip4 + ip6 + late = true; # Only accept after any rejects have been processed + verdict = "accept"; + }; + # forward firezone traffic + forward-incoming-firezone-traffic = { + from = [ "firezone" ]; + to = [ "vlan-services" ]; + verdict = "accept"; + }; + + # FIXME: is this needed? conntrack should take care of it and we want to masquerade anyway + forward-outgoing-firezone-traffic = { + from = [ "vlan-services" ]; + to = [ "firezone" ]; + verdict = "accept"; + }; + }; + }; + chains.postrouting = { + masquerade-firezone = { + after = [ "hook" ]; + late = true; + rules = + lib.forEach + [ + "firezone" + ] + ( + zone: + lib.concatStringsSep " " [ + "meta protocol { ip, ip6 }" + (lib.head nodeCfg.networking.nftables.firewall.zones.${zone}.ingressExpression) + (lib.head nodeCfg.networking.nftables.firewall.zones.vlan-services.egressExpression) + "masquerade random" + ] + ); + }; + }; + }; + + boot.kernel.sysctl = { + "net.core.wmem_max" = 16777216; + "net.core.rmem_max" = 134217728; + }; + services.firezone.gateway = { + enable = true; + logLevel = "trace"; + inherit (nodeCfg.node) name; + apiUrl = "wss://${globals.services.firezone.domain}/api/"; + tokenFile = nodeCfg.sops.secrets.firezone-gateway-token.path; + package = nodePkgs.stable25_05.firezone-gateway; # newer versions of firezone-gateway are not compatible with server package + }; + }; + ${idmServer} = + let + nodeCfg = nodes.${idmServer}.config; + accountId = "6b3c6ba7-5240-4684-95ce-f40fdae45096"; + externalId = "08d714e9-1ab9-4133-a39d-00e843a960cc"; + in + { + sops.secrets.kanidm-firezone = { inherit (nodeCfg.swarselsystems) sopsFile; owner = "kanidm"; group = "kanidm"; mode = "0440"; }; + services.kanidm.provision = { + groups."firezone.access" = { }; + systems.oauth2.firezone = { + displayName = "Firezone VPN"; + # NOTE: state: both uuids are runtime values + originUrl = [ + "https://${globals.services.firezone.domain}/${accountId}/sign_in/providers/${externalId}/handle_callback" + "https://${globals.services.firezone.domain}/${accountId}/settings/identity_providers/openid_connect/${externalId}/handle_callback" + ]; + originLanding = "https://${globals.services.firezone.domain}/"; + basicSecretFile = nodeCfg.sops.secrets.kanidm-firezone.path; + preferShortUsername = true; + scopeMaps."firezone.access" = [ + "openid" + "email" + "profile" + ]; + }; + + }; + }; + ${webProxy} = { + services.nginx = { + upstreams = { + ${serviceName} = { + servers."${serviceAddress}:${builtins.toString webPort}" = { }; + }; + "${serviceName}-api" = { + servers."${serviceAddress}:${builtins.toString apiPort}" = { }; + }; + }; + virtualHosts = { + ${serviceDomain} = { + useACMEHost = globals.domains.main; + forceSSL = true; + acmeRoot = null; + locations."/" = { + # The trailing slash is important to strip the location prefix from the request + proxyPass = "http://${serviceName}/"; + proxyWebsockets = true; + }; + locations."/api/" = { + # The trailing slash is important to strip the location prefix from the request + proxyPass = "http://${serviceName}-api/"; + proxyWebsockets = true; + }; + }; + }; + }; + }; + }; + + }; +} diff --git a/modules/nixos/server/forgejo.nix b/modules/nixos/server/forgejo.nix index 255138e..deeaa2a 100644 --- a/modules/nixos/server/forgejo.nix +++ b/modules/nixos/server/forgejo.nix @@ -1,7 +1,7 @@ { lib, config, pkgs, globals, dns, confLib, ... }: let inherit (config.swarselsystems) sopsFile; - inherit (confLib.gen { name = "forgejo"; port = 3004; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "forgejo"; port = 3004; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; kanidmDomain = globals.services.kanidm.domain; in @@ -9,11 +9,11 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; users.users.${serviceUser} = { group = serviceGroup; @@ -26,9 +26,19 @@ in kanidm-forgejo-client = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = { @@ -130,7 +140,7 @@ in ''; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/freshrss.nix b/modules/nixos/server/freshrss.nix index 154e225..d7d5cd5 100644 --- a/modules/nixos/server/freshrss.nix +++ b/modules/nixos/server/freshrss.nix @@ -1,6 +1,6 @@ { self, lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "freshrss"; port = 80; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "freshrss"; port = 80; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome webProxy dnsServer; inherit (config.swarselsystems) sopsFile; in @@ -8,7 +8,7 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -55,7 +55,7 @@ in globals.services.${serviceName} = { domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + inherit proxyAddress4 proxyAddress6 isHome; }; services.${serviceName} = @@ -76,7 +76,7 @@ in # config.sops.templates.freshrss-env.path # ]; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/garage.nix b/modules/nixos/server/garage.nix index 0d7f310..d180487 100644 --- a/modules/nixos/server/garage.nix +++ b/modules/nixos/server/garage.nix @@ -5,7 +5,7 @@ let name = "garage"; port = 3900; domain = config.repo.secrets.common.services.domains."garage-${config.node.name}"; - }) servicePort serviceName specificServiceName serviceDomain subDomain baseDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + }) servicePort serviceName specificServiceName serviceDomain subDomain baseDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; cfg = lib.recursiveUpdate config.services.${serviceName} config.swarselsystems.server.${serviceName}; inherit (config.swarselsystems) sopsFile mainUser; @@ -71,9 +71,9 @@ in } ]; - networking.firewall.allowedTCPPorts = [ servicePort 3901 3902 3903 3904 ]; + # networking.firewall.allowedTCPPorts = [ servicePort 3901 3902 3903 3904 ]; - nodes.stoicclub.swarselsystems.server.dns.${baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${baseDomain}.subdomainRecords = { "${subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; "${subDomain}-admin" = dns.lib.combinators.host proxyAddress4 proxyAddress6; "${subDomain}-web" = dns.lib.combinators.host proxyAddress4 proxyAddress6; @@ -102,9 +102,19 @@ in ]; }; - globals.services.${specificServiceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort 3901 3902 3903 3904 ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort 3901 3902 3903 3904 ]; + }; + }; + services.${specificServiceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; @@ -309,7 +319,7 @@ in }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/homebox.nix b/modules/nixos/server/homebox.nix index 5fc4d87..5dba937 100644 --- a/modules/nixos/server/homebox.nix +++ b/modules/nixos/server/homebox.nix @@ -1,20 +1,30 @@ { lib, pkgs, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "homebox"; port = 7745; }) servicePort serviceName serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "homebox"; port = 7745; }) servicePort serviceName serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; topology.self.services.${serviceName}.info = "https://${serviceDomain}"; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = { @@ -29,9 +39,9 @@ in }; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/hydra.nix b/modules/nixos/server/hydra.nix index 4751d09..9227117 100644 --- a/modules/nixos/server/hydra.nix +++ b/modules/nixos/server/hydra.nix @@ -1,6 +1,6 @@ { inputs, lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "hydra"; port = 8002; }) serviceName servicePort serviceUser serviceGroup serviceAddress serviceDomain serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "hydra"; port = 8002; }) serviceName servicePort serviceUser serviceGroup serviceAddress serviceDomain proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; inherit (config.swarselsystems) sopsFile; in { @@ -9,13 +9,23 @@ in }; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; sops = { @@ -94,7 +104,7 @@ in ]; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; programs.ssh = { extraConfig = '' @@ -102,7 +112,7 @@ in ''; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/immich.nix b/modules/nixos/server/immich.nix index 6288958..25ad031 100644 --- a/modules/nixos/server/immich.nix +++ b/modules/nixos/server/immich.nix @@ -1,12 +1,12 @@ { lib, pkgs, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "immich"; port = 3001; }) servicePort serviceName serviceUser serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "immich"; port = 3001; }) servicePort serviceName serviceUser serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -16,9 +16,20 @@ in topology.self.services.${serviceName}.info = "https://${serviceDomain}"; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + # networking.firewall.allowedTCPPorts = [ servicePort ]; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = { @@ -26,16 +37,15 @@ in package = pkgs.immich; host = "0.0.0.0"; port = servicePort; - openFirewall = true; + # openFirewall = true; mediaLocation = "/Vault/Eternor/Immich"; # dataDir environment = { IMMICH_MACHINE_LEARNING_URL = lib.mkForce "http://localhost:3003"; }; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/jellyfin.nix b/modules/nixos/server/jellyfin.nix index 54c44d1..b525075 100644 --- a/modules/nixos/server/jellyfin.nix +++ b/modules/nixos/server/jellyfin.nix @@ -1,12 +1,12 @@ { pkgs, lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "jellyfin"; port = 8096; }) servicePort serviceName serviceUser serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "jellyfin"; port = 8096; }) servicePort serviceName serviceUser serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -30,18 +30,28 @@ in topology.self.services.${serviceName}.info = "https://${serviceDomain}"; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = { enable = true; user = serviceUser; - openFirewall = true; # this works only for the default ports + # openFirewall = true; # this works only for the default ports }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/jenkins.nix b/modules/nixos/server/jenkins.nix index 9b000e9..aed10f1 100644 --- a/modules/nixos/server/jenkins.nix +++ b/modules/nixos/server/jenkins.nix @@ -1,18 +1,28 @@ { pkgs, lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "jenkins"; port = 8088; }) servicePort serviceName serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "jenkins"; port = 8088; }) servicePort serviceName serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.jenkins = { @@ -24,7 +34,7 @@ in home = "/Vault/apps/${serviceName}"; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/kanidm.nix b/modules/nixos/server/kanidm.nix index 50bccf0..339da48 100644 --- a/modules/nixos/server/kanidm.nix +++ b/modules/nixos/server/kanidm.nix @@ -2,7 +2,7 @@ let certsSopsFile = self + /secrets/repo/certs.yaml; inherit (config.swarselsystems) sopsFile; - inherit (confLib.gen { name = "kanidm"; port = 8300; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "kanidm"; port = 8300; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy homeProxyIf webProxyIf dnsServer; oauth2ProxyDomain = globals.services.oauth2-proxy.domain; immichDomain = globals.services.immich.domain; @@ -31,16 +31,18 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; - users.users.${serviceUser} = { - group = serviceGroup; - isSystemUser = true; - }; + users = { + users.${serviceUser} = { + group = serviceGroup; + isSystemUser = true; + }; - users.groups.${serviceGroup} = { }; + groups.${serviceGroup} = { }; + }; sops = { secrets = { @@ -58,11 +60,22 @@ in }; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + general.idmServer = config.node.name; + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; environment.persistence."/persist" = lib.mkIf config.swarselsystems.isImpermanence { @@ -380,7 +393,7 @@ in ${serviceName}.serviceConfig.RestartSec = "30"; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/kavita.nix b/modules/nixos/server/kavita.nix index d5821aa..717266d 100644 --- a/modules/nixos/server/kavita.nix +++ b/modules/nixos/server/kavita.nix @@ -2,7 +2,7 @@ let inherit (config.swarselsystems) sopsFile; - inherit (confLib.gen { name = "kavita"; port = 8080; }) servicePort serviceName serviceUser serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "kavita"; port = 8080; }) servicePort serviceName serviceUser serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; @@ -11,7 +11,7 @@ in calibre ]; - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -21,17 +21,26 @@ in sops.secrets.kavita-token = { inherit sopsFile; owner = serviceUser; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; - + # networking.firewall.allowedTCPPorts = [ servicePort ]; topology.self.services.${serviceName} = { name = "Kavita"; info = "https://${serviceDomain}"; icon = "${self}/files/topology-images/${serviceName}.png"; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = { @@ -42,7 +51,7 @@ in dataDir = "/Vault/data/${serviceName}"; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/koillection.nix b/modules/nixos/server/koillection.nix index 4dfa68a..951812e 100644 --- a/modules/nixos/server/koillection.nix +++ b/modules/nixos/server/koillection.nix @@ -1,6 +1,6 @@ { self, lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "koillection"; port = 2282; dir = "/Vault/data/koillection"; }) servicePort serviceName serviceUser serviceDir serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "koillection"; port = 2282; dir = "/Vault/data/koillection"; }) servicePort serviceName serviceUser serviceDir serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; serviceDB = "koillection"; postgresUser = config.systemd.services.postgresql.serviceConfig.User; # postgres @@ -14,7 +14,7 @@ in config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; sops.secrets = { @@ -28,9 +28,19 @@ in icon = "${self}/files/topology-images/${serviceName}.png"; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort postgresPort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort postgresPort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; virtualisation.oci-containers.containers = { @@ -74,7 +84,7 @@ in }; }; - networking.firewall.allowedTCPPorts = [ servicePort postgresPort ]; + # networking.firewall.allowedTCPPorts = [ servicePort postgresPort ]; systemd.services.postgresql.postStart = let @@ -107,7 +117,7 @@ in }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/mailserver.nix b/modules/nixos/server/mailserver.nix index 917d3f1..8d08b9f 100644 --- a/modules/nixos/server/mailserver.nix +++ b/modules/nixos/server/mailserver.nix @@ -1,8 +1,8 @@ { lib, config, globals, dns, confLib, ... }: let inherit (config.swarselsystems) sopsFile; - inherit (confLib.gen { name = "mailserver"; dir = "/var/lib/dovecot"; user = "virtualMail"; group = "virtualMail"; port = 443; }) serviceName serviceDir servicePort serviceUser serviceGroup serviceAddress serviceDomain serviceProxy proxyAddress4 proxyAddress6; - inherit (config.repo.secrets.local.mailserver) user1 alias1_1 alias1_2 alias1_3 alias1_4 user2 alias2_1 alias2_2 user3; + inherit (confLib.gen { name = "mailserver"; dir = "/var/lib/dovecot"; user = "virtualMail"; group = "virtualMail"; port = 443; }) serviceName serviceDir servicePort serviceUser serviceGroup serviceAddress serviceDomain proxyAddress4 proxyAddress6 isHome webProxy dnsServer; + inherit (config.repo.secrets.local.mailserver) user1 alias1_1 alias1_2 alias1_3 alias1_4 user2 alias2_1 alias2_2 alias2_3 user3; baseDomain = globals.domains.main; roundcubeDomain = config.repo.secrets.common.services.domains.roundcube; @@ -15,7 +15,7 @@ in }; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host endpointAddress4 endpointAddress6; "${globals.services.roundcube.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -28,7 +28,7 @@ in }; roundcube = { domain = roundcubeDomain; - inherit proxyAddress4 proxyAddress6; + inherit proxyAddress4 proxyAddress6 isHome; }; }; @@ -60,6 +60,9 @@ in openFirewall = true; certificateScheme = "acme"; dmarcReporting.enable = true; + enableSubmission = true; + enableSubmissionSsl = true; + enableImapSsl = true; loginAccounts = { "${user1}@${baseDomain}" = { @@ -76,6 +79,7 @@ in aliases = [ "${alias2_1}@${baseDomain}" "${alias2_2}@${baseDomain}" + "${alias2_3}@${baseDomain}" ]; sendOnly = true; }; @@ -125,7 +129,7 @@ in }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { @@ -144,6 +148,8 @@ in proxyPass = "https://${serviceName}"; extraConfig = '' client_max_body_size 0; + proxy_ssl_server_name on; + proxy_ssl_name ${roundcubeDomain}; ''; }; }; diff --git a/modules/nixos/server/matrix.nix b/modules/nixos/server/matrix.nix index 94b9c3c..7982ee9 100644 --- a/modules/nixos/server/matrix.nix +++ b/modules/nixos/server/matrix.nix @@ -1,7 +1,7 @@ { lib, config, pkgs, globals, dns, confLib, ... }: let inherit (config.swarselsystems) sopsFile; - inherit (confLib.gen { name = "matrix"; user = "matrix-synapse"; port = 8008; }) servicePort serviceName serviceUser serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "matrix"; user = "matrix-synapse"; port = 8008; }) servicePort serviceName serviceUser serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; federationPort = 8448; whatsappPort = 29318; @@ -20,7 +20,7 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -60,7 +60,7 @@ in }; }; - networking.firewall.allowedTCPPorts = [ servicePort federationPort ]; + # networking.firewall.allowedTCPPorts = [ servicePort federationPort ]; systemd = { timers."restart-bridges" = { @@ -91,9 +91,19 @@ in }; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort federationPort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services = { @@ -293,7 +303,7 @@ in # messages out after a while. - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/microbin.nix b/modules/nixos/server/microbin.nix index 82eb0db..3d338f1 100644 --- a/modules/nixos/server/microbin.nix +++ b/modules/nixos/server/microbin.nix @@ -1,6 +1,6 @@ { self, lib, config, dns, globals, confLib, ... }: let - inherit (confLib.gen { name = "microbin"; port = 8777; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "microbin"; port = 8777; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; inherit (config.swarselsystems) sopsFile; @@ -10,7 +10,7 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -50,9 +50,19 @@ in icon = "${self}/files/topology-images/${serviceName}.png"; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = { @@ -99,13 +109,13 @@ in }; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ { directory = cfg.dataDir; user = serviceUser; group = serviceGroup; mode = "0700"; } ]; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/minecraft/default.nix b/modules/nixos/server/minecraft/default.nix index 1b483c7..d333d0a 100644 --- a/modules/nixos/server/minecraft/default.nix +++ b/modules/nixos/server/minecraft/default.nix @@ -1,6 +1,6 @@ { lib, config, pkgs, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "minecraft"; port = 25565; dir = "/opt/minecraft"; proxy = config.node.name; }) serviceName servicePort serviceDir serviceDomain proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "minecraft"; port = 25565; dir = "/opt/minecraft"; proxy = config.node.name; }) serviceName servicePort serviceDir serviceDomain proxyAddress4 proxyAddress6 isHome dnsServer; inherit (config.swarselsystems) mainUser; worldName = "${mainUser}craft"; in @@ -8,7 +8,7 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -16,7 +16,7 @@ in globals.services.${serviceName} = { domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + inherit proxyAddress4 proxyAddress6 isHome; }; networking.firewall.allowedTCPPorts = [ servicePort ]; diff --git a/modules/nixos/server/monitoring.nix b/modules/nixos/server/monitoring.nix index 21133d3..a878060 100644 --- a/modules/nixos/server/monitoring.nix +++ b/modules/nixos/server/monitoring.nix @@ -1,6 +1,6 @@ { lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "grafana"; port = 3000; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "grafana"; port = 3000; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; prometheusPort = 9090; prometheusUser = "prometheus"; @@ -18,7 +18,7 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -56,13 +56,23 @@ in }; }; - networking.firewall.allowedTCPPorts = [ servicePort prometheusPort ]; + # networking.firewall.allowedTCPPorts = [ servicePort prometheusPort ]; topology.self.services.prometheus.info = "https://${serviceDomain}/${prometheusWebRoot}"; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort prometheusPort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort prometheusPort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services = { @@ -212,7 +222,7 @@ in }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { "${grafanaUpstream}" = { servers = { diff --git a/modules/nixos/server/navidrome.nix b/modules/nixos/server/navidrome.nix index 89f21d1..9bf5fc8 100644 --- a/modules/nixos/server/navidrome.nix +++ b/modules/nixos/server/navidrome.nix @@ -1,12 +1,12 @@ { pkgs, config, lib, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "navidrome"; port = 4040; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "navidrome"; port = 4040; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; in { options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -37,11 +37,21 @@ in enableAllFirmware = lib.mkForce true; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.snapserver = { @@ -67,7 +77,7 @@ in services.${serviceName} = { enable = true; - openFirewall = true; + # openFirewall = true; settings = { LogLevel = "debug"; Address = "0.0.0.0"; @@ -106,7 +116,7 @@ in }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/network.nix b/modules/nixos/server/network.nix index 01b57ad..9172d54 100644 --- a/modules/nixos/server/network.nix +++ b/modules/nixos/server/network.nix @@ -48,6 +48,7 @@ in inherit (config.repo.secrets.local.networking) defaultGateway4; wanAddress4 = netConfig.wanAddress4 or null; wanAddress6 = netConfig.wanAddress6 or null; + isHome = if (netPrefix == "home") then true else false; }; networking = { diff --git a/modules/nixos/server/nextcloud.nix b/modules/nixos/server/nextcloud.nix index b3f9e98..c536802 100644 --- a/modules/nixos/server/nextcloud.nix +++ b/modules/nixos/server/nextcloud.nix @@ -2,7 +2,7 @@ let inherit (config.repo.secrets.local.nextcloud) adminuser; inherit (config.swarselsystems) sopsFile; - inherit (confLib.gen { name = "nextcloud"; port = 80; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "nextcloud"; port = 80; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome dnsServer webProxy; nextcloudVersion = "32"; in @@ -10,7 +10,7 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -21,7 +21,7 @@ in globals.services.${serviceName} = { domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + inherit proxyAddress4 proxyAddress6 isHome; }; services = { @@ -50,7 +50,7 @@ in }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/nsd/site1.nix b/modules/nixos/server/nsd/site1.nix index 97ef596..901774c 100644 --- a/modules/nixos/server/nsd/site1.nix +++ b/modules/nixos/server/nsd/site1.nix @@ -3,7 +3,7 @@ with dns.lib.combinators; { SOA = { nameServer = "soa"; adminEmail = "admin@${globals.domains.main}"; # this option is not parsed as domain (we cannot just write "admin") - serial = 2025122204; # update this on changes for secondary dns + serial = 2025122401; # update this on changes for secondary dns }; useOrigin = false; diff --git a/modules/nixos/server/oauth2-proxy.nix b/modules/nixos/server/oauth2-proxy.nix index ef8d563..2d85ba0 100644 --- a/modules/nixos/server/oauth2-proxy.nix +++ b/modules/nixos/server/oauth2-proxy.nix @@ -1,6 +1,6 @@ { lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "oauth2-proxy"; port = 3004; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "oauth2-proxy"; port = 3004; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; kanidmDomain = globals.services.kanidm.domain; mainDomain = globals.domains.main; @@ -119,7 +119,7 @@ in }; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -142,11 +142,21 @@ in }; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services = { @@ -198,7 +208,7 @@ in }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/paperless.nix b/modules/nixos/server/paperless.nix index 40e15c6..d169557 100644 --- a/modules/nixos/server/paperless.nix +++ b/modules/nixos/server/paperless.nix @@ -1,7 +1,7 @@ { lib, pkgs, config, dns, globals, confLib, ... }: let inherit (config.swarselsystems) sopsFile; - inherit (confLib.gen { name = "paperless"; port = 28981; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "paperless"; port = 28981; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; tikaPort = 9998; gotenbergPort = 3002; @@ -11,7 +11,7 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -24,11 +24,21 @@ in kanidm-paperless-client = { inherit sopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services = { @@ -99,7 +109,7 @@ in ) ''; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/radicale.nix b/modules/nixos/server/radicale.nix index 519442a..a6e30fe 100644 --- a/modules/nixos/server/radicale.nix +++ b/modules/nixos/server/radicale.nix @@ -1,7 +1,6 @@ { lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "radicale"; port = 8000; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; - # sopsFile = config.node.secretsDir + "/secrets2.yaml"; + inherit (confLib.gen { name = "radicale"; port = 8000; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; inherit (config.swarselsystems) sopsFile; cfg = config.services.${serviceName}; @@ -10,7 +9,7 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -35,9 +34,19 @@ in topology.self.services.${serviceName}.info = "https://${serviceDomain}"; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = { @@ -89,9 +98,9 @@ in }; }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/settings.nix b/modules/nixos/server/settings.nix index d2b7006..c9d393c 100644 --- a/modules/nixos/server/settings.nix +++ b/modules/nixos/server/settings.nix @@ -23,7 +23,7 @@ in config.swarselsystems.shellAliases; nixpkgs.config = lib.mkIf (!config.swarselsystems.isMicroVM) { - perittedInsecurePackages = [ + permittedInsecurePackages = [ # matrix "olm-3.2.16" # sonarr diff --git a/modules/nixos/server/shlink.nix b/modules/nixos/server/shlink.nix index 1987eae..023b831 100644 --- a/modules/nixos/server/shlink.nix +++ b/modules/nixos/server/shlink.nix @@ -1,6 +1,6 @@ { self, lib, config, dns, globals, confLib, ... }: let - inherit (confLib.gen { name = "shlink"; port = 8081; dir = "/var/lib/shlink"; }) servicePort serviceName serviceDomain serviceDir serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "shlink"; port = 8081; dir = "/var/lib/shlink"; }) servicePort serviceName serviceDomain serviceDir serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; containerRev = "sha256:1a697baca56ab8821783e0ce53eb4fb22e51bb66749ec50581adc0cb6d031d7a"; @@ -12,7 +12,7 @@ in }; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -69,7 +69,7 @@ in ] ); - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ { directory = serviceDir; } @@ -82,12 +82,22 @@ in icon = "${self}/files/topology-images/${serviceName}.png"; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/slink.nix b/modules/nixos/server/slink.nix index a55aa36..8e71aae 100644 --- a/modules/nixos/server/slink.nix +++ b/modules/nixos/server/slink.nix @@ -1,6 +1,6 @@ { self, lib, config, dns, globals, confLib, ... }: let - inherit (confLib.gen { name = "slink"; port = 3000; dir = "/var/lib/slink"; }) servicePort serviceName serviceDomain serviceDir serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "slink"; port = 3000; dir = "/var/lib/slink"; }) servicePort serviceName serviceDomain serviceDir serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; containerRev = "sha256:98b9442696f0a8cbc92f0447f54fa4bad227af5dcfd6680545fedab2ed28ddd9"; in @@ -10,7 +10,7 @@ in }; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -47,7 +47,7 @@ in ] ); - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ { directory = serviceDir; } @@ -59,12 +59,22 @@ in icon = "${self}/files/topology-images/shlink.png"; }; - globals.services.${serviceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy}.allowedTCPPorts = [ servicePort ]; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy}.allowedTCPPorts = [ servicePort ]; + }; + }; + services.${serviceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/snipe-it.nix b/modules/nixos/server/snipe-it.nix index ebc6103..a7fb222 100644 --- a/modules/nixos/server/snipe-it.nix +++ b/modules/nixos/server/snipe-it.nix @@ -1,6 +1,6 @@ { lib, config, globals, dns, confLib, ... }: let - inherit (confLib.gen { name = "snipeit"; port = 80; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "snipeit"; port = 80; }) servicePort serviceName serviceUser serviceGroup serviceDomain serviceAddress proxyAddress4 proxyAddress6 isHome webProxy dnsServer; # sopsFile = config.node.secretsDir + "/secrets2.yaml"; inherit (config.swarselsystems) sopsFile; @@ -12,7 +12,7 @@ in options.swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${serviceName}.baseDomain}.subdomainRecords = { "${globals.services.${serviceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -26,7 +26,7 @@ in globals.services.${serviceName} = { domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + inherit proxyAddress4 proxyAddress6 isHome; }; services.snipe-it = { @@ -46,7 +46,7 @@ in }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${serviceName} = { servers = { diff --git a/modules/nixos/server/syncthing.nix b/modules/nixos/server/syncthing.nix index d10abb9..e96fd21 100644 --- a/modules/nixos/server/syncthing.nix +++ b/modules/nixos/server/syncthing.nix @@ -1,7 +1,7 @@ { lib, config, globals, dns, confLib, ... }: let inherit (config.swarselsystems.syncthing) serviceDomain; - inherit (confLib.gen { name = "syncthing"; port = 8384; }) servicePort serviceName serviceUser serviceGroup serviceAddress serviceProxy proxyAddress4 proxyAddress6; + inherit (confLib.gen { name = "syncthing"; port = 8384; }) servicePort serviceName serviceUser serviceGroup serviceAddress proxyAddress4 proxyAddress6 isHome isProxied homeProxy webProxy dnsServer homeProxyIf webProxyIf; specificServiceName = "${serviceName}-${config.node.name}"; @@ -42,7 +42,7 @@ in }; config = lib.mkIf config.swarselmodules.server.${serviceName} { - nodes.stoicclub.swarselsystems.server.dns.${globals.services.${specificServiceName}.baseDomain}.subdomainRecords = { + nodes.${dnsServer}.swarselsystems.server.dns.${globals.services.${specificServiceName}.baseDomain}.subdomainRecords = { "${globals.services.${specificServiceName}.subDomain}" = dns.lib.combinators.host proxyAddress4 proxyAddress6; }; @@ -54,11 +54,27 @@ in users.groups.${serviceGroup} = { }; - networking.firewall.allowedTCPPorts = [ servicePort ]; + # networking.firewall.allowedTCPPorts = [ servicePort ]; - globals.services.${specificServiceName} = { - domain = serviceDomain; - inherit proxyAddress4 proxyAddress6; + globals = { + networks = { + ${webProxyIf}.hosts = lib.mkIf isProxied { + ${config.node.name}.firewallRuleForNode.${webProxy} = { + allowedTCPPorts = [ servicePort 22000 ]; + allowedUDPPorts = [ 20000 21027 ]; + }; + }; + ${homeProxyIf}.hosts = lib.mkIf isHome { + ${config.node.name}.firewallRuleForNode.${homeProxy} = { + allowedTCPPorts = [ servicePort 20000 ]; + allowedUDPPorts = [ 20000 21027 ]; + }; + }; + }; + services.${specificServiceName} = { + domain = serviceDomain; + inherit proxyAddress4 proxyAddress6 isHome; + }; }; services.${serviceName} = rec { @@ -68,7 +84,7 @@ in dataDir = lib.mkDefault "/Vault/data/${serviceName}"; configDir = "${cfg.dataDir}/.config/${serviceName}"; guiAddress = "0.0.0.0:${builtins.toString servicePort}"; - openDefaultPorts = true; # opens ports TCP/UDP 22000 and UDP 21027 for discovery + openDefaultPorts = lib.mkIf (!isProxied) true; # opens ports TCP/UDP 22000 and UDP 21027 for discovery relay.enable = false; settings = { urAccepted = -1; @@ -115,7 +131,7 @@ in }; }; - nodes.${serviceProxy}.services.nginx = { + nodes.${webProxy}.services.nginx = { upstreams = { ${specificServiceName} = { servers = { diff --git a/modules/nixos/server/transmission.nix b/modules/nixos/server/transmission.nix index 7dfcd87..718f974 100644 --- a/modules/nixos/server/transmission.nix +++ b/modules/nixos/server/transmission.nix @@ -1,6 +1,6 @@ { self, pkgs, lib, config, confLib, ... }: let - inherit (confLib.gen { name = "transmission"; }) serviceName serviceDomain; + inherit (confLib.gen { name = "transmission"; }) serviceName serviceDomain isHome; lidarrUser = "lidarr"; lidarrGroup = lidarrUser; @@ -86,7 +86,10 @@ in prowlarr.info = "https://${serviceDomain}/prowlarr"; }; - globals.services.transmission.domain = serviceDomain; + globals.services.transmission = { + domain = serviceDomain; + inherit isHome; + }; services = { radarr = { diff --git a/modules/nixos/server/wireguard.nix b/modules/nixos/server/wireguard.nix index 318fdce..98d9fdf 100644 --- a/modules/nixos/server/wireguard.nix +++ b/modules/nixos/server/wireguard.nix @@ -20,52 +20,81 @@ in lib.mkEnableOption "enable ${serviceName} settings"; swarselsystems.server.wireguard = { - interfaces = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({ name, config, ... }: { - options = { - isServer = lib.mkEnableOption "set this interface as a wireguard server"; - isClient = lib.mkEnableOption "set this interface as a wireguard client"; + interfaces = + let + topConfig = config; + in + lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({ config, name, ... }: { + options = { + isServer = lib.mkEnableOption "set this interface as a wireguard server"; + isClient = lib.mkEnableOption "set this interface as a wireguard client"; - serverName = lib.mkOption { - type = lib.types.str; - default = ""; - description = "Hostname of the WireGuard server this interface connects to (when isClient = true)."; - }; + serverName = lib.mkOption { + type = lib.types.str; + default = if config.isServer then topConfig.node.name else ""; + description = "Hostname of the WireGuard server this interface connects to (when isClient = true)."; + }; - serverNetConfigPrefix = lib.mkOption { - type = lib.types.str; - default = - let - serverCfg = nodes.${config.serverName}.config; - in - if serverCfg.swarselsystems.isCloud - then serverCfg.node.name - else "home"; - readOnly = true; - description = "Prefix used to look up the server network in globals.networks.\"-wg\"."; - }; + serverNetConfigPrefix = lib.mkOption { + type = lib.types.str; + default = + let + serverCfg = nodes.${config.serverName}.config; + in + if serverCfg.swarselsystems.isCloud + then serverCfg.node.name + else "home"; + readOnly = true; + description = "Prefix used to look up the server network in globals.networks.\"-wg\"."; + }; - ifName = lib.mkOption { - type = lib.types.str; - default = name; - description = "Name of the WireGuard interface."; - }; + ifName = lib.mkOption { + type = lib.types.str; + default = name; + description = "Name of the WireGuard interface."; + }; - peers = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ ]; - description = "WireGuard peer config names (clients when this host is server, or additional peers)."; + port = lib.mkOption { + type = lib.types.int; + default = servicePort; + description = "Port of the WireGuard interface."; + }; + + peers = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = lib.attrNames (lib.filterAttrs (name: _: name != topConfig.node.name) globals.networks."${config.serverNetConfigPrefix}-${config.ifName}".hosts); + description = "WireGuard peer config names of this wireguardinterface."; + }; }; - }; - })); - default = { }; - description = "WireGuard interfaces defined on this host."; - }; + })); + default = { }; + description = "WireGuard interfaces defined on this host."; + }; }; }; config = lib.mkIf config.swarselmodules.server.${serviceName} { + assertions = lib.concatLists ( + lib.flip lib.mapAttrsToList interfaces ( + ifName: ifCfg: + let + assertionPrefix = "While evaluating the wireguard network ${ifName}:"; + in + [ + { + assertion = ifCfg.isServer || (ifCfg.isClient && ifCfg.serverName != ""); + message = "${assertionPrefix}: This node must either be a server for the wireguard network or a client with serverName set."; + } + { + assertion = lib.stringLength ifName < 16; + message = "${assertionPrefix}: The specified linkName '${ifName}' is too long (must be max 15 characters)."; + } + ] + ) + ); + environment.systemPackages = with pkgs; [ wireguard-tools ]; @@ -74,7 +103,6 @@ in lib.mkMerge ( [ { - # shared host private key wireguard-private-key = { inherit sopsFile; owner = serviceUser; @@ -85,8 +113,8 @@ in ] ++ (map (i: let - simpleClientSecrets = - lib.optionalAttrs (i.isClient && i.peers == [ ]) { + clientSecrets = + lib.optionalAttrs i.isClient { "wireguard-${i.serverName}-${config.node.name}-${i.ifName}-presharedKey" = { sopsFile = wgSopsFile; owner = serviceUser; @@ -95,8 +123,8 @@ in }; }; - multiPeerSecrets = - lib.optionalAttrs (i.peers != [ ]) (builtins.listToAttrs (map + serverSecrets = + lib.optionalAttrs i.isServer (builtins.listToAttrs (map (clientName: { name = "wireguard-${config.node.name}-${clientName}-${i.ifName}-presharedKey"; value = { @@ -108,17 +136,72 @@ in }) i.peers)); in - simpleClientSecrets // multiPeerSecrets + clientSecrets // serverSecrets ) ifaceList) ); - networking = { - firewall.checkReversePath = - lib.mkIf (lib.any (i: i.isClient) ifaceList) "loose"; + networking.firewall = { + checkReversePath = lib.mkIf (lib.any (i: i.isClient) ifaceList) "loose"; + allowedUDPPorts = lib.mkMerge ( + lib.flip lib.mapAttrsToList interfaces ( + _: ifCfg: + lib.optional ifCfg.isServer ifCfg.port + ) + ); + }; - firewall.allowedUDPPorts = - lib.mkIf (lib.any (i: i.isServer) ifaceList) [ servicePort ]; + networking.nftables.firewall = { + zones = lib.mkMerge + ( + lib.flip lib.mapAttrsToList interfaces ( + ifName: ifCfg: + { + ${ifName}.interfaces = [ ifName ]; + } + // lib.listToAttrs (map + (peer: + let + peerNet = globals.networks."${ifCfg.serverNetConfigPrefix}-${ifName}".hosts.${peer}; + in + lib.nameValuePair "${ifName}-node-${peer}" { + parent = ifName; + ipv4Addresses = lib.optional (peerNet.ipv4 != null) peerNet.ipv4; + ipv6Addresses = lib.optional (peerNet.ipv6 != null) peerNet.ipv6; + } + ) + ifCfg.peers) + ) + ); + rules = lib.mkMerge ( + lib.flip lib.mapAttrsToList interfaces ( + ifName: ifCfg: + let + inherit (config.networking.nftables.firewall) localZoneName; + netCfg = globals.networks."${ifCfg.serverNetConfigPrefix}-${ifName}"; + in + { + "${ifName}-to-${localZoneName}" = { + inherit (netCfg.firewallRuleForAll) allowedTCPPorts allowedUDPPorts allowedTCPPortRanges allowedUDPPortRanges; + from = [ ifName ]; + to = [ localZoneName ]; + ignoreEmptyRule = true; + }; + } + // lib.listToAttrs (map + (peer: + lib.nameValuePair "${ifName}-node-${peer}-to-${localZoneName}" ( + lib.mkIf (netCfg.hosts.${config.node.name}.firewallRuleForNode ? ${peer}) { + inherit (netCfg.hosts.${config.node.name}.firewallRuleForNode.${peer}) allowedTCPPorts allowedTCPPortRanges allowedUDPPorts allowedUDPPortRanges; + from = [ "${ifName}-node-${peer}" ]; + to = [ localZoneName ]; + ignoreEmptyRule = true; + } + ) + ) + ifCfg.peers) + ) + ); }; systemd.network = { @@ -136,14 +219,10 @@ in MTUBytes = 1408; # TODO: figure out where we lose those 12 bits (8 from pppoe maybe + ???) }; - address = - if i.isServer then [ - globals.networks."${config.swarselsystems.server.netConfigPrefix}-${ifName}".hosts.${config.node.name}.cidrv4 - globals.networks."${config.swarselsystems.server.netConfigPrefix}-${ifName}".hosts.${config.node.name}.cidrv6 - ] else [ - globals.networks."${i.serverNetConfigPrefix}-${ifName}".hosts.${config.node.name}.cidrv4 - globals.networks."${i.serverNetConfigPrefix}-${ifName}".hosts.${config.node.name}.cidrv6 - ]; + address = [ + globals.networks."${i.serverNetConfigPrefix}-${ifName}".hosts.${config.node.name}.cidrv4 + globals.networks."${i.serverNetConfigPrefix}-${ifName}".hosts.${config.node.name}.cidrv6 + ]; }; }) ifaceList); @@ -196,12 +275,12 @@ in builtins.readFile "${self}/secrets/public/wg/${clientName}.pub"; PresharedKeyFile = - config.sops.secrets."wireguard-${config.node.name}-${clientName}-${i.ifName}-presharedKey".path; + config.sops.secrets."wireguard-${i.serverName}-${clientName}-${i.ifName}-presharedKey".path; AllowedIPs = let clientInWgNetwork = - globals.networks."${config.swarselsystems.server.netConfigPrefix}-${i.ifName}".hosts.${clientName}; + globals.networks."${i.serverNetConfigPrefix}-${i.ifName}".hosts.${clientName}; in (lib.optional (clientInWgNetwork.ipv4 != null) (lib.net.cidr.make 32 clientInWgNetwork.ipv4)) diff --git a/modules/shared/config-lib.nix b/modules/shared/config-lib.nix index e80d548..1bc7e2a 100644 --- a/modules/shared/config-lib.nix +++ b/modules/shared/config-lib.nix @@ -35,6 +35,11 @@ serviceProxy = proxy; proxyAddress4 = globals.hosts.${proxy}.wanAddress4 or null; proxyAddress6 = globals.hosts.${proxy}.wanAddress6 or null; + inherit (globals.hosts.${config.node.name}) isHome; + inherit (globals.general) homeProxy webProxy dnsServer idmServer; + webProxyIf = "${webProxy}-wgProxy"; + homeProxyIf = "home-wgHome"; + isProxied = config.node.name != webProxy; }; mkMicrovm = diff --git a/nix/globals.nix b/nix/globals.nix index fae2b0d..cf6ee94 100644 --- a/nix/globals.nix +++ b/nix/globals.nix @@ -67,6 +67,7 @@ hosts user root + general ; }; }; diff --git a/profiles/home/personal/default.nix b/profiles/home/personal/default.nix index 049bb74..dba9980 100644 --- a/profiles/home/personal/default.nix +++ b/profiles/home/personal/default.nix @@ -18,6 +18,7 @@ env = lib.mkDefault true; eza = lib.mkDefault true; firefox = lib.mkDefault true; + firezone-tray = lib.mkDefault true; fuzzel = lib.mkDefault true; gammastep = lib.mkDefault true; general = lib.mkDefault true; diff --git a/profiles/nixos/localserver/default.nix b/profiles/nixos/localserver/default.nix index 0c70be5..7de318e 100644 --- a/profiles/nixos/localserver/default.nix +++ b/profiles/nixos/localserver/default.nix @@ -22,6 +22,7 @@ packages = lib.mkDefault true; ssh = lib.mkDefault true; attic-setup = lib.mkDefault true; + dns-hostrecord = lib.mkDefault true; }; }; }; diff --git a/profiles/nixos/personal/default.nix b/profiles/nixos/personal/default.nix index 13d7d21..e27e8dc 100644 --- a/profiles/nixos/personal/default.nix +++ b/profiles/nixos/personal/default.nix @@ -11,6 +11,7 @@ btrfs = lib.mkDefault true; distrobox = lib.mkDefault true; env = lib.mkDefault true; + firezone-client = lib.mkDefault true; general = lib.mkDefault true; gnome-keyring = lib.mkDefault true; gvfs = lib.mkDefault true; diff --git a/secrets/repo/globals.nix.enc b/secrets/repo/globals.nix.enc index 4fb98ce..bcead07 100644 --- a/secrets/repo/globals.nix.enc +++ b/secrets/repo/globals.nix.enc @@ -1,5 +1,5 @@ { - "data": "ENC[AES256_GCM,data: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,iv:lIDbetXOcbEk3F/OkAb01rmXu820Sq7qXyJE/SEY1aw=,tag://ndiLviaDLDAiwWG408bQ==,type:str]", + "data": "ENC[AES256_GCM,data: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,iv:Fep8PNrxh18J4n8gYLbmJ2+Jn7kD/4KCLs11GDDfshM=,tag:UwkE59mcIslEcrqNQ0qCTw==,type:str]", "sops": { "age": [ { @@ -51,8 +51,8 @@ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvbTFqR3RScWxnTFR3dlhv\nNUNEVGRkUDB4L3J6ckJIMWZCWk44RjdpVmxRCkhzZTBuSGduanBmQWsvNW5XMWQv\nYVZmNS9FVzN3a05kUVBheDljYUUrcHcKLS0tIGxPN1dWVkg4NUpnZGJ6VWFJWFVZ\nYnNvRG94MmFxYnlDQ0JyeDNFQldzdlkKsp+nYSR6Lxq8b3/dpMO7uTbNnO0Bva7w\nb9s908PLaZEN1jywEoba3yq743vuEHCKQWFIfDtsRcbNR6Yr4d2eGw==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2025-12-22T13:11:00Z", - "mac": "ENC[AES256_GCM,data:NDk4ChgEfqnX4lltTZ7uX0ZPa/AVmRM7BK0G81uHRQ/UtrsbZG3YRnjJ9p0LIIHdprH5Fcs9XZHZBpoSR6MhzwviqPZJAsmaYFmTVqoJcicqL8pW2UNYP30yRZqlLMzVrVkk3hEedwO9RKeTJndu0gGzs2XKZBRVi0/5j3Uf9n8=,iv:GdoYMc39Ug0CwnvY9NIFoOwMI8rJmdXF/HdkXCDqgE4=,tag:Q/LD1WTMYmxqURpAN0NOFA==,type:str]", + "lastmodified": "2025-12-24T23:33:32Z", + "mac": "ENC[AES256_GCM,data:mrwZUGeMtgAqSd2v387rGaf0bHZDC1IjIUlFPzB3Tj7CHE9Li4vAp7f9Bvd6v62eNcuwrnW8UoXju63fdDLK7l3EczdWsA/EJrvxIr29x+NByzfbUtShexg24mwsN7HMBnhyvil1CF4PMN4EmxxOn05cXOsAqln38IEPY6n6+SM=,iv:7PeUcjVA5u1KXsBZv+UrVQaCqEjXqY6wnELDEeHyQ9c=,tag:A+bETVg1tlHZlAm0rJ9Z8A==,type:str]", "pgp": [ { "created_at": "2025-12-15T21:53:38Z", diff --git a/secrets/repo/pii.nix.enc b/secrets/repo/pii.nix.enc index e430c7c..4cd6a2a 100644 --- a/secrets/repo/pii.nix.enc +++ b/secrets/repo/pii.nix.enc @@ -1,5 +1,5 @@ { - "data": "ENC[AES256_GCM,data:TTRt+/F3VgN3D9mZcklKJ0xJShC5EUfdCo1NlcK/QONERmYYmnOyfbwbPxaUwBq2ZIwxznBAwwU42XMFclRiO/I9UKI4fmwsOoYZByIflNAKX/kveoig3WBRXWbXwTYTJfggErEW5WPiZRzk26Ji8L3cm4GFt25qeMORFVs/iN9Fiy1c9JSbasxjRI2UGfmsfILD/BTnTHo1QTibnCwdNerjQEETHD+QOTDQSlYJOVeop884BE3neXeYCnr7cWhy+YTrln8OX3y+Sgfv5jLEYNM8R9xaOTa1OpmadF9mx/RrOCABctBhuWFWLGPk/sLhCvGUylD5J/pyaYJN+cvtVT6bRCX0wj+CeCMAt3SHHrphXiU2BiOAneB90HGRbEJ8oyykt/ltjgwWmRwagKAp9CtzEqhuFouhow/W9LRgsksupMX6viv1YS52XgWbNXOHiqiEwBh+onjMz4Xg8Sat9thvgpy89k1ep9KJ5QgJYhqFKb2pvFgFkkDl49C4UpdImvdlePqXK46J1YzKWB1EMWPZ+stg3V/HDJGbXonTuom0e96AeGPY192p6wLpqsbZ5cH4vosSmhQTlkrGDvINGZS/q0Xxz7X8eLZMTW05kH/Ks7m71NHrx6xAkHaO8iRdbpQdVtqLIIh6OAt2vdcK4xVox9WyIiCWG4nvyCivjXXeYBLj0f9siINyFYI543wUpsO1zd129HKO40ZDcdXjrnR0n246uxDAlSBkA1VFWIZ3tincTcqT8tjkKQzIfNdDE/X0zLhzLn1NEIXMdUi/og3Oxh93A16pvpBNPmyR4CX8wQLu0aO5ceYIf7DCVSTZ5dDtbbA2Pi+qhb77bdS5oynnlYOAi0UHgB1H+bv4KTZiD7L/wMBxtoJbHVJJCvA3OEeKDwR0GmPBsjtjICib6dTJteidYTdY3p3XAbSZWjTotCn53vj40N+FzhfpgZei5fQKaE5jsAVqbzqdsJltWd07MB16SHA3aRXt5DC02lGtAsbPSSgJ7hxnxR5rT+z4bD9wFaNQ3NseEuSNHZ9Jep0qmwE5jd9oaH5J9As+8W+4FqQSRLQ28nhVFKNt1R9Bzcfmcx9hARbJUtx/aXvA8qFAC6/c9DLVK1WVWnvs4iOBzVfXB6NfQfaynuIzcbRYTx+hD86AHvwyW15RwbYHee2A4X74/qcn02Y9ilByhel4rt6tokBGuEJziYVtg6vzuH7hF2S+2Zl11MtNBlxSmD2ghLdzNhGQRi5XxDvbrPDndDHS9ppTpn/JaRLdzAaLwSM576hKwQmyC7O+sqmgUw2H4gy0iYkFVsKuTkRuOEa/wkyK2cLWCxUY4jOgclhS2wfDRbL+4FhtdNBzTPtduqnQVgGSKY8LJg+2dWDBmstpfbIiYNvNGI+ho4hO+82vNpPnY4MT5Bz/GiznAgVc8vZ2ELZD6PpRtT7SKdxI8dRcY/I0FNqfUhTYyW0Wmz2lZ/+KKv074BoihtqgLbLErge7Fe7v+SbYuKHj6w2wsEm2XsbTEzv3fskMgDW6eyuEtDhDATWIdrVjXlBjXfoYU5wBtWXNE0MzQISxYhSZHiTFei43JafagG3S7RQEOiCUGeqjjFjX1dCxalhTITIzFp0uGmHG6BiOAepHOi7TOqELorRMcglP5EPaDC+M5Eeax6PQgMSs+h50LVdSdNbYvREtllIqzL/B6ntMebAELFUxVttsk/Ky8eIbWUbXB5YOZpChJAk7vDlVeKye/pn49aHgkHp5MmkDlNYURnnX/wIQkZsKH7m0tOfmIBPXnnm8v8AgiZIfzH9GySfM3EtNsvmh6qshViGagyvXnSh9kgnotuOsWkuXVuNH8nDZQsqGWlATMctzYY2lsKXQ1lxmWhcTHlpDVyqzqpmn/5V6ivn3ltbE4uhYygbt0auOYuwnMGFuzZRB0xh/dp0J25grEeA9hKICb0OOjVSYTVKyUidxKGvHMtySv0g1qH5jBJXe1mBliJlFKgHKjQ+FK0oGzPxxakTOBoh2jY+sZdV0ZiytXPtuEVOVUu4rixPubuQiuE980+ZhShFtimjTt87BlTRBBaf04qYoux1MN7zVc3HQDbyA31Nhe1fsx+vylVT/1htqyw9Xe13FboLCFaf0hus8VJ7ycd/Ohf7EJgm3f+O/9pGXUamSRgRSE8tx6NwDAUQuSiGnYbi5hN9j+1zG/bC2atOia5SEGmn9ZjJB5s3mQC4M68af2Q7oFbkpajEKb8DyB2WlqcF1ke+cCrl6ADZWxuUezk6KKJ7Co2gVPNmczgUzJcEnYOV/YhqHOjdQDEf1peZlByq4sE6d17YkM9BrNXzVhftOuFUbbNl5VZIdd10YgVy5rMQsyB4vrB0HsR3s+8H+6Yon8wAaI1/A8ZeREjLq43Gb7qNy+Y703B5RzHTxUFHFFkviSAvP515YSVF7R7zT6Ik/5l2Nzg3DBBFqLfkVlub7eft11wd8XNUFJekvTubfh5GJW+lyMp8pXIGWPBH8PCbchuj/yBiBwhHPvu+sACtHYtu88ufQnQuS42CIVXG3AH5awDSOH048uHH8395Xu/bA0jWOX1f9b7m01yec2yxDhtG98gWiR4zRC7teIafxT6nq3pV8pxPOx8RxXWZhtvRFmcHlTtn4TO9cTm2e4yiUiNm7R3HGNvB/QzfLtcu8teIL7JL62Lca7e5fPiKkjoee9TFW4WbH5PQO4gX1LCM1XI+Az1YorNrIgzkX7tvASKCaWMHuMJGzVSS3rZ2A+HFmVaaO5nvWrxIQa+4z1nucg9+/so4iMaKaSKz8ZIv8pW9HFUyq99wVHhtYxj6Tatc2JYcln+oUErJ8OZ0ep7gq9b8gQLdbVQKVhCz9tWbG6tteClsIs2CflJ88LgMrRd50j75b8r9luVNnBvnhRpKOc7eYtd1XPtFj8Oup08p3BJoKSjkoQga8XDydEAEqmn2vvaNb84Em1kBaN8tpKZx0vgBG8y9Mfrlbxar56LD46+gQOPNCp1g92+0f9+kxPL7eZfr8Afsaoa2fvEScfVxfNE3TG0n32Q02y/UrzPn+tmkqhgHmJFXkR7oUVwcRFyge7jRmGKgkpCX8YbUGKvVje7/v98SfdQWHaWZ7ONb7lRrKt/T9FMwton+/pcREDdqP8aRw0uZsPdgL5n13iWcuvn2ba/gDpblIng44IDYLwDW3ldJDUGfzNGl30WzGdn7RDmYXwfnLzuo7f0sShjLIPay4B+L5f76ZaR/kcDrneRzU7/igpiMxo1uYHfjgWWEln53V67IT+8ae/Xf+oLIjHtVlWuQ+y4gnqF4u1nK5EfHmF3c8Wunc7nLx71UQGpggiHpnQrP8WbY0toEX1jmlYH9OlJqZLI67wnV0LbDgYLu012i0ovkjGYEvtL29kb4X32qAEiKc/jDrlm4gZ1hid2613KltkuH67iC9fDN8A4ocw3qgLMtOGzev0MXdCtL2QO3VeR+373iDNW6pFt95wduMEO0Urc/8hVkPCGX+VCTwgUQYdYL4eR8xiXSYp1EhiTgiuVHjEkk++cf2fh+CUqZCex+1Frj5CCnLS6QM9ccR6b5WJTHS6JezkHXsodnnnefixHi/QsVZ+u1BL7xBVUzvw5H35Lt1BCmAiVOMFpAnMcN9jHepoN25RWNwizCVO9I6UpBcl+e/gSwRo8gmNR2eexaPz2f+2Z5NLvNNyREBXpfNv5J6Nyv+Splb+QQogvYT+JURUTEQI1Ja0bl3dbJqOhQVBN9ms7TjKpSvyvJqVB5chQjE3MkrPISSAgG18MGFvpDcwPkn/INzSXsGceAcHLN1y+NQvo6pasGx6cYzZgc7JUhosmqGai7H21H4X7K3TlmW5KySTlwIx+pouj09aUeP97e+05PU2ov0wXN+lNHFLMV68QDykiQdhm1HZeicePoYYbLrSrodnIxamjSj9NCWaLoGRGf+ZqMb6xMBOj3y1SmzEmFn2Eyf/xNtCD0rADpbKhFZMmmDzk2YGYjQvzpbGAZm3qY62aqe4tgck26D2IPlK7XcSivpxmfiCj46oBa80kUfZGhmrTcq5btiVeu5B3c9ffIlcUIeUMCLA+G8opYHdijATFN2Od1ieUvkY/EE45xlHP2fyUn0Tfs7BvchhSkCoZpV/I3Gq/FOW+3o8pQkWDy+neYaZEIvfdqcbxf4AEYBVcj/1nNeYjCzQUfc8O1hLVR+t4Q/LUQY0zs0fWQS1M9jRxp4HE6Pa2DFAjo5hQZU0iuhuoPmhTWXvCTjuntddfyvbPb4FPk3QD3mDlBt39C8MkIuRGvH1g/a+BWttdzXqPFapLuvjr712fl58uGJc55IMmLrZZlkAGeL1tor3Kd1PvAFrqYS1kQamioXzubmZm2aJbrRQlJD16gNWoirBAyOKVrw4Na2simnv+3DPqVOvmQWPUAHBsTk3oaLU8ifZI7BUDdXTJ3Yn2HBLwW4QN3u+alPjyyDf0VE1M60O7ufN1DPDgsQC2hnkUsnr+mX+lyWPrZGZrlaiaUsLa5JIt4NA1h/aOMeP4JC2wxeQGU5T4KApusyGqE3PsfRWkCCiFDddZi0ayj8jZKwDseBwwVswvbyAOxrErtE0Xn6DdUocfL7R2DnHA3KwB6rSkWFAzksk7b4V5t8KhydaZZEqFKUW0gvoioGF973hrCt1idcN/SiiBaCUsM++ryyDCOqp4awJ3vvNa67gJiDjZodC63AXZOrkWKxtk0TXAgvjKKnwleJ7RabZHQiA+XuAXv799np1rniJ4f4WiS5QE+oMYNzjselv7Z/pzdE8gf58l4OV05j8X+cmELQUZSzBy1ZdQBxF5QcEv63Nq4h55pYB4/pDmRU5l+yEXrB4eeCbSfWjmOkggybwQ21M2EDTstLnheHvgEsKTmRXmr0fZ8Sdt81dcuZazG+cUKE65qDAYQSLj57wQIeE/d6pW5M+Ng2aMl1aNltkIliSsna/uf7v0cOtt9krIWXOL5C9o2TGJxWuv6S7keDozIXpARpopbMh5d4rRzCBpmaosr3gvw4Ea1HXp63N4noY45ImN2BLUv/vXAeeirbvhzN09ToNGjur0Hlvb77AhDFS511j2p3VyIwFiYRoxixI+zVzt4UpGjbsfyKOJhXg2fsV4qKtRo0iSU4ZjnmwTGR70aP8S8iJirirj1/UycQRlohWIY8eqhmKZS+HeSTy6bf3qGcue57Zn1ywXdXdjLrrz5tNg+qywOXMkG3ITz6Uv6Xn/hDaPdtGq0b1StNzPfEA87oIrT1QGoxZD5YgzSxyWkzc2cjA4ay9FS1tbBUtWOjy9tMBIsAyKR3MNq2qMGN9q/5OcagwyFJnepax3dGNj77btXMgIy5MLvNItGRQx4b8SvI2u9nfbBUVPTRYpjIaxaBmXyr1aJvmFRYCLgSH2Zq4xlOOapU3Y1kQeQCXuRulcdnittmx3j2FZAXf3fKyfstS9w/kUOeQcBYj72SAzkPoTiYHwQ1aBPxL6nP+oND+az8yASd2lnX7areK4ob+Oz6mXNZ+fzI9HCEAa9IQhMdrXrhYvumSaN+Nl/6d9Vob7nVeEBxSK+i6F1Oco17cU45S/MWGgUUZ3sEeciH6BnsWjTMDSTEAPlGqEX+TwzHvnY/mmoyZI5Ayg2U6moh04rGy9csDOWw34FARWdGlWnL9tbRqHJBfOXjkUcUmGkV7k9tlD0mTbhyxhmzXC2eFCI8rPstI9v3EKKBP55V6xt0auxP14M3f/gbKmn2iAlCFcutlwTp6a4jWqjdlB0XCxDdQj4JesVdyiZVhCSipvBbKO7wFo+n+majpHvoXP9292mJBHUPfU9XVqupX6HR8/Tbs1+6T3PTegW2dIzi1RGIcUdfCANTccUZsCG/qMZPQ0JnMtxi+K4/jdyKjKUokwMzZ4Qp/xylXT5k+LvIXpCWCkP3Nzcwi62nvie5RE5fEwIwEC/nYAzv3yau4B4bBIkF84Z5B/Rv+3urvYlfBXOuLLlxmnoZ0MomaeWuoBggJcPsdG4CsGgO6BuVdn3xVbHpwvDhk67gzeLIca/0h5qBM7C23H15J/fOflT8YgfuNrqW+AusJbm3AEHhUq3bMiX4j7BW20d4LJH5zpuFfXKZZKuu7T9Ujh04YS+ziFGNXegKYrnieOTdN5KVbqoWxWmRqptwfNYzugoeAzCjqV09TFD5NQeKdUw1qxVtyrbDwqi+ZfOu0L7r3EBbxq8XBEZPwAgqg3xmMokDdb1sPvPX4GnKO2y6uEpRaE6ilVusbks4ITW7cMpE6NliapADFaOm/w8L6F4yy5O7YF4DyLfuxvw++Z6Q9mrAD58RnD3sB392RRwHnWK1TXbtetdqcN2DlL6zUEQuez9/YCpMQbyTvb9SHNT/ZW/UIkSSvapTrOQMi6ZCD7mUt8X37HfEPskJexlLiOuflcfAIQxq8cAjGtiNME83hLFY2C77skw5WqQ7Yth5coFKhS2SX3kEDUZo7jmGeNTAkQHUBGfcxQ/fWsh3wbFH09V3X4/p24aLYgehp+T+8VblKCgpxCV43XuO9VHpPEuxLZVeWxuPy9VJ/Bw2B39LPkAFv847ncS0aUrcnPs8iSXu3WzUVAL1qpk/TwMUm7KrYdDGp705GwI8EgRjJnCqOUEsqEliNvblTkMVp6a+V3B7U9+Bhqa5HONHIEeD+vP5xHynzjRkbO3wQm8PEdUaXOpdqlLZKES/j7Dp9rfgHKNMGlJEanS9aNrk9KnSp9AZdWaEGMenTjZNkWYJgedXtg01NVxo+QyEA6TH7mm3201P3hAzsCMffDg3edxJa5KHHmLcWnokkMPiCgwWR3VFnQpEKZCkX9mmf5qPdJaogIzotk9SolLCrNxu8qLJkoYQEJ6a0mwngNZWDSViW2d0etxFnC9dg4bk3BmPyFOr+eEzv454v2ZLHBgnU1m4akTFou/gMr5gkf4ue09eliWE+7uz4jNgYedLhwKLEvA5tvWHSCnjqOAS387tHYV7I+tII2ZNtxo4tVLq9DaqQOd8mvaVTcHjzNKOfVubpTeNQxUNqZ+zwDR6E/8jfdo1D3wqAKi3zS61/T1jnEvjxNwukV9kbp7S+okFQ/L/t3yMBYUEB8pu6at+rN0YpytcaWMDxFKmYLIMXoCmKbkp1Zgye+d8gRTQYgkcVaYzGVXAtp/A8Q/WVmDEddMYk3EzaYNrTqYNGaKM7wTPbeeXXFKNYKRYPRhWGz/FoxGkXfKQjzm61bSjgjjtNiuyO4p2iuHDnQBTf4/vBs1cYfA3ZkH1uFCpiCDMRn6mgoRCgv7jIFhG38m/8gDfqPJN7ze/tqdWcWNp01YMBDe9WCtAnG8oCRmG3I6k7FBB7A7mQNthkMa0uTixC359aPHUsHVrreC9YrNc39YnE56MqzOBzIOjNfqcivygT/4ArpBpo/qK4LW2BGxk+6bH4pBoIF+8fDWegQg+8Ut4oqYDKXujS+/Pml35+ztST4xMVwlVI0GbMSu4R2dec73TD2jrghqfG5A9r7iRswH9uim25uNaJ/5kJJe+iwAnijFOTcgRleQ40G28crNPHC8pjwm0khW5JNZWE8e+tsi+Q0g1QkU9Q2S6eCNHOGgmJd7yfrizfzfZLDFYHQdHyZyDwXNq1H/8AR+RWb62S7GP7Amu0D9L7k0VLM/M3nB+eCX3aKNJimPGZ/sFuPyZ0kho0BHsUcPVinfOBwn72Dw4OE6g5EpbQzs+r+Dqdzhx8xZTsUnsaww2UGfk2p8GioHCVCzBnWO9kCYO93d/OB/0GV3yAtqG72DPmM1neKiwpdDr33EBYMfwxUd03SY0HbB8xUj34z26pEk/V+3EHX99MnN4Ac0r1HdF5PNJHj32RBsFhiWwwicXEWNkAC6Qb3Fuf7MgH0DoFRuvu5b+mUbVBGi4q/NCpZGyd/jtzmEHemYONNuNpF0XPloQ0mR8PM9GAzQ9mId9WJ6j0qYCwVCTGFsc4GxM+21HQjAW/QQs9drGcSVZ8/NvUcVK51OyJofFeqEWn5HpWAM45bHeqL1V3hKrlWDby+fTTuu1OPrG4Xpzaw200zmL//jxmBh6/sRRSkGzNU48v9KWYft+H3hM/9wsNSX9tNV70O+GOjYI2ITSCIev5hCkMSev6pzuTm6BB8LH7rCW8l9FH56TSmdutnxebmCQ/ZWvonT29TqNRvZyN14dKRYZ1d0Q6lChYSa+6PGhPsZXDIOaHVyc5UgCnoaKyLdddsJAOwGM0Uk6gl2fCX/m9XuhbeGTMZs66JGjBs0cdVeYAx9+hQTryrsGfFK4CvzBgLx4k+5XbZd4+B7Wi3vR6etJv/6JR03zQ+3VDeOmU7G7m6cL2SWlI7eqarPszN2pV8gZPjtnOw3PYinHlphFZMqIfP0dlNppyevOkHZZnCgq2r5cPX/mSn9uFNUsDv+206Nrp8060Aos6V/e4hPB8vdrpcvlljt5UTorVLs9Xsl1NwmAjtTT0LzIdWy80DFKLwkGViaM7J1VEeuMOE92lbl9v/Dp4UtCrul3lBzj/i3AfGVpIFIP9HI+iijTQ0IM28bLAD04f76xDM49MV32do7K0OYJLUPMbJpLu7zcGxeI5BzTayBnR7oySMcEDaUa4rLKEDx6TMvLE5rjQCNGs4KYoKuCQe/ngbb9KBEFRH4a0XB5XenNSsaRDCODzGsgw+ESaL2I1e67OXw1geZQ,iv:poRBCLTfTrmzqU4D9jrfRkiVrKX7bxdtLPCqFIg4GRY=,tag:hWozJXghX1TQRsYHbusFFw==,type:str]", + "data": "ENC[AES256_GCM,data:lFtbrmFeLlpEWcUv4PBBokP4PQe3sW1NCGXnP6zGDuk8C7UNlF103cXXBhwsgKjpxeC3vcwCyO8QFsYS5U0Xf8SjnWYwZS78CSs4rRmkB1WCt8ivb3KOLwK1xU/F1GH3czpzpaIwyB3MyGr+dno8qeeeXYPls6mxbyVd3QVnuL4MoF3JE4qBFyiefM2wfWYlTqqvVbo0J4j03uvnlBiVNPUdGTpnKHt5emZybyg0NYz6LnirSlbjc6RFyd6H6wwu/UMXrb6etRgxOYxsB5OC5MRyqDcsoBxt5MQ96PBkbw3RbyO5CKyYOms573DcQBAQmeH04+C3YDAIRQVbAoKejmK+Cp4Tbzo7OXjVTuPZ+koJHGUvmVQa1MXMroi+7DKdEfdKUEgycbImHMqZonUfdL4O9CCfsAjc28vT1+hLhmLHsgGQg1C3gBkuJ/fxk2NYERYLjbQ81zhqq9KOo+1SDB0kMHPMh7/ZkeYoho3R5kRa4lF915NJy9/us5HXTI4cWjxaA54V4MyzbHljOwiLY0oeIXvnu5qmNZ7q3AgaoHzsXovy/KXzy5ElH0HxyWKXGJd5JhYhN737LboIBFr3XnYWdXgN3NHVLWBUDS22CgHykwraRstrFs3Yu1CekLqVwFGGrRpxp3Ioa0gqQuE4k90gp9UFQ7Qt0EEUeKUPqXXmkuGjBTUFtkn77bfjWJwmpMEW9cYhJVqfb0KnJiqJz6D1rbGj3ilcWy3bi/SP1qhv/KAwSErwmFyUccoSLrj+8t2fts5qlisTVbDoHackGh0QzaT1y5ilT6kw+TUIEircrqqM/NLzyFF9ZPMzXADYt3NMZHvrXdmn1lUfQVbWZPtmq+dGl/PMM3g3qc7gLW0Vi45J4uDF3OFk2sZBuCMH1gUshvZ4zXvuwETp0vHUbuQn+SmM9F8PhaMXpxn7zGtYFoLS/mQTPr9gyqycczLlNPmK796oiYjSn8ZfFhEvbcS+PAdF82Rv9ukxeRK2QMC6r6yjkesQJ8BH/2EcZPVA/hkbOl9Z5ZYmmZSEN29WJvxMOd1HHwCmg1v1UXFoZp9zDOgMasDlk3tnk69o3JDKu2MM2AfH2KLJYr00fuW4BCd/MZgQfTE60I4+SVfpGsgWtbcvmz2qA9uykIVWA6c2l3i/UXfLEap0XL7+Wrseu5Un7LpFeUxnJYjeol+UsnMzLHaXE3O7ABpWqSAUc/VegTz1JT1nbr8w+Zn9h0lKt0vflTrKBj/RtESTsD9MFGjVduaob5tRd8MBa8po1MOoZRIo9deaJTGYLYZ8yL/J4usYP0xf3nTdc8BErK5G3D2Jzm7sSdpDWTjdAjmTk5fvjXh+vWTReXYxRlV+G/h26OhZ/gancOezeRTeEeNxkz4vg87YbesSq95Z3t7eUg0uXHIBoEKFuBhBgqPh1szczmlOkQKncAAh5YvdWq1K9kkDV3lzvwbs4hSP8wTl9E7UcRdlqhlTtL6uukJ5Vn+4pvjZ2y3ca849jKoxU5I1ghYeoi+S7MWoaOG2+15lslgKOBW2k6moVtFmzT/NKxKgK5a2P96Yunab7x+LjXjMU1K53CJ3lwhLxn0U7pFIVebOHAnO8VejJg/rRjfykkDmW0SWpZAP7snTnNcbuBsMN5rWSKvxXQClgYskU5dUIxmrk7GDfZoL+5B+F/XjS+WQJEnYO0veZzx7y3O5En8QG6KsPvf9lvgw2jUGKAOb7M0i+S/o0JKBjeNT3lf4npRXzsUEgH3zpUEYMOSt44rVOFRwzw/j2R9QwJG7g4I5RWsCz/7plcbUwLbaqiSyhzF6PQge5UTLQahpEbnq6B8mjX476b9SHRTMcOy1t6WbqgoX3Fc3QU8w5ob9/kG6V6MYOYEmT6mrhvpU6G8FgTlAnqKYv0ubyXpj6udhUjGmb77qXFnYYbz9m20GoF7lRkEmTNg8A2LCs82WBRGUkylmlI+zGg3oRkN/h01I9iLlNPTvPyIVbMgdHlY3FT+wAHF0A0jrKoMszF8L7MK6awMbETQo/jPBxUUBmmAE5cLlUH31pCdjMvNNx0VNbwRSc/ilOjtLmq1NYl8krmVKksAQZnWNDtcJ2aaSaoiCvD2w/p93zHsnzAheTKo8VLwK0rYNzlEahgaIvJ6ZCI3Q7o6Z7Di7KyI6bcp0/IpYFOFDsQzAN0tesbCdQ1mfIgNmE7FqExPrdE16VCCD1ftyaC2sLv1LhmK3xV6nUb3b5Tj4pH9dOllC2+rkYTe6SX9PWmU31qJ+Zltlprz/C15Iu9I+VvotRE6JcsbIBaamlhy0gDsyoftzqbtwAcokhSV5jEGyUw1gNiyy0ICe3VsN+B5TNQ9H2nS1xZ2IAC0Q+GA0v1MUsaC1lpn/JJe+GfzQcnQNHHhKu8STtqB+gV25gd0Wtw63M/f3J8qx3XhU3Y9U7M4fkxHiMx2pkQkWnySMG9EMgLj+5F9WTB3TuHe8SQnJiKeNNnbthlc/5sDQAxPI+G2OWC8Vl1WxUF4T9DD0R1NxIN26hh/AkI9sigr7dZDuFOKEpaoh1RqtuwkOJDR1070YqKckZXJL0SUPjO2yGeS+h80ue78rEMFJQIw6zOoRCC7lPjqklXZq8lPHG0yLxtBFzGaSBK+xajxYqlx5uuy3riRpyJ/rCgInJNKk0sLrVmyM860PpH8i9mMHgDZQT81e3E5bgKt/lqtsbEW9SEUE3ZvoBjgkZU7w0HHnqWdEgHm1oWh7Vpgo7pS0dbvXlXkWWhjh1Hjbc97p8bK49aNgZQXbD6tdP6e9s2y88hZanlWwqHc/H2oZFC0uRtpN/JKzPZqfObj1QSkELzp5bTNJJFv6rKjDdCbIFC9YENxFH92H8LzSugtThA8bWF6IahK74QDc7C0hnQ1AMPpHWKXpLJQ8+JAcd+tcih0hGPuJWikHC0sDSW/y/JvyR0WDkw7VlTDJDIJoBtfw70sFfxyWLumHFZ/DlApCc7eqBGWnX86MxV3nfNP3aMIh42T5J4MaZP2ZJZv3FE0do4/Fi9ia9RB4RPWTiUmcgbBckd9qQE5Hr7GTSk3L8IA42PpYXt5SHtkq8IEOVBrTw5IQormbIa2Vr6AjURAfTpfeQEKfMBEoJlAyDVZWCNYXVNi0kmL3eDomuHLgMIY/bjoGm5xZceM76YbTujKdZaazsILUGokRHvVhnGwrzNOolN0jw7JzFST/gzr6yJQNLK806aYAg36roP5CL30C4VNHmyYd1ExfY16YHmmYxVEf1JFVjPvdGLoDu6TlhabvOxMus4dwocaKIaIZeeSbKl4zmU2MwtVJGWJalueW/2Mowj56Ph7x4mJL1jeO2umGPeZ3iAO9dCOz/zcakrr0uONeLvKKts8NFIZFt6a83GvHp2kuw/qNHVNJLzU42AQdbfakJaK9pw5xUL8BKTlsFiWGT47xPNJkpD0+Uorx5ZmoVLCBjmEsVXv84p1t/oaCMvCzXMXz078zbcV8zn3P0z4SMpnQvxuWSMHFtQdeaZkdqg8/C0lMMzpsT7Ig8oalvWpb6rxIit5B2s8z5x6alFXj5Z94C+W0othKdaIO2pZKj5U/LQleQQTEay5o1dxo6gWquyLaVtrPmBThogE1XD+Tr8mSV0B5fEI7qH9IicWAFi6d3uxcvRly0Grvli3Abe99l+05wqFU8hWUMYi9JinrmuAtDToL8K8c/sGlKBUWhn3wzV7RNqgJbSYSHQJKHZLaJkmEhE+MQvpMg118tnfLhCdxBMd4YnqpWHSgamWunGe5AFyzlzAzBUEAnqQCvXedj0bKAnytfR9v1eE3m1i17rCXE/8HBycnp+g/Evyvji04l1SeI4+F8HULg7O9o9VaoNud6KQjeEYn4d44GTUrPNWUXrP/uAvq887CWt+NH37cMo3oJvGL8BqhexoTG+aHbAmwhjM8bTZ6PBVClftpr0f6Ntql/L1zhS235elM4yVRXLAiLX86VlLP0cqAYxoqV2EcfcMzHQI+IgVC6uDWePuidIjrIiwMldRC5ats6uPPjGqSI9BMAVXAEBdPV/DAYhdNtK3iBZnATn4sTng3XXOhqoAB1X1/s1CrRxRFB8s+N9ihXeIVH5IjLl9K2Qd9413sgnA1cF3WW/vm1R9f3R5u/xvnCv3jl6rSiYovJ9lMJ/SAxQ/lKCNBxPobw3Fj7Xw4EDe76Kd+7nzYLW0rq4fc2fxXy0tx5RK2Rznn1IoVQqaa9IZczFbiKOvNk9bnq6vnVO6uklMX63hhr0s6Qvuyg0ZqdpBT99ZVU/NvMv/NuTqg3wr4pmabvJp7cdW24g9/xJjUW2/ZpZIWmsBdz8Owtn3uO1WyAr5+5U2f2MCI5WjHEcbRhJ1NBVFfNgsPaUIy2uqnBfcdq72b4zXvdR4huk3nZvYgdnjKDjZLOEe0peME3f3jajqs0lJKWm61eAjUZ/72VQ8HPf2MzXpwksEUNzwyNwgqyFxO+LUt6MYKRNOmCAqnx2Q+NYaz4DmEm4G6bohTVUc/xA/fJZgH3m5cisKhv604AOpZozxxH0PjpsiT9v2Kp0527QlfoALFa9nw+KvZHThgx7acklRhbR2zmou/GlCl0nE1Rn3h0F3boS0FHkpA2wddL4Unx3dZ8qZcgE7NBuc3PU2dJtdvksQqorOIm6I8wBYg4CX3Mat8HIkNORAB6Twyi3647UPSmVK82hq2/WWY7X1PRgs0EPQWAfzEGJzSSIjczls4ax1ZhGSD3GlFPdC1M1xvIb4aqv1fPWRGDK71XtqBucoSa+8/RqN+9fl0VDqTXBR/ZC8tg0VC8f+Si6XRNLoILfDiBsAi9aaK7zvI6rb9tD27eLnqYY1RYEZmRMRSEba18TvaSCEsZo5ctUU3w9b5PwBnANRaXcU4B1mHjPC07cBuLZP0fbVc+DAjYGoZep6SVNNyL0dLmrVDtFIXKEJK6SqeO2bMztMLARYYSM2NokLhGDhHF7LUMDh3sObBqczJ1F+JUGC0pLsVCFIfqgns6vJWv7lYkBnxmUfMpwfIuytgsdMWpLxQ9HWiIbm+CMTawu7igxu1/7lPfehdFkcGz6HktUQPqH/br2cJU9JP9Tl16t4y3Ev+FOAgGmZZ6KZNJ7jDQd4jGgzZMjxgPAstE0HmhC7/WNcda8wb5in4hnty4rkuCeWfxUcR6LCxYJjm5UslthkoOXPFPKKpm23tyjBsOKQuZECg8Mh5xw7UzMjir/ekqT75gvI8fu+hJLlLwMZH0Ey01ii810PSjD9tm8fge9f9ffRMuRbYXApVsxMfs1XTtSr1ofm8XP9Gs0GbedEbTdioka5oq5KJc+yMzbmaC3qW7uCdZDUT/X4UZANP2eApaGydn4pXlrcWMEO+Q+4+tvCMQpqE/KPcfbeOEMpzyf4gjlEGsO1cAWjB0bcRKXhvBynUhT7asPiDR95QqwNrqukAucQMp8Pa7WO0muH3cIvpbGY0Q8rATrBBCS6ZtOMXSccFq+Xg++2vD7h/jPkQL5fSwnzecReXmBwODBD6gMko6DLr5GpTI6nWoO11B5HXR0g0HxdUjwO+ZPMjYD0j/2E1Vf3Qf7X41dh596LHoK7bNkrh3mmNm29pEvEfXyif8vwQfE+BCG3ebnvyZuHEUQvD0gKA+4NLfYTnAOKJBPXjabtl2zWlxRSB10eGaefKXvCaiIfI0e3nTlqw5C/MW3m6bf9k+jyjxPgtgY90guk8tR0NM6PbvHlWAw+6F8AqDPNnDjuPUY1/fSUzw4+dpoX3YXLVEUMpxXWj4rghnaEwJw1QdOOC7luSi19KswoIV9brXhVW82yB5XZcGMeifA9y/BjdePl5B/vsHXc4GVSUJtcXKZ6XiG1hclKptF3dzrMA0ZT7wY3W86BxDXuiNEEW+Cue0jaZ6Bn5QF5ydk6dwUDa7mAyw288qeLfhO8unUeHriqPQSth3oSofJB7u/KfC+wn0EZZxzCl2KOd4gSLRoooK0QJn+VHE07ePWnuPMsp+ludCorel8xn8VkZMOD9eCQqp19C1R9/gxXN2XM5oO71m0Rv4d4AFiISmwAzMkwanbuk7+W5ugUgwRX6ISzR1wELc12i6B7QesK5d8Aw8kL9x6AcKvB5QjLOUjOjU12bUd2t7BLcXlRnR8tf3QygvG2Lo+ikHhk+3wsUt9a7/3SpDYlI5b29/1W+zauznyTts8cwc8wZgFNxaQS0fi9oExLcHDpQrX1FjJCmie0lfboBrJV4Xfbx18cE8sKGYpZ4FJtIJi5LMbpxGS8/RwNYAy+LACRG2MKhxZjeXoKIxdwshBmDnuz3oFT0k6lJJf/A7Na68n95uzAj5TvK6Wo8GPI0OCudfMiVCsTjN6HwurWqUB1MKoznLE0G75hRaIoyJbS568L4+YMEEDhOUKPxe5upf3AwPiodswhzlthZcUS0b6pYLHe7uKdePEcjgRGiufrqAduMMWdXqUaH3iO9UZbXohXmdrIa0hSNuyMbX1mqFqkTYQxTBCCgGR+Oz4WWTA0jIpNIRhYiXztMFUitOseNDLc06SFtVuDW4x+nyHXWW5JSsMbjXJrAyp2GOvVXRGAQ6/JWgL51kO3hEye9UFepB2pm9XDXX4qHwLlvF0gk5gYELUjNp+61Zh2phg07yl6L274+GUNcT0udnLf5TUIaDK6KCwf5PAhxKjRjx/DIrTfKXW3JarQzH5BYRgttMs/xGCWxAcc06gnRZpz3CqHG+Xq2PHzsaakWdjSdjAKOnOVQ7nnuZbcAyXKqphBEt0jCndgDVTuBpKgUzCqU+EXErnTUdwJM2w9JS8d4vEFRBpvtTTti7pFJ7pOJe7zSYpPqH+7VqRK0n4YojVLMKjRynbSz/XY52RjFRjt2O/drF2A+Oz+5lBqkQcAQWdgqBg8FgVIFlR79usL3IOeRqXqxm4gFZJNXNUgC3B9308r4mBbcp1QOdYXi43bi4RASOuzHrdNBpq19F68r7HE/I7dXlIlDw5Q3CT2STw7ehLUkHo/D10CDF2+fvE5+UfBi/dxIw8j/Sa7bQLVs3zbPcen0JZyBg2FaUypgMqcQnoujLZM/UWNqEoqSj5JKpRl2BjIwf9nG1H+Ik99EuubjqZGfWn8M45Vn1YQ7glUYizluvMxQ9RYfhB44WSWnTftcnhUf4dx7XyK8VevedpZjSCwtyWS7O+XCEjKQjGwfGlA7w5FdIsu4PwgqYh7/IdlRdcLYXDpzBCC2mpC6tpt8rD/3/WgAtD55IyVeyeyPyMTXFeGJVdMxWujR0G97Ei9qUdLydazKLDajlYgZXrwxbNaWdZMwksjR6lWkMSGsFXuCHpJKnBI0uue4Qpg83np3QuWfMTOExB8Kvgxfhd+EHLxwjh1Gt4UrtNheZPMB87hihQgSVYjhvtToAjzahEsyZheShGnKv5kvGPcqcukeb8IeauXfO2W6JfPtuvOk/mB7mgN1mlFVqXHvX/JjgEco15X5IRZCQIQJYNoA/hEKF0yNM94skrIB9zCTHtXj0OXq8orYitFZ84R61w2CI5UUEzq5AwtvBO6UegG9UAgihwRmk6nByBxpZqJbLGOKitUlE/kQOZBVw/16IDAKz2RDBooTAnXS/SW1Ah7HfVzHCSjM4nOfwpFm6uDwV9UWtQb7DGXi/3pREAKFuHQX3AUdJdwdR3GeK9v650ql9+uwELvZeRtawI4kvqiisc+9v6HgF0qE7jbO76dgUFXRN4bGNktwFX42xllwsUq6JvA08sJn+oE+URPs4cASuElvzLkUQ+VAr39MykC451r4LL1E+YakIdAa9DFAunVmxkow3amnecCZyuR6O2sKQaEsmykf6o3LqiPjFXF+ePjUszBrjjtXsvF1eRPDbGjDMujEWoG2uhmY7KLWDgei/+yhXaKfUieS8FhBnXPIcOomqkkgVvEYDasFz6torS3bO5Yz1dtSE3RQ5nsC3RDEPGVe9JlAWMe8Fx1EEAXFMOfnPrhetJ/7OJUm6zXEPzZYEpqH0hRf6YpVw159t5Vtd4AVWQmBovYWCCuH1Z0nHcirN5Ny48sQd7XhOX9kvA3/+eqrwRr8rHE4ZC5ykAzifRDLouujzlY1qPuejKquCXbK7D/q/39uE4fjVMFRArmk3FaLLIEYPwDWmIIHZ45/VD8wIUOCRfHhHFMa70H4Qq+bOM+X3yb3o77MFcNRqk2gWhDi3ToHUoV3rvWwf69/QtTX8ynBKbVQU3vNuOjy4DaxAXPPCGvW3tratsJy2A4OhubqlaP+Il3M4iQdunv9zinPiPtQxOfyer43LH8LBsVV+ALRX0kbko5f+fV78W+aVzQZOIR0IFteCXMWZCwqYz/O5d+3Ong8GIU3PGc7fpx7al6cFpuRIQQCFtV6nNkgyf6zbt6FDPbX23C3UaTJzd9S02aL6nOUjtu4P+poEQu9TJOmOmB6Fnj3a346MBzxZvi5Lneb+pssHXNHqwmoymUZv4RBdO977ZAhugGVYUq6w/u2x7yaYjkjjaQyU0n99Ayf0rDBMPbvj/WhlIoo1FG9+4tnjnbhiOjvXG+fKzgE6FfqPPxs7L4ICLoiMch6yFW8n++RuiftIUiz0YuTyNnJzIUl39qzK5T5q6x03SJlwGZKn45j8xywnrwCkp0A75s/dcJTUE+Eb0Bnll5LBNFLBSWeoBw6r/qogPBmWuybYql3dav/N8GIYGSpt9atJApnxkyfJW5qr/23LWqSChpQMbsmXf9G6JPGI0CnnfrhkCjCVbXeJsyp,iv:4ZbxVxZHzxn2eSqP8h8eV53IV9J0avHBg1R6/TgAmtY=,tag:SqndgxyiwbD89LSrZBMnuQ==,type:str]", "sops": { "age": [ { @@ -51,8 +51,8 @@ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGNi96SEpTVU1wa29DeDdw\nYm1weGNFVU1kdEdZMmRZTEJCeWliTm5mVGhrCkZhbmZzTGZoVHRmVTE0ZFcvMno2\nUkhVNFU5RGdOS1dEN3pZeGtBbHlhTW8KLS0tIEcwQk1LN1dydEpjSjlna3BwYlBZ\nZjNwdU5leHhJWTZqRElGZERVbFg0SkkKR5LI86bCwuWnzzF/+sYgnsEdHSCMYwr7\nhEaK43fFnD1zUKve/D8NmscJO0VAZIt7duzdeMLv7h+a2ebSXAJ7fA==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2025-12-22T18:01:00Z", - "mac": "ENC[AES256_GCM,data:P8D/ArbLH/BrIL06OSmOjQ1iO2hZTnrryjp9YgZIExJ3rBWE8pdPpCiJ5BXYk2CEe+ZPpbF6Bh7YzUoUVoZTRjBSWWKd51MNoVVn39qIqx6+GK5d7R2/a+1+LeJYlG4QGCjPStIg1QBmgnzKP+rAsu4bOfoVXZL9KyHOGDELfAk=,iv:8fK/UDHfEaMOte+qWzgp8BHS2Q+lkYewFkvfCRnGh34=,tag:PuHdUK3F3UXFuNWPDXeO1g==,type:str]", + "lastmodified": "2025-12-24T13:04:51Z", + "mac": "ENC[AES256_GCM,data:ktWaSwQTDR1bu9ztdt/KzvItcPxxCzhjAUqBKipe0SUZjxqmjGpQDBD1+p6dH6vSQEdcHDk9vaSMUZOMbOQYrQiEEqL+DLhAtRnc7zf8Mcd03uWjXm/4/J1CETKXlAIY/qsh6aHJoCpaSPOBnR0/AnP8xw7ae6c5j5wVYRQMmVY=,iv:Db56gtwzmAZuSFMAeyMTJLT3LMOTvDtc0CCCtCJzuRM=,tag:RtnhRlavbUxaSDMKp44VgA==,type:str]", "pgp": [ { "created_at": "2025-12-15T21:53:39Z",