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