cfg = config.services.websites;
in
{
+ options.services.websitesCerts = mkOption {
+ description = "Default websites configuration for certificates as accepted by acme";
+ };
options.services.websites = with types; mkOption {
default = {};
description = "Each type of website to enable will target a distinct httpd server";
type = attrsOf (submodule {
options = {
certName = mkOption { type = string; };
+ addToCerts = mkOption {
+ type = bool;
+ default = false;
+ description = "Use these to certificates. Is ignored (considered true) if certMainHost is not null";
+ };
+ certMainHost = mkOption {
+ type = nullOr string;
+ description = "Use that host as 'main host' for acme certs";
+ default = null;
+ };
hosts = mkOption { type = listOf string; };
root = mkOption { type = nullOr path; };
extraConfig = mkOption { type = listOf lines; default = []; };
serverAliases = [ "*" ];
enableSSL = false;
logFormat = "combinedVhost";
- documentRoot = "/var/lib/acme/acme-challenge";
+ documentRoot = "${config.security.acme.directory}/acme-challenge";
extraConfig = ''
RewriteEngine on
RewriteCond "%{REQUEST_URI}" "!^/\.well-known"
};
toVhost = ips: vhostConf: {
enableSSL = true;
- sslServerCert = "/var/lib/acme/${vhostConf.certName}/cert.pem";
- sslServerKey = "/var/lib/acme/${vhostConf.certName}/key.pem";
- sslServerChain = "/var/lib/acme/${vhostConf.certName}/chain.pem";
+ sslServerCert = "${config.security.acme.directory}/${vhostConf.certName}/cert.pem";
+ sslServerKey = "${config.security.acme.directory}/${vhostConf.certName}/key.pem";
+ sslServerChain = "${config.security.acme.directory}/${vhostConf.certName}/chain.pem";
logFormat = "combinedVhost";
listen = map (ip: { inherit ip; port = 443; }) ips;
hostName = builtins.head vhostConf.hosts;
++ [ (redirectVhost icfg.ips) ];
})
) cfg;
+
+ config.security.acme.certs = let
+ typesToManage = attrsets.filterAttrs (k: v: v.enable) cfg;
+ flatVhosts = lists.flatten (attrsets.mapAttrsToList (k: v:
+ attrValues v.vhostConfs
+ ) typesToManage);
+ groupedCerts = attrsets.filterAttrs
+ (_: group: builtins.any (v: v.addToCerts || !isNull v.certMainHost) group)
+ (lists.groupBy (v: v.certName) flatVhosts);
+ groupToDomain = group:
+ let
+ nonNull = builtins.filter (v: !isNull v.certMainHost) group;
+ domains = lists.unique (map (v: v.certMainHost) nonNull);
+ in
+ if builtins.length domains == 0
+ then null
+ else assert (builtins.length domains == 1); (elemAt domains 0);
+ extraDomains = group:
+ let
+ mainDomain = groupToDomain group;
+ in
+ lists.remove mainDomain (
+ lists.unique (
+ lists.flatten (map (c: optionals (c.addToCerts || !isNull c.certMainHost) c.hosts) group)
+ )
+ );
+ in attrsets.mapAttrs (k: g:
+ if (!isNull (groupToDomain g))
+ then config.services.websitesCerts // {
+ domain = groupToDomain g;
+ extraDomains = builtins.listToAttrs (
+ map (d: attrsets.nameValuePair d null) (extraDomains g));
+ }
+ else {
+ extraDomains = builtins.listToAttrs (
+ map (d: attrsets.nameValuePair d null) (extraDomains g));
+ }
+ ) groupedCerts;
}