nixos/netbox: uplift (#531455)
This commit is contained in:
@@ -97,6 +97,16 @@
|
||||
|
||||
- The `shell_interact()` function on interactive runs of NixOS VM tests has been deprecated. Use the SSH backdoor instead.
|
||||
|
||||
- [services.netbox](#opt-services.netbox.enable) has received a number of updates:
|
||||
- Default settings can now be introspected at [](#opt-services.netbox.settings).
|
||||
- Environment files can now be passed at [](#opt-services.netbox.environmentFiles).
|
||||
- When Django [secret key](#opt-services.netbox.secretKeyFile) or [API token peppers](#opt-services.netbox.apiTokenPepperFiles)
|
||||
remain unset, random values will automatically be generated and stored below `/var/lib/netbox`.
|
||||
- Multiple peppers can now be maintained, which allows for pepper rotation.
|
||||
- All options to bind the gunicorn socket have been unified in [](#opt-services.netbox.bind)
|
||||
and the default changed to a UNIX domain socket.
|
||||
- A cookie-cutter nginx vhost can be enabled at [](#opt-services.netbox.nginx.enable).
|
||||
|
||||
- `security.run0.enableSudoAlias` now uses the `run0-sudo-shim` instead of a shell-script to improve compatibility.
|
||||
|
||||
- With `system.etc.overlay.mutable = false`, NixOS now ships an empty `/etc/machine-id` in the image. Previously the file was absent and systemd logged `System cannot boot: Missing /etc/machine-id and /etc/ is read-only` while `ConditionFirstBoot` fired on every boot. With this change, systemd now overlays a transient ID from `/run/machine-id` for the session, and `systemd-machine-id-commit.service` has `ConditionFirstBoot` so it writes the machine-id through to a persistent backing file when one is bind-mounted over `/etc/machine-id`. To persist the machine-id across reboots, bind-mount a writable file containing `uninitialized` over `/etc/machine-id` from the initrd, or set `systemd.machine_id=` on the kernel command line (use `systemd.machine_id=firmware` to derive a stable ID on hardware that supports it).
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,23 +34,10 @@ in
|
||||
# Pick the NetBox package from this config's "pkgs" argument,
|
||||
# so that `nixpkgs.config.permittedInsecurePackages` works
|
||||
package = pkgs.${oldNetbox};
|
||||
secretKeyFile = pkgs.writeText "secret" ''
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
|
||||
'';
|
||||
apiTokenPeppersFile = pkgs.writeText "pepper" ''
|
||||
kp7ht*76fiQAhUi5dHfASLlYUE_S^gI^(7J^K5M!LfoH@vl&b_
|
||||
'';
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
|
||||
recommendedProxySettings = true;
|
||||
|
||||
virtualHosts.netbox = {
|
||||
default = true;
|
||||
locations."/".proxyPass = "http://localhost:${toString config.services.netbox.port}";
|
||||
locations."/static/".alias = "/var/lib/netbox/static/";
|
||||
nginx = {
|
||||
enable = true;
|
||||
hostname = "localhost";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -81,7 +68,7 @@ in
|
||||
headers = machine.succeed(
|
||||
"curl -sSL http://localhost/api/ --head -H 'Content-Type: application/json'"
|
||||
)
|
||||
assert api_version(headers) == version
|
||||
t.assertEqual(api_version(headers), version)
|
||||
|
||||
with subtest("NetBox version is the old one"):
|
||||
check_api_version("${oldApiVersion}")
|
||||
|
||||
@@ -27,23 +27,23 @@ import ../../make-test-python.nix (
|
||||
|
||||
skipTypeCheck = true;
|
||||
|
||||
nodes.machine =
|
||||
containers.machine =
|
||||
{ config, ... }:
|
||||
{
|
||||
virtualisation.memorySize = 2048;
|
||||
boot.kernelParams = [
|
||||
# helps debugging seccomp filter issues
|
||||
"audit=1"
|
||||
];
|
||||
services.netbox = {
|
||||
enable = true;
|
||||
package = netbox;
|
||||
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" ''
|
||||
nginx = {
|
||||
enable = true;
|
||||
hostname = "localhost";
|
||||
};
|
||||
|
||||
ldapConfigFile = pkgs.writeText "ldap_config.py" ''
|
||||
import ldap
|
||||
from django_auth_ldap.config import LDAPSearch, PosixGroupType
|
||||
|
||||
@@ -70,18 +70,6 @@ import ../../make-test-python.nix (
|
||||
'';
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
|
||||
recommendedProxySettings = true;
|
||||
|
||||
virtualHosts.netbox = {
|
||||
default = true;
|
||||
locations."/".proxyPass = "http://localhost:${toString config.services.netbox.port}";
|
||||
locations."/static/".alias = "/var/lib/netbox/static/";
|
||||
};
|
||||
};
|
||||
|
||||
# Adapted from the sssd-ldap NixOS test
|
||||
services.openldap = {
|
||||
enable = true;
|
||||
|
||||
@@ -62,11 +62,11 @@ def compare(a: str, b: str):
|
||||
|
||||
with subtest("Home screen loads"):
|
||||
machine.wait_until_succeeds(
|
||||
"curl -sSfL http://[::1]:8001 | grep '<title>Home | NetBox</title>'"
|
||||
"curl -sSfL http://[::1]:80 | grep '<title>Home | NetBox</title>'"
|
||||
)
|
||||
|
||||
with subtest("Staticfiles are generated"):
|
||||
machine.succeed("test -e /var/lib/netbox/static/netbox.js")
|
||||
machine.wait_for_file("/var/lib/netbox/static/netbox.js")
|
||||
|
||||
with subtest("Superuser can be created"):
|
||||
machine.succeed(
|
||||
@@ -155,7 +155,7 @@ def patch(uri: str, data: Dict[str, Any]):
|
||||
return data_request(uri, "PATCH", data)
|
||||
|
||||
with subtest("Can retrieve netbox version"):
|
||||
assert netbox_version == get("/status/")["netbox-version"]
|
||||
t.assertEqual(netbox_version, get("/status/")["netbox-version"])
|
||||
|
||||
with subtest("Can create objects"):
|
||||
result = post("/dcim/sites/", {"name": "Test site", "slug": "test-site"})
|
||||
@@ -196,28 +196,28 @@ with subtest("Can create objects"):
|
||||
with subtest("Can list objects"):
|
||||
result = get("/dcim/sites/")
|
||||
|
||||
assert result["count"] == 1
|
||||
assert result["results"][0]["id"] == site_id
|
||||
assert result["results"][0]["name"] == "Test site"
|
||||
assert result["results"][0]["description"] == ""
|
||||
t.assertEqual(result["count"], 1)
|
||||
t.assertEqual(result["results"][0]["id"], site_id)
|
||||
t.assertEqual(result["results"][0]["name"], "Test site")
|
||||
t.assertEqual(result["results"][0]["description"], "")
|
||||
|
||||
result = get("/dcim/device-types/")
|
||||
assert result["count"] == 1
|
||||
assert result["results"][0]["id"] == device_type_id
|
||||
assert result["results"][0]["model"] == "Test device type"
|
||||
t.assertEqual(result["count"], 1)
|
||||
t.assertEqual(result["results"][0]["id"], device_type_id)
|
||||
t.assertEqual(result["results"][0]["model"], "Test device type")
|
||||
|
||||
with subtest("Can update objects"):
|
||||
new_description = "Test site description"
|
||||
patch(f"/dcim/sites/{site_id}/", {"description": new_description})
|
||||
result = get(f"/dcim/sites/{site_id}/")
|
||||
assert result["description"] == new_description
|
||||
t.assertEqual(result["description"], new_description)
|
||||
|
||||
with subtest("Can delete objects"):
|
||||
# Delete a device-type since no object depends on it
|
||||
delete(f"/dcim/device-types/{device_type_id}/")
|
||||
|
||||
result = get("/dcim/device-types/")
|
||||
assert result["count"] == 0
|
||||
t.assertEqual(result["count"], 0)
|
||||
|
||||
def request_graphql(query: str):
|
||||
return machine.succeed(
|
||||
@@ -252,10 +252,10 @@ if compare(netbox_version, '4.2.0') >= 0:
|
||||
|
||||
answer = request_graphql(graphql_query)
|
||||
result = json.loads(answer)
|
||||
assert len(result["data"]["prefix_list"]) == 3
|
||||
assert test_objects["prefixes"]["v4-with-updated-desc"] in result["data"]["prefix_list"]
|
||||
assert test_objects["prefixes"]["v6-cidr-32"] in result["data"]["prefix_list"]
|
||||
assert test_objects["prefixes"]["v6-cidr-48"] in result["data"]["prefix_list"]
|
||||
t.assertEqual(len(result["data"]["prefix_list"]), 3)
|
||||
t.assertIn(test_objects["prefixes"]["v4-with-updated-desc"], result["data"]["prefix_list"])
|
||||
t.assertIn(test_objects["prefixes"]["v6-cidr-32"], result["data"]["prefix_list"])
|
||||
t.assertIn(test_objects["prefixes"]["v6-cidr-48"], result["data"]["prefix_list"])
|
||||
|
||||
if compare(netbox_version, '4.2.0') < 0:
|
||||
with subtest("Can use the GraphQL API (Netbox <= 4.2.0)"):
|
||||
@@ -270,8 +270,8 @@ if compare(netbox_version, '4.2.0') < 0:
|
||||
''')
|
||||
result = json.loads(answer)
|
||||
print(result["data"]["prefix_list"][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"])
|
||||
t.assertEqual(result["data"]["prefix_list"][0]["prefix"], test_objects["prefixes"]["v4-with-updated-desc"]["prefix"])
|
||||
t.assertEqual(int(result["data"]["prefix_list"][0]["site"]["id"]), int(test_objects["prefixes"]["v4-with-updated-desc"]["scope"]["id"]))
|
||||
|
||||
# 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
|
||||
@@ -283,5 +283,8 @@ if compare(netbox_version, '4.5.2') < 0:
|
||||
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"])
|
||||
t.assertEqual(result["count"], 1)
|
||||
t.assertTrue(any(group["name"] == "${testGroup}" for group in result["results"][0]["groups"]))
|
||||
|
||||
# Print systemd unit hardening state
|
||||
machine.log(machine.execute("systemd-analyze security netbox.service netbox-rq.service netbox-housekeeping.service | grep -v ✓")[1])
|
||||
|
||||
Reference in New Issue
Block a user