1
0
Fork 0
mirror of https://github.com/Mic92/sops-nix.git synced 2024-12-14 11:57:52 +00:00

Merge pull request #299 from Ninlives/master

Add `sops.templates`
This commit is contained in:
Jörg Thalheim 2023-04-17 08:43:35 +01:00 committed by GitHub
commit b72eabc86e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 176 additions and 0 deletions

View file

@ -294,6 +294,7 @@ in {
};
};
imports = [
./templates
(mkRenamedOptionModule [ "sops" "gnupgHome" ] [ "sops" "gnupg" "home" ])
(mkRenamedOptionModule [ "sops" "sshKeyPaths" ] [ "sops" "gnupg" "sshKeyPaths" ])
];

View file

@ -0,0 +1,102 @@
{ config, pkgs, lib, options, ... }:
with lib;
with lib.types;
with builtins;
let
cfg = config.sops;
secretsForUsers = lib.filterAttrs (_: v: v.neededForUsers) cfg.secrets;
in {
options.sops = {
templates = mkOption {
type = attrsOf (submodule ({ config, ... }: {
options = {
name = mkOption {
type = str;
default = config._module.args.name;
description = ''
Name of the file used in /run/secrets-rendered
'';
};
path = mkOption {
type = str;
default = "/run/secrets-rendered/${config.name}";
};
content = mkOption {
type = lines;
default = "";
description = ''
Content of the file
'';
};
mode = mkOption {
type = str;
default = "0400";
description = ''
Permissions mode of the rendered secret file in octal.
'';
};
owner = mkOption {
type = str;
default = "root";
description = ''
User of the file.
'';
};
group = mkOption {
type = str;
default = config.users.users.${config.owner}.group;
description = ''
Group of the file.
'';
};
file = mkOption {
type = types.path;
default = pkgs.writeText config.name config.content;
visible = false;
readOnly = true;
};
};
}));
default = { };
};
placeholder = mkOption {
type = attrsOf (mkOptionType {
name = "coercibleToString";
description = "value that can be coerced to string";
check = strings.isCoercibleToString;
merge = mergeEqualOption;
});
default = { };
visible = false;
};
};
config = optionalAttrs (options ? sops.secrets)
(mkIf (config.sops.templates != { }) {
sops.placeholder = mapAttrs
(name: _: mkDefault "<SOPS:${hashString "sha256" name}:PLACEHOLDER>")
config.sops.secrets;
system.activationScripts.renderSecrets = mkIf (cfg.templates != { })
(stringAfter ([ "setupSecrets" ]
++ optional (secretsForUsers != { }) "setupSecretsForUsers") ''
echo Setting up sops templates...
${concatMapStringsSep "\n" (name:
let
tpl = config.sops.templates.${name};
substitute = pkgs.writers.writePython3 "substitute" { }
(readFile ./subs.py);
subst-pairs = pkgs.writeText "pairs" (concatMapStringsSep "\n"
(name:
"${toString config.sops.placeholder.${name}} ${
config.sops.secrets.${name}.path
}") (attrNames config.sops.secrets));
in ''
mkdir -p "${dirOf tpl.path}"
(umask 077; ${substitute} ${tpl.file} ${subst-pairs} > ${tpl.path})
chmod "${tpl.mode}" "${tpl.path}"
chown "${tpl.owner}:${tpl.group}" "${tpl.path}"
'') (attrNames config.sops.templates)}
'');
});
}

View file

@ -0,0 +1,25 @@
from sys import argv
def substitute(target: str, subst: str) -> str:
with open(target) as f:
content = f.read()
with open(subst) as f:
subst_pairs = f.read().splitlines()
for pair in subst_pairs:
placeholder, path = pair.split()
with open(path) as f:
content = content.replace(placeholder, f.read())
return content
def main() -> None:
target = argv[1]
subst = argv[2]
print(substitute(target, subst))
main()

View file

@ -202,6 +202,54 @@
inherit (pkgs) system;
};
templates = makeTest {
name = "sops-templates";
nodes.machine = { config, ... }: {
imports = [ ../../modules/sops ];
sops = {
age.keyFile = ./test-assets/age-keys.txt;
defaultSopsFile = ./test-assets/secrets.yaml;
secrets.test_key = { };
};
sops.templates.test_template = {
content = ''
This line is not modified.
The next value will be replaced by ${config.sops.placeholder.test_key}
This line is also not modified.
'';
mode = "0400";
owner = "someuser";
group = "somegroup";
};
users.groups.somegroup = {};
users.users.someuser = {
isSystemUser = true;
group = "somegroup";
};
};
testScript = ''
start_all()
machine.succeed("[ $(stat -c%U /run/secrets-rendered/test_template) = 'someuser' ]")
machine.succeed("[ $(stat -c%G /run/secrets-rendered/test_template) = 'somegroup' ]")
expected = """
This line is not modified.
The next value will be replaced by test_value
This line is also not modified.
"""
rendered = machine.succeed("cat /run/secrets-rendered/test_template")
if rendered.strip() != expected.strip():
raise Exception("Template is not rendered correctly")
'';
} {
inherit pkgs;
inherit (pkgs) system;
};
restart-and-reload = makeTest {
name = "sops-restart-and-reload";
nodes.machine = { pkgs, lib, config, ... }: {