nixos/caddy: add support for reload

This commit is contained in:
Sylvain Fankhauser 2023-02-21 17:21:38 +01:00
parent d9c0d0ddfb
commit 1f0ac736b4
No known key found for this signature in database
GPG Key ID: 4228AB9EC0612ADA
3 changed files with 48 additions and 8 deletions

View File

@ -64,6 +64,8 @@
- `spamassassin` no longer supports the `Hashcash` module. The module needs to be removed from the `loadplugin` list if it was copied over from the default `initPreConf` option.
- The Caddy module gained a new option named `services.caddy.enableReload` which is enabled by default. It allows reloading the service instead of restarting it, if only a config file has changed. This option must be disabled if you have turned off the [Caddy admin API](https://caddyserver.com/docs/caddyfile/options#admin). If you keep this option enabled, you should consider setting [`grace_period`](https://caddyserver.com/docs/caddyfile/options#grace-period) to a non-infinite value to prevent Caddy from delaying the reload indefinitely.
## Other Notable Changes {#sec-release-23.11-notable-changes}
- The Cinnamon module now enables XDG desktop integration by default. If you are experiencing collisions related to xdg-desktop-portal-gtk you can safely remove `xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ];` from your NixOS configuration.

View File

@ -41,6 +41,14 @@ let
in
"${if pkgs.stdenv.buildPlatform == pkgs.stdenv.hostPlatform then Caddyfile-formatted else Caddyfile}/Caddyfile";
adminDisabled = lib.fileContents (pkgs.runCommand "caddy-config-adapted" {} ''
${cfg.package}/bin/caddy adapt --config ${configFile} ${optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"} | ${pkgs.jq}/bin/jq .admin.disabled > $out
'') == "true";
etcConfigFile = "caddy/caddy_config";
configPath = "/etc/${etcConfigFile}";
acmeHosts = unique (catAttrs "useACMEHost" acmeVHosts);
mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix;
@ -155,11 +163,16 @@ in
description = lib.mdDoc ''
Override the configuration file used by Caddy. By default,
NixOS generates one automatically.
The configuration file is exposed at {file}`${configPath}`.
'';
};
adapter = mkOption {
default = null;
default = if (builtins.baseNameOf cfg.configFile) == "Caddyfile" then "caddyfile" else null;
defaultText = literalExpression ''
if (builtins.baseNameOf cfg.configFile) == "Caddyfile" then "caddyfile" else null
'';
example = literalExpression "nginx";
type = with types; nullOr str;
description = lib.mdDoc ''
@ -275,6 +288,21 @@ in
'';
};
enableReload = mkOption {
default = true;
type = types.bool;
description = lib.mdDoc ''
Reload Caddy instead of restarting it when configuration file changes.
Note that enabling this option requires the [admin API](https://caddyserver.com/docs/caddyfile/options#admin)
to not be turned off.
If you enable this option, consider setting [`grace_period`](https://caddyserver.com/docs/caddyfile/options#grace-period)
to a non-infinite value in {option}`services.caddy.globalConfig`
to prevent Caddy waiting for active connections to finish,
which could delay the reload essentially indefinitely.
'';
};
};
# implementation
@ -284,6 +312,9 @@ in
{ assertion = cfg.configFile == configFile -> cfg.adapter == "caddyfile" || cfg.adapter == null;
message = "To specify an adapter other than 'caddyfile' please provide your own configuration via `services.caddy.configFile`";
}
{ assertion = cfg.enableReload -> !adminDisabled;
message = "You need to remove `admin off` from your Caddy configuration in order to use `services.caddy.enableReload`";
}
] ++ map (name: mkCertOwnershipAssertion {
inherit (cfg) group user;
cert = config.security.acme.certs.${name};
@ -311,13 +342,16 @@ in
wantedBy = [ "multi-user.target" ];
startLimitIntervalSec = 14400;
startLimitBurst = 10;
reloadTriggers = optional cfg.enableReload cfg.configFile;
serviceConfig = {
serviceConfig = let
runOptions = ''--config ${configPath} ${optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"}'';
in {
# https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=
# 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.
ExecStart = [ "" ''${cfg.package}/bin/caddy run --config ${cfg.configFile} ${optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"} ${optionalString cfg.resume "--resume"}'' ];
ExecReload = [ "" ''${cfg.package}/bin/caddy reload --config ${cfg.configFile} ${optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"} --force'' ];
ExecStartPre = ''${cfg.package}/bin/caddy validate --config ${cfg.configFile} ${optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"}'';
ExecStart = [ "" ''${cfg.package}/bin/caddy run ${runOptions} ${optionalString cfg.resume "--resume"}'' ];
# Validating the configuration before applying it ensures well get a proper error that will be reported when switching to the configuration
ExecReload = [ "" ''${cfg.package}/bin/caddy reload ${runOptions} --force'' ];
User = cfg.user;
Group = cfg.group;
ReadWriteDirectories = cfg.dataDir;
@ -353,5 +387,6 @@ in
in
listToAttrs certCfg;
environment.etc.${etcConfigFile}.source = cfg.configFile;
};
}

View File

@ -20,6 +20,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
}
}
'';
services.caddy.enableReload = true;
specialisation.etag.configuration = {
services.caddy.extraConfig = lib.mkForce ''
@ -54,9 +55,9 @@ import ./make-test-python.nix ({ pkgs, ... }: {
testScript = { nodes, ... }:
let
etagSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/etag";
justReloadSystem = "${nodes.webserver.config.system.build.toplevel}/specialisation/config-reload";
multipleConfigs = "${nodes.webserver.config.system.build.toplevel}/specialisation/multiple-configs";
etagSystem = "${nodes.webserver.system.build.toplevel}/specialisation/etag";
justReloadSystem = "${nodes.webserver.system.build.toplevel}/specialisation/config-reload";
multipleConfigs = "${nodes.webserver.system.build.toplevel}/specialisation/multiple-configs";
in
''
url = "http://localhost/example.html"
@ -96,6 +97,8 @@ import ./make-test-python.nix ({ pkgs, ... }: {
"${justReloadSystem}/bin/switch-to-configuration test >&2"
)
webserver.wait_for_open_port(8080)
webserver.fail("journalctl -u caddy | grep -q -i stopped")
webserver.succeed("journalctl -u caddy | grep -q -i reloaded")
with subtest("multiple configs are correctly merged"):
webserver.succeed(