reformat code base with nixfmt

This commit is contained in:
Jörg Thalheim 2024-11-17 12:17:45 +01:00 committed by Jörg Thalheim
parent b05bdb2650
commit 6b85086bcc
24 changed files with 1592 additions and 1159 deletions

View file

@ -1,4 +1,10 @@
{ config, options, lib, pkgs, ... }:
{
config,
options,
lib,
pkgs,
...
}:
let
cfg = config.sops;
@ -8,7 +14,7 @@ let
inherit cfg;
inherit (pkgs) writeTextFile;
};
manifest = manifestFor "" regularSecrets regularTemplates {};
manifest = manifestFor "" regularSecrets regularTemplates { };
pathNotInStore = lib.mkOptionType {
name = "pathNotInStore";
@ -23,143 +29,165 @@ let
# Currently, all templates are "regular" (there's no support for `neededForUsers` for templates.)
regularTemplates = cfg.templates;
useSystemdActivation = (options.systemd ? sysusers && config.systemd.sysusers.enable) ||
(options.services ? userborn && config.services.userborn.enable);
useSystemdActivation =
(options.systemd ? sysusers && config.systemd.sysusers.enable)
|| (options.services ? userborn && config.services.userborn.enable);
withEnvironment = import ./with-environment.nix {
inherit cfg lib;
};
secretType = lib.types.submodule ({ config, ... }: {
config = {
sopsFile = lib.mkOptionDefault cfg.defaultSopsFile;
sopsFileHash = lib.mkOptionDefault (lib.optionalString cfg.validateSopsFiles "${builtins.hashFile "sha256" config.sopsFile}");
};
options = {
name = lib.mkOption {
type = lib.types.str;
default = config._module.args.name;
description = ''
Name of the file used in /run/secrets
'';
secretType = lib.types.submodule (
{ config, ... }:
{
config = {
sopsFile = lib.mkOptionDefault cfg.defaultSopsFile;
sopsFileHash = lib.mkOptionDefault (
lib.optionalString cfg.validateSopsFiles "${builtins.hashFile "sha256" config.sopsFile}"
);
};
key = lib.mkOption {
type = lib.types.str;
default = if cfg.defaultSopsKey != null then cfg.defaultSopsKey else config._module.args.name;
description = ''
Key used to lookup in the sops file.
No tested data structures are supported right now.
This option is ignored if format is binary.
"" means whole file.
'';
options = {
name = lib.mkOption {
type = lib.types.str;
default = config._module.args.name;
description = ''
Name of the file used in /run/secrets
'';
};
key = lib.mkOption {
type = lib.types.str;
default = if cfg.defaultSopsKey != null then cfg.defaultSopsKey else config._module.args.name;
description = ''
Key used to lookup in the sops file.
No tested data structures are supported right now.
This option is ignored if format is binary.
"" means whole file.
'';
};
path = lib.mkOption {
type = lib.types.str;
default =
if config.neededForUsers then
"/run/secrets-for-users/${config.name}"
else
"/run/secrets/${config.name}";
defaultText = "/run/secrets-for-users/$name when neededForUsers is set, /run/secrets/$name when otherwise.";
description = ''
Path where secrets are symlinked to.
If the default is kept no symlink is created.
'';
};
format = lib.mkOption {
type = lib.types.enum [
"yaml"
"json"
"binary"
"dotenv"
"ini"
];
default = cfg.defaultSopsFormat;
description = ''
File format used to decrypt the sops secret.
Binary files are written to the target file as is.
'';
};
mode = lib.mkOption {
type = lib.types.str;
default = "0400";
description = ''
Permissions mode of the in octal.
'';
};
owner = lib.mkOption {
type = with lib.types; nullOr str;
default = null;
description = ''
User of the file. Can only be set if uid is 0.
'';
};
uid = lib.mkOption {
type = with lib.types; nullOr int;
default = 0;
description = ''
UID of the file, only applied when owner is null. The UID will be applied even if the corresponding user doesn't exist.
'';
};
group = lib.mkOption {
type = with lib.types; nullOr str;
default = if config.owner != null then users.${config.owner}.group else null;
defaultText = lib.literalMD "{option}`config.users.users.\${owner}.group`";
description = ''
Group of the file. Can only be set if gid is 0.
'';
};
gid = lib.mkOption {
type = with lib.types; nullOr int;
default = 0;
description = ''
GID of the file, only applied when group is null. The GID will be applied even if the corresponding group doesn't exist.
'';
};
sopsFile = lib.mkOption {
type = lib.types.path;
defaultText = lib.literalExpression "\${config.sops.defaultSopsFile}";
description = ''
Sops file the secret is loaded from.
'';
};
sopsFileHash = lib.mkOption {
type = lib.types.str;
readOnly = true;
description = ''
Hash of the sops file, useful in <xref linkend="opt-systemd.services._name_.restartTriggers" />.
'';
};
restartUnits = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [ "sshd.service" ];
description = ''
Names of units that should be restarted when this secret changes.
This works the same way as <xref linkend="opt-systemd.services._name_.restartTriggers" />.
'';
};
reloadUnits = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [ "sshd.service" ];
description = ''
Names of units that should be reloaded when this secret changes.
This works the same way as <xref linkend="opt-systemd.services._name_.reloadTriggers" />.
'';
};
neededForUsers = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Enabling this option causes the secret to be decrypted before users and groups are created.
This can be used to retrieve user's passwords from sops-nix.
Setting this option moves the secret to /run/secrets-for-users and disallows setting owner and group to anything else than root.
'';
};
};
path = lib.mkOption {
type = lib.types.str;
default = if config.neededForUsers then "/run/secrets-for-users/${config.name}" else "/run/secrets/${config.name}";
defaultText = "/run/secrets-for-users/$name when neededForUsers is set, /run/secrets/$name when otherwise.";
description = ''
Path where secrets are symlinked to.
If the default is kept no symlink is created.
'';
};
format = lib.mkOption {
type = lib.types.enum ["yaml" "json" "binary" "dotenv" "ini"];
default = cfg.defaultSopsFormat;
description = ''
File format used to decrypt the sops secret.
Binary files are written to the target file as is.
'';
};
mode = lib.mkOption {
type = lib.types.str;
default = "0400";
description = ''
Permissions mode of the in octal.
'';
};
owner = lib.mkOption {
type = with lib.types; nullOr str;
default = null;
description = ''
User of the file. Can only be set if uid is 0.
'';
};
uid = lib.mkOption {
type = with lib.types; nullOr int;
default = 0;
description = ''
UID of the file, only applied when owner is null. The UID will be applied even if the corresponding user doesn't exist.
'';
};
group = lib.mkOption {
type = with lib.types; nullOr str;
default = if config.owner != null then users.${config.owner}.group else null;
defaultText = lib.literalMD "{option}`config.users.users.\${owner}.group`";
description = ''
Group of the file. Can only be set if gid is 0.
'';
};
gid = lib.mkOption {
type = with lib.types; nullOr int;
default = 0;
description = ''
GID of the file, only applied when group is null. The GID will be applied even if the corresponding group doesn't exist.
'';
};
sopsFile = lib.mkOption {
type = lib.types.path;
defaultText = lib.literalExpression "\${config.sops.defaultSopsFile}";
description = ''
Sops file the secret is loaded from.
'';
};
sopsFileHash = lib.mkOption {
type = lib.types.str;
readOnly = true;
description = ''
Hash of the sops file, useful in <xref linkend="opt-systemd.services._name_.restartTriggers" />.
'';
};
restartUnits = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [ "sshd.service" ];
description = ''
Names of units that should be restarted when this secret changes.
This works the same way as <xref linkend="opt-systemd.services._name_.restartTriggers" />.
'';
};
reloadUnits = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [ "sshd.service" ];
description = ''
Names of units that should be reloaded when this secret changes.
This works the same way as <xref linkend="opt-systemd.services._name_.reloadTriggers" />.
'';
};
neededForUsers = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Enabling this option causes the secret to be decrypted before users and groups are created.
This can be used to retrieve user's passwords from sops-nix.
Setting this option moves the secret to /run/secrets-for-users and disallows setting owner and group to anything else than root.
'';
};
};
});
}
);
# Skip ssh keys deployed with sops to avoid a catch 22
defaultImportKeys = algo:
defaultImportKeys =
algo:
if config.services.openssh.enable then
map (e: e.path) (lib.filter (e: e.type == algo && !(lib.hasPrefix "/run/secrets" e.path)) config.services.openssh.hostKeys)
map (e: e.path) (
lib.filter (
e: e.type == algo && !(lib.hasPrefix "/run/secrets" e.path)
) config.services.openssh.hostKeys
)
else
[];
in {
[ ];
in
{
options.sops = {
secrets = lib.mkOption {
type = lib.types.attrsOf secretType;
default = {};
default = { };
description = ''
Path where the latest secrets are mounted to.
'';
@ -208,14 +236,22 @@ in {
};
log = lib.mkOption {
type = lib.types.listOf (lib.types.enum [ "keyImport" "secretChanges" ]);
default = [ "keyImport" "secretChanges" ];
type = lib.types.listOf (
lib.types.enum [
"keyImport"
"secretChanges"
]
);
default = [
"keyImport"
"secretChanges"
];
description = "What to log";
};
environment = lib.mkOption {
type = lib.types.attrsOf (lib.types.either lib.types.str lib.types.path);
default = {};
default = { };
description = ''
Environment variables to set before calling sops-install-secrets.
@ -230,7 +266,7 @@ in {
package = lib.mkOption {
type = lib.types.package;
default = (pkgs.callPackage ../.. {}).sops-install-secrets;
default = (pkgs.callPackage ../.. { }).sops-install-secrets;
defaultText = lib.literalExpression "(pkgs.callPackage ../.. {}).sops-install-secrets";
description = ''
sops-install-secrets package to use.
@ -240,9 +276,10 @@ in {
validationPackage = lib.mkOption {
type = lib.types.package;
default =
if pkgs.stdenv.buildPlatform == pkgs.stdenv.hostPlatform
then sops-install-secrets
else (pkgs.pkgsBuildHost.callPackage ../.. {}).sops-install-secrets;
if pkgs.stdenv.buildPlatform == pkgs.stdenv.hostPlatform then
sops-install-secrets
else
(pkgs.pkgsBuildHost.callPackage ../.. { }).sops-install-secrets;
defaultText = lib.literalExpression "config.sops.package";
description = ''
@ -326,40 +363,78 @@ in {
imports = [
./templates
./secrets-for-users
(lib.mkRenamedOptionModule [ "sops" "gnupgHome" ] [ "sops" "gnupg" "home" ])
(lib.mkRenamedOptionModule [ "sops" "sshKeyPaths" ] [ "sops" "gnupg" "sshKeyPaths" ])
(lib.mkRenamedOptionModule
[
"sops"
"gnupgHome"
]
[
"sops"
"gnupg"
"home"
]
)
(lib.mkRenamedOptionModule
[
"sops"
"sshKeyPaths"
]
[
"sops"
"gnupg"
"sshKeyPaths"
]
)
];
config = lib.mkMerge [
(lib.mkIf (cfg.secrets != {}) {
assertions = [{
assertion = cfg.gnupg.home != null || cfg.gnupg.sshKeyPaths != [] || cfg.age.keyFile != null || cfg.age.sshKeyPaths != [];
message = "No key source configured for sops. Either set services.openssh.enable or set sops.age.keyFile or sops.gnupg.home";
} {
assertion = !(cfg.gnupg.home != null && cfg.gnupg.sshKeyPaths != []);
message = "Exactly one of sops.gnupg.home and sops.gnupg.sshKeyPaths must be set";
}] ++ lib.optionals cfg.validateSopsFiles (
lib.concatLists (lib.mapAttrsToList (name: secret: [{
assertion = builtins.pathExists secret.sopsFile;
message = "Cannot find path '${secret.sopsFile}' set in sops.secrets.${lib.strings.escapeNixIdentifier name}.sopsFile";
} {
assertion =
builtins.isPath secret.sopsFile ||
(builtins.isString secret.sopsFile && lib.hasPrefix builtins.storeDir secret.sopsFile);
message = "'${secret.sopsFile}' is not in the Nix store. Either add it to the Nix store or set sops.validateSopsFiles to false";
} {
assertion = secret.uid != null && secret.uid != 0 -> secret.owner == null;
message = "In ${secret.name} exactly one of sops.owner and sops.uid must be set";
} {
assertion = secret.gid != null && secret.gid != 0 -> secret.group == null;
message = "In ${secret.name} exactly one of sops.group and sops.gid must be set";
}]) cfg.secrets)
);
(lib.mkIf (cfg.secrets != { }) {
assertions =
[
{
assertion =
cfg.gnupg.home != null
|| cfg.gnupg.sshKeyPaths != [ ]
|| cfg.age.keyFile != null
|| cfg.age.sshKeyPaths != [ ];
message = "No key source configured for sops. Either set services.openssh.enable or set sops.age.keyFile or sops.gnupg.home";
}
{
assertion = !(cfg.gnupg.home != null && cfg.gnupg.sshKeyPaths != [ ]);
message = "Exactly one of sops.gnupg.home and sops.gnupg.sshKeyPaths must be set";
}
]
++ lib.optionals cfg.validateSopsFiles (
lib.concatLists (
lib.mapAttrsToList (name: secret: [
{
assertion = builtins.pathExists secret.sopsFile;
message = "Cannot find path '${secret.sopsFile}' set in sops.secrets.${lib.strings.escapeNixIdentifier name}.sopsFile";
}
{
assertion =
builtins.isPath secret.sopsFile
|| (builtins.isString secret.sopsFile && lib.hasPrefix builtins.storeDir secret.sopsFile);
message = "'${secret.sopsFile}' is not in the Nix store. Either add it to the Nix store or set sops.validateSopsFiles to false";
}
{
assertion = secret.uid != null && secret.uid != 0 -> secret.owner == null;
message = "In ${secret.name} exactly one of sops.owner and sops.uid must be set";
}
{
assertion = secret.gid != null && secret.gid != 0 -> secret.group == null;
message = "In ${secret.name} exactly one of sops.group and sops.gid must be set";
}
]) cfg.secrets
)
);
sops.environment.SOPS_GPG_EXEC = lib.mkIf (cfg.gnupg.home != null || cfg.gnupg.sshKeyPaths != []) (lib.mkDefault "${pkgs.gnupg}/bin/gpg");
sops.environment.SOPS_GPG_EXEC = lib.mkIf (cfg.gnupg.home != null || cfg.gnupg.sshKeyPaths != [ ]) (
lib.mkDefault "${pkgs.gnupg}/bin/gpg"
);
# When using sysusers we no longer are started as an activation script because those are started in initrd while sysusers is started later.
systemd.services.sops-install-secrets = lib.mkIf (regularSecrets != { } && useSystemdActivation) {
wantedBy = [ "sysinit.target" ];
wantedBy = [ "sysinit.target" ];
after = [ "systemd-sysusers.service" ];
environment = cfg.environment;
unitConfig.DefaultDependencies = "no";
@ -372,27 +447,43 @@ in {
};
system.activationScripts = {
setupSecrets = lib.mkIf (regularSecrets != {} && !useSystemdActivation) (lib.stringAfter ([ "specialfs" "users" "groups" ] ++ lib.optional cfg.age.generateKey "generate-age-key") ''
[ -e /run/current-system ] || echo setting up secrets...
${withEnvironment "${sops-install-secrets}/bin/sops-install-secrets ${manifest}"}
'' // lib.optionalAttrs (config.system ? dryActivationScript) {
supportsDryActivation = true;
});
setupSecrets = lib.mkIf (regularSecrets != { } && !useSystemdActivation) (
lib.stringAfter
(
[
"specialfs"
"users"
"groups"
]
++ lib.optional cfg.age.generateKey "generate-age-key"
)
''
[ -e /run/current-system ] || echo setting up secrets...
${withEnvironment "${sops-install-secrets}/bin/sops-install-secrets ${manifest}"}
''
// lib.optionalAttrs (config.system ? dryActivationScript) {
supportsDryActivation = true;
}
);
generate-age-key = let
escapedKeyFile = lib.escapeShellArg cfg.age.keyFile;
in lib.mkIf cfg.age.generateKey (lib.stringAfter [] ''
if [[ ! -f ${escapedKeyFile} ]]; then
echo generating machine-specific age key...
mkdir -p $(dirname ${escapedKeyFile})
# age-keygen sets 0600 by default, no need to chmod.
${pkgs.age}/bin/age-keygen -o ${escapedKeyFile}
fi
'');
generate-age-key =
let
escapedKeyFile = lib.escapeShellArg cfg.age.keyFile;
in
lib.mkIf cfg.age.generateKey (
lib.stringAfter [ ] ''
if [[ ! -f ${escapedKeyFile} ]]; then
echo generating machine-specific age key...
mkdir -p $(dirname ${escapedKeyFile})
# age-keygen sets 0600 by default, no need to chmod.
${pkgs.age}/bin/age-keygen -o ${escapedKeyFile}
fi
''
);
};
})
{
system.build.sops-nix-manifest = manifest;
system.build.sops-nix-manifest = manifest;
}
];
}