nixpkgs/nixos/modules/services/security/certmgr.nix
pennae bd56368848 nixos/*: md-convert hidden plaintext options
most of these are hidden because they're either part of a submodule that
doesn't have its type rendered (eg because the submodule type is used in
an either type) or because they are explicitly hidden. some of them are
merely hidden from nix-doc-munge by how their option is put together.
2022-08-31 16:32:54 +02:00

202 lines
5.9 KiB
Nix

{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.certmgr;
specs = mapAttrsToList (n: v: rec {
name = n + ".json";
path = if isAttrs v then pkgs.writeText name (builtins.toJSON v) else v;
}) cfg.specs;
allSpecs = pkgs.linkFarm "certmgr.d" specs;
certmgrYaml = pkgs.writeText "certmgr.yaml" (builtins.toJSON {
dir = allSpecs;
default_remote = cfg.defaultRemote;
svcmgr = cfg.svcManager;
before = cfg.validMin;
interval = cfg.renewInterval;
inherit (cfg) metricsPort metricsAddress;
});
specPaths = map dirOf (concatMap (spec:
if isAttrs spec then
collect isString (filterAttrsRecursive (n: v: isAttrs v || n == "path") spec)
else
[ spec ]
) (attrValues cfg.specs));
preStart = ''
${concatStringsSep " \\\n" (["mkdir -p"] ++ map escapeShellArg specPaths)}
${cfg.package}/bin/certmgr -f ${certmgrYaml} check
'';
in
{
options.services.certmgr = {
enable = mkEnableOption (lib.mdDoc "certmgr");
package = mkOption {
type = types.package;
default = pkgs.certmgr;
defaultText = literalExpression "pkgs.certmgr";
description = lib.mdDoc "Which certmgr package to use in the service.";
};
defaultRemote = mkOption {
type = types.str;
default = "127.0.0.1:8888";
description = lib.mdDoc "The default CA host:port to use.";
};
validMin = mkOption {
default = "72h";
type = types.str;
description = lib.mdDoc "The interval before a certificate expires to start attempting to renew it.";
};
renewInterval = mkOption {
default = "30m";
type = types.str;
description = lib.mdDoc "How often to check certificate expirations and how often to update the cert_next_expires metric.";
};
metricsAddress = mkOption {
default = "127.0.0.1";
type = types.str;
description = lib.mdDoc "The address for the Prometheus HTTP endpoint.";
};
metricsPort = mkOption {
default = 9488;
type = types.ints.u16;
description = lib.mdDoc "The port for the Prometheus HTTP endpoint.";
};
specs = mkOption {
default = {};
example = literalExpression ''
{
exampleCert =
let
domain = "example.com";
secret = name: "/var/lib/secrets/''${name}.pem";
in {
service = "nginx";
action = "reload";
authority = {
file.path = secret "ca";
};
certificate = {
path = secret domain;
};
private_key = {
owner = "root";
group = "root";
mode = "0600";
path = secret "''${domain}-key";
};
request = {
CN = domain;
hosts = [ "mail.''${domain}" "www.''${domain}" ];
key = {
algo = "rsa";
size = 2048;
};
names = {
O = "Example Organization";
C = "USA";
};
};
};
otherCert = "/var/certmgr/specs/other-cert.json";
}
'';
type = with types; attrsOf (either path (submodule {
options = {
service = mkOption {
type = nullOr str;
default = null;
description = lib.mdDoc "The service on which to perform \<action\> after fetching.";
};
action = mkOption {
type = addCheck str (x: cfg.svcManager == "command" || elem x ["restart" "reload" "nop"]);
default = "nop";
description = lib.mdDoc "The action to take after fetching.";
};
# These ought all to be specified according to certmgr spec def.
authority = mkOption {
type = attrs;
description = lib.mdDoc "certmgr spec authority object.";
};
certificate = mkOption {
type = nullOr attrs;
description = lib.mdDoc "certmgr spec certificate object.";
};
private_key = mkOption {
type = nullOr attrs;
description = lib.mdDoc "certmgr spec private_key object.";
};
request = mkOption {
type = nullOr attrs;
description = lib.mdDoc "certmgr spec request object.";
};
};
}));
description = lib.mdDoc ''
Certificate specs as described by:
<https://github.com/cloudflare/certmgr#certificate-specs>
These will be added to the Nix store, so they will be world readable.
'';
};
svcManager = mkOption {
default = "systemd";
type = types.enum [ "circus" "command" "dummy" "openrc" "systemd" "sysv" ];
description = lib.mdDoc ''
This specifies the service manager to use for restarting or reloading services.
See: <https://github.com/cloudflare/certmgr#certmgryaml>.
For how to use the "command" service manager in particular,
see: <https://github.com/cloudflare/certmgr#command-svcmgr-and-how-to-use-it>.
'';
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = cfg.specs != {};
message = "Certmgr specs cannot be empty.";
}
{
assertion = !any (hasAttrByPath [ "authority" "auth_key" ]) (attrValues cfg.specs);
message = ''
Inline services.certmgr.specs are added to the Nix store rendering them world readable.
Specify paths as specs, if you want to use include auth_key - or use the auth_key_file option."
'';
}
];
systemd.services.certmgr = {
description = "certmgr";
path = mkIf (cfg.svcManager == "command") [ pkgs.bash ];
after = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
inherit preStart;
serviceConfig = {
Restart = "always";
RestartSec = "10s";
ExecStart = "${cfg.package}/bin/certmgr -f ${certmgrYaml}";
};
};
};
}