grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / security / certmgr.nix
blobc6d6e83576c5c16e20b837111a467b6848cf0d1d
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.services.certmgr;
8   specs = mapAttrsToList (n: v: rec {
9     name = n + ".json";
10     path = if isAttrs v then pkgs.writeText name (builtins.toJSON v) else v;
11   }) cfg.specs;
13   allSpecs = pkgs.linkFarm "certmgr.d" specs;
15   certmgrYaml = pkgs.writeText "certmgr.yaml" (builtins.toJSON {
16     dir = allSpecs;
17     default_remote = cfg.defaultRemote;
18     svcmgr = cfg.svcManager;
19     before = cfg.validMin;
20     interval = cfg.renewInterval;
21     inherit (cfg) metricsPort metricsAddress;
22   });
24   specPaths = map dirOf (concatMap (spec:
25     if isAttrs spec then
26       collect isString (filterAttrsRecursive (n: v: isAttrs v || n == "path") spec)
27     else
28       [ spec ]
29   ) (attrValues cfg.specs));
31   preStart = ''
32     ${concatStringsSep " \\\n" (["mkdir -p"] ++ map escapeShellArg specPaths)}
33     ${cfg.package}/bin/certmgr -f ${certmgrYaml} check
34   '';
37   options.services.certmgr = {
38     enable = mkEnableOption "certmgr";
40     package = mkPackageOption pkgs "certmgr" { };
42     defaultRemote = mkOption {
43       type = types.str;
44       default = "127.0.0.1:8888";
45       description = "The default CA host:port to use.";
46     };
48     validMin = mkOption {
49       default = "72h";
50       type = types.str;
51       description = "The interval before a certificate expires to start attempting to renew it.";
52     };
54     renewInterval = mkOption {
55       default = "30m";
56       type = types.str;
57       description = "How often to check certificate expirations and how often to update the cert_next_expires metric.";
58     };
60     metricsAddress = mkOption {
61       default = "127.0.0.1";
62       type = types.str;
63       description = "The address for the Prometheus HTTP endpoint.";
64     };
66     metricsPort = mkOption {
67       default = 9488;
68       type = types.ints.u16;
69       description = "The port for the Prometheus HTTP endpoint.";
70     };
72     specs = mkOption {
73       default = {};
74       example = literalExpression ''
75       {
76         exampleCert =
77         let
78           domain = "example.com";
79           secret = name: "/var/lib/secrets/''${name}.pem";
80         in {
81           service = "nginx";
82           action = "reload";
83           authority = {
84             file.path = secret "ca";
85           };
86           certificate = {
87             path = secret domain;
88           };
89           private_key = {
90             owner = "root";
91             group = "root";
92             mode = "0600";
93             path = secret "''${domain}-key";
94           };
95           request = {
96             CN = domain;
97             hosts = [ "mail.''${domain}" "www.''${domain}" ];
98             key = {
99               algo = "rsa";
100               size = 2048;
101             };
102             names = {
103               O = "Example Organization";
104               C = "USA";
105             };
106           };
107         };
108         otherCert = "/var/certmgr/specs/other-cert.json";
109       }
110       '';
111       type = with types; attrsOf (either path (submodule {
112         options = {
113           service = mkOption {
114             type = nullOr str;
115             default = null;
116             description = "The service on which to perform \<action\> after fetching.";
117           };
119           action = mkOption {
120             type = addCheck str (x: cfg.svcManager == "command" || elem x ["restart" "reload" "nop"]);
121             default = "nop";
122             description = "The action to take after fetching.";
123           };
125           # These ought all to be specified according to certmgr spec def.
126           authority = mkOption {
127             type = attrs;
128             description = "certmgr spec authority object.";
129           };
131           certificate = mkOption {
132             type = nullOr attrs;
133             description = "certmgr spec certificate object.";
134           };
136           private_key = mkOption {
137             type = nullOr attrs;
138             description = "certmgr spec private_key object.";
139           };
141           request = mkOption {
142             type = nullOr attrs;
143             description = "certmgr spec request object.";
144           };
145         };
146     }));
147       description = ''
148         Certificate specs as described by:
149         <https://github.com/cloudflare/certmgr#certificate-specs>
150         These will be added to the Nix store, so they will be world readable.
151       '';
152     };
154     svcManager = mkOption {
155       default = "systemd";
156       type = types.enum [ "circus" "command" "dummy" "openrc" "systemd" "sysv" ];
157       description = ''
158         This specifies the service manager to use for restarting or reloading services.
159         See: <https://github.com/cloudflare/certmgr#certmgryaml>.
160         For how to use the "command" service manager in particular,
161         see: <https://github.com/cloudflare/certmgr#command-svcmgr-and-how-to-use-it>.
162       '';
163     };
165   };
167   config = mkIf cfg.enable {
168     assertions = [
169       {
170         assertion = cfg.specs != {};
171         message = "Certmgr specs cannot be empty.";
172       }
173       {
174         assertion = !any (hasAttrByPath [ "authority" "auth_key" ]) (attrValues cfg.specs);
175         message = ''
176           Inline services.certmgr.specs are added to the Nix store rendering them world readable.
177           Specify paths as specs, if you want to use include auth_key - or use the auth_key_file option."
178         '';
179       }
180     ];
182     systemd.services.certmgr = {
183       description = "certmgr";
184       path = mkIf (cfg.svcManager == "command") [ pkgs.bash ];
185       wants = [ "network-online.target" ];
186       after = [ "network-online.target" ];
187       wantedBy = [ "multi-user.target" ];
188       inherit preStart;
190       serviceConfig = {
191         Restart = "always";
192         RestartSec = "10s";
193         ExecStart = "${cfg.package}/bin/certmgr -f ${certmgrYaml}";
194       };
195     };
196   };