From 94fdc678e4d4a3c98c2f996d220c4afa42b94025 Mon Sep 17 00:00:00 2001 From: Gene Liverman Date: Thu, 28 May 2026 22:42:49 -0400 Subject: [PATCH] Add dots.ports module: fleet-wide service port registry (nixnuc + hetznix01) Co-Authored-By: Claude Sonnet 4.6 --- modules/hosts/nixos/hetznix01/default.nix | 37 +++---- modules/hosts/nixos/hetznix01/ports.nix | 82 ++++++++++++++ .../post-install/containers/emqx.nix | 4 +- .../nixos/hetznix01/post-install/default.nix | 6 +- .../hetznix01/post-install/matrix-synapse.nix | 2 +- .../hetznix01/post-install/monitoring.nix | 8 +- .../nixos/hetznix01/post-install/nginx.nix | 56 +++++----- .../nixnuc/containers/audiobookshelf.nix | 5 +- .../hosts/nixos/nixnuc/containers/photon.nix | 3 +- .../nixos/nixnuc/containers/psitransfer.nix | 3 +- modules/hosts/nixos/nixnuc/cup-collector.nix | 4 +- modules/hosts/nixos/nixnuc/default.nix | 94 +++++++--------- .../hosts/nixos/nixnuc/monitoring-stack.nix | 22 ++-- modules/hosts/nixos/nixnuc/ports.nix | 102 ++++++++++++++++++ modules/shared/nixos/ports.nix | 60 +++++++++++ 15 files changed, 353 insertions(+), 135 deletions(-) create mode 100644 modules/hosts/nixos/hetznix01/ports.nix create mode 100644 modules/hosts/nixos/nixnuc/ports.nix create mode 100644 modules/shared/nixos/ports.nix diff --git a/modules/hosts/nixos/hetznix01/default.nix b/modules/hosts/nixos/hetznix01/default.nix index 092dbb3..61d7d66 100644 --- a/modules/hosts/nixos/hetznix01/default.nix +++ b/modules/hosts/nixos/hetznix01/default.nix @@ -1,4 +1,6 @@ { + config, + lib, pkgs, username, ... @@ -6,8 +8,10 @@ { imports = [ ../../../shared/nixos/nixroutes.nix + ../../../shared/nixos/ports.nix ./disk-config.nix ./hardware-configuration.nix + ./ports.nix ./post-install ]; @@ -27,27 +31,18 @@ ]; networking = { - # Open ports in the firewall. - firewall.allowedTCPPorts = [ - 22 # ssh - 25 # SMTP (unencrypted) - 80 # http to local Nginx - 143 # imap - 443 # https to local Nginx - 465 # SMTP with TLS - 587 # SMTP with STARTTLS - 993 # imaps - 1883 # mqtt - 8333 # Bitcoin Core - 8448 # Matrix Synapse - 8883 # mqtt over tls - 9001 # mqtt websockets over tls - 9333 # Bitcoin Knots - 9735 # LND - ]; - # firewall.allowedUDPPorts = [ ... ]; - # Or disable the firewall altogether. - # firewall.enable = false; + firewall = { + allowedTCPPorts = lib.pipe config.dots.ports [ + builtins.attrValues + (builtins.filter (e: e.openFirewall && e.protocol == "tcp")) + (map (e: e.port)) + ]; + allowedUDPPorts = lib.pipe config.dots.ports [ + builtins.attrValues + (builtins.filter (e: e.openFirewall && e.protocol == "udp")) + (map (e: e.port)) + ]; + }; hostId = "85d0e6cb"; # head -c4 /dev/urandom | od -A none -t x4 diff --git a/modules/hosts/nixos/hetznix01/ports.nix b/modules/hosts/nixos/hetznix01/ports.nix new file mode 100644 index 0000000..cc19f17 --- /dev/null +++ b/modules/hosts/nixos/hetznix01/ports.nix @@ -0,0 +1,82 @@ +{ + config.dots.ports = { + # Firewalled TCP services (email) + smtp = { + port = 25; + openFirewall = true; + }; + imap = { + port = 143; + openFirewall = true; + }; + smtp-tls = { + port = 465; + openFirewall = true; + }; + smtp-starttls = { + port = 587; + openFirewall = true; + }; + imaps = { + port = 993; + openFirewall = true; + }; + + # MQTT (via EMQX container) + mqtt = { + port = 1883; + openFirewall = true; + }; + mqtt-tls = { + port = 8883; + openFirewall = true; + }; + mqtt-ws = { + port = 9001; + openFirewall = true; + }; + + # Bitcoin / Lightning (proxied to umbrel on tailnet) + bitcoin-core = { + port = 8333; + openFirewall = true; + }; + bitcoin-knots = { + port = 9333; + openFirewall = true; + }; + lnd = { + port = 9735; + openFirewall = true; + }; + + # Matrix federation listener (nginx terminates, proxies to matrix-synapse) + matrix-federation = { + port = 8448; + openFirewall = true; + }; + + # Internal-only TCP services (proxied via nginx, not firewalled) + matrix-synapse = { + port = 8008; + }; + owntracks-frontend = { + port = 8082; + }; + owntracks-recorder = { + port = 8083; + }; + plausible = { + port = 8001; + }; + uptime-kuma = { + port = 3001; + }; + collabora = { + port = 9980; + }; + emqx-admin = { + port = 18083; + }; + }; +} diff --git a/modules/hosts/nixos/hetznix01/post-install/containers/emqx.nix b/modules/hosts/nixos/hetznix01/post-install/containers/emqx.nix index f350c4e..c9a8a52 100644 --- a/modules/hosts/nixos/hetznix01/post-install/containers/emqx.nix +++ b/modules/hosts/nixos/hetznix01/post-install/containers/emqx.nix @@ -16,10 +16,10 @@ in ]; hostname = "emqx1.hetznix01.technicalissues.us"; ports = [ - "1883:1883" + "${toString config.dots.ports.mqtt.port}:1883" #"8083:8083" #"8084:8084" - "18083:18083" + "${toString config.dots.ports.emqx-admin.port}:18083" ]; volumes = [ "${volume_base}/data:/opt/emqx/data" diff --git a/modules/hosts/nixos/hetznix01/post-install/default.nix b/modules/hosts/nixos/hetznix01/post-install/default.nix index 06a5a6b..19a11ff 100644 --- a/modules/hosts/nixos/hetznix01/post-install/default.nix +++ b/modules/hosts/nixos/hetznix01/post-install/default.nix @@ -23,7 +23,7 @@ in services = { collabora-online = { enable = true; - port = 9980; # default + inherit (config.dots.ports.collabora) port; settings = { # Rely on reverse proxy for SSL ssl = { @@ -51,7 +51,7 @@ in enable = true; configureNginx = true; environment = { - PHOTON_API_HOST = "nixnuc.${config.private-flake.tailnetDomain}:2322"; + PHOTON_API_HOST = "nixnuc.${config.private-flake.tailnetDomain}:${toString config.dots.ports.photon.port}"; PHOTON_API_USE_HTTPS = "false"; }; extraEnvFiles = [ @@ -122,7 +122,7 @@ in server = { baseUrl = "https://stats.${domain}"; disableRegistration = true; - port = 8001; + inherit (config.dots.ports.plausible) port; # secretKeybaseFile is a path to the file which contains the secret generated # with openssl as described above. secretKeybaseFile = config.sops.secrets.plausible_secret_key_base.path; diff --git a/modules/hosts/nixos/hetznix01/post-install/matrix-synapse.nix b/modules/hosts/nixos/hetznix01/post-install/matrix-synapse.nix index 0421a1d..8c42198 100644 --- a/modules/hosts/nixos/hetznix01/post-install/matrix-synapse.nix +++ b/modules/hosts/nixos/hetznix01/post-install/matrix-synapse.nix @@ -13,7 +13,7 @@ signing_key_path = config.sops.secrets.matrix_homeserver_signing_key.path; listeners = [ { - port = 8008; + inherit (config.dots.ports.matrix-synapse) port; tls = false; type = "http"; x_forwarded = true; diff --git a/modules/hosts/nixos/hetznix01/post-install/monitoring.nix b/modules/hosts/nixos/hetznix01/post-install/monitoring.nix index 8f44f00..37861df 100644 --- a/modules/hosts/nixos/hetznix01/post-install/monitoring.nix +++ b/modules/hosts/nixos/hetznix01/post-install/monitoring.nix @@ -16,7 +16,7 @@ in { job_name = "node"; static_configs = [ - { targets = [ "127.0.0.1:9100" ]; } + { targets = [ "127.0.0.1:${toString config.dots.ports.node-exporter.port}" ]; } ]; metric_relabel_configs = [ { @@ -37,7 +37,7 @@ in { job_name = "nginx"; static_configs = [ - { targets = [ "127.0.0.1:9113" ]; } + { targets = [ "127.0.0.1:${toString config.dots.ports.nginx-exporter.port}" ]; } ]; metric_relabel_configs = [ { @@ -84,7 +84,7 @@ in prometheus.exporters.node = { enable = true; listenAddress = "127.0.0.1"; - port = 9100; + inherit (config.dots.ports.node-exporter) port; enabledCollectors = [ "systemd" ]; @@ -98,7 +98,7 @@ in prometheus.exporters.nginx = { enable = true; listenAddress = "127.0.0.1"; - port = 9113; + inherit (config.dots.ports.nginx-exporter) port; scrapeUri = "https://127.0.0.1/server_status"; sslVerify = false; }; diff --git a/modules/hosts/nixos/hetznix01/post-install/nginx.nix b/modules/hosts/nixos/hetznix01/post-install/nginx.nix index 2b8d472..f67c2c3 100644 --- a/modules/hosts/nixos/hetznix01/post-install/nginx.nix +++ b/modules/hosts/nixos/hetznix01/post-install/nginx.nix @@ -1,8 +1,6 @@ { config, ... }: let domain = "technicalissues.us"; - http_port = 80; - https_port = 443; private_btc = "umbrel.${config.private-flake.tailnetDomain}"; in { @@ -25,13 +23,13 @@ in streamConfig = '' server { # https://docs.emqx.com/en/emqx/latest/deploy/cluster/lb-nginx.html - listen 8883 ssl; + listen ${toString config.dots.ports.mqtt-tls.port} ssl; ssl_session_timeout 10m; ssl_certificate ${config.security.acme.certs."mqtt.${domain}".directory}/fullchain.pem; ssl_certificate_key ${config.security.acme.certs."mqtt.${domain}".directory}/key.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305; - proxy_pass 127.0.0.0:1883; + proxy_pass 127.0.0.0:${toString config.dots.ports.mqtt.port}; proxy_protocol on; proxy_connect_timeout 10s; # Default keep-alive time is 10 minutes @@ -41,17 +39,17 @@ in } server { - listen 0.0.0.0:8333; - listen 0.0.0.0:9333; - listen [::]:8333; - listen [::]:9333; - proxy_pass ${private_btc}:8333; + listen 0.0.0.0:${toString config.dots.ports.bitcoin-core.port}; + listen 0.0.0.0:${toString config.dots.ports.bitcoin-knots.port}; + listen [::]:${toString config.dots.ports.bitcoin-core.port}; + listen [::]:${toString config.dots.ports.bitcoin-knots.port}; + proxy_pass ${private_btc}:${toString config.dots.ports.bitcoin-core.port}; } server { - listen 0.0.0.0:9735; - listen [::]:9735; - proxy_pass ${private_btc}:9735; + listen 0.0.0.0:${toString config.dots.ports.lnd.port}; + listen [::]:${toString config.dots.ports.lnd.port}; + proxy_pass ${private_btc}:${toString config.dots.ports.lnd.port}; } ''; virtualHosts = { @@ -137,32 +135,32 @@ in "matrix.${domain}" = { listen = [ { - port = http_port; + inherit (config.dots.ports.http) port; addr = "0.0.0.0"; } { - port = http_port; + inherit (config.dots.ports.http) port; addr = "[::]"; } { - port = https_port; + inherit (config.dots.ports.https) port; addr = "0.0.0.0"; ssl = true; } { - port = https_port; + inherit (config.dots.ports.https) port; addr = "[::]"; ssl = true; } { - port = 8448; + inherit (config.dots.ports.matrix-federation) port; addr = "0.0.0.0"; ssl = true; } { - port = 8448; + inherit (config.dots.ports.matrix-federation) port; addr = "[::]"; ssl = true; } @@ -182,9 +180,9 @@ in }; # Forward all Matrix API calls to the synapse Matrix homeserver. A trailing slash # *must not* be used here. - "/_matrix".proxyPass = "http://[::1]:8008"; + "/_matrix".proxyPass = "http://[::1]:${toString config.dots.ports.matrix-synapse.port}"; # Forward requests for e.g. SSO and password-resets. - "/_synapse/client".proxyPass = "http://[::1]:8008"; + "/_synapse/client".proxyPass = "http://[::1]:${toString config.dots.ports.matrix-synapse.port}"; }; }; "mqtt.${domain}" = { @@ -199,7 +197,7 @@ in forceSSL = true; basicAuthFile = config.sops.secrets.owntracks_basic_auth.path; # OwnTracks Frontend container - locations."/".proxyPass = "http://127.0.0.1:8082"; + locations."/".proxyPass = "http://127.0.0.1:${toString config.dots.ports.owntracks-frontend.port}"; }; "pack1828.org" = { enableACME = true; @@ -217,26 +215,26 @@ in locations = { # OwnTracks Recorder "/" = { - proxyPass = "http://127.0.0.1:8083"; + proxyPass = "http://127.0.0.1:${toString config.dots.ports.owntracks-recorder.port}"; }; "/pub" = { # Client apps need to point to this path extraConfig = "proxy_set_header X-Limit-U $remote_user;"; - proxyPass = "http://127.0.0.1:8083/pub"; + proxyPass = "http://127.0.0.1:${toString config.dots.ports.owntracks-recorder.port}/pub"; }; "/static/" = { - proxyPass = "http://127.0.0.1:8083/static/"; + proxyPass = "http://127.0.0.1:${toString config.dots.ports.owntracks-recorder.port}/static/"; }; "/utils/" = { - proxyPass = "http://127.0.0.1:8083/utils/"; + proxyPass = "http://127.0.0.1:${toString config.dots.ports.owntracks-recorder.port}/utils/"; }; "/view/" = { extraConfig = "proxy_buffering off;"; - proxyPass = "http://127.0.0.1:8083/view/"; + proxyPass = "http://127.0.0.1:${toString config.dots.ports.owntracks-recorder.port}/view/"; }; "/ws" = { extraConfig = "rewrite ^/(.*) /$1 break;"; - proxyPass = "http://127.0.0.1:8083"; + proxyPass = "http://127.0.0.1:${toString config.dots.ports.owntracks-recorder.port}"; }; }; }; @@ -244,7 +242,7 @@ in enableACME = true; acmeRoot = null; forceSSL = true; - locations."/".proxyPass = "http://127.0.0.1:8001"; + locations."/".proxyPass = "http://127.0.0.1:${toString config.dots.ports.plausible.port}"; locations."/".proxyWebsockets = true; extraConfig = '' access_log /var/log/nginx/stats.${domain}.log; @@ -259,7 +257,7 @@ in acmeRoot = null; forceSSL = true; locations."/".proxyWebsockets = true; - locations."/".proxyPass = "http://127.0.0.1:3001"; + locations."/".proxyPass = "http://127.0.0.1:${toString config.dots.ports.uptime-kuma.port}"; }; }; # end virtualHosts }; # end nginx diff --git a/modules/hosts/nixos/nixnuc/containers/audiobookshelf.nix b/modules/hosts/nixos/nixnuc/containers/audiobookshelf.nix index f84b626..7e9a3ad 100644 --- a/modules/hosts/nixos/nixnuc/containers/audiobookshelf.nix +++ b/modules/hosts/nixos/nixnuc/containers/audiobookshelf.nix @@ -1,7 +1,6 @@ -_: +{ config, ... }: let volume_base = "/var/lib/audiobookshelf"; - http_port = "13378"; in { # Audiobookshelf @@ -14,7 +13,7 @@ in AUDIOBOOKSHELF_UID = "99"; AUDIOBOOKSHELF_GID = "100"; }; - ports = [ "${http_port}:80" ]; + ports = [ "${toString config.dots.ports.audiobookshelf.port}:80" ]; volumes = [ "${volume_base}/audiobooks:/audiobooks" "${volume_base}/podcasts:/podcasts" diff --git a/modules/hosts/nixos/nixnuc/containers/photon.nix b/modules/hosts/nixos/nixnuc/containers/photon.nix index 55801cd..835c25a 100644 --- a/modules/hosts/nixos/nixnuc/containers/photon.nix +++ b/modules/hosts/nixos/nixnuc/containers/photon.nix @@ -1,7 +1,6 @@ { config, ... }: let volume_base = "/orico/photon"; - http_port = "2322"; in { systemd.services."${config.virtualisation.oci-containers.containers.photon.serviceName}" = { @@ -19,7 +18,7 @@ in UPDATE_STRATEGY = "PARALLEL"; UPDATE_INTERVAL = "30d"; }; - ports = [ "${http_port}:2322" ]; + ports = [ "${toString config.dots.ports.photon.port}:2322" ]; volumes = [ "${volume_base}:/photon/data" ]; diff --git a/modules/hosts/nixos/nixnuc/containers/psitransfer.nix b/modules/hosts/nixos/nixnuc/containers/psitransfer.nix index f1e3558..87a8823 100644 --- a/modules/hosts/nixos/nixnuc/containers/psitransfer.nix +++ b/modules/hosts/nixos/nixnuc/containers/psitransfer.nix @@ -1,7 +1,6 @@ { config, ... }: let volume_base = "/orico/psitransfer"; - http_port = "3000"; psitransfer_dot_env = "${config.sops.secrets.psitransfer_dot_env.path}"; in { @@ -24,7 +23,7 @@ in autoStart = true; image = "psitrax/psitransfer"; environmentFiles = [ psitransfer_dot_env ]; - ports = [ "${http_port}:3000" ]; + ports = [ "${toString config.dots.ports.psitransfer.port}:3000" ]; volumes = [ "${volume_base}/data:/data" ]; diff --git a/modules/hosts/nixos/nixnuc/cup-collector.nix b/modules/hosts/nixos/nixnuc/cup-collector.nix index 24fdaa9..d38aa87 100644 --- a/modules/hosts/nixos/nixnuc/cup-collector.nix +++ b/modules/hosts/nixos/nixnuc/cup-collector.nix @@ -22,9 +22,9 @@ in ]; migrationsDir = inputs.cup-collector.packages.${pkgs.stdenv.hostPlatform.system}.migrations; pbBindIp = "0.0.0.0"; - pbPort = 8091; # override default due to conflict + pbPort = config.dots.ports.pocketbase.port; # override default due to conflict pocketidIssuerUrl = config.services.pocket-id.settings.APP_URL; - port = 3010; # override default due to conflict + inherit (config.dots.ports.cup-collector) port; # override default due to conflict }; restic.backups.daily = { diff --git a/modules/hosts/nixos/nixnuc/default.nix b/modules/hosts/nixos/nixnuc/default.nix index d027b5e..03fdbfd 100644 --- a/modules/hosts/nixos/nixnuc/default.nix +++ b/modules/hosts/nixos/nixnuc/default.nix @@ -1,12 +1,12 @@ { inputs, config, + lib, pkgs, username, ... }: let - https_port = 443; home_domain = "home.technicalissues.us"; backend_ip = "127.0.0.1"; restic_backup_time = "02:00"; @@ -20,8 +20,10 @@ in ./containers/psitransfer.nix ./cup-collector.nix ./monitoring-stack.nix + ./ports.nix ./zfs-datasets.nix ../../../shared/nixos/lets-encrypt.nix + ../../../shared/nixos/ports.nix ../../../shared/nixos/restic.nix ]; @@ -71,36 +73,18 @@ in }; networking = { - # Open ports in the firewall. firewall = { - allowedTCPPorts = [ - 22 # ssh - 80 # http to local Nginx - 443 # https to local Nginx - 2322 # Photon geocoder in oci-container - 3000 # PsiTransfer in oci-container - 3001 # immich-kiosk in compose - 3002 # grafana - 3005 # Firefly III - 3006 # Firefly III Data Importer - 3010 # Cup Collector - 3030 # Forgejo - 3087 # Youtarr in docker compose - 8001 # Tube Archivist - 8384 # Syncthing gui - 8888 # Atuin - 8090 # Wallabag in docker compose - 8091 # PocketBase - 8945 # Pinchflat - 13378 # Audiobookshelf in oci-container + allowedTCPPorts = lib.pipe config.dots.ports [ + builtins.attrValues + (builtins.filter (e: e.openFirewall && e.protocol == "tcp")) + (map (e: e.port)) ]; - allowedUDPPorts = [ - 1900 # Jellyfin service auto-discovery - 7359 # Jellyfin auto-discovery + allowedUDPPorts = lib.pipe config.dots.ports [ + builtins.attrValues + (builtins.filter (e: e.openFirewall && e.protocol == "udp")) + (map (e: e.port)) ]; }; - # Or disable the firewall altogether. - # firewall.enable = false; hostId = "c5826b45"; # head -c4 /dev/urandom | od -A none -t x4 @@ -177,7 +161,7 @@ in enable = true; enableNginx = true; settings = { - FIREFLY_III_URL = "http://localhost:3005"; + FIREFLY_III_URL = "http://localhost:${toString config.dots.ports.fireflyiii.port}"; VANITY_URL_FILE = "${config.sops.secrets.firefly_vanity_url.path}"; FIREFLY_III_ACCESS_TOKEN_FILE = "${config.sops.secrets.firefly_pat_data_import.path}"; SIMPLEFIN_TOKEN_FILE = "${config.sops.secrets.firefly_simplefin_token.path}"; @@ -203,7 +187,7 @@ in }; server = { DOMAIN = "git.${home_domain}"; - HTTP_PORT = 3030; + HTTP_PORT = config.dots.ports.forgejo.port; LANDING_PAGE = "explore"; ROOT_URL = "https://git.${home_domain}/"; }; @@ -222,7 +206,7 @@ in enable = true; credentialsFile = config.sops.secrets.mealie.path; listenAddress = "0.0.0.0"; - port = 9000; + inherit (config.dots.ports.mealie) port; settings = { ALLOW_SIGNUP = "false"; BASE_URL = "https://mealie.${home_domain}"; @@ -314,7 +298,7 @@ in ]; listen = [ { - port = https_port; + inherit (config.dots.ports.https) port; addr = "0.0.0.0"; ssl = true; } @@ -339,7 +323,7 @@ in "ab.${home_domain}" = { listen = [ { - port = https_port; + inherit (config.dots.ports.https) port; addr = "0.0.0.0"; ssl = true; } @@ -348,7 +332,7 @@ in acmeRoot = null; forceSSL = true; locations."/".proxyWebsockets = true; - locations."/".proxyPass = "http://${backend_ip}:13378"; + locations."/".proxyPass = "http://${backend_ip}:${toString config.dots.ports.audiobookshelf.port}"; extraConfig = '' client_max_body_size 0; ''; @@ -356,7 +340,7 @@ in "atuin.${home_domain}" = { listen = [ { - port = https_port; + inherit (config.dots.ports.https) port; addr = "0.0.0.0"; ssl = true; } @@ -364,19 +348,19 @@ in enableACME = true; acmeRoot = null; forceSSL = true; - locations."/".proxyPass = "http://${backend_ip}:8888"; + locations."/".proxyPass = "http://${backend_ip}:${toString config.dots.ports.atuin.port}"; }; # budget.${home_domain} "${config.services.firefly-iii.virtualHost}".listen = [ { - port = 3005; + inherit (config.dots.ports.fireflyiii) port; addr = "0.0.0.0"; ssl = false; } ]; "${config.services.firefly-iii-data-importer.virtualHost}".listen = [ { - port = 3006; + inherit (config.dots.ports.fireflyiii-importer) port; addr = "0.0.0.0"; ssl = false; } @@ -384,7 +368,7 @@ in "git.${home_domain}" = { listen = [ { - port = https_port; + inherit (config.dots.ports.https) port; addr = "0.0.0.0"; ssl = true; } @@ -392,7 +376,7 @@ in enableACME = true; acmeRoot = null; forceSSL = true; - locations."/".proxyPass = "http://${backend_ip}:3030"; + locations."/".proxyPass = "http://${backend_ip}:${toString config.dots.ports.forgejo.port}"; extraConfig = '' client_max_body_size 0; ''; @@ -400,7 +384,7 @@ in "id.${home_domain}" = { listen = [ { - port = https_port; + inherit (config.dots.ports.https) port; addr = "0.0.0.0"; ssl = true; } @@ -408,7 +392,7 @@ in enableACME = true; acmeRoot = null; forceSSL = true; - locations."/".proxyPass = "http://${backend_ip}:1411"; + locations."/".proxyPass = "http://${backend_ip}:${toString config.dots.ports.pocket-id.port}"; extraConfig = '' proxy_busy_buffers_size 512k; proxy_buffers 4 512k; @@ -418,7 +402,7 @@ in "immich.${home_domain}" = { listen = [ { - port = https_port; + inherit (config.dots.ports.https) port; addr = "0.0.0.0"; ssl = true; } @@ -426,7 +410,7 @@ in enableACME = true; acmeRoot = null; forceSSL = true; - locations."/".proxyPass = "http://${backend_ip}:2283"; + locations."/".proxyPass = "http://${backend_ip}:${toString config.dots.ports.immich.port}"; locations."/".proxyWebsockets = true; extraConfig = '' client_max_body_size 0; @@ -438,7 +422,7 @@ in "immich-kiosk.${home_domain}" = { listen = [ { - port = https_port; + inherit (config.dots.ports.https) port; addr = "0.0.0.0"; ssl = true; } @@ -447,7 +431,7 @@ in acmeRoot = null; forceSSL = true; basicAuthFile = config.sops.secrets.immich_kiosk_basic_auth.path; - locations."/".proxyPass = "http://${backend_ip}:3001"; + locations."/".proxyPass = "http://${backend_ip}:${toString config.dots.ports.immich-kiosk.port}"; locations."/".proxyWebsockets = true; extraConfig = '' client_max_body_size 0; @@ -459,7 +443,7 @@ in "jellyfin.${home_domain}" = { listen = [ { - port = https_port; + inherit (config.dots.ports.https) port; addr = "0.0.0.0"; ssl = true; } @@ -469,14 +453,14 @@ in forceSSL = true; locations = { "/" = { - proxyPass = "http://${backend_ip}:8096"; + proxyPass = "http://${backend_ip}:${toString config.dots.ports.jellyfin.port}"; extraConfig = '' proxy_buffering off; proxy_set_header X-Forwarded-Protocol $scheme; ''; }; "/socket" = { - proxyPass = "http://${backend_ip}:8096"; + proxyPass = "http://${backend_ip}:${toString config.dots.ports.jellyfin.port}"; proxyWebsockets = true; extraConfig = '' proxy_set_header X-Forwarded-Protocol $scheme; @@ -490,7 +474,7 @@ in "mealie.${home_domain}" = { listen = [ { - port = https_port; + inherit (config.dots.ports.https) port; addr = "0.0.0.0"; ssl = true; } @@ -498,7 +482,7 @@ in enableACME = true; acmeRoot = null; forceSSL = true; - locations."/".proxyPass = "http://${backend_ip}:9000"; + locations."/".proxyPass = "http://${backend_ip}:${toString config.dots.ports.mealie.port}"; extraConfig = '' client_max_body_size 10M; ''; @@ -506,7 +490,7 @@ in "monitoring.${home_domain}" = { listen = [ { - port = https_port; + inherit (config.dots.ports.https) port; addr = "0.0.0.0"; ssl = true; } @@ -515,10 +499,10 @@ in acmeRoot = null; forceSSL = true; locations = { - "/grafana/".proxyPass = "http://${backend_ip}:3002/grafana/"; + "/grafana/".proxyPass = "http://${backend_ip}:${toString config.dots.ports.grafana.port}/grafana/"; "/remotewrite" = { basicAuthFile = config.sops.secrets.nginx_basic_auth.path; - proxyPass = "http://127.0.0.1:8428/api/v1/write"; + proxyPass = "http://127.0.0.1:${toString config.dots.ports.victoriametrics.port}/api/v1/write"; proxyWebsockets = true; }; }; @@ -531,7 +515,7 @@ in "readit.${home_domain}" = { listen = [ { - port = https_port; + inherit (config.dots.ports.https) port; addr = "0.0.0.0"; ssl = true; } @@ -539,7 +523,7 @@ in enableACME = true; acmeRoot = null; forceSSL = true; - locations."/".proxyPass = "http://${backend_ip}:8090"; + locations."/".proxyPass = "http://${backend_ip}:${toString config.dots.ports.wallabag.port}"; }; }; }; diff --git a/modules/hosts/nixos/nixnuc/monitoring-stack.nix b/modules/hosts/nixos/nixnuc/monitoring-stack.nix index 3d489f8..2c9da20 100644 --- a/modules/hosts/nixos/nixnuc/monitoring-stack.nix +++ b/modules/hosts/nixos/nixnuc/monitoring-stack.nix @@ -49,9 +49,9 @@ in static_configs = [ { targets = [ - "127.0.0.1:9100" # nixnuc - "192.168.22.22:9100" # home assistant - "umbrel:9100" + "127.0.0.1:${toString config.dots.ports.node-exporter.port}" # nixnuc + "192.168.22.22:${toString config.dots.ports.node-exporter.port}" # home assistant + "umbrel:${toString config.dots.ports.node-exporter.port}" ]; } ]; @@ -89,7 +89,7 @@ in { job_name = "cadvisor"; static_configs = [ - { targets = [ "127.0.0.1:8081" ]; } + { targets = [ "127.0.0.1:${toString config.dots.ports.cadvisor.port}" ]; } ]; metric_relabel_configs = [ { @@ -110,7 +110,7 @@ in { job_name = "nginx"; static_configs = [ - { targets = [ "127.0.0.1:9113" ]; } + { targets = [ "127.0.0.1:${toString config.dots.ports.nginx-exporter.port}" ]; } ]; metric_relabel_configs = [ { @@ -181,7 +181,7 @@ in }; # Remote write to VictoriaMetrics - remoteWrite.url = "http://127.0.0.1:8428/api/v1/write"; + remoteWrite.url = "http://127.0.0.1:${toString config.dots.ports.victoriametrics.port}/api/v1/write"; extraArgs = [ # Pass other remote write flags the module does not expose natively: @@ -219,7 +219,7 @@ in name = "VictoriaMetrics"; type = "victoriametrics-metrics-datasource"; access = "proxy"; - url = "http://127.0.0.1:8428"; + url = "http://127.0.0.1:${toString config.dots.ports.victoriametrics.port}"; isDefault = true; uid = "VictoriaMetrics"; # Set explicit UID for use in alert rules } @@ -272,7 +272,7 @@ in server = { domain = "monitoring.${home_domain}"; http_addr = "0.0.0.0"; - http_port = 3002; + http_port = config.dots.ports.grafana.port; root_url = "https://monitoring.${home_domain}/grafana/"; serve_from_sub_path = true; }; @@ -295,7 +295,7 @@ in prometheus.exporters.node = { enable = true; listenAddress = "127.0.0.1"; - port = 9100; + inherit (config.dots.ports.node-exporter) port; enabledCollectors = [ "zfs" "systemd" @@ -310,7 +310,7 @@ in prometheus.exporters.nginx = { enable = true; listenAddress = "127.0.0.1"; - port = 9113; + inherit (config.dots.ports.nginx-exporter) port; scrapeUri = "https://127.0.0.1/server_status"; sslVerify = false; }; @@ -319,7 +319,7 @@ in cadvisor = { enable = true; listenAddress = "127.0.0.1"; - port = 8081; + inherit (config.dots.ports.cadvisor) port; extraOptions = [ "--docker_only=true" "--housekeeping_interval=30s" diff --git a/modules/hosts/nixos/nixnuc/ports.nix b/modules/hosts/nixos/nixnuc/ports.nix new file mode 100644 index 0000000..7764ef8 --- /dev/null +++ b/modules/hosts/nixos/nixnuc/ports.nix @@ -0,0 +1,102 @@ +{ + config.dots.ports = { + # Override global photon default: open the firewall on this host + photon = { + openFirewall = true; + }; + + # Firewalled TCP services + psitransfer = { + port = 3000; + openFirewall = true; + }; + immich-kiosk = { + port = 3001; + openFirewall = true; + }; + grafana = { + port = 3002; + openFirewall = true; + }; + fireflyiii = { + port = 3005; + openFirewall = true; + }; + fireflyiii-importer = { + port = 3006; + openFirewall = true; + }; + cup-collector = { + port = 3010; + openFirewall = true; + }; + forgejo = { + port = 3030; + openFirewall = true; + }; + youtarr = { + port = 3087; + openFirewall = true; + }; + tube-archivist = { + port = 8001; + openFirewall = true; + }; + syncthing-gui = { + port = 8384; + openFirewall = true; + }; + atuin = { + port = 8888; + openFirewall = true; + }; + wallabag = { + port = 8090; + openFirewall = true; + }; + pocketbase = { + port = 8091; + openFirewall = true; + }; + pinchflat = { + port = 8945; + openFirewall = true; + }; + audiobookshelf = { + port = 13378; + openFirewall = true; + }; + + # Internal-only TCP services (proxied via nginx, not firewalled) + pocket-id = { + port = 1411; + }; + immich = { + port = 2283; + }; + cadvisor = { + port = 8081; + }; + victoriametrics = { + port = 8428; + }; + jellyfin = { + port = 8096; + }; + mealie = { + port = 9000; + }; + + # UDP services + jellyfin-ssdp = { + port = 1900; + protocol = "udp"; + openFirewall = true; + }; + jellyfin-discovery = { + port = 7359; + protocol = "udp"; + openFirewall = true; + }; + }; +} diff --git a/modules/shared/nixos/ports.nix b/modules/shared/nixos/ports.nix new file mode 100644 index 0000000..e51a212 --- /dev/null +++ b/modules/shared/nixos/ports.nix @@ -0,0 +1,60 @@ +{ lib, ... }: +{ + options.dots.ports = lib.mkOption { + description = "Fleet-wide service port registry"; + default = { }; + type = lib.types.attrsOf ( + lib.types.submodule { + options = { + port = lib.mkOption { + type = lib.types.port; + description = "Port number"; + }; + protocol = lib.mkOption { + type = lib.types.enum [ + "tcp" + "udp" + ]; + default = "tcp"; + description = "Transport protocol"; + }; + openFirewall = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Open this port in the host firewall"; + }; + }; + } + ); + }; + + # Ports known fleet-wide: either universal (ssh/http/https) or referenced + # by multiple hosts (e.g. hetznix01 references photon to configure Dawarich). + # openFirewall is false by default; each host's ports.nix sets it to true + # for the ports that host actually exposes. + config.dots.ports = { + ssh = { + port = 22; + openFirewall = true; + }; + http = { + port = 80; + openFirewall = true; + }; + https = { + port = 443; + openFirewall = true; + }; + # nixnuc service; hetznix01 references this port for Dawarich's PHOTON_API_HOST. + photon = { + port = 2322; + }; + # Standard defaults for prometheus exporters, used on all monitored hosts. + node-exporter = { + port = 9100; + }; + nginx-exporter = { + port = 9113; + }; + }; +}