152 lines
4.5 KiB
Nix
152 lines
4.5 KiB
Nix
{ config, lib, pkgs, ... } @ toplevel:
|
|
|
|
with lib;
|
|
let
|
|
# localCopyOpts = { copy, ... }: let
|
|
# in {
|
|
# options = with types; {
|
|
# user = mkOption {
|
|
# type = str;
|
|
# description = "User to which this copy belongs.";
|
|
# };
|
|
|
|
# group = mkOption {
|
|
# type = nullOr str;
|
|
# description = "Group to which this copy belongs.";
|
|
# default = null;
|
|
# };
|
|
|
|
# path = mkOption {
|
|
# type = str;
|
|
# description = "Path at which to store the local copy.";
|
|
# #default = "/var/run/${toplevel.config.domain}/${copy}";
|
|
# };
|
|
|
|
# service = mkOption {
|
|
# type = str;
|
|
# description = "systemd job to copy certs.";
|
|
# default = "fudo-${toplevel.config.domain}-${copy}-certs.service";
|
|
# };
|
|
# };
|
|
# };
|
|
|
|
domainOpts = { domain, ... }: {
|
|
options = with types; {
|
|
email = mkOption {
|
|
type = str;
|
|
description = "Domain administrator email.";
|
|
default = "admin@${domain}";
|
|
};
|
|
|
|
extra-domains = mkOption {
|
|
type = listOf str;
|
|
description = "List of domains to add to this certificate.";
|
|
default = [];
|
|
};
|
|
|
|
local-copies = let
|
|
localCopyOpts = { copy, ... }: {
|
|
options = with types; {
|
|
user = mkOption {
|
|
type = str;
|
|
description = "User to which this copy belongs.";
|
|
};
|
|
|
|
group = mkOption {
|
|
type = nullOr str;
|
|
description = "Group to which this copy belongs.";
|
|
default = null;
|
|
};
|
|
|
|
path = mkOption {
|
|
type = str;
|
|
description = "Path at which to store the local copy.";
|
|
default = "/var/run/${domain}/${copy}";
|
|
};
|
|
|
|
service = mkOption {
|
|
type = str;
|
|
description = "systemd job to copy certs.";
|
|
default = "fudo-${domain}-${copy}-certs.service";
|
|
};
|
|
};
|
|
};
|
|
in mkOption {
|
|
type = attrsOf (submodule localCopyOpts);
|
|
description = "Map of copies to make for use by services.";
|
|
default = {};
|
|
};
|
|
};
|
|
};
|
|
|
|
head-or-null = lst: if (lst == []) then null else head lst;
|
|
rm-service-ext = filename:
|
|
head-or-null (builtins.match "^(.+)\.service$" filename);
|
|
|
|
concatMapAttrs = f: attrs:
|
|
foldr (a: b: a // b) {} (mapAttrsToList f attrs);
|
|
|
|
hostname = config.instance.hostname;
|
|
cfg = config.fudo.acme;
|
|
localDomains = if (hasAttr hostname cfg.host-domains) then
|
|
cfg.host-domains.${hostname} else {};
|
|
|
|
optionalStringOr = str: default:
|
|
if cond then str else default;
|
|
|
|
in {
|
|
options.fudo.acme = with types; {
|
|
host-domains = mkOption {
|
|
type = attrsOf (attrsOf (submodule domainOpts));
|
|
description = "Map of host to domains to domain options.";
|
|
default = { };
|
|
};
|
|
};
|
|
|
|
config = {
|
|
security.acme.certs = mapAttrs (domain: domainOpts: {
|
|
email = domainOpts.email;
|
|
extraDomainNames = domainOpts.extra-domains;
|
|
}) localDomains;
|
|
|
|
systemd = {
|
|
tmpfiles.rules = let
|
|
copies = concatMapAttrs (domain: domainOpts:
|
|
domainOpts.local-copies) localDomains;
|
|
copy-paths = mapAttrsToList (copy: copyOpts:
|
|
"D '${path}' 0550 ${copyOpts.user} ${optionalStringOr copyOpts.group "-"} - -")
|
|
copies;
|
|
in copy-paths;
|
|
|
|
# TODO: Make this a Fudo service?
|
|
services = concatMapAttrs (domain: domainOpts:
|
|
mapAttrs' (copy: copyOpts: let
|
|
source = config.security.acme.certs.${domain}.directory;
|
|
target = copyOpts.path;
|
|
install-certs = pkgs.writeShellScript "fudo-install-${domain}-${site}-certs.sh" ''
|
|
for cert in cert chain fullchain full key; do
|
|
cp ${source}/$cert.pem ${target}/$cert.pem
|
|
chmod 0440 ${source}/$cert.pem
|
|
done
|
|
'';
|
|
remove-certs = pkgs.writeShellScript "fudo-remove-${domain}-${site}-certs.sh" ''
|
|
for cert in cert chain fullchain full key; do
|
|
rm -rf ${target}/$cert.pem
|
|
done
|
|
'';
|
|
in nameValuePair
|
|
(rm-service-ext copyOpts.service) {
|
|
description = "Copy ${domain} ACME certs for ${copy}.";
|
|
after = [ "acme-${domain}.service" ];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
ExecStart = install-certs;
|
|
ExecStop = remove-certs;
|
|
RemainAfterExit = true;
|
|
StandardOutput = "journal";
|
|
};
|
|
}) domainOpts.local-copies) localDomains;
|
|
};
|
|
};
|
|
}
|