Unverified Commit 0453fe23 authored by K900's avatar K900 Committed by GitHub
Browse files

{apache,caddy,nginx}: not "before" ACME certs using DNS validation (#336412)

parents a6fd7907 b432e86c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -217,7 +217,7 @@ let

    protocolOpts = if useDns then (
      [ "--dns" data.dnsProvider ]
      ++ lib.optionals (!data.dnsPropagationCheck) [ "--dns.disable-cp" ]
      ++ lib.optionals (!data.dnsPropagationCheck) [ "--dns.propagation-disable-ans" ]
      ++ lib.optionals (data.dnsResolver != null) [ "--dns.resolvers" data.dnsResolver ]
    ) else if data.s3Bucket != null then [ "--http" "--http.s3-bucket" data.s3Bucket ]
    else if data.listenHTTP != null then [ "--http" "--http.port" data.listenHTTP ]
+20 −3
Original line number Diff line number Diff line
{ 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}";
lib:

{ cert, groups, services }:
let
  catSep = builtins.concatStringsSep;

  svcGroups = svc:
    (lib.optional (svc.serviceConfig ? Group) svc.serviceConfig.Group)
    ++ (svc.serviceConfig.SupplementaryGroups or [ ]);
in
{
  assertion = builtins.all (svc:
    svc.serviceConfig.User or "root" == "root"
    || builtins.elem svc.serviceConfig.User groups.${cert.group}.members
    || builtins.elem cert.group (svcGroups svc)
  ) services;

  message = "Certificate ${cert.domain} (group=${cert.group}) must be readable by service(s) ${
    catSep ", " (map (svc: "${svc.name} (user=${svc.serviceConfig.User} groups=${catSep " " (svcGroups svc)})") services)
  }";
}
+14 −10
Original line number Diff line number Diff line
@@ -33,7 +33,9 @@ let
    certName = if hostOpts.useACMEHost != null then hostOpts.useACMEHost else hostOpts.hostName;
  }) (filter (hostOpts: hostOpts.enableACME || hostOpts.useACMEHost != null) vhosts);

  dependentCertNames = unique (map (hostOpts: hostOpts.certName) acmeEnabledVhosts);
  vhostCertNames = unique (map (hostOpts: hostOpts.certName) acmeEnabledVhosts);
  dependentCertNames = filter (cert: certs.${cert}.dnsProvider == null) vhostCertNames; # those that might depend on the HTTP server
  independentCertNames = filter (cert: certs.${cert}.dnsProvider != null) vhostCertNames; # those that don't depend on the HTTP server

  mkListenInfo = hostOpts:
    if hostOpts.listen != [] then
@@ -371,7 +373,7 @@ let
      echo "$options" >> $out
    '';

  mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix;
  mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix lib;
in


@@ -641,10 +643,10 @@ in
        '';
      }
    ] ++ map (name: mkCertOwnershipAssertion {
      inherit (cfg) group user;
      cert = config.security.acme.certs.${name};
      groups = config.users.groups;
    }) dependentCertNames;
      services = [ config.systemd.services.httpd ] ++ lib.optional (vhostCertNames != []) config.systemd.services.httpd-config-reload;
    }) vhostCertNames;

    warnings =
      mapAttrsToList (name: hostOpts: ''
@@ -747,8 +749,10 @@ in
    systemd.services.httpd = {
        description = "Apache HTTPD";
        wantedBy = [ "multi-user.target" ];
        wants = concatLists (map (certName: [ "acme-finished-${certName}.target" ]) dependentCertNames);
        after = [ "network.target" ] ++ map (certName: "acme-selfsigned-${certName}.service") dependentCertNames;
        wants = concatLists (map (certName: [ "acme-finished-${certName}.target" ]) vhostCertNames);
        after = [ "network.target" ]
          ++ map (certName: "acme-selfsigned-${certName}.service") vhostCertNames
          ++ map (certName: "acme-${certName}.service") independentCertNames; # avoid loading self-signed key w/ real cert, or vice-versa
        before = map (certName: "acme-${certName}.service") dependentCertNames;
        restartTriggers = [ cfg.configFile ];

@@ -789,9 +793,9 @@ in
    # which allows the acme-finished-$cert.target to signify the successful updating
    # of certs end-to-end.
    systemd.services.httpd-config-reload = let
      sslServices = map (certName: "acme-${certName}.service") dependentCertNames;
      sslTargets = map (certName: "acme-finished-${certName}.target") dependentCertNames;
    in mkIf (sslServices != []) {
      sslServices = map (certName: "acme-${certName}.service") vhostCertNames;
      sslTargets = map (certName: "acme-finished-${certName}.target") vhostCertNames;
    in mkIf (vhostCertNames != []) {
      wantedBy = sslServices ++ [ "multi-user.target" ];
      # Before the finished targets, after the renew services.
      # This service might be needed for HTTP-01 challenges, but we only want to confirm
@@ -801,7 +805,7 @@ in
      restartTriggers = [ cfg.configFile ];
      # Block reloading if not all certs exist yet.
      # Happens when config changes add new vhosts/certs.
      unitConfig.ConditionPathExists = map (certName: certs.${certName}.directory + "/fullchain.pem") dependentCertNames;
      unitConfig.ConditionPathExists = map (certName: certs.${certName}.directory + "/fullchain.pem") vhostCertNames;
      serviceConfig = {
        Type = "oneshot";
        TimeoutSec = 60;
+14 −11
Original line number Diff line number Diff line
@@ -5,8 +5,12 @@ with lib;
let
  cfg = config.services.caddy;

  certs = config.security.acme.certs;
  virtualHosts = attrValues cfg.virtualHosts;
  acmeVHosts = filter (hostOpts: hostOpts.useACMEHost != null) virtualHosts;
  acmeEnabledVhosts = filter (hostOpts: hostOpts.useACMEHost != null) virtualHosts;
  vhostCertNames = unique (map (hostOpts: hostOpts.useACMEHost) acmeEnabledVhosts);
  dependentCertNames = filter (cert: certs.${cert}.dnsProvider == null) vhostCertNames; # those that might depend on the HTTP server
  independentCertNames = filter (cert: certs.${cert}.dnsProvider != null) vhostCertNames; # those that don't depend on the HTTP server

  mkVHostConf = hostOpts:
    let
@@ -51,9 +55,7 @@ let

  configPath = "/etc/${etcConfigFile}";

  acmeHosts = unique (catAttrs "useACMEHost" acmeVHosts);

  mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix;
  mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix lib;
in
{
  imports = [
@@ -329,10 +331,10 @@ in
        message = "To specify an adapter other than 'caddyfile' please provide your own configuration via `services.caddy.configFile`";
      }
    ] ++ map (name: mkCertOwnershipAssertion {
      inherit (cfg) group user;
      cert = config.security.acme.certs.${name};
      groups = config.users.groups;
    }) acmeHosts;
      services = [ config.systemd.services.caddy ];
    }) vhostCertNames;

    services.caddy.globalConfig = ''
      ${optionalString (cfg.email != null) "email ${cfg.email}"}
@@ -348,9 +350,10 @@ in

    systemd.packages = [ cfg.package ];
    systemd.services.caddy = {
      wants = map (hostOpts: "acme-finished-${hostOpts.useACMEHost}.target") acmeVHosts;
      after = map (hostOpts: "acme-selfsigned-${hostOpts.useACMEHost}.service") acmeVHosts;
      before = map (hostOpts: "acme-${hostOpts.useACMEHost}.service") acmeVHosts;
      wants = map (certName: "acme-finished-${certName}.target") vhostCertNames;
      after = map (certName: "acme-selfsigned-${certName}.service") vhostCertNames
        ++ map (certName: "acme-${certName}.service") independentCertNames; # avoid loading self-signed key w/ real cert, or vice-versa
      before = map (certName: "acme-${certName}.service") dependentCertNames;

      wantedBy = [ "multi-user.target" ];
      startLimitIntervalSec = 14400;
@@ -397,10 +400,10 @@ in

    security.acme.certs =
      let
        certCfg = map (useACMEHost: nameValuePair useACMEHost {
        certCfg = map (certName: nameValuePair certName {
          group = mkDefault cfg.group;
          reloadServices = [ "caddy.service" ];
        }) acmeHosts;
        }) vhostCertNames;
      in
        listToAttrs certCfg;

+14 −10
Original line number Diff line number Diff line
@@ -7,7 +7,9 @@ let
  inherit (config.security.acme) certs;
  vhostsConfigs = mapAttrsToList (vhostName: vhostConfig: vhostConfig) virtualHosts;
  acmeEnabledVhosts = filter (vhostConfig: vhostConfig.enableACME || vhostConfig.useACMEHost != null) vhostsConfigs;
  dependentCertNames = unique (map (hostOpts: hostOpts.certName) acmeEnabledVhosts);
  vhostCertNames = unique (map (hostOpts: hostOpts.certName) acmeEnabledVhosts);
  dependentCertNames = filter (cert: certs.${cert}.dnsProvider == null) vhostCertNames; # those that might depend on the HTTP server
  independentCertNames = filter (cert: certs.${cert}.dnsProvider != null) vhostCertNames; # those that don't depend on the HTTP server
  virtualHosts = mapAttrs (vhostName: vhostConfig:
    let
      serverName = if vhostConfig.serverName != null
@@ -471,7 +473,7 @@ let
    '') authDef)
  );

  mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix;
  mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix lib;

  oldHTTP2 = (versionOlder cfg.package.version "1.25.1" && !(cfg.package.pname == "angie" || cfg.package.pname == "angieQuic"));
in
@@ -1209,10 +1211,10 @@ in
        '';
      }
    ] ++ map (name: mkCertOwnershipAssertion {
      inherit (cfg) group user;
      cert = config.security.acme.certs.${name};
      groups = config.users.groups;
    }) dependentCertNames;
      services = [ config.systemd.services.nginx ] ++ lib.optional (cfg.enableReload || vhostCertNames != []) config.systemd.services.nginx-config-reload;
    }) vhostCertNames;

    services.nginx.additionalModules = optional cfg.recommendedBrotliSettings pkgs.nginxModules.brotli
      ++ lib.optional cfg.recommendedZstdSettings pkgs.nginxModules.zstd;
@@ -1236,8 +1238,10 @@ in
    systemd.services.nginx = {
      description = "Nginx Web Server";
      wantedBy = [ "multi-user.target" ];
      wants = concatLists (map (certName: [ "acme-finished-${certName}.target" ]) dependentCertNames);
      after = [ "network.target" ] ++ map (certName: "acme-selfsigned-${certName}.service") dependentCertNames;
      wants = concatLists (map (certName: [ "acme-finished-${certName}.target" ]) vhostCertNames);
      after = [ "network.target" ]
        ++ map (certName: "acme-selfsigned-${certName}.service") vhostCertNames
        ++ map (certName: "acme-${certName}.service") independentCertNames; # avoid loading self-signed key w/ real cert, or vice-versa
      # Nginx needs to be started in order to be able to request certificates
      # (it's hosting the acme challenge after all)
      # This fixes https://github.com/NixOS/nixpkgs/issues/81842
@@ -1316,9 +1320,9 @@ in
    # which allows the acme-finished-$cert.target to signify the successful updating
    # of certs end-to-end.
    systemd.services.nginx-config-reload = let
      sslServices = map (certName: "acme-${certName}.service") dependentCertNames;
      sslTargets = map (certName: "acme-finished-${certName}.target") dependentCertNames;
    in mkIf (cfg.enableReload || sslServices != []) {
      sslServices = map (certName: "acme-${certName}.service") vhostCertNames;
      sslTargets = map (certName: "acme-finished-${certName}.target") vhostCertNames;
    in mkIf (cfg.enableReload || vhostCertNames != []) {
      wants = optionals cfg.enableReload [ "nginx.service" ];
      wantedBy = sslServices ++ [ "multi-user.target" ];
      # Before the finished targets, after the renew services.
@@ -1329,7 +1333,7 @@ in
      restartTriggers = optionals cfg.enableReload [ configFile ];
      # Block reloading if not all certs exist yet.
      # Happens when config changes add new vhosts/certs.
      unitConfig.ConditionPathExists = optionals (sslServices != []) (map (certName: certs.${certName}.directory + "/fullchain.pem") dependentCertNames);
      unitConfig.ConditionPathExists = optionals (sslServices != []) (map (certName: certs.${certName}.directory + "/fullchain.pem") vhostCertNames);
      serviceConfig = {
        Type = "oneshot";
        TimeoutSec = 60;
Loading