default.nix 2.97 KB
Newer Older
Matthias Adamczyk's avatar
Matthias Adamczyk committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
{ config, lib, pkgs, ... }:

with lib;

let
  secret-file = types.submodule ({ ... }@moduleAttrs: {
    options = {
      name = mkOption {
        type = types.str;
        default = moduleAttrs.config._module.args.name;
      };
      path = mkOption {
        type = types.str;
        readOnly = true;
        default = "/run/secrets/${removeSuffix ".gpg" (baseNameOf moduleAttrs.config.source-path)}";
      };
      mode = mkOption {
        type = types.str;
        default = "0400";
      };
      owner = mkOption {
        type = types.str;
        default = "root";
      };
      group-name = mkOption {
        type = types.str;
        default = "root";
      };
      source-path = mkOption {
        type = types.str;
        default = pkgs.copyPathToStore "${toString ../../secrets}/${config.networking.hostName}/${moduleAttrs.config.name}.gpg";
      };
      encrypted = mkOption {
        type = types.bool;
        default = true;
      };
      enable = mkOption {
        type = types.bool;
        default = true;
      };
    };
  });
  enabledFiles = filterAttrs (n: file: file.enable) config.secrets;

  mkDecryptSecret = file: pkgs.writeScript "decrypt-secret-${removeSuffix ".gpg" (baseNameOf file.source-path)}.sh" ''
    #!${pkgs.runtimeShell}
    set -eu pipefail
    if [ ! -f "${file.path}" ]; then
      umask 0077
      echo "${file.source-path} -> ${file.path}"
      ${if file.encrypted then ''
        ${pkgs.gnupg}/bin/gpg --decrypt ${escapeShellArg file.source-path} > ${file.path}
      '' else ''
        cat ${escapeShellArg file.source-path} > ${file.path}
      ''}
    fi
  '';
  mkSetupSecret = file: pkgs.writeScript "setup-secret-${removeSuffix ".gpg" (baseNameOf file.source-path)}.sh" ''
    #!${pkgs.runtimeShell}
    set -eu pipefail
    chown ${escapeShellArg file.owner}:${escapeShellArg file.group-name} ${escapeShellArg file.path}
    chmod ${escapeShellArg file.mode} ${escapeShellArg file.path}
  '';

in {
  options.secrets = mkOption {
    type = with types; attrsOf secret-file;
    default = {};
  };
  config = mkIf (enabledFiles != {}) {
    system.activationScripts = let
      files = unique (map (flip removeAttrs ["_module"]) (attrValues enabledFiles));
      decrypt = ''
        function fail() {
          rm $1
          echo "failed to decrypt $1"
        }
        echo setting up secrets...
        mkdir -p /run/secrets
        chown 0:0 /run/secrets
        chmod 0755 /run/secrets
        ${concatMapStringsSep "\n" (file: ''
          ${mkDecryptSecret file} || fail ${file.path}
        '') files}
      '';
      setup = ''
        echo setting up secrets...
        ${concatMapStringsSep "\n" (file: ''
          ${mkSetupSecret file} || echo "failed to set up ${file.path}"
        '') files}
      '';
    in {
      decrypt-secrets.text = "source ${pkgs.writeText "setup-secrets.sh" decrypt}";
      setup-secrets = stringAfter [ "users" "groups" ] "source ${pkgs.writeText "setup-secrets.sh" setup}";
      users.deps = [ "decrypt-secrets" ];
    };
  };
}