nixos/netbox: improve secret management
With this change secrets will be automatically generated if left at their individual option defaults. We now also allow configuration of multiple peppers, to allow proper rotation as intended by upstream. Finally, we now we prevent secrets from leaking into the store.
This commit is contained in:
@@ -6,6 +6,13 @@
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
any
|
||||
attrValues
|
||||
mkChangedOptionModule
|
||||
optionalString
|
||||
;
|
||||
|
||||
cfg = config.services.netbox;
|
||||
pythonFmt = pkgs.formats.pythonVars { };
|
||||
staticDir = cfg.dataDir + "/static";
|
||||
@@ -19,6 +26,8 @@ let
|
||||
settingsFile
|
||||
extraConfigFile
|
||||
];
|
||||
secretKeyFile =
|
||||
if cfg.secretKeyFile != null then cfg.secretKeyFile else "${cfg.dataDir}/secret.key";
|
||||
|
||||
pkg =
|
||||
(cfg.package.overrideAttrs (old: {
|
||||
@@ -51,6 +60,16 @@ let
|
||||
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(mkChangedOptionModule
|
||||
[ "services" "netbox" "apiTokenPeppersFile" ]
|
||||
[ "services" "netbox" "apiTokenPepperFiles" ]
|
||||
(config: {
|
||||
"1" = config.services.netbox.apiTokenPeppersFile;
|
||||
})
|
||||
)
|
||||
];
|
||||
|
||||
options.services.netbox = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
@@ -159,18 +178,58 @@ in
|
||||
};
|
||||
|
||||
secretKeyFile = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (pathWith {
|
||||
inStore = false;
|
||||
});
|
||||
default = null;
|
||||
description = ''
|
||||
Path to a file containing the secret key.
|
||||
Path to a file containing the [secret key].
|
||||
|
||||
The secret key is used for hashing passwords and signing HTTP cookies.
|
||||
It can be rotated without data loss; however all existing user sessions
|
||||
will be invalidated.
|
||||
|
||||
::: {.note}
|
||||
If unset, a random secret will be created automatically at
|
||||
`/var/lib/netbox/secret.key`.
|
||||
:::
|
||||
|
||||
[secret key]: https://netboxlabs.com/docs/netbox/configuration/required-parameters/#secret_key
|
||||
'';
|
||||
};
|
||||
|
||||
apiTokenPeppersFile = lib.mkOption {
|
||||
type = with lib.types; nullOr path;
|
||||
apiTokenPepperFiles = lib.mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
attrsOf (pathWith {
|
||||
inStore = false;
|
||||
});
|
||||
default = {
|
||||
"1" = "${cfg.dataDir}/pepper.1";
|
||||
};
|
||||
defaultText = lib.literalExpression ''
|
||||
{
|
||||
"1" = "''${config.services.netbox.dataDir}/pepper.1";
|
||||
}
|
||||
'';
|
||||
example = {
|
||||
"1" = "/run/keys/netbox-pepper-old";
|
||||
"2" = "/run/keys/netbox-pepper-current";
|
||||
};
|
||||
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)
|
||||
Mapping of cryptographic pepper IDs to files containing the pepper values.
|
||||
|
||||
Peppers provide an additional secret input in hashing operations. They
|
||||
are required for v2 API tokens (NetBox 4.5+).
|
||||
|
||||
::: {.note}
|
||||
By default a random pepper will be created automatically at
|
||||
`/var/lib/netbox/pepper.1` and configured with pepper ID 1.
|
||||
:::
|
||||
|
||||
[cryptographic peppers]: https://netboxlabs.com/docs/netbox/configuration/required-parameters/#api_token_peppers
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -226,6 +285,7 @@ in
|
||||
AUTH_LDAP_FIND_GROUP_PERMS = True
|
||||
'';
|
||||
};
|
||||
|
||||
keycloakClientSecret = lib.mkOption {
|
||||
type = with lib.types; nullOr path;
|
||||
default = null;
|
||||
@@ -291,15 +351,17 @@ in
|
||||
};
|
||||
|
||||
extraConfig = ''
|
||||
with open("${cfg.secretKeyFile}", "r") as file:
|
||||
with open("${secretKeyFile}", "r") as file:
|
||||
SECRET_KEY = file.readline()
|
||||
|
||||
API_TOKEN_PEPPERS = {
|
||||
${lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (id: file: ''
|
||||
${id}: open("${file}", "r").read().strip(),
|
||||
'') cfg.apiTokenPepperFiles
|
||||
)}
|
||||
}
|
||||
''
|
||||
+ (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()
|
||||
@@ -356,6 +418,21 @@ in
|
||||
environment.PYTHONPATH = pkg.pythonPath;
|
||||
|
||||
preStart = ''
|
||||
# Generate random default secrets, if the user didn't supply any.
|
||||
${optionalString (cfg.secretKeyFile == null) ''
|
||||
if [ ! -e "${secretKeyFile}" ]; then
|
||||
${pkg}/opt/netbox/netbox/generate_secret_key.py > "${secretKeyFile}"
|
||||
fi
|
||||
''}
|
||||
${optionalString
|
||||
(any (path: path == "${cfg.dataDir}/pepper.1") (attrValues cfg.apiTokenPepperFiles))
|
||||
''
|
||||
if [ ! -e "${cfg.dataDir}/pepper.1" ]; then
|
||||
${pkg}/opt/netbox/netbox/generate_secret_key.py > "${cfg.dataDir}/pepper.1"
|
||||
fi
|
||||
''
|
||||
}
|
||||
|
||||
# On the first run, or on upgrade / downgrade, run migrations and related.
|
||||
# This mostly correspond to upstream NetBox's 'upgrade.sh' script.
|
||||
versionFile="${cfg.dataDir}/version"
|
||||
@@ -463,12 +540,5 @@ 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`";
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,12 +34,6 @@ 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 = {
|
||||
|
||||
@@ -34,13 +34,6 @@ import ../../make-test-python.nix (
|
||||
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" ''
|
||||
|
||||
Reference in New Issue
Block a user