|
4 | 4 | pkgs, |
5 | 5 | ... |
6 | 6 | }: |
7 | | - |
8 | | -with lib; |
9 | | - |
10 | | -let |
| 7 | +with lib; let |
11 | 8 | cfg = config.services.caddy; |
12 | 9 |
|
13 | 10 | certs = config.security.acme.certs; |
|
17 | 14 | dependentCertNames = filter (cert: certs.${cert}.dnsProvider == null) vhostCertNames; # those that might depend on the HTTP server |
18 | 15 | independentCertNames = filter (cert: certs.${cert}.dnsProvider != null) vhostCertNames; # those that don't depend on the HTTP server |
19 | 16 |
|
20 | | - mkVHostConf = |
21 | | - hostOpts: |
22 | | - let |
23 | | - sslCertDir = config.security.acme.certs.${hostOpts.useACMEHost}.directory; |
24 | | - in |
25 | | - '' |
26 | | - ${hostOpts.hostName} ${concatStringsSep " " hostOpts.serverAliases} { |
27 | | - ${optionalString ( |
28 | | - hostOpts.listenAddresses != [ ] |
29 | | - ) "bind ${concatStringsSep " " hostOpts.listenAddresses}"} |
30 | | - ${optionalString ( |
31 | | - hostOpts.useACMEHost != null |
32 | | - ) "tls ${sslCertDir}/cert.pem ${sslCertDir}/key.pem"} |
33 | | - log { |
34 | | - ${hostOpts.logFormat} |
35 | | - } |
36 | | -
|
37 | | - ${hostOpts.extraConfig} |
| 17 | + mkVHostConf = hostOpts: let |
| 18 | + sslCertDir = config.security.acme.certs.${hostOpts.useACMEHost}.directory; |
| 19 | + in '' |
| 20 | + ${hostOpts.hostName} ${concatStringsSep " " hostOpts.serverAliases} { |
| 21 | + ${optionalString ( |
| 22 | + hostOpts.listenAddresses != [] |
| 23 | + ) "bind ${concatStringsSep " " hostOpts.listenAddresses}"} |
| 24 | + ${optionalString ( |
| 25 | + hostOpts.useACMEHost != null |
| 26 | + ) "tls ${sslCertDir}/cert.pem ${sslCertDir}/key.pem"} |
| 27 | + log { |
| 28 | + ${hostOpts.logFormat} |
38 | 29 | } |
39 | | - ''; |
40 | 30 |
|
41 | | - settingsFormat = pkgs.formats.json { }; |
| 31 | + ${hostOpts.extraConfig} |
| 32 | + } |
| 33 | + ''; |
| 34 | + |
| 35 | + settingsFormat = pkgs.formats.json {}; |
42 | 36 |
|
43 | 37 | configFile = |
44 | | - if cfg.settings != { } then |
45 | | - settingsFormat.generate "caddy.json" cfg.settings |
46 | | - else |
47 | | - let |
48 | | - Caddyfile = pkgs.writeTextDir "Caddyfile" '' |
49 | | - { |
50 | | - ${cfg.globalConfig} |
51 | | - } |
52 | | - ${cfg.extraConfig} |
53 | | - ${concatMapStringsSep "\n" mkVHostConf virtualHosts} |
54 | | - ''; |
55 | | - |
56 | | - Caddyfile-formatted = pkgs.runCommand "Caddyfile-formatted" { } '' |
57 | | - mkdir -p $out |
58 | | - cp --no-preserve=mode ${Caddyfile}/Caddyfile $out/Caddyfile |
59 | | - ${lib.getExe cfg.package} fmt --overwrite $out/Caddyfile |
60 | | - ''; |
61 | | - in |
62 | | - "${ |
63 | | - if pkgs.stdenv.buildPlatform == pkgs.stdenv.hostPlatform then Caddyfile-formatted else Caddyfile |
64 | | - }/Caddyfile"; |
| 38 | + if cfg.settings != {} |
| 39 | + then settingsFormat.generate "caddy.json" cfg.settings |
| 40 | + else let |
| 41 | + Caddyfile = pkgs.writeTextDir "Caddyfile" '' |
| 42 | + { |
| 43 | + ${cfg.globalConfig} |
| 44 | + } |
| 45 | + ${cfg.extraConfig} |
| 46 | + ${concatMapStringsSep "\n" mkVHostConf virtualHosts} |
| 47 | + ''; |
| 48 | + |
| 49 | + Caddyfile-formatted = pkgs.runCommand "Caddyfile-formatted" {} '' |
| 50 | + mkdir -p $out |
| 51 | + cp --no-preserve=mode ${Caddyfile}/Caddyfile $out/Caddyfile |
| 52 | + ${lib.getExe cfg.package} fmt --overwrite $out/Caddyfile |
| 53 | + ''; |
| 54 | + in "${ |
| 55 | + if pkgs.stdenv.buildPlatform == pkgs.stdenv.hostPlatform |
| 56 | + then Caddyfile-formatted |
| 57 | + else Caddyfile |
| 58 | + }/Caddyfile"; |
65 | 59 |
|
66 | 60 | etcConfigFile = "caddy/caddy_config"; |
67 | 61 |
|
68 | 62 | configPath = "/etc/${etcConfigFile}"; |
69 | 63 |
|
70 | 64 | mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix lib; |
71 | | -in |
72 | | -{ |
| 65 | +in { |
73 | 66 | imports = [ |
74 | 67 | (mkRemovedOptionModule [ |
75 | 68 | "services" |
76 | 69 | "caddy" |
77 | 70 | "agree" |
78 | 71 | ] "this option is no longer necessary for Caddy 2") |
79 | | - (mkRenamedOptionModule [ "services" "caddy" "ca" ] [ "services" "caddy" "acmeCA" ]) |
80 | | - (mkRenamedOptionModule [ "services" "caddy" "config" ] [ "services" "caddy" "extraConfig" ]) |
| 72 | + (mkRenamedOptionModule ["services" "caddy" "ca"] ["services" "caddy" "acmeCA"]) |
| 73 | + (mkRenamedOptionModule ["services" "caddy" "config"] ["services" "caddy" "extraConfig"]) |
81 | 74 | ]; |
82 | 75 |
|
83 | 76 | # interface |
|
112 | 105 | ''; |
113 | 106 | }; |
114 | 107 |
|
115 | | - package = mkPackageOption pkgs "caddy" { }; |
| 108 | + package = mkPackageOption pkgs "caddy" {}; |
116 | 109 |
|
117 | 110 | dataDir = mkOption { |
118 | 111 | type = types.path; |
|
183 | 176 |
|
184 | 177 | adapter = mkOption { |
185 | 178 | default = |
186 | | - if ((cfg.configFile != configFile) || (builtins.baseNameOf cfg.configFile) == "Caddyfile") then |
187 | | - "caddyfile" |
188 | | - else |
189 | | - null; |
| 179 | + if ((cfg.configFile != configFile) || (builtins.baseNameOf cfg.configFile) == "Caddyfile") |
| 180 | + then "caddyfile" |
| 181 | + else null; |
190 | 182 | defaultText = literalExpression '' |
191 | 183 | if ((cfg.configFile != configFile) || (builtins.baseNameOf cfg.configFile) == "Caddyfile") then "caddyfile" else null |
192 | 184 | ''; |
|
256 | 248 | }; |
257 | 249 |
|
258 | 250 | virtualHosts = mkOption { |
259 | | - type = with types; attrsOf (submodule (import ./vhost-options.nix { inherit cfg; })); |
260 | | - default = { }; |
| 251 | + type = with types; attrsOf (submodule (import ./vhost-options.nix {inherit cfg;})); |
| 252 | + default = {}; |
261 | 253 | example = literalExpression '' |
262 | 254 | { |
263 | 255 | "hydra.example.com" = { |
|
323 | 315 |
|
324 | 316 | settings = mkOption { |
325 | 317 | type = settingsFormat.type; |
326 | | - default = { }; |
| 318 | + default = {}; |
327 | 319 | description = '' |
328 | 320 | Structured configuration for Caddy to generate a Caddy JSON configuration file. |
329 | 321 | See <https://caddyserver.com/docs/json/> for available options. |
|
380 | 372 |
|
381 | 373 | # implementation |
382 | 374 | config = mkIf cfg.enable { |
383 | | - |
384 | | - assertions = [ |
385 | | - { |
386 | | - assertion = cfg.configFile == configFile -> cfg.adapter == "caddyfile" || cfg.adapter == null; |
387 | | - message = "To specify an adapter other than 'caddyfile' please provide your own configuration via `services.caddy.configFile`"; |
388 | | - } |
389 | | - ] |
390 | | - ++ map ( |
391 | | - name: |
392 | | - mkCertOwnershipAssertion { |
393 | | - cert = config.security.acme.certs.${name}; |
394 | | - groups = config.users.groups; |
395 | | - services = [ config.systemd.services.caddy ]; |
396 | | - } |
397 | | - ) vhostCertNames; |
| 375 | + assertions = |
| 376 | + [ |
| 377 | + { |
| 378 | + assertion = cfg.configFile == configFile -> cfg.adapter == "caddyfile" || cfg.adapter == null; |
| 379 | + message = "To specify an adapter other than 'caddyfile' please provide your own configuration via `services.caddy.configFile`"; |
| 380 | + } |
| 381 | + ] |
| 382 | + ++ map ( |
| 383 | + name: |
| 384 | + mkCertOwnershipAssertion { |
| 385 | + cert = config.security.acme.certs.${name}; |
| 386 | + groups = config.users.groups; |
| 387 | + services = [config.systemd.services.caddy]; |
| 388 | + } |
| 389 | + ) |
| 390 | + vhostCertNames; |
398 | 391 |
|
399 | 392 | services.caddy.globalConfig = '' |
400 | 393 | ${optionalString (cfg.email != null) "email ${cfg.email}"} |
|
408 | 401 | boot.kernel.sysctl."net.core.rmem_max" = mkDefault 2500000; |
409 | 402 | boot.kernel.sysctl."net.core.wmem_max" = mkDefault 2500000; |
410 | 403 |
|
411 | | - systemd.packages = [ cfg.package ]; |
| 404 | + systemd.packages = [cfg.package]; |
412 | 405 | systemd.services.caddy = { |
413 | 406 | wants = map (certName: "acme-finished-${certName}.target") vhostCertNames; |
414 | 407 | after = |
415 | 408 | map (certName: "acme-selfsigned-${certName}.service") vhostCertNames |
416 | 409 | ++ map (certName: "acme-${certName}.service") independentCertNames; # avoid loading self-signed key w/ real cert, or vice-versa |
417 | 410 | before = map (certName: "acme-${certName}.service") dependentCertNames; |
418 | 411 |
|
419 | | - wantedBy = [ "multi-user.target" ]; |
| 412 | + wantedBy = ["multi-user.target"]; |
420 | 413 | startLimitIntervalSec = 14400; |
421 | 414 | startLimitBurst = 10; |
422 | 415 | reloadTriggers = optional cfg.enableReload cfg.configFile; |
423 | 416 | restartTriggers = optional (!cfg.enableReload) cfg.configFile; |
424 | 417 |
|
425 | | - serviceConfig = |
426 | | - let |
427 | | - runOptions = ''--config ${configPath} ${ |
| 418 | + serviceConfig = let |
| 419 | + runOptions = ''--config ${configPath} ${ |
428 | 420 | optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}" |
429 | 421 | }''; |
430 | | - in |
431 | | - { |
432 | | - # Override the `ExecStart` line from upstream's systemd unit file by our own: |
433 | | - # https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart= |
434 | | - # If the empty string is assigned to this option, the list of commands to start is reset, prior assignments of this option will have no effect. |
435 | | - ExecStart = [ |
436 | | - "" |
437 | | - ''${lib.getExe cfg.package} run ${runOptions} ${optionalString cfg.resume "--resume"}'' |
438 | | - ]; |
439 | | - # Validating the configuration before applying it ensures we’ll get a proper error that will be reported when switching to the configuration |
440 | | - ExecReload = [ |
| 422 | + in { |
| 423 | + # Override the `ExecStart` line from upstream's systemd unit file by our own: |
| 424 | + # https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart= |
| 425 | + # If the empty string is assigned to this option, the list of commands to start is reset, prior assignments of this option will have no effect. |
| 426 | + ExecStart = [ |
| 427 | + "" |
| 428 | + ''${lib.getExe cfg.package} run ${runOptions} ${optionalString cfg.resume "--resume"}'' |
| 429 | + ]; |
| 430 | + # Validating the configuration before applying it ensures we’ll get a proper error that will be reported when switching to the configuration |
| 431 | + ExecReload = |
| 432 | + [ |
441 | 433 | "" |
442 | 434 | ] |
443 | 435 | ++ lib.optional cfg.enableReload "${lib.getExe cfg.package} reload ${runOptions} --force"; |
444 | | - User = cfg.user; |
445 | | - Group = cfg.group; |
446 | | - ReadWritePaths = [ cfg.dataDir ]; |
447 | | - StateDirectory = mkIf (cfg.dataDir == "/var/lib/caddy") [ "caddy" ]; |
448 | | - LogsDirectory = mkIf (cfg.logDir == "/var/log/caddy") [ "caddy" ]; |
449 | | - Restart = "on-failure"; |
450 | | - RestartPreventExitStatus = 1; |
451 | | - RestartSec = "5s"; |
452 | | - EnvironmentFile = optional (cfg.environmentFile != null) cfg.environmentFile; |
453 | | - |
454 | | - # TODO: attempt to upstream these options |
455 | | - NoNewPrivileges = true; |
456 | | - PrivateDevices = true; |
457 | | - ProtectHome = true; |
458 | | - }; |
| 436 | + User = cfg.user; |
| 437 | + Group = cfg.group; |
| 438 | + ReadWritePaths = [cfg.dataDir]; |
| 439 | + StateDirectory = mkIf (cfg.dataDir == "/var/lib/caddy") ["caddy"]; |
| 440 | + LogsDirectory = mkIf (cfg.logDir == "/var/log/caddy") ["caddy"]; |
| 441 | + Restart = "on-failure"; |
| 442 | + RestartPreventExitStatus = 1; |
| 443 | + RestartSec = "5s"; |
| 444 | + EnvironmentFile = optional (cfg.environmentFile != null) cfg.environmentFile; |
| 445 | + |
| 446 | + # TODO: attempt to upstream these options |
| 447 | + NoNewPrivileges = true; |
| 448 | + PrivateDevices = true; |
| 449 | + ProtectHome = true; |
| 450 | + }; |
459 | 451 | }; |
460 | 452 |
|
461 | 453 | users.users = optionalAttrs (cfg.user == "caddy") { |
|
470 | 462 | caddy.gid = config.ids.gids.caddy; |
471 | 463 | }; |
472 | 464 |
|
473 | | - security.acme.certs = |
474 | | - let |
475 | | - certCfg = map ( |
| 465 | + security.acme.certs = let |
| 466 | + certCfg = |
| 467 | + map ( |
476 | 468 | certName: |
477 | | - nameValuePair certName { |
478 | | - group = mkDefault cfg.group; |
479 | | - reloadServices = [ "caddy.service" ]; |
480 | | - } |
481 | | - ) vhostCertNames; |
482 | | - in |
| 469 | + nameValuePair certName { |
| 470 | + group = mkDefault cfg.group; |
| 471 | + reloadServices = ["caddy.service"]; |
| 472 | + } |
| 473 | + ) |
| 474 | + vhostCertNames; |
| 475 | + in |
483 | 476 | listToAttrs certCfg; |
484 | 477 |
|
485 | 478 | environment.etc.${etcConfigFile}.source = cfg.configFile; |
|
0 commit comments