{ config, pkgs, ... }: let serviceName = "netbox"; servicePort = config.m3ta.ports.get serviceName; staticPort = config.m3ta.ports.get "netbox-static"; hostName = "nb.l.az-gruppe.com"; staticRoot = "${config.services.netbox.dataDir}/static"; # nixpkgs >= 25.11: Plugin-Pakete liegen im passthru-Set netbox.plugins # (python3Packages.netbox-* sind Throw-Stubs); pluginName = NetBox-Modulname netboxPlugins = with pkgs.unstable.netbox.plugins; [ netbox-dns netbox-qrcode netbox-routing netbox-topology-views netbox-floorplan-plugin ]; in { services.netbox = { enable = true; package = pkgs.unstable.netbox; listenAddress = "127.0.0.1"; port = servicePort; secretKeyFile = config.age.secrets.netbox-secret-key.path; apiTokenPeppersFile = config.age.secrets.netbox-api-token-pepper.path; settings = { ALLOWED_HOSTS = [hostName "localhost" "127.0.0.1"]; SECURE_SSL_REDIRECT = true; SESSION_COOKIE_SECURE = true; CSRF_COOKIE_SECURE = true; # NetBox aktiviert Plugins erst über PLUGINS in configuration.py — # der NixOS-NetBox-Modul trägt sie NICHT automatisch ein. PLUGINS = map (p: p.pluginName) netboxPlugins; }; # installiert die Pakete in NetBox' Python-Environment (extraBuildInputs) plugins = _: netboxPlugins; extraConfig = '' SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") ''; }; # Der Upgrade-Guard im NetBox-Modul (preStart) vergleicht /var/lib/netbox/version # nur mit dem BASISPAKET (services.netbox.package). Reine Config-/Plugin- # Änderungen bei gleicher NetBox-Version überspringen damit migrate + # collectstatic → Plugin-Tabellen fehlen ("database migration missing"). # Deshalb Migrationen/Statics hier VOR jedem netbox.service-Start ausführen # (idempotent, entspricht dem offiziellen NetBox-Upgrade-Pfad): systemd.services.netbox = { wants = ["netbox-migrate.service"]; after = ["netbox-migrate.service"]; }; systemd.services.netbox-migrate = { description = "NetBox: DB-Migrationen & Statics (v. a. Plugins)"; wants = ["network-online.target"]; after = ["network-online.target" "postgresql.service" "redis-netbox.service"]; serviceConfig = { Type = "oneshot"; User = "netbox"; Group = "netbox"; StateDirectory = "netbox"; TimeoutStartSec = "10min"; }; script = '' /run/current-system/sw/bin/netbox-manage migrate --no-input /run/current-system/sw/bin/netbox-manage collectstatic --no-input ''; }; services.nginx = { enable = true; virtualHosts.netbox-static = { listen = [ { addr = "127.0.0.1"; port = staticPort; } ]; locations."/static/" = { alias = "${staticRoot}/"; extraConfig = '' expires 1h; access_log off; ''; }; }; }; users.users.nginx.extraGroups = ["netbox"]; # Traefik-Routing: zwei Backends (App + Static), ein Host services.traefik.dynamicConfigOptions.http = { services = { ${serviceName}.loadBalancer.servers = [ {url = "http://127.0.0.1:${toString servicePort}/";} ]; "${serviceName}-static".loadBalancer.servers = [ {url = "http://127.0.0.1:${toString staticPort}/";} ]; }; routers = { ${serviceName} = { rule = "Host(`${hostName}`) && !PathPrefix(`/static`)"; tls.certResolver = "ionos"; service = serviceName; entrypoints = "websecure"; }; "${serviceName}-static" = { rule = "Host(`${hostName}`) && PathPrefix(`/static`)"; tls.certResolver = "ionos"; service = "${serviceName}-static"; entrypoints = "websecure"; }; }; }; }