diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 2bcf6e8dee31..4e0bff076794 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -226,7 +226,7 @@ ./programs/zsh/zsh-autosuggestions.nix ./programs/zsh/zsh-syntax-highlighting.nix ./rename.nix - ./security/acme.nix + ./security/acme ./security/apparmor.nix ./security/audit.nix ./security/auditd.nix diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme/default.nix similarity index 99% rename from nixos/modules/security/acme.nix rename to nixos/modules/security/acme/default.nix index e244989d6408..d827c448055b 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme/default.nix @@ -916,6 +916,6 @@ in { meta = { maintainers = lib.teams.acme.members; - doc = ./acme.xml; + doc = ./doc.xml; }; } diff --git a/nixos/modules/security/acme.xml b/nixos/modules/security/acme/doc.xml similarity index 100% rename from nixos/modules/security/acme.xml rename to nixos/modules/security/acme/doc.xml diff --git a/nixos/modules/security/acme/mk-cert-ownership-assertion.nix b/nixos/modules/security/acme/mk-cert-ownership-assertion.nix new file mode 100644 index 000000000000..b80d89aeb9fc --- /dev/null +++ b/nixos/modules/security/acme/mk-cert-ownership-assertion.nix @@ -0,0 +1,4 @@ +{ cert, group, groups, user }: { + assertion = cert.group == group || builtins.any (u: u == user) groups.${cert.group}.members; + message = "Group for certificate ${cert.domain} must be ${group}, or user ${user} must be a member of group ${cert.group}"; +} diff --git a/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixos/modules/services/web-servers/apache-httpd/default.nix index 1a49b4ca15c7..d817ff6019a3 100644 --- a/nixos/modules/services/web-servers/apache-httpd/default.nix +++ b/nixos/modules/services/web-servers/apache-httpd/default.nix @@ -370,6 +370,8 @@ let cat ${php.phpIni} > $out echo "$options" >> $out ''; + + mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix; in @@ -657,7 +659,11 @@ in `services.httpd.virtualHosts..useACMEHost` are mutually exclusive. ''; } - ]; + ] ++ map (name: mkCertOwnershipAssertion { + inherit (cfg) group user; + cert = config.security.acme.certs.${name}; + groups = config.users.groups; + }) dependentCertNames; warnings = mapAttrsToList (name: hostOpts: '' diff --git a/nixos/modules/services/web-servers/caddy/default.nix b/nixos/modules/services/web-servers/caddy/default.nix index a4ada662cfbd..2b8c6f2e308b 100644 --- a/nixos/modules/services/web-servers/caddy/default.nix +++ b/nixos/modules/services/web-servers/caddy/default.nix @@ -38,6 +38,10 @@ let ''; in if pkgs.stdenv.buildPlatform == pkgs.stdenv.hostPlatform then Caddyfile-formatted else Caddyfile; + + acmeHosts = unique (catAttrs "useACMEHost" acmeVHosts); + + mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix; in { imports = [ @@ -266,7 +270,11 @@ in { assertion = cfg.adapter != "caddyfile" -> cfg.configFile != configFile; message = "Any value other than 'caddyfile' is only valid when providing your own `services.caddy.configFile`"; } - ]; + ] ++ map (name: mkCertOwnershipAssertion { + inherit (cfg) group user; + cert = config.security.acme.certs.${name}; + groups = config.users.groups; + }) acmeHosts; services.caddy.extraConfig = concatMapStringsSep "\n" mkVHostConf virtualHosts; services.caddy.globalConfig = '' @@ -323,8 +331,7 @@ in security.acme.certs = let - eachACMEHost = unique (catAttrs "useACMEHost" acmeVHosts); - reloads = map (useACMEHost: nameValuePair useACMEHost { reloadServices = [ "caddy.service" ]; }) eachACMEHost; + reloads = map (useACMEHost: nameValuePair useACMEHost { reloadServices = [ "caddy.service" ]; }) acmeHosts; in listToAttrs reloads; diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index dc174c8b41d0..41bce3669c58 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -374,6 +374,8 @@ let ${user}:{PLAIN}${password} '') authDef) ); + + mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix; in { @@ -842,7 +844,11 @@ in services.nginx.virtualHosts..useACMEHost are mutually exclusive. ''; } - ]; + ] ++ map (name: mkCertOwnershipAssertion { + inherit (cfg) group user; + cert = config.security.acme.certs.${name}; + groups = config.users.groups; + }) dependentCertNames; systemd.services.nginx = { description = "Nginx Web Server"; diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix index 0dd7743c52b6..2dd06a50f40b 100644 --- a/nixos/tests/acme.nix +++ b/nixos/tests/acme.nix @@ -54,15 +54,15 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: let baseConfig = { nodes, config, specialConfig ? {} }: lib.mkMerge [ { security.acme = { - defaults = (dnsConfig nodes) // { - inherit group; - }; + defaults = (dnsConfig nodes); # One manual wildcard cert certs."example.test" = { domain = "*.example.test"; }; }; + users.users."${config.services."${server}".user}".extraGroups = ["acme"]; + services."${server}" = { enable = true; virtualHosts = { @@ -252,15 +252,15 @@ in { } // (let baseCaddyConfig = { nodes, config, ... }: { security.acme = { - defaults = (dnsConfig nodes) // { - group = config.services.caddy.group; - }; + defaults = (dnsConfig nodes); # One manual wildcard cert certs."example.test" = { domain = "*.example.test"; }; }; + users.users."${config.services.caddy.user}".extraGroups = ["acme"]; + services.caddy = { enable = true; virtualHosts."a.exmaple.test" = {