2022-12-04 13:45:43 +00:00
|
|
|
{
|
|
|
|
lib,
|
|
|
|
pkgs,
|
2022-12-04 16:10:00 +00:00
|
|
|
config,
|
2022-12-04 13:45:43 +00:00
|
|
|
...
|
2023-09-11 23:22:18 +01:00
|
|
|
}: let
|
|
|
|
inherit (lib.modules) mkIf mkMerge;
|
2023-09-18 03:56:58 +01:00
|
|
|
inherit (lib.options) mkOption mkEnableOption;
|
2023-09-18 15:40:33 +01:00
|
|
|
inherit (lib.lists) filter;
|
2023-09-11 23:22:18 +01:00
|
|
|
inherit (lib) types;
|
2023-09-21 05:06:27 +01:00
|
|
|
inherit (builtins) isString listToAttrs attrNames;
|
2023-09-11 23:22:18 +01:00
|
|
|
|
2022-11-15 13:42:28 +00:00
|
|
|
cfg = config.services.secrets;
|
2023-09-11 23:22:18 +01:00
|
|
|
|
2023-09-18 03:56:58 +01:00
|
|
|
secretsLib = import ./secretsLib/lib.nix {
|
2023-09-11 23:22:18 +01:00
|
|
|
inherit lib pkgs;
|
|
|
|
};
|
2022-11-15 13:42:28 +00:00
|
|
|
in {
|
|
|
|
options = {
|
|
|
|
services.secrets = {
|
2023-09-18 03:56:58 +01:00
|
|
|
enable = mkEnableOption "secrets";
|
2022-11-15 13:42:28 +00:00
|
|
|
|
|
|
|
debug = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
};
|
|
|
|
|
|
|
|
createSecretsDir = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
secretsDirUser = mkOption {
|
2023-09-11 23:22:18 +01:00
|
|
|
type = types.either types.str types.int;
|
2022-11-15 13:42:28 +00:00
|
|
|
default = "root";
|
|
|
|
};
|
|
|
|
|
|
|
|
secretsDirGroup = mkOption {
|
2023-09-11 23:22:18 +01:00
|
|
|
type = types.either types.str types.int;
|
2022-11-15 13:42:28 +00:00
|
|
|
default = "root";
|
|
|
|
};
|
|
|
|
|
|
|
|
secretsDir = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "/secrets";
|
|
|
|
};
|
|
|
|
|
2023-09-18 03:56:58 +01:00
|
|
|
vaultLogin = {
|
|
|
|
enable = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
};
|
|
|
|
vaultURL = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = cfg.vaultURL;
|
|
|
|
};
|
|
|
|
loginUsername = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
};
|
|
|
|
loginPasswordFile = mkOption {
|
|
|
|
type = types.nullOr types.path;
|
|
|
|
default =
|
|
|
|
if cfg.secrets ? "vault_password"
|
|
|
|
then cfg.secrets."vault_password".path
|
|
|
|
else null;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
autoSecrets = {
|
|
|
|
enable = mkEnableOption "autoSecrets";
|
|
|
|
affectedSystemdServices = mkOption {
|
2024-03-10 17:26:18 +00:00
|
|
|
type = types.listOf (types.either types.str (types.submodule {
|
2023-09-18 03:56:58 +01:00
|
|
|
options = {
|
|
|
|
name = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
};
|
|
|
|
withPartOf = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = true;
|
2023-09-18 15:40:33 +01:00
|
|
|
description = "add service to auto-secrets's PartOf; this may not be wanted for stuff on a timer such as backups";
|
2023-09-18 03:56:58 +01:00
|
|
|
};
|
|
|
|
};
|
|
|
|
default = {};
|
|
|
|
}));
|
|
|
|
default = [];
|
|
|
|
description = "systemd target names to be restarted with and start after auto-secrets has run, without the .service suffix";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2023-09-14 13:54:56 +01:00
|
|
|
requiredVaultPaths = mkOption {
|
|
|
|
type = types.listOf (types.oneOf [
|
|
|
|
types.str
|
|
|
|
(types.submodule {
|
|
|
|
options = {
|
|
|
|
path = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
};
|
|
|
|
capabilities = mkOption {
|
|
|
|
type = types.listOf types.str;
|
|
|
|
default = ["read" "list"];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
})
|
|
|
|
]);
|
|
|
|
default = [];
|
|
|
|
description = "default vault paths as API path, not kv2 path";
|
|
|
|
};
|
|
|
|
|
2022-11-15 13:42:28 +00:00
|
|
|
vaultURL = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "https://vault.owo.monster";
|
|
|
|
description = "default Vault URL, can be overrided with env variables";
|
|
|
|
};
|
|
|
|
|
|
|
|
extraFunctions = mkOption {
|
|
|
|
type = types.lines;
|
|
|
|
default = "";
|
2023-09-21 05:06:27 +01:00
|
|
|
description = "extra bash functions to add to top of script for init";
|
|
|
|
};
|
|
|
|
|
|
|
|
extraCheckFunctions = mkOption {
|
|
|
|
type = types.lines;
|
|
|
|
default = "";
|
|
|
|
description = "extra bash functions to add to top of script for check";
|
2022-11-15 13:42:28 +00:00
|
|
|
};
|
|
|
|
|
2023-09-11 23:22:18 +01:00
|
|
|
uidMap = mkOption {
|
|
|
|
type = types.attrsOf types.int;
|
2023-09-21 05:06:27 +01:00
|
|
|
default = listToAttrs (map (
|
|
|
|
secretName: let
|
|
|
|
secretUserName = cfg.secrets.${secretName}.user;
|
|
|
|
in {
|
|
|
|
name = "${secretUserName}";
|
|
|
|
value = config.users.users.${secretUserName}.uid;
|
|
|
|
}
|
|
|
|
) (filter
|
|
|
|
(secretName: let
|
|
|
|
secret = cfg.secrets.${secretName};
|
|
|
|
in
|
|
|
|
isString secret.user
|
|
|
|
&& (
|
|
|
|
if config.users.users ? "${secret.user}"
|
|
|
|
then true
|
|
|
|
else builtins.trace "warning: secrets module could not find a uid mapping for user ${secret.user}" false
|
|
|
|
))
|
|
|
|
(attrNames cfg.secrets)));
|
|
|
|
description = ''
|
|
|
|
optional mapping of users to user IDs
|
|
|
|
required for SYSROOT when user isn't available on host
|
|
|
|
defaults to getting values from users.users.<name>.uid for all secrets with user set as string
|
|
|
|
'';
|
2023-09-11 23:22:18 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
gidMap = mkOption {
|
|
|
|
type = types.attrsOf types.int;
|
2023-09-21 05:06:27 +01:00
|
|
|
default = listToAttrs (map (
|
|
|
|
secretName: let
|
|
|
|
secretGroupName = cfg.secrets.${secretName}.group;
|
|
|
|
in {
|
|
|
|
name = "${secretGroupName}";
|
|
|
|
value = config.users.groups.${secretGroupName}.gid;
|
|
|
|
}
|
|
|
|
) (filter
|
|
|
|
(secretName: let
|
|
|
|
secret = cfg.secrets.${secretName};
|
|
|
|
in
|
|
|
|
isString secret.group
|
|
|
|
&& (
|
|
|
|
if config.users.groups ? "${secret.group}"
|
|
|
|
then true
|
|
|
|
else builtins.trace "warning: secrets module could not find a gid mapping for group ${secret.group}" false
|
|
|
|
))
|
|
|
|
(attrNames cfg.secrets)));
|
|
|
|
description = ''
|
|
|
|
optional mapping of groups to group IDs
|
|
|
|
required for SYSROOT when group isn't available on host
|
|
|
|
defaults to getting values from users.groups.<group>.gid for all secrets with group set as string
|
|
|
|
'';
|
2023-09-11 23:22:18 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
packages = mkOption {
|
2022-11-15 13:42:28 +00:00
|
|
|
type = types.listOf types.package;
|
2023-09-13 16:21:54 +01:00
|
|
|
default = [];
|
2023-09-21 05:06:27 +01:00
|
|
|
description = "packages for init script";
|
|
|
|
};
|
|
|
|
|
|
|
|
checkPackages = mkOption {
|
|
|
|
type = types.listOf types.package;
|
|
|
|
default = [];
|
|
|
|
description = "packages for checkscript";
|
2022-11-15 13:42:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
secrets = mkOption {
|
2022-12-04 16:10:00 +00:00
|
|
|
type = types.attrsOf (types.submodule ({name, ...}: {
|
2022-11-15 13:42:28 +00:00
|
|
|
options = {
|
|
|
|
user = mkOption {
|
2023-09-11 23:22:18 +01:00
|
|
|
type = types.either types.str types.int;
|
2022-11-15 13:42:28 +00:00
|
|
|
default = "root";
|
|
|
|
};
|
|
|
|
group = mkOption {
|
2023-09-11 23:22:18 +01:00
|
|
|
type = types.either types.str types.int;
|
2022-11-15 13:42:28 +00:00
|
|
|
default = "root";
|
|
|
|
};
|
|
|
|
permissions = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "660";
|
|
|
|
};
|
|
|
|
path = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "${cfg.secretsDir}/${name}";
|
|
|
|
};
|
|
|
|
|
|
|
|
fetchScript = mkOption {
|
2023-09-11 23:22:18 +01:00
|
|
|
type = types.nullOr types.lines;
|
|
|
|
default = null;
|
2022-11-15 13:42:28 +00:00
|
|
|
description = ''
|
2023-09-11 23:22:18 +01:00
|
|
|
script used to fetch secrets, $secretFile is secret.path
|
2022-11-15 13:42:28 +00:00
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
checkScript = mkOption {
|
|
|
|
type = types.nullOr types.lines;
|
|
|
|
default = null;
|
|
|
|
description = ''
|
|
|
|
script used to check contents of secret file, set LOCAL_FAIL to true on failure
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
manual = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
description = "should the secret be manually deployed";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}));
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2023-09-18 03:56:58 +01:00
|
|
|
config =
|
|
|
|
{
|
|
|
|
assertions = [
|
|
|
|
{
|
|
|
|
assertion = !(cfg.autoSecrets.enable && !cfg.vaultLogin.enable);
|
|
|
|
message = "vaultLogin needs to be enabled and configured for autoSecrets to work";
|
|
|
|
}
|
|
|
|
{
|
|
|
|
assertion = !(cfg.vaultLogin.enable && !cfg.vaultLogin.loginPasswordFile == null);
|
|
|
|
message = "loginPasswordFile needs to be set or a secret called vault_password needs to be configured for vaultLogin to work";
|
|
|
|
}
|
2022-11-15 13:42:28 +00:00
|
|
|
];
|
2023-09-18 03:56:58 +01:00
|
|
|
}
|
|
|
|
// (mkMerge [
|
2024-03-10 17:26:18 +00:00
|
|
|
(mkIf cfg.enable {
|
2023-09-18 03:56:58 +01:00
|
|
|
environment.systemPackages = [
|
|
|
|
(secretsLib.mkSecretsInitScript cfg)
|
|
|
|
(secretsLib.mkSecretsCheckScript cfg)
|
|
|
|
];
|
|
|
|
})
|
2022-11-15 13:42:28 +00:00
|
|
|
|
2023-09-18 03:56:58 +01:00
|
|
|
(mkIf (cfg.enable && cfg.vaultLogin.enable) {
|
|
|
|
environment.systemPackages = [
|
|
|
|
(secretsLib.mkVaultLoginScript cfg)
|
|
|
|
];
|
|
|
|
})
|
|
|
|
|
|
|
|
(mkIf (cfg.enable && cfg.autoSecrets.enable) {
|
|
|
|
systemd = let
|
|
|
|
# normalise this to make next part easier
|
|
|
|
affectedSystemdServices =
|
|
|
|
map (unit: let
|
|
|
|
name =
|
|
|
|
if isString unit
|
|
|
|
then unit
|
|
|
|
else unit.name;
|
|
|
|
withPartOf =
|
|
|
|
if isString unit
|
|
|
|
then true
|
|
|
|
else unit.withPartOf;
|
|
|
|
in {
|
|
|
|
inherit name withPartOf;
|
|
|
|
})
|
|
|
|
cfg.autoSecrets.affectedSystemdServices;
|
|
|
|
in {
|
2023-09-18 15:40:33 +01:00
|
|
|
services =
|
|
|
|
(listToAttrs (map (unitConfig: {
|
2024-03-10 17:26:18 +00:00
|
|
|
inherit (unitConfig) name;
|
2023-09-18 15:40:33 +01:00
|
|
|
value = {
|
|
|
|
after = ["auto-secrets.service"];
|
|
|
|
wants = ["auto-secrets.service"];
|
2023-09-18 15:41:02 +01:00
|
|
|
partOf = mkIf unitConfig.withPartOf ["auto-secrets.service"];
|
2023-09-18 15:40:33 +01:00
|
|
|
};
|
|
|
|
})
|
|
|
|
affectedSystemdServices))
|
|
|
|
// {
|
|
|
|
auto-secrets.partOf =
|
|
|
|
map (unitConfig: unitConfig.name + ".service")
|
2023-09-21 05:06:27 +01:00
|
|
|
(filter
|
2023-09-18 15:40:33 +01:00
|
|
|
(unitConfig: unitConfig.withPartOf)
|
|
|
|
affectedSystemdServices);
|
2023-09-18 15:41:02 +01:00
|
|
|
};
|
2023-09-18 03:56:58 +01:00
|
|
|
};
|
|
|
|
})
|
|
|
|
|
|
|
|
(mkIf (cfg.enable && cfg.autoSecrets.enable) {
|
|
|
|
systemd.services.auto-secrets = {
|
|
|
|
wantedBy = ["multi-user.target"];
|
|
|
|
after = ["network.target"];
|
2024-05-25 15:28:51 +01:00
|
|
|
path = with pkgs; [bash vault getent];
|
2023-09-18 03:56:58 +01:00
|
|
|
script = ''
|
|
|
|
${secretsLib.mkVaultLoginScript cfg}/bin/vault-login
|
|
|
|
${secretsLib.mkSecretsInitScript cfg}/bin/secrets-init
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
})
|
|
|
|
|
|
|
|
(mkIf (cfg.enable && cfg.createSecretsDir) {
|
|
|
|
systemd.tmpfiles.rules = [
|
|
|
|
"d ${cfg.secretsDir} - ${toString cfg.secretsDirUser} ${toString cfg.secretsDirGroup}"
|
|
|
|
];
|
|
|
|
})
|
|
|
|
]);
|
2022-11-15 13:42:28 +00:00
|
|
|
}
|