diff --git a/hosts/vps-monitor/default.nix b/hosts/vps-monitor/default.nix index 20b3433..34083b6 100644 --- a/hosts/vps-monitor/default.nix +++ b/hosts/vps-monitor/default.nix @@ -8,6 +8,11 @@ system.stateVersion = "25.11"; + meta = { + domains.validate = true; + ports.validate = true; + }; + custom = { persistence.enable = true; diff --git a/hosts/vps-private/default.nix b/hosts/vps-private/default.nix index 134b29e..2489fb9 100644 --- a/hosts/vps-private/default.nix +++ b/hosts/vps-private/default.nix @@ -8,6 +8,11 @@ system.stateVersion = "25.11"; + meta = { + domains.validate = true; + ports.validate = true; + }; + custom = let privateDomain = config.custom.networking.overlay.domain; diff --git a/hosts/vps-public/default.nix b/hosts/vps-public/default.nix index f80104f..bbbfa13 100644 --- a/hosts/vps-public/default.nix +++ b/hosts/vps-public/default.nix @@ -8,6 +8,11 @@ system.stateVersion = "25.11"; + meta = { + domains.validate = true; + ports.validate = true; + }; + custom = let sproutedDomain = "sprouted.cloud"; diff --git a/modules/home/de/hyprland/settings.nix b/modules/home/de/hyprland/settings.nix index 659101a..3a4b2c4 100644 --- a/modules/home/de/hyprland/settings.nix +++ b/modules/home/de/hyprland/settings.nix @@ -83,16 +83,16 @@ ]; windowrule = [ # No borders on floating window when it's the only window - "bordersize 0, floating:1, onworkspace:w[1]" + "border_size 0, match:float 1, match:workspace w[1]" # No borders on tiled window when it's the only tiled window - "bordersize 0, floating:0, onworkspace:w[t1]" - # No rounding on tiled window when it's the only tiled window on a normal workspace - "rounding 0, floating:0, onworkspace:w[t1]s[false]" + "border_size 0, match:float 0, match:workspace w[t1]" + # No rounding on tiled window when it's the only tiled windonoinitialfocus, class:(jetbrains-)(.*), floating:1w on a normal workspace + "rounding 0, match:float 0, match:workspace w[t1]s[false]" - "idleinhibit fullscreen, class:.*" + "idle_inhibit fullscreen, match:class .*" # Fix flickering in JetBrains-IDEs - "noinitialfocus, class:(jetbrains-)(.*), floating:1" + "no_initial_focus on, match:class (jetbrains-)(.*), match:float 1" ]; }; }; diff --git a/modules/system/meta/domains.nix b/modules/system/meta/domains.nix new file mode 100644 index 0000000..a2001e3 --- /dev/null +++ b/modules/system/meta/domains.nix @@ -0,0 +1,62 @@ +{ + config, + self, + lib, + ... +}: +let + cfg = config.meta.domains; +in +{ + options.meta.domains = { + local = lib.mkOption { + type = lib.types.listOf lib.types.nonEmptyStr; + default = [ ]; + }; + global = lib.mkOption { + type = lib.types.listOf lib.types.nonEmptyStr; + default = + self.nixosConfigurations + |> lib.attrValues + |> lib.map (host: host.config.meta.domains.local) + |> lib.concatLists; + readOnly = true; + }; + validate = lib.mkEnableOption ""; + }; + + config = lib.mkIf cfg.validate { + assertions = + let + duplicateDomains = + self.nixosConfigurations + |> lib.attrValues + |> lib.map (host: host.options.meta.domains.local.definitionsWithLocations) + |> lib.concatLists + |> lib.concatMap ( + { file, value }: + value + |> lib.map (domain: { + file = self.lib.relativePath file; + inherit domain; + }) + ) + |> builtins.groupBy (entry: toString entry.domain) + |> lib.mapAttrs (_: values: values |> lib.map (value: value.file)) + |> lib.filterAttrs (_: files: lib.length files > 1); + + errorMessage = + duplicateDomains + |> lib.mapAttrsToList ( + domain: files: + "Duplicate domain `${domain}` found in:\n" + + (files |> lib.map (file: " - ${file}") |> lib.concatLines) + ) + |> lib.concatStrings; + in + lib.singleton { + assertion = duplicateDomains == { }; + message = errorMessage; + }; + }; +} diff --git a/modules/system/meta/ports.nix b/modules/system/meta/ports.nix new file mode 100644 index 0000000..77ff548 --- /dev/null +++ b/modules/system/meta/ports.nix @@ -0,0 +1,65 @@ +{ + config, + options, + self, + lib, + ... +}: +let + cfg = config.meta.ports; +in +{ + options.meta.ports = { + tcp = lib.mkOption { + type = lib.types.listOf lib.types.port; + default = [ ]; + }; + udp = lib.mkOption { + type = lib.types.listOf lib.types.port; + default = [ ]; + }; + validate = lib.mkEnableOption ""; + }; + + config = { + assertions = + let + findDuplicatePorts = + protocol: + options.meta.ports.${protocol}.definitionsWithLocations + |> lib.concatMap ( + { file, value }: + value + |> lib.map (port: { + file = self.lib.relativePath file; + inherit port; + }) + ) + |> builtins.groupBy (entry: toString entry.port) + |> lib.mapAttrs (_: values: values |> lib.map (value: value.file)) + |> lib.filterAttrs (_: files: lib.length files > 1); + + mkErrorMessage = + duplicatePorts: + duplicatePorts + |> lib.mapAttrsToList ( + port: files: + "Duplicate port `${port}` found in:\n" + (files |> lib.map (file: " - ${file}") |> lib.concatLines) + ) + |> lib.concatStrings; + + duplicateTcpPorts = findDuplicatePorts "tcp"; + duplicateUdpPorts = findDuplicatePorts "udp"; + in + lib.mkIf cfg.validate [ + { + assertion = duplicateTcpPorts == { }; + message = mkErrorMessage duplicateTcpPorts; + } + { + assertion = duplicateUdpPorts == { }; + message = mkErrorMessage duplicateUdpPorts; + } + ]; + }; +} diff --git a/modules/system/services/caddy.nix b/modules/system/services/caddy.nix index cd863fb..2323d11 100644 --- a/modules/system/services/caddy.nix +++ b/modules/system/services/caddy.nix @@ -95,7 +95,7 @@ in message = "Each caddy virtual host must set exactly one of `port` or `files`"; }; - networking.firewall.allowedTCPPorts = lib.mkIf publicHostsExist webPorts; + meta.ports.tcp = [ cfg.metricsPort ]; services.caddy = { enable = true; @@ -111,6 +111,11 @@ in custom.persistence.directories = [ "/var/lib/caddy" ]; } + (lib.mkIf publicHostsExist { + meta.ports.tcp = webPorts; + networking.firewall.allowedTCPPorts = webPorts; + }) + (lib.mkIf privateHostsExist { sops.secrets = { "porkbun/api-key".owner = config.users.users.acme.name; diff --git a/modules/system/services/crowdsec/default.nix b/modules/system/services/crowdsec/default.nix index ffc45ed..7e00097 100644 --- a/modules/system/services/crowdsec/default.nix +++ b/modules/system/services/crowdsec/default.nix @@ -38,6 +38,11 @@ in }; config = lib.mkIf cfg.enable { + meta.ports.tcp = [ + cfg.apiPort + cfg.prometheusPort + ]; + sops.secrets."crowdsec/enrollment-key" = { owner = user; restartUnits = [ "crowdsec.service" ]; diff --git a/modules/system/services/dns.nix b/modules/system/services/dns.nix index cb73b2e..754c84a 100644 --- a/modules/system/services/dns.nix +++ b/modules/system/services/dns.nix @@ -12,6 +12,11 @@ in options.custom.services.dns.enable = lib.mkEnableOption ""; config = lib.mkIf cfg.enable { + # meta.ports = { + # tcp = [ 53 ]; + # udp = [ 53 ]; + # }; + services = { unbound = { enable = true; @@ -34,9 +39,7 @@ in |> lib.attrValues |> lib.concatMap ( host: - host.config.custom.services.caddy.virtualHosts - |> lib.attrValues - |> lib.map (vHost: vHost.domain) + host.config.meta.domains.local |> lib.filter (domain: self.lib.isPrivateDomain domain) |> lib.map (domain: "\"${domain}. A ${host.config.custom.networking.overlay.address}\"") ); diff --git a/modules/system/services/nebula/default.nix b/modules/system/services/nebula/default.nix index 651bf9e..e362ab3 100644 --- a/modules/system/services/nebula/default.nix +++ b/modules/system/services/nebula/default.nix @@ -38,6 +38,8 @@ in systemdUnit = "nebula@mesh.service"; }; + meta.ports.udp = lib.optional netCfg.underlay.isPublic publicPort; + sops.secrets."nebula/host-key" = { owner = config.users.users.nebula-mesh.name; restartUnits = [ "nebula@mesh.service" ]; diff --git a/modules/system/services/resolved.nix b/modules/system/services/resolved.nix index 9782bde..3002197 100644 --- a/modules/system/services/resolved.nix +++ b/modules/system/services/resolved.nix @@ -1,10 +1,22 @@ { config, lib, ... }: +let + ports = [ + 53 + 5353 + 5355 + ]; +in { options.custom.services.resolved.enable = lib.mkEnableOption "" // { default = config.systemd.network.enable; }; config = lib.mkIf config.custom.services.resolved.enable { + meta.ports = { + tcp = ports; + udp = ports; + }; + services.resolved = { enable = true; dnssec = "allow-downgrade"; diff --git a/modules/system/services/sshd.nix b/modules/system/services/sshd.nix index a4621f4..2aea658 100644 --- a/modules/system/services/sshd.nix +++ b/modules/system/services/sshd.nix @@ -12,6 +12,8 @@ in options.custom.services.sshd.enable = lib.mkEnableOption ""; config = lib.mkIf cfg.enable { + meta.ports.tcp = [ 22 ]; + services = { openssh = { enable = true; diff --git a/modules/system/services/syncthing.nix b/modules/system/services/syncthing.nix index b2bf8bc..1cf3966 100644 --- a/modules/system/services/syncthing.nix +++ b/modules/system/services/syncthing.nix @@ -61,6 +61,17 @@ in } ]; + meta = { + domains.local = lib.mkIf (cfg.gui.domain != null) [ cfg.gui.domain ]; + ports = { + tcp = [ + cfg.syncPort + cfg.gui.port + ]; + udp = [ cfg.syncPort ]; + }; + }; + sops.secrets = lib.mkIf useSopsSecrets { "syncthing/cert" = { owner = config.services.syncthing.user; diff --git a/modules/system/web-services/actualbudget.nix b/modules/system/web-services/actualbudget.nix index fc80e34..126726f 100644 --- a/modules/system/web-services/actualbudget.nix +++ b/modules/system/web-services/actualbudget.nix @@ -19,6 +19,11 @@ in }; config = lib.mkIf cfg.enable { + meta = { + domains.local = [ cfg.domain ]; + ports.tcp = [ cfg.port ]; + }; + users = { users.actual = { isSystemUser = true; diff --git a/modules/system/web-services/alloy.nix b/modules/system/web-services/alloy.nix index 9ab821c..3fea35b 100644 --- a/modules/system/web-services/alloy.nix +++ b/modules/system/web-services/alloy.nix @@ -63,6 +63,11 @@ in in metricsAssertions ++ logsAssertions; + meta = { + domains.local = [ cfg.domain ]; + ports.tcp = [ cfg.port ]; + }; + services.alloy = { enable = true; extraFlags = [ diff --git a/modules/system/web-services/filebrowser.nix b/modules/system/web-services/filebrowser.nix index c7d1821..7f239f8 100644 --- a/modules/system/web-services/filebrowser.nix +++ b/modules/system/web-services/filebrowser.nix @@ -29,6 +29,11 @@ in message = self.lib.mkUnprotectedMessage "Filebrowser"; }; + meta = { + domains.local = [ cfg.domain ]; + ports.tcp = [ cfg.port ]; + }; + services.filebrowser = { enable = true; settings = { diff --git a/modules/system/web-services/forgejo/default.nix b/modules/system/web-services/forgejo/default.nix index f657248..6888e07 100644 --- a/modules/system/web-services/forgejo/default.nix +++ b/modules/system/web-services/forgejo/default.nix @@ -17,6 +17,11 @@ in }; config = lib.mkIf cfg.enable { + meta = { + domains.local = [ cfg.domain ]; + ports.tcp = [ cfg.port ]; + }; + users = { users.git = { isSystemUser = true; diff --git a/modules/system/web-services/forgejo/ssh.nix b/modules/system/web-services/forgejo/ssh.nix index 885ab98..b54c908 100644 --- a/modules/system/web-services/forgejo/ssh.nix +++ b/modules/system/web-services/forgejo/ssh.nix @@ -12,6 +12,8 @@ in }; config = lib.mkIf cfg.enable { + meta.ports.tcp = [ cfg.port ]; + services.forgejo.settings.server.SSH_PORT = cfg.port; services.openssh = { diff --git a/modules/system/web-services/freshrss.nix b/modules/system/web-services/freshrss.nix index acae4e2..fe3ea6b 100644 --- a/modules/system/web-services/freshrss.nix +++ b/modules/system/web-services/freshrss.nix @@ -29,6 +29,11 @@ in message = self.lib.mkUnprotectedMessage "FreshRSS"; }; + meta = { + domains.local = [ cfg.domain ]; + ports.tcp = [ cfg.port ]; + }; + services.freshrss = { enable = true; baseUrl = "https://${cfg.domain}"; diff --git a/modules/system/web-services/gatus.nix b/modules/system/web-services/gatus.nix index d2e44df..bb6e87b 100644 --- a/modules/system/web-services/gatus.nix +++ b/modules/system/web-services/gatus.nix @@ -70,6 +70,11 @@ in }; config = lib.mkIf cfg.enable { + meta = { + domains.local = [ cfg.domain ]; + ports.tcp = [ cfg.port ]; + }; + sops = { secrets."healthchecks/ping-key" = { }; templates."gatus.env" = { @@ -171,10 +176,7 @@ in let defaultEndpoints = self.nixosConfigurations - |> lib.mapAttrs ( - _: host: - host.config.custom.services.caddy.virtualHosts |> lib.attrValues |> lib.map (vHost: vHost.domain) - ) + |> lib.mapAttrs (_: host: host.config.meta.domains.local) |> lib.concatMapAttrs ( hostName: domains: domains diff --git a/modules/system/web-services/grafana.nix b/modules/system/web-services/grafana.nix index 307833b..c5ed60e 100644 --- a/modules/system/web-services/grafana.nix +++ b/modules/system/web-services/grafana.nix @@ -64,6 +64,11 @@ in }; config = lib.mkIf cfg.enable { + meta = { + domains.local = [ cfg.domain ]; + ports.tcp = [ cfg.port ]; + }; + sops.secrets."grafana/admin-password" = { owner = config.users.users.grafana.name; restartUnits = [ "grafana.service" ]; diff --git a/modules/system/web-services/it-tools.nix b/modules/system/web-services/it-tools.nix index 00b2a02..728bea7 100644 --- a/modules/system/web-services/it-tools.nix +++ b/modules/system/web-services/it-tools.nix @@ -17,6 +17,8 @@ in }; config = lib.mkIf cfg.enable { + meta.domains.local = [ cfg.domain ]; + custom.services.caddy.virtualHosts.${cfg.domain}.files = "${pkgs.it-tools}/lib"; }; } diff --git a/modules/system/web-services/memos.nix b/modules/system/web-services/memos.nix index 9b5e407..8704e67 100644 --- a/modules/system/web-services/memos.nix +++ b/modules/system/web-services/memos.nix @@ -24,6 +24,11 @@ in }; config = lib.mkIf cfg.enable { + meta = { + domains.local = [ cfg.domain ]; + ports.tcp = [ cfg.port ]; + }; + services.memos = { enable = true; settings = options.services.memos.settings.default // { diff --git a/modules/system/web-services/ntfy.nix b/modules/system/web-services/ntfy.nix index 9cc9dd1..a8f6eec 100644 --- a/modules/system/web-services/ntfy.nix +++ b/modules/system/web-services/ntfy.nix @@ -16,6 +16,11 @@ in }; config = lib.mkIf cfg.enable { + meta = { + domains.local = [ cfg.domain ]; + ports.tcp = [ cfg.port ]; + }; + services.ntfy-sh = { enable = true; settings = lib.mkForce { diff --git a/modules/system/web-services/outline.nix b/modules/system/web-services/outline.nix index fb662c6..3f7179a 100644 --- a/modules/system/web-services/outline.nix +++ b/modules/system/web-services/outline.nix @@ -22,6 +22,11 @@ in }; config = lib.mkIf cfg.enable { + meta = { + domains.local = [ cfg.domain ]; + ports.tcp = [ cfg.port ]; + }; + sops.secrets."outline/gitlab-auth-secret" = { owner = config.users.users.outline.name; restartUnits = [ "outline.service" ]; diff --git a/modules/system/web-services/personal-blog.nix b/modules/system/web-services/personal-blog.nix index 1930a2d..a4ff641 100644 --- a/modules/system/web-services/personal-blog.nix +++ b/modules/system/web-services/personal-blog.nix @@ -19,6 +19,8 @@ in }; config = lib.mkIf cfg.enable { + meta.domains.local = [ cfg.domain ]; + systemd.services.generate-blog = { serviceConfig.Type = "oneshot"; wantedBy = [ "multi-user.target" ]; diff --git a/modules/system/web-services/privatebin.nix b/modules/system/web-services/privatebin.nix index 7285fa9..3751b52 100644 --- a/modules/system/web-services/privatebin.nix +++ b/modules/system/web-services/privatebin.nix @@ -20,6 +20,11 @@ in }; config = lib.mkIf cfg.enable { + meta = { + domains.local = [ cfg.domain ]; + ports.tcp = [ cfg.port ]; + }; + services = { privatebin = { enable = true; diff --git a/modules/system/web-services/radicale.nix b/modules/system/web-services/radicale.nix index ee4163a..ce371b9 100644 --- a/modules/system/web-services/radicale.nix +++ b/modules/system/web-services/radicale.nix @@ -25,6 +25,11 @@ in }; config = lib.mkIf cfg.enable { + meta = { + domains.local = [ cfg.domain ]; + ports.tcp = [ cfg.port ]; + }; + sops.secrets."radicale/htpasswd" = { owner = config.users.users.radicale.name; restartUnits = [ "radicale.service" ]; diff --git a/modules/system/web-services/stirling-pdf.nix b/modules/system/web-services/stirling-pdf.nix index a0efb72..4212ac6 100644 --- a/modules/system/web-services/stirling-pdf.nix +++ b/modules/system/web-services/stirling-pdf.nix @@ -26,6 +26,11 @@ in }; config = lib.mkIf cfg.enable { + meta = { + domains.local = [ cfg.domain ]; + ports.tcp = [ cfg.port ]; + }; + services.stirling-pdf = { enable = true; environment = { diff --git a/modules/system/web-services/uptime-kuma.nix b/modules/system/web-services/uptime-kuma.nix index bb1522e..13f6763 100644 --- a/modules/system/web-services/uptime-kuma.nix +++ b/modules/system/web-services/uptime-kuma.nix @@ -16,6 +16,11 @@ in }; config = lib.mkIf cfg.enable { + meta = { + domains.local = [ cfg.domain ]; + ports.tcp = [ cfg.port ]; + }; + services.uptime-kuma = { enable = true; settings.PORT = toString cfg.port; diff --git a/modules/system/web-services/victorialogs.nix b/modules/system/web-services/victorialogs.nix index 80c9dc9..23c92df 100644 --- a/modules/system/web-services/victorialogs.nix +++ b/modules/system/web-services/victorialogs.nix @@ -16,6 +16,11 @@ in }; config = lib.mkIf cfg.enable { + meta = { + domains.local = [ cfg.domain ]; + ports.tcp = [ cfg.port ]; + }; + users = { users.victorialogs = { isSystemUser = true; diff --git a/modules/system/web-services/victoriametrics.nix b/modules/system/web-services/victoriametrics.nix index 0aae07b..42877a5 100644 --- a/modules/system/web-services/victoriametrics.nix +++ b/modules/system/web-services/victoriametrics.nix @@ -16,6 +16,11 @@ in }; config = lib.mkIf cfg.enable { + meta = { + domains.local = [ cfg.domain ]; + ports.tcp = [ cfg.port ]; + }; + users = { users.victoriametrics = { isSystemUser = true;