94 lines
3.3 KiB
Nix
94 lines
3.3 KiB
Nix
|
# SPDX-FileCopyrightText: 2022 Luke Granger-Brown <depot@lukegb.com>
|
||
|
#
|
||
|
# SPDX-License-Identifier: Apache-2.0
|
||
|
|
||
|
{ pkgs, config, depot, lib, ... }:
|
||
|
let
|
||
|
inherit (lib) mkOption types mkBefore optionalAttrs mkDefault mkIf mapAttrsToList mkAfter;
|
||
|
|
||
|
acmeCertificates = builtins.attrValues config.my.vault.acmeCertificates;
|
||
|
|
||
|
# Work out where we're being asked to write things, and which groups, so we can correctly get permissions.
|
||
|
fullchainPath = c: pathFor c "fullchain.pem";
|
||
|
chainPath = c: pathFor c "chain.pem";
|
||
|
keyPath = c: pathFor c "privkey.pem";
|
||
|
pathFor = c: suffix: "/var/lib/acme/${c.name}/${suffix}";
|
||
|
|
||
|
isNginx = c: builtins.length c.nginxVirtualHosts > 0;
|
||
|
defaultGroup = c: if isNull c.group then if isNginx c then "nginx" else "acme" else c.group;
|
||
|
groupOrDefault = p: c: if isNull p then defaultGroup c else p;
|
||
|
|
||
|
allRestartableUnits = builtins.concatMap (c: c.reloadOrRestartUnits) acmeCertificates;
|
||
|
allGroups = lib.unique (map (c: c.group) acmeCertificates);
|
||
|
in
|
||
|
{
|
||
|
imports = [
|
||
|
./secretsmgr.nix
|
||
|
];
|
||
|
|
||
|
options.my.vault.acmeCertificates = mkOption {
|
||
|
default = {};
|
||
|
type = with types; attrsOf (submodule ({ name, config, ... }: {
|
||
|
options = let
|
||
|
isNginx = builtins.length config.nginxVirtualHosts > 0;
|
||
|
in {
|
||
|
name = mkOption {
|
||
|
type = str;
|
||
|
default = name;
|
||
|
description = "Path to put the certificate.";
|
||
|
};
|
||
|
|
||
|
nginxVirtualHosts = mkOption {
|
||
|
type = listOf str;
|
||
|
default = [];
|
||
|
description = "List of nginx virtual hosts to apply SSL to.";
|
||
|
};
|
||
|
group = mkOption {
|
||
|
type = str;
|
||
|
default = if isNginx then "nginx" else "acme";
|
||
|
description = "Owner group to set for the ${what}. If null, taken from parent.";
|
||
|
};
|
||
|
|
||
|
role = mkOption {
|
||
|
type = str;
|
||
|
default = "letsencrypt-cloudflare";
|
||
|
description = "Which role to use for certificate issuance.";
|
||
|
};
|
||
|
|
||
|
hostnames = mkOption {
|
||
|
type = listOf str;
|
||
|
description = "List of hostnames to issue certificate for.";
|
||
|
};
|
||
|
|
||
|
reloadOrRestartUnits = mkOption {
|
||
|
type = listOf str;
|
||
|
default = lib.optional isNginx "nginx.service";
|
||
|
description = "List of systemd units to reload/restart after obtaining a new certificate.";
|
||
|
};
|
||
|
};
|
||
|
}));
|
||
|
};
|
||
|
|
||
|
config = mkIf config.my.vault.secretsmgr.acmeCertificates.enable {
|
||
|
services.nginx = optionalAttrs config.my.vault.enable {
|
||
|
virtualHosts = builtins.listToAttrs (builtins.concatMap (certData: let
|
||
|
fullchain = fullchainPath certData;
|
||
|
chain = chainPath certData;
|
||
|
key = keyPath certData;
|
||
|
in map (hostName: lib.nameValuePair hostName {
|
||
|
sslCertificate = mkDefault (fullchainPath certData);
|
||
|
sslCertificateKey = mkDefault (keyPath certData);
|
||
|
sslTrustedCertificate = mkDefault (chainPath certData);
|
||
|
}) certData.nginxVirtualHosts) acmeCertificates);
|
||
|
};
|
||
|
|
||
|
my.vault.secretsmgr.groups = mkAfter allGroups;
|
||
|
my.vault.secretsmgr.restartableUnits = mkAfter allRestartableUnits;
|
||
|
my.vault.secretsmgr.acmeCertificates.config = mapAttrsToList (_: c: {
|
||
|
inherit (c) group hostnames role;
|
||
|
output_name = c.name;
|
||
|
units_to_reload_or_restart = c.reloadOrRestartUnits;
|
||
|
}) config.my.vault.acmeCertificates;
|
||
|
};
|
||
|
}
|