1
0
Fork 0
mirror of https://github.com/Mic92/sops-nix.git synced 2025-03-15 21:18:15 +00:00
sops-nix/modules/sops/default.nix

227 lines
7.3 KiB
Nix
Raw Normal View History

2020-07-06 07:30:09 +01:00
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.sops;
users = config.users.users;
secretType = types.submodule ({ config, ... }: {
config = {
sopsFile = lib.mkOptionDefault cfg.defaultSopsFile;
sopsFileHash = mkOptionDefault (optionalString cfg.validateSopsFiles "${builtins.hashFile "sha256" config.sopsFile}");
};
2020-07-06 07:30:09 +01:00
options = {
name = mkOption {
type = types.str;
default = config._module.args.name;
description = ''
Name of the file used in /run/secrets
'';
};
key = mkOption {
type = types.str;
default = 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.
'';
};
2021-03-03 16:51:28 +01:00
path = mkOption {
type = types.str;
default = "/run/secrets/${config.name}";
description = ''
Path where secrets are symlinked to.
If the default is kept no symlink is created.
2020-07-06 07:30:09 +01:00
'';
2021-03-03 16:51:28 +01:00
};
2020-07-06 07:30:09 +01:00
format = mkOption {
type = types.enum ["yaml" "json" "binary"];
2020-07-23 08:34:52 +01:00
default = cfg.defaultSopsFormat;
2020-07-06 07:30:09 +01:00
description = ''
File format used to decrypt the sops secret.
Binary files are written to the target file as is.
'';
};
mode = mkOption {
type = types.str;
default = "0400";
description = ''
Permissions mode of the in octal.
'';
};
owner = mkOption {
type = types.str;
default = "root";
description = ''
User of the file.
'';
};
group = mkOption {
type = types.str;
default = users.${config.owner}.group;
description = ''
Group of the file.
'';
};
sopsFile = mkOption {
2021-03-03 16:51:28 +01:00
type = types.path;
defaultText = "\${config.sops.defaultSopsFile}";
2020-07-06 07:30:09 +01:00
description = ''
Sops file the secret is loaded from.
'';
};
2021-07-04 13:45:09 +08:00
sopsFileHash = mkOption {
type = types.str;
readOnly = true;
description = ''
Hash of the sops file, useful in <xref linkend="opt-systemd.services._name_.restartTriggers" />.
2021-07-04 13:45:09 +08:00
'';
};
2020-07-06 07:30:09 +01:00
};
});
2021-03-03 16:51:28 +01:00
manifest = pkgs.writeText "manifest.json" (builtins.toJSON {
2020-07-06 07:30:09 +01:00
secrets = builtins.attrValues cfg.secrets;
# Does this need to be configurable?
secretsMountPoint = "/run/secrets.d";
symlinkPath = "/run/secrets";
2021-08-27 13:35:53 +02:00
gnupgHome = cfg.gnupg.home;
sshKeyPaths = cfg.gnupg.sshKeyPaths;
ageKeyFile = cfg.age.keyFile;
2021-08-27 20:09:28 +02:00
ageSshKeyPaths = cfg.age.sshKeyPaths;
2020-07-06 07:30:09 +01:00
});
2020-07-19 19:13:48 +01:00
2021-01-30 10:25:38 +01:00
checkedManifest = let
sops-install-secrets = (pkgs.buildPackages.callPackage ../.. {}).sops-install-secrets;
2021-08-26 20:01:22 +02:00
in pkgs.runCommand "checked-manifest.json" {
2020-07-19 19:13:48 +01:00
nativeBuildInputs = [ sops-install-secrets ];
} ''
sops-install-secrets -check-mode=${if cfg.validateSopsFiles then "sopsfile" else "manifest"} ${manifest}
cp ${manifest} $out
'';
2020-07-06 07:30:09 +01:00
in {
options.sops = {
secrets = mkOption {
type = types.attrsOf secretType;
default = {};
description = ''
Path where the latest secrets are mounted to.
'';
};
defaultSopsFile = mkOption {
2021-03-03 16:51:28 +01:00
type = types.path;
2020-07-06 07:30:09 +01:00
description = ''
Default sops file used for all secrets.
'';
};
2020-07-23 08:34:52 +01:00
defaultSopsFormat = mkOption {
type = types.str;
default = "yaml";
description = ''
Default sops format used for all secrets.
'';
};
2020-07-19 19:13:48 +01:00
validateSopsFiles = mkOption {
type = types.bool;
default = true;
description = ''
Check all sops files at evaluation time.
This requires sops files to be added to the nix store.
'';
};
2021-08-27 13:35:53 +02:00
age = {
keyFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/var/lib/sops-nix/key.txt";
description = ''
Path to age key file used for sops decryption.
'';
};
2021-08-27 00:49:58 +02:00
2021-08-27 13:35:53 +02:00
generateKey = mkOption {
type = types.bool;
default = false;
description = ''
Whether or not to generate the age key. If this
option is set to false, the key must already be
present at the specified location.
'';
};
2021-08-27 20:09:28 +02:00
sshKeyPaths = mkOption {
type = types.listOf types.path;
2021-08-28 12:37:10 +02:00
default = if config.services.openssh.enable then map (e: e.path) (lib.filter (e: e.type == "ed25519") config.services.openssh.hostKeys) else [];
2021-08-27 20:09:28 +02:00
description = ''
2021-08-28 12:37:10 +02:00
Paths to ssh keys added as age keys during sops description.
2021-08-27 20:09:28 +02:00
'';
};
2021-08-27 00:49:58 +02:00
};
2021-08-27 13:35:53 +02:00
gnupg = {
home = mkOption {
type = types.nullOr types.str;
default = null;
example = "/root/.gnupg";
description = ''
Path to gnupg database directory containing the key for decrypting the sops file.
'';
};
2020-07-12 13:50:55 +01:00
2021-08-27 13:35:53 +02:00
sshKeyPaths = mkOption {
type = types.listOf types.path;
default = if config.services.openssh.enable then
map (e: e.path) (lib.filter (e: e.type == "rsa") config.services.openssh.hostKeys)
else [];
description = ''
Path to ssh keys added as GPG keys during sops description.
This option must be explicitly unset if <literal>config.sops.gnupg.sshKeyPaths</literal> is set.
'';
};
2020-07-06 07:30:09 +01:00
};
};
2021-08-27 13:35:53 +02:00
imports = [
(mkRenamedOptionModule [ "sops" "gnupgHome" ] [ "sops" "gnupg" "home" ])
(mkRenamedOptionModule [ "sops" "sshKeyPaths" ] [ "sops" "gnupg" "sshKeyPaths" ])
];
2020-07-06 07:30:09 +01:00
config = mkIf (cfg.secrets != {}) {
2020-07-12 13:50:55 +01:00
assertions = [{
assertion = cfg.gnupg.home != null || cfg.gnupg.sshKeyPaths != [] || cfg.age.keyFile != null || cfg.age.sshKeyPaths != [];
message = "No key source configurated for sops";
} {
assertion = !(cfg.gnupg.home != null && cfg.gnupg.sshKeyPaths != []);
message = "Exactly one of sops.gnupg.home and sops.gnupg.sshKeyPaths must be set";
2021-03-03 16:51:28 +01:00
}] ++ optionals cfg.validateSopsFiles (
concatLists (mapAttrsToList (name: secret: [{
assertion = builtins.pathExists secret.sopsFile;
message = "Cannot find path '${secret.sopsFile}' set in sops.secrets.${strings.escapeNixIdentifier name}.sopsFile";
} {
assertion =
builtins.isPath secret.sopsFile ||
(builtins.isString secret.sopsFile && 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";
}]) cfg.secrets)
);
2020-07-12 13:50:55 +01:00
2021-01-30 10:25:38 +01:00
system.activationScripts.setup-secrets = let
sops-install-secrets = (pkgs.callPackage ../.. {}).sops-install-secrets;
2021-08-27 13:35:53 +02:00
in stringAfter ([ "specialfs" "users" "groups" ] ++ optional cfg.age.generateKey "generate-age-key") ''
2020-07-06 07:30:09 +01:00
echo setting up secrets...
2021-08-27 13:35:53 +02:00
${optionalString (cfg.gnupg.home != null) "SOPS_GPG_EXEC=${pkgs.gnupg}/bin/gpg"} ${sops-install-secrets}/bin/sops-install-secrets ${checkedManifest}
2020-07-06 07:30:09 +01:00
'';
2021-08-27 00:49:58 +02:00
2021-08-27 13:35:53 +02:00
system.activationScripts.generate-age-key = (mkIf cfg.age.generateKey) (stringAfter [] ''
2021-09-30 15:28:39 +02:00
if [[ ! -f '${cfg.age.keyFile}' ]]; then
2021-08-27 00:49:58 +02:00
echo generating machine-specific age key...
2021-08-27 13:35:53 +02:00
mkdir -p $(dirname ${cfg.age.keyFile})
2021-08-27 00:49:58 +02:00
# age-keygen sets 0600 by default, no need to chmod.
2021-08-27 13:35:53 +02:00
${pkgs.age}/bin/age-keygen -o ${cfg.age.keyFile}
2021-08-27 00:49:58 +02:00
fi
'');
2020-07-06 07:30:09 +01:00
};
}