diff --git a/doc/release-notes/rl-2605.section.md b/doc/release-notes/rl-2605.section.md index 9ad7fdcb9e5c..54c1f3d750c5 100644 --- a/doc/release-notes/rl-2605.section.md +++ b/doc/release-notes/rl-2605.section.md @@ -165,6 +165,10 @@ - The `programs.captive-browser` module no longer falls back on a setcap wrapper around udhcpc to discover your network's DNS server due to [GHSA-wc3r-c66x-8xmc](https://github.com/NixOS/nixpkgs/security/advisories/GHSA-wc3r-c66x-8xmc) (CVE-2026-25740). If you're using this module, you must either configure `programs.captive-browser.dhcp-dns` manually or enable one of NetworkManager, dhcpcd, or systemd-networkd. +- NetBox was updated to `>= 4.5.5`. Have a look at the breaking changes + of the [4.5 release](https://github.com/netbox-community/netbox/releases/tag/v4.5.0), + make the required changes to your database, if needed, then upgrade by setting `services.netbox.package = pkgs.netbox_4_5;` in your configuration. + - The `services.yggdrasil` module has been refactored with the following breaking changes: - The `services.yggdrasil.configFile` option has been removed. Configuration should now be specified directly via `services.yggdrasil.settings`. - The `services.yggdrasil.persistentKeys` option has been removed. To maintain persistent keys and IPv6 addresses across reboots, use `services.yggdrasil.settings.PrivateKeyPath` to securely load your private key from a file via systemd credentials. The private key must be in PEM format (PKCS #8). diff --git a/nixos/modules/services/web-apps/netbox.nix b/nixos/modules/services/web-apps/netbox.nix index f49b8271928d..d380ab60bc41 100644 --- a/nixos/modules/services/web-apps/netbox.nix +++ b/nixos/modules/services/web-apps/netbox.nix @@ -119,11 +119,11 @@ in package = lib.mkOption { type = lib.types.package; default = - if lib.versionAtLeast config.system.stateVersion "25.11" then pkgs.netbox_4_4 else pkgs.netbox_4_2; + if lib.versionAtLeast config.system.stateVersion "26.05" then pkgs.netbox_4_5 else pkgs.netbox_4_4; defaultText = lib.literalExpression '' - if lib.versionAtLeast config.system.stateVersion "25.11" - then pkgs.netbox_4_4 - else pkgs.netbox_4_2; + if lib.versionAtLeast config.system.stateVersion "26.05" + then pkgs.netbox_4_5 + else pkgs.netbox_4_4; ''; description = '' NetBox package to use. @@ -165,6 +165,15 @@ in ''; }; + apiTokenPeppersFile = lib.mkOption { + type = with lib.types; nullOr path; + description = '' + Path to a file containing the API_TOKEN_PEPPER that will be configured for the pepper_id with the id 1. + This is required for netbox >= 4.5. For generating this pepper, + [consider using `$INSTALL_ROOT/netbox/generate_secret_key.py`](https://netboxlabs.com/docs/netbox/configuration/required-parameters/#api_token_peppers) + ''; + }; + extraConfig = lib.mkOption { type = lib.types.lines; default = ""; @@ -237,10 +246,12 @@ in GIT_PATH = "${pkgs.gitMinimal}/bin/git"; - DATABASE = { - NAME = "netbox"; - USER = "netbox"; - HOST = "/run/postgresql"; + DATABASES = { + "default" = { + NAME = "netbox"; + USER = "netbox"; + HOST = "/run/postgresql"; + }; }; # Redis database settings. Redis is used for caching and for queuing @@ -283,6 +294,12 @@ in with open("${cfg.secretKeyFile}", "r") as file: SECRET_KEY = file.readline() '' + + (lib.optionalString (cfg.apiTokenPeppersFile != null) '' + with open("${cfg.apiTokenPeppersFile}", "r") as pepper_file: + API_TOKEN_PEPPERS = { + 1: pepper_file.readline(), + }; + '') + (lib.optionalString (cfg.keycloakClientSecret != null) '' with open("${cfg.keycloakClientSecret}", "r") as file: SOCIAL_AUTH_KEYCLOAK_SECRET = file.readline() @@ -446,5 +463,12 @@ in }; users.groups.netbox = { }; users.groups."${config.services.redis.servers.netbox.user}".members = [ "netbox" ]; + + assertions = [ + { + assertion = (lib.versionAtLeast cfg.package.version "4.5") -> (cfg.apiTokenPeppersFile != null); + message = "NetBox 4.5 or newer require setting `services.netbox.apiTokenPeppersFile`"; + } + ]; }; } diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 32c49bf3a0e9..ad85b07f30ce 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1054,9 +1054,8 @@ in neo4j = runTest ./neo4j.nix; netbird = runTest ./netbird.nix; netbox-upgrade = runTest ./web-apps/netbox-upgrade.nix; - netbox_4_2 = handleTest ./web-apps/netbox/default.nix { netbox = pkgs.netbox_4_2; }; - netbox_4_3 = handleTest ./web-apps/netbox/default.nix { netbox = pkgs.netbox_4_3; }; netbox_4_4 = handleTest ./web-apps/netbox/default.nix { netbox = pkgs.netbox_4_4; }; + netbox_4_5 = handleTest ./web-apps/netbox/default.nix { netbox = pkgs.netbox_4_5; }; netdata = runTest ./netdata.nix; networking.networkd = handleTest ./networking/networkd-and-scripted.nix { networkd = true; }; networking.networkmanager = handleTest ./networking/networkmanager.nix { }; diff --git a/nixos/tests/web-apps/netbox-upgrade.nix b/nixos/tests/web-apps/netbox-upgrade.nix index 5b60e8939ccb..ee84a6b34fd8 100644 --- a/nixos/tests/web-apps/netbox-upgrade.nix +++ b/nixos/tests/web-apps/netbox-upgrade.nix @@ -1,7 +1,7 @@ { lib, pkgs, ... }: let - oldNetbox = "netbox_4_3"; - newNetbox = "netbox_4_4"; + oldNetbox = "netbox_4_4"; + newNetbox = "netbox_4_5"; apiVersion = version: @@ -38,6 +38,9 @@ in secretKeyFile = pkgs.writeText "secret" '' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ''; + apiTokenPeppersFile = pkgs.writeText "pepper" '' + kp7ht*76fiQAhUi5dHfASLlYUE_S^gI^(7J^K5M!LfoH@vl&b_ + ''; }; services.nginx = { diff --git a/nixos/tests/web-apps/netbox/default.nix b/nixos/tests/web-apps/netbox/default.nix index b614b965ae6d..803cf5c25ed3 100644 --- a/nixos/tests/web-apps/netbox/default.nix +++ b/nixos/tests/web-apps/netbox/default.nix @@ -37,6 +37,10 @@ import ../../make-test-python.nix ( secretKeyFile = pkgs.writeText "secret" '' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ''; + # Value from https://netbox.readthedocs.io/en/feature/configuration/required-parameters/#api_token_peppers + apiTokenPeppersFile = pkgs.writeText "pepper" '' + kp7ht*76fiQAhUi5dHfASLlYUE_S^gI^(7J^K5M!LfoH@vl&b_ + ''; enableLdap = true; ldapConfigPath = pkgs.writeText "ldap_config.py" '' @@ -155,10 +159,32 @@ import ../../make-test-python.nix ( u.set_password('netbox') u.save() ''; + createToken = pkgs.writeText "create-token-v2.py" '' + from users.models import Token, User + from users.choices import TokenVersionChoices + u = User.objects.first() + t = Token.objects.create(user=u, token="0123456789abcdef0123456789abcdef01234567", version=TokenVersionChoices.V2) + print(t.get_auth_header_prefix()) + ''; + netboxVersion = netbox.version; in builtins.replaceStrings - [ "\${changePassword}" "\${testUser}" "\${testPassword}" "\${testGroup}" ] - [ "${changePassword}" "${testUser}" "${testPassword}" "${testGroup}" ] + [ + "\${changePassword}" + "\${testUser}" + "\${testPassword}" + "\${testGroup}" + "\${createToken}" + "\${netboxVersion}" + ] + [ + "${changePassword}" + "${testUser}" + "${testPassword}" + "${testGroup}" + "${createToken}" + "${netboxVersion}" + ] (lib.readFile "${./testScript.py}"); } ) diff --git a/nixos/tests/web-apps/netbox/testScript.py b/nixos/tests/web-apps/netbox/testScript.py index f6c2c9821e12..4ff035a1f0cf 100644 --- a/nixos/tests/web-apps/netbox/testScript.py +++ b/nixos/tests/web-apps/netbox/testScript.py @@ -73,18 +73,7 @@ with subtest("Superuser can be created"): "netbox-manage createsuperuser --noinput --username netbox --email netbox@example.com" ) # Django doesn't have a "clean" way of inputting the password from the command line - machine.succeed("cat '${changePassword}' | netbox-manage shell") - -machine.wait_for_unit("network.target") - -with subtest("Home screen loads from nginx"): - machine.succeed( - "curl -sSfL http://localhost | grep 'Home | NetBox'" - ) - -with subtest("Staticfiles can be fetched"): - machine.succeed("curl -sSfL http://localhost/static/netbox.js") - machine.succeed("curl -sSfL http://localhost/static/docs/") + machine.succeed("cat '${changePassword}' | netbox-manage shell --interface python") def login(username: str, password: str): encoded_data = json.dumps({"username": username, "password": password}) @@ -101,8 +90,29 @@ def login(username: str, password: str): ) return result["key"] -with subtest("Can login"): - auth_token = login("netbox", "netbox") + +netbox_version = "${netboxVersion}" +if compare(netbox_version, '4.5.2') < 0: + with subtest("Can login"): + auth_token = login("netbox", "netbox") + +else: + with subtest("Superusertoken can be created"): + full_token = "0123456789abcdef0123456789abcdef01234567" + stdout = machine.succeed("cat ${createToken} | netbox-manage shell") + token_prefix = stdout.split(r"Bearer ")[-1].lstrip().rstrip() + auth_token = f"{token_prefix}{full_token}" + +machine.wait_for_unit("network.target") + +with subtest("Home screen loads from nginx"): + machine.succeed( + "curl -sSfL http://localhost | grep 'Home | NetBox'" + ) + +with subtest("Staticfiles can be fetched"): + machine.succeed("curl -sSfL http://localhost/static/netbox.js") + machine.succeed("curl -sSfL http://localhost/static/docs/") def get(uri: str): return json.loads( @@ -144,8 +154,8 @@ def post(uri: str, data: Dict[str, Any]): def patch(uri: str, data: Dict[str, Any]): return data_request(uri, "PATCH", data) -# Retrieve netbox version -netbox_version = get("/status/")["netbox-version"] +with subtest("Can retrieve netbox version"): + assert netbox_version == get("/status/")["netbox-version"] with subtest("Can create objects"): result = post("/dcim/sites/", {"name": "Test site", "slug": "test-site"}) @@ -263,12 +273,15 @@ if compare(netbox_version, '4.2.0') < 0: assert result["data"]["prefix_list"][0]["prefix"] == test_objects["prefixes"]["v4-with-updated-desc"]["prefix"] assert int(result["data"]["prefix_list"][0]["site"]["id"]) == int(test_objects["prefixes"]["v4-with-updated-desc"]["scope"]["id"]) -with subtest("Can login with LDAP"): - machine.wait_for_unit("openldap.service") - login("alice", "${testPassword}") +# With 4.5.2 and higher, obtaining a session cookie or token without supplying +# proper CSRF tokens on the frontend /login/ endpoint is no longer possible +if compare(netbox_version, '4.5.2') < 0: + with subtest("Can login with LDAP"): + machine.wait_for_unit("openldap.service") + login("alice", "${testPassword}") -with subtest("Can associate LDAP groups"): - result = get("/users/users/?username=${testUser}") + with subtest("Can associate LDAP groups"): + result = get("/users/users/?username=${testUser}") - assert result["count"] == 1 - assert any(group["name"] == "${testGroup}" for group in result["results"][0]["groups"]) + assert result["count"] == 1 + assert any(group["name"] == "${testGroup}" for group in result["results"][0]["groups"]) diff --git a/pkgs/by-name/ne/netbox_4_2/package.nix b/pkgs/by-name/ne/netbox_4_2/package.nix deleted file mode 100644 index ec7d07d0b243..000000000000 --- a/pkgs/by-name/ne/netbox_4_2/package.nix +++ /dev/null @@ -1,132 +0,0 @@ -{ - lib, - fetchFromGitHub, - python3, - plugins ? _ps: [ ], - nixosTests, - nix-update-script, -}: -let - py = python3.override { - packageOverrides = _final: prev: { django = prev.django_5; }; - }; - - extraBuildInputs = plugins py.pkgs; -in -py.pkgs.buildPythonApplication rec { - pname = "netbox"; - version = "4.2.9"; - - pyproject = false; - - src = fetchFromGitHub { - owner = "netbox-community"; - repo = "netbox"; - tag = "v${version}"; - hash = "sha256-uVe4YTZoxRMBfvItFa9SMHu4AaVvygfAg9GDB115TFc="; - }; - - patches = [ - ./custom-static-root.patch - ]; - - propagatedBuildInputs = - ( - with py.pkgs; - [ - django - django-cors-headers - django-debug-toolbar - django-filter - django-graphiql-debug-toolbar - django-htmx - django-mptt - django-pglocks - django-prometheus - django-redis - django-rq - django-tables2 - django-taggit - django-timezone-field - djangorestframework - drf-spectacular - drf-spectacular-sidecar - feedparser - jinja2 - markdown - netaddr - nh3 - pillow - psycopg - psycopg.optional-dependencies.c - psycopg.optional-dependencies.pool - pyyaml - requests - social-auth-core - social-auth-app-django - strawberry-graphql - strawberry-django - svgwrite - tablib - - # Optional dependencies, kept here for backward compatibility - - # for the S3 data source backend - boto3 - # for Git data source backend - dulwich - # for error reporting - sentry-sdk - ] - ++ social-auth-core.passthru.optional-dependencies.openidconnect - ) - ++ extraBuildInputs; - - buildInputs = with py.pkgs; [ - mkdocs-material - mkdocs-material-extensions - mkdocstrings - mkdocstrings-python - ]; - - nativeBuildInputs = [ py.pkgs.mkdocs ]; - - postBuild = '' - PYTHONPATH=$PYTHONPATH:netbox/ - python -m mkdocs build - ''; - - installPhase = '' - mkdir -p $out/opt/netbox - cp -r . $out/opt/netbox - chmod +x $out/opt/netbox/netbox/manage.py - makeWrapper $out/opt/netbox/netbox/manage.py $out/bin/netbox \ - --prefix PYTHONPATH : "$PYTHONPATH" - ''; - - passthru = { - python = python3; - # PYTHONPATH of all dependencies used by the package - pythonPath = py.pkgs.makePythonPath propagatedBuildInputs; - inherit (py.pkgs) gunicorn; - tests = { - netbox = nixosTests.netbox_4_2; - inherit (nixosTests) netbox-upgrade; - }; - updateScript = nix-update-script { }; - }; - - meta = { - homepage = "https://github.com/netbox-community/netbox"; - description = "IP address management (IPAM) and data center infrastructure management (DCIM) tool"; - mainProgram = "netbox"; - license = lib.licenses.asl20; - knownVulnerabilities = [ - "Netbox Version ${version} is EOL; please upgrade by following the current release notes instructions" - ]; - maintainers = with lib.maintainers; [ - minijackson - raitobezarius - ]; - }; -} diff --git a/pkgs/by-name/ne/netbox_4_3/custom-static-root.patch b/pkgs/by-name/ne/netbox_4_3/custom-static-root.patch deleted file mode 100644 index c9219fa2b871..000000000000 --- a/pkgs/by-name/ne/netbox_4_3/custom-static-root.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py -index 2de06dd10..00406af48 100644 ---- a/netbox/netbox/settings.py -+++ b/netbox/netbox/settings.py -@@ -410,7 +412,7 @@ USE_X_FORWARDED_HOST = True - X_FRAME_OPTIONS = 'SAMEORIGIN' - - # Static files (CSS, JavaScript, Images) --STATIC_ROOT = BASE_DIR + '/static' -+STATIC_ROOT = getattr(configuration, 'STATIC_ROOT', os.path.join(BASE_DIR, 'static')).rstrip('/') - STATIC_URL = f'/{BASE_PATH}static/' - STATICFILES_DIRS = ( - os.path.join(BASE_DIR, 'project-static', 'dist'), diff --git a/pkgs/by-name/ne/netbox_4_4/package.nix b/pkgs/by-name/ne/netbox_4_4/package.nix index 1a6bea9d66bf..80abaa7093f8 100644 --- a/pkgs/by-name/ne/netbox_4_4/package.nix +++ b/pkgs/by-name/ne/netbox_4_4/package.nix @@ -111,7 +111,7 @@ py.pkgs.buildPythonApplication rec { pythonPath = py.pkgs.makePythonPath dependencies; inherit (py.pkgs) gunicorn; tests = { - netbox = nixosTests.netbox_4_3; + netbox = nixosTests.netbox_4_4; inherit (nixosTests) netbox-upgrade; }; updateScript = nix-update-script { }; @@ -123,6 +123,9 @@ py.pkgs.buildPythonApplication rec { description = "IP address management (IPAM) and data center infrastructure management (DCIM) tool"; mainProgram = "netbox"; license = lib.licenses.asl20; + knownVulnerabilities = [ + "Netbox Version ${version} is EOL; please upgrade by following the current release notes instructions" + ]; maintainers = with lib.maintainers; [ minijackson raitobezarius diff --git a/pkgs/by-name/ne/netbox_4_2/custom-static-root.patch b/pkgs/by-name/ne/netbox_4_5/custom-static-root.patch similarity index 100% rename from pkgs/by-name/ne/netbox_4_2/custom-static-root.patch rename to pkgs/by-name/ne/netbox_4_5/custom-static-root.patch diff --git a/pkgs/by-name/ne/netbox_4_3/package.nix b/pkgs/by-name/ne/netbox_4_5/package.nix similarity index 91% rename from pkgs/by-name/ne/netbox_4_3/package.nix rename to pkgs/by-name/ne/netbox_4_5/package.nix index 3689a4177415..f080e6294209 100644 --- a/pkgs/by-name/ne/netbox_4_3/package.nix +++ b/pkgs/by-name/ne/netbox_4_5/package.nix @@ -16,14 +16,14 @@ let in py.pkgs.buildPythonApplication rec { pname = "netbox"; - version = "4.3.7"; + version = "4.5.5"; pyproject = false; src = fetchFromGitHub { owner = "netbox-community"; repo = "netbox"; tag = "v${version}"; - hash = "sha256-xVOP1D3C12M0M8oTrCA0M17NLuor+46UwiaKymSAVJM="; + hash = "sha256-he+WNbzIZSc2q97YjnAKHeFR0MDZCkDuAF/mfgAZuU4="; }; patches = [ @@ -34,6 +34,7 @@ py.pkgs.buildPythonApplication rec { ( with py.pkgs; [ + colorama django django-cors-headers django-debug-toolbar @@ -63,6 +64,7 @@ py.pkgs.buildPythonApplication rec { requests social-auth-core social-auth-app-django + sorl-thumbnail strawberry-graphql strawberry-django svgwrite @@ -109,7 +111,7 @@ py.pkgs.buildPythonApplication rec { pythonPath = py.pkgs.makePythonPath dependencies; inherit (py.pkgs) gunicorn; tests = { - netbox = nixosTests.netbox_4_3; + netbox = nixosTests.netbox_4_5; inherit (nixosTests) netbox-upgrade; }; updateScript = nix-update-script { }; @@ -121,9 +123,6 @@ py.pkgs.buildPythonApplication rec { description = "IP address management (IPAM) and data center infrastructure management (DCIM) tool"; mainProgram = "netbox"; license = lib.licenses.asl20; - knownVulnerabilities = [ - "Netbox Version ${version} is EOL; please upgrade by following the current release notes instructions" - ]; maintainers = with lib.maintainers; [ minijackson raitobezarius diff --git a/pkgs/development/python-modules/strawberry-django/default.nix b/pkgs/development/python-modules/strawberry-django/default.nix index 777e3ebccbd2..b7b256cc71c7 100644 --- a/pkgs/development/python-modules/strawberry-django/default.nix +++ b/pkgs/development/python-modules/strawberry-django/default.nix @@ -34,14 +34,14 @@ buildPythonPackage rec { pname = "strawberry-django"; - version = "0.74.1"; + version = "0.75.1"; pyproject = true; src = fetchFromGitHub { owner = "strawberry-graphql"; repo = "strawberry-django"; tag = version; - hash = "sha256-8T5lYM5JrxbeDCgvSGvBsimsF+tMwjS1hroqw2Gf7aA="; + hash = "sha256-vosD0JZYZzzl6Mp+AAxdXVox/7ay4FqnwqE6f1lSw3s="; }; postPatch = '' diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index f5046a7f2fea..4380dac2f84f 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -2996,7 +2996,7 @@ with pkgs; }; # Not in aliases because it wouldn't get picked up by callPackage - netbox = netbox_4_4; + netbox = netbox_4_5; netcap-nodpi = callPackage ../by-name/ne/netcap/package.nix { withDpi = false;