1
0
Fork 0
mirror of https://github.com/Mic92/sops-nix.git synced 2024-12-15 17:50:51 +00:00
sops-nix/modules/sops/default.nix

226 lines
7.4 KiB
Nix
Raw Normal View History

2020-07-06 06:30:09 +00: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 06:30:09 +00: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 15:51:28 +00: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 06:30:09 +00:00
'';
2021-03-03 15:51:28 +00:00
};
2020-07-06 06:30:09 +00:00
format = mkOption {
type = types.enum ["yaml" "json" "binary"];
2020-07-23 07:34:52 +00:00
default = cfg.defaultSopsFormat;
2020-07-06 06:30:09 +00: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 15:51:28 +00:00
type = types.path;
defaultText = "\${config.sops.defaultSopsFile}";
2020-07-06 06:30:09 +00:00
description = ''
Sops file the secret is loaded from.
'';
};
2021-07-04 05:45:09 +00: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 05:45:09 +00:00
'';
};
2020-07-06 06:30:09 +00:00
};
});
2021-03-03 15:51:28 +00:00
manifest = pkgs.writeText "manifest.json" (builtins.toJSON {
2020-07-06 06:30:09 +00:00
secrets = builtins.attrValues cfg.secrets;
# Does this need to be configurable?
secretsMountPoint = "/run/secrets.d";
symlinkPath = "/run/secrets";
2021-08-27 11:35:53 +00:00
gnupgHome = cfg.gnupg.home;
sshKeyPaths = cfg.gnupg.sshKeyPaths;
ageKeyFile = cfg.age.keyFile;
2021-08-27 18:09:28 +00:00
ageSshKeyPaths = cfg.age.sshKeyPaths;
2020-07-06 06:30:09 +00:00
});
2020-07-19 18:13:48 +00:00
2021-01-30 09:25:38 +00:00
checkedManifest = let
sops-install-secrets = (pkgs.buildPackages.callPackage ../.. {}).sops-install-secrets;
2021-08-26 18:01:22 +00:00
in pkgs.runCommand "checked-manifest.json" {
2020-07-19 18:13:48 +00:00
nativeBuildInputs = [ sops-install-secrets ];
} ''
sops-install-secrets -check-mode=${if cfg.validateSopsFiles then "sopsfile" else "manifest"} ${manifest}
cp ${manifest} $out
'';
2020-07-06 06:30:09 +00: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 15:51:28 +00:00
type = types.path;
2020-07-06 06:30:09 +00:00
description = ''
Default sops file used for all secrets.
'';
};
2020-07-23 07:34:52 +00:00
defaultSopsFormat = mkOption {
type = types.str;
default = "yaml";
description = ''
Default sops format used for all secrets.
'';
};
2020-07-19 18:13:48 +00: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 11:35:53 +00: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-28 10:37:10 +00:00
Setting this to a non-null value causes the ssh keys to be ignored.
2021-08-27 11:35:53 +00:00
'';
};
2021-08-26 22:49:58 +00:00
2021-08-27 11:35:53 +00: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 18:09:28 +00:00
sshKeyPaths = mkOption {
type = types.listOf types.path;
2021-08-28 10:37:10 +00: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 18:09:28 +00:00
description = ''
2021-08-28 10:37:10 +00:00
Paths to ssh keys added as age keys during sops description.
This setting is ignored when the keyFile is set to a non-null value.
2021-08-27 18:09:28 +00:00
'';
};
2021-08-26 22:49:58 +00:00
};
2021-08-27 11:35:53 +00: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 12:50:55 +00:00
2021-08-27 11:35:53 +00: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 06:30:09 +00:00
};
};
2021-08-27 11:35:53 +00:00
imports = [
(mkRenamedOptionModule [ "sops" "gnupgHome" ] [ "sops" "gnupg" "home" ])
(mkRenamedOptionModule [ "sops" "sshKeyPaths" ] [ "sops" "gnupg" "sshKeyPaths" ])
];
2020-07-06 06:30:09 +00:00
config = mkIf (cfg.secrets != {}) {
2020-07-12 12:50:55 +00:00
assertions = [{
2021-08-27 18:09:28 +00:00
assertion = (cfg.age.keyFile == null && cfg.age.sshKeyPaths == []) -> (cfg.gnupg.home == null) != (cfg.gnupg.sshKeyPaths == []);
2021-08-27 11:35:53 +00:00
message = "Exactly one of sops.gnupg.home and sops.gnupg.sshKeyPaths must be set for gnupg mode";
2021-03-03 15:51:28 +00: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 12:50:55 +00:00
2021-01-30 09:25:38 +00:00
system.activationScripts.setup-secrets = let
sops-install-secrets = (pkgs.callPackage ../.. {}).sops-install-secrets;
2021-08-27 11:35:53 +00:00
in stringAfter ([ "specialfs" "users" "groups" ] ++ optional cfg.age.generateKey "generate-age-key") ''
2020-07-06 06:30:09 +00:00
echo setting up secrets...
2021-08-27 11:35:53 +00:00
${optionalString (cfg.gnupg.home != null) "SOPS_GPG_EXEC=${pkgs.gnupg}/bin/gpg"} ${sops-install-secrets}/bin/sops-install-secrets ${checkedManifest}
2020-07-06 06:30:09 +00:00
'';
2021-08-26 22:49:58 +00:00
2021-08-27 11:35:53 +00:00
system.activationScripts.generate-age-key = (mkIf cfg.age.generateKey) (stringAfter [] ''
if [[ ! -f "${cfg.age.keyFile}" ]]; then;
2021-08-26 22:49:58 +00:00
echo generating machine-specific age key...
2021-08-27 11:35:53 +00:00
mkdir -p $(dirname ${cfg.age.keyFile})
2021-08-26 22:49:58 +00:00
# age-keygen sets 0600 by default, no need to chmod.
2021-08-27 11:35:53 +00:00
${pkgs.age}/bin/age-keygen -o ${cfg.age.keyFile}
2021-08-26 22:49:58 +00:00
fi
'');
2020-07-06 06:30:09 +00:00
};
}