feat(darwin): integrate Caddy server with SOPS for secret management
All checks were successful
Flake checker / Build Nix targets (push) Successful in 9m29s
All checks were successful
Flake checker / Build Nix targets (push) Successful in 9m29s
This commit is contained in:
@@ -9,9 +9,13 @@
|
|||||||
name: device:
|
name: device:
|
||||||
nix-darwin.lib.darwinSystem {
|
nix-darwin.lib.darwinSystem {
|
||||||
system = device.system;
|
system = device.system;
|
||||||
|
specialArgs = {
|
||||||
|
inherit device;
|
||||||
|
};
|
||||||
modules = [
|
modules = [
|
||||||
{nixpkgs.overlays = overlays;}
|
{nixpkgs.overlays = overlays;}
|
||||||
./${device.name}/configuration.nix
|
./${device.name}/configuration.nix
|
||||||
|
inputs.sops-nix.darwinModules.sops
|
||||||
home-manager.darwinModules.home-manager
|
home-manager.darwinModules.home-manager
|
||||||
{
|
{
|
||||||
nixpkgs.config.allowUnfree = true;
|
nixpkgs.config.allowUnfree = true;
|
||||||
|
|||||||
@@ -14,6 +14,10 @@
|
|||||||
services = {
|
services = {
|
||||||
caddy = {
|
caddy = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
environmentFile = config.sops.templates."HETZNER_API_KEY.env".path;
|
||||||
|
globalConfig = ''
|
||||||
|
debug
|
||||||
|
'';
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
(hetzner) {
|
(hetzner) {
|
||||||
tls {
|
tls {
|
||||||
@@ -26,17 +30,8 @@
|
|||||||
'';
|
'';
|
||||||
package = pkgs.caddy.withPlugins {
|
package = pkgs.caddy.withPlugins {
|
||||||
plugins = ["github.com/caddy-dns/hetzner@v1.0.0"];
|
plugins = ["github.com/caddy-dns/hetzner@v1.0.0"];
|
||||||
# hash = "sha256-9ea0CfOHG7JhejB73HjfXQpnonn+ZRBqLNz1fFRkcDQ=";
|
|
||||||
# hash = "sha256-9ea0CfOHG7JhejB73HjfXQpnonn+ZRBqLNz1fFRkcDQ="
|
|
||||||
hash = "sha256-YUrprDZQL+cX3P8fVLKHouXTMG4rw3sCaQdGqiq37uA=";
|
hash = "sha256-YUrprDZQL+cX3P8fVLKHouXTMG4rw3sCaQdGqiq37uA=";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
systemd.services.caddy = {
|
|
||||||
serviceConfig = {
|
|
||||||
EnvironmentFile = config.sops.templates."HETZNER_API_KEY.env".path;
|
|
||||||
Requires = ["sops.service"];
|
|
||||||
After = ["sops.service"];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
{...}: {
|
{...}: {
|
||||||
imports = [
|
imports = [
|
||||||
|
../../../modules/darwin/caddy
|
||||||
./yabai.nix
|
./yabai.nix
|
||||||
./skhd.nix
|
./skhd.nix
|
||||||
./tailscale.nix
|
./tailscale.nix
|
||||||
./autossh.nix
|
./autossh.nix
|
||||||
# ./caddy.nix
|
./caddy.nix
|
||||||
|
./sops.nix
|
||||||
# ./lmstudio.nix
|
# ./lmstudio.nix
|
||||||
# ./colima.nix
|
# ./colima.nix
|
||||||
# ./zerotier.nix
|
# ./zerotier.nix
|
||||||
|
|||||||
13
darwin/shiro/services/sops.nix
Normal file
13
darwin/shiro/services/sops.nix
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
# config,
|
||||||
|
# pkgs,
|
||||||
|
inputs,
|
||||||
|
device,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
sops = {
|
||||||
|
defaultSopsFile = ../../../secrets/secrets.yaml;
|
||||||
|
defaultSopsFormat = "yaml";
|
||||||
|
age.keyFile = "/Users/${device.user}/.config/sops/age/keys.txt";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -330,6 +330,7 @@
|
|||||||
in
|
in
|
||||||
import ./darwin {
|
import ./darwin {
|
||||||
inherit devices inputs nixpkgs home-manager overlays nur nix-darwin;
|
inherit devices inputs nixpkgs home-manager overlays nur nix-darwin;
|
||||||
|
sops-nix = inputs.sops-nix;
|
||||||
};
|
};
|
||||||
|
|
||||||
homeConfigurations = {
|
homeConfigurations = {
|
||||||
|
|||||||
416
modules/darwin/caddy/default.nix
Normal file
416
modules/darwin/caddy/default.nix
Normal file
@@ -0,0 +1,416 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
with lib; let
|
||||||
|
cfg = config.services.caddy;
|
||||||
|
|
||||||
|
virtualHosts = attrValues cfg.virtualHosts;
|
||||||
|
|
||||||
|
mkVHostConf = hostOpts: ''
|
||||||
|
${hostOpts.hostName} ${concatStringsSep " " hostOpts.serverAliases} {
|
||||||
|
${optionalString (
|
||||||
|
hostOpts.listenAddresses != []
|
||||||
|
) "bind ${concatStringsSep " " hostOpts.listenAddresses}"}
|
||||||
|
log {
|
||||||
|
${hostOpts.logFormat}
|
||||||
|
}
|
||||||
|
|
||||||
|
${hostOpts.extraConfig}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
|
settingsFormat = pkgs.formats.json {};
|
||||||
|
|
||||||
|
configFile =
|
||||||
|
if cfg.settings != {}
|
||||||
|
then settingsFormat.generate "caddy.json" cfg.settings
|
||||||
|
else let
|
||||||
|
Caddyfile = pkgs.writeTextDir "Caddyfile" ''
|
||||||
|
{
|
||||||
|
${cfg.globalConfig}
|
||||||
|
}
|
||||||
|
${cfg.extraConfig}
|
||||||
|
${concatMapStringsSep "\n" mkVHostConf virtualHosts}
|
||||||
|
'';
|
||||||
|
|
||||||
|
Caddyfile-formatted = pkgs.runCommand "Caddyfile-formatted" {} ''
|
||||||
|
mkdir -p $out
|
||||||
|
cp --no-preserve=mode ${Caddyfile}/Caddyfile $out/Caddyfile
|
||||||
|
${lib.getExe cfg.package} fmt --overwrite $out/Caddyfile
|
||||||
|
'';
|
||||||
|
in "${
|
||||||
|
if pkgs.stdenv.buildPlatform == pkgs.stdenv.hostPlatform
|
||||||
|
then Caddyfile-formatted
|
||||||
|
else Caddyfile
|
||||||
|
}/Caddyfile";
|
||||||
|
in {
|
||||||
|
imports = [
|
||||||
|
(mkRemovedOptionModule [
|
||||||
|
"services"
|
||||||
|
"caddy"
|
||||||
|
"agree"
|
||||||
|
] "this option is no longer necessary for Caddy 2")
|
||||||
|
(mkRenamedOptionModule ["services" "caddy" "ca"] ["services" "caddy" "acmeCA"])
|
||||||
|
(mkRenamedOptionModule ["services" "caddy" "config"] ["services" "caddy" "extraConfig"])
|
||||||
|
];
|
||||||
|
|
||||||
|
# interface
|
||||||
|
options.services.caddy = {
|
||||||
|
enable = mkEnableOption "Caddy web server";
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
default = "caddy";
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
User account under which caddy runs.
|
||||||
|
|
||||||
|
::: {.note}
|
||||||
|
If left as the default value this user will automatically be created
|
||||||
|
on system activation, otherwise you are responsible for
|
||||||
|
ensuring the user exists before the Caddy service starts.
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
group = mkOption {
|
||||||
|
default = "caddy";
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
Group under which caddy runs.
|
||||||
|
|
||||||
|
::: {.note}
|
||||||
|
If left as the default value this group will automatically be created
|
||||||
|
on system activation, otherwise you are responsible for
|
||||||
|
ensuring the group exists before the Caddy service starts.
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
package = mkPackageOption pkgs "caddy" {};
|
||||||
|
|
||||||
|
dataDir = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "/usr/local/var/lib/caddy";
|
||||||
|
description = ''
|
||||||
|
The data directory for caddy.
|
||||||
|
|
||||||
|
::: {.note}
|
||||||
|
If left as the default value this directory will automatically be created
|
||||||
|
before the Caddy server starts, otherwise you are responsible for ensuring
|
||||||
|
the directory exists with appropriate ownership and permissions.
|
||||||
|
|
||||||
|
Caddy v2 replaced `CADDYPATH` with XDG directories.
|
||||||
|
See <https://caddyserver.com/docs/conventions#file-locations>.
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
logDir = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "/usr/local/var/log/caddy";
|
||||||
|
description = ''
|
||||||
|
Directory for storing Caddy access logs.
|
||||||
|
|
||||||
|
::: {.note}
|
||||||
|
If left as the default value this directory will automatically be created
|
||||||
|
before the Caddy server starts, otherwise the sysadmin is responsible for
|
||||||
|
ensuring the directory exists with appropriate ownership and permissions.
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
logFormat = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
default = ''
|
||||||
|
level ERROR
|
||||||
|
'';
|
||||||
|
example = literalExpression ''
|
||||||
|
mkForce "level INFO";
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Configuration for the default logger. See
|
||||||
|
<https://caddyserver.com/docs/caddyfile/options#log>
|
||||||
|
for details.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
configFile = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = configFile;
|
||||||
|
defaultText = "A Caddyfile automatically generated by values from services.caddy.*";
|
||||||
|
example = literalExpression ''
|
||||||
|
pkgs.writeText "Caddyfile" '''
|
||||||
|
example.com
|
||||||
|
|
||||||
|
root * /var/www/wordpress
|
||||||
|
php_fastcgi unix//run/php/php-version-fpm.sock
|
||||||
|
file_server
|
||||||
|
''';
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Override the configuration file used by Caddy. By default,
|
||||||
|
NixOS generates one automatically.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
adapter = mkOption {
|
||||||
|
default =
|
||||||
|
if ((cfg.configFile != configFile) || (builtins.baseNameOf cfg.configFile) == "Caddyfile")
|
||||||
|
then "caddyfile"
|
||||||
|
else null;
|
||||||
|
defaultText = literalExpression ''
|
||||||
|
if ((cfg.configFile != configFile) || (builtins.baseNameOf cfg.configFile) == "Caddyfile") then "caddyfile" else null
|
||||||
|
'';
|
||||||
|
example = literalExpression "nginx";
|
||||||
|
type = with types; nullOr str;
|
||||||
|
description = ''
|
||||||
|
Name of the config adapter to use.
|
||||||
|
See <https://caddyserver.com/docs/config-adapters>
|
||||||
|
for the full list.
|
||||||
|
|
||||||
|
If `null` is specified, the `--adapter` argument is omitted when
|
||||||
|
starting or restarting Caddy. Notably, this allows specification of a
|
||||||
|
configuration file in Caddy's native JSON format, as long as the
|
||||||
|
filename does not start with `Caddyfile` (in which case the `caddyfile`
|
||||||
|
adapter is implicitly enabled). See
|
||||||
|
<https://caddyserver.com/docs/command-line#caddy-run> for details.
|
||||||
|
|
||||||
|
::: {.note}
|
||||||
|
Any value other than `null` or `caddyfile` is only valid when providing
|
||||||
|
your own `configFile`.
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
resume = mkOption {
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
description = ''
|
||||||
|
Use saved config, if any (and prefer over any specified configuration passed with `--config`).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
globalConfig = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
default = "";
|
||||||
|
example = ''
|
||||||
|
debug
|
||||||
|
servers {
|
||||||
|
protocol {
|
||||||
|
experimental_http3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Additional lines of configuration appended to the global config section
|
||||||
|
of the `Caddyfile`.
|
||||||
|
|
||||||
|
Refer to <https://caddyserver.com/docs/caddyfile/options#global-options>
|
||||||
|
for details on supported values.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
extraConfig = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
default = "";
|
||||||
|
example = ''
|
||||||
|
example.com {
|
||||||
|
encode gzip
|
||||||
|
log
|
||||||
|
root /srv/http
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Additional lines of configuration appended to the automatically
|
||||||
|
generated `Caddyfile`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
virtualHosts = mkOption {
|
||||||
|
type = with types; attrsOf (submodule (import ./vhost-options.nix {inherit cfg;}));
|
||||||
|
default = {};
|
||||||
|
example = literalExpression ''
|
||||||
|
{
|
||||||
|
"hydra.example.com" = {
|
||||||
|
serverAliases = [ "www.hydra.example.com" ];
|
||||||
|
extraConfig = '''
|
||||||
|
encode gzip
|
||||||
|
root * /srv/http
|
||||||
|
''';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Declarative specification of virtual hosts served by Caddy.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
acmeCA = mkOption {
|
||||||
|
default = null;
|
||||||
|
example = "https://acme-v02.api.letsencrypt.org/directory";
|
||||||
|
type = with types; nullOr str;
|
||||||
|
description = ''
|
||||||
|
::: {.note}
|
||||||
|
Sets the [`acme_ca` option](https://caddyserver.com/docs/caddyfile/options#acme-ca)
|
||||||
|
in the global options block of the resulting Caddyfile.
|
||||||
|
:::
|
||||||
|
|
||||||
|
The URL to the ACME CA's directory. It is strongly recommended to set
|
||||||
|
this to `https://acme-staging-v02.api.letsencrypt.org/directory` for
|
||||||
|
Let's Encrypt's [staging endpoint](https://letsencrypt.org/docs/staging-environment/)
|
||||||
|
while testing or in development.
|
||||||
|
|
||||||
|
Value `null` should be prefered for production setups,
|
||||||
|
as it omits the `acme_ca` option to enable
|
||||||
|
[automatic issuer fallback](https://caddyserver.com/docs/automatic-https#issuer-fallback).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
email = mkOption {
|
||||||
|
default = null;
|
||||||
|
type = with types; nullOr str;
|
||||||
|
description = ''
|
||||||
|
Your email address. Mainly used when creating an ACME account with your
|
||||||
|
CA, and is highly recommended in case there are problems with your
|
||||||
|
certificates.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
settings = mkOption {
|
||||||
|
type = settingsFormat.type;
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Structured configuration for Caddy to generate a Caddy JSON configuration file.
|
||||||
|
See <https://caddyserver.com/docs/json/> for available options.
|
||||||
|
|
||||||
|
::: {.warning}
|
||||||
|
Using a [Caddyfile](https://caddyserver.com/docs/caddyfile) instead of a JSON config is highly recommended by upstream.
|
||||||
|
There are only very few exception to this.
|
||||||
|
|
||||||
|
Please use a Caddyfile via {option}`services.caddy.configFile`, {option}`services.caddy.virtualHosts` or
|
||||||
|
{option}`services.caddy.extraConfig` with {option}`services.caddy.globalConfig` instead.
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: {.note}
|
||||||
|
Takes presence over most `services.caddy.*` options, such as {option}`services.caddy.configFile` and {option}`services.caddy.virtualHosts`, if specified.
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
environmentFile = mkOption {
|
||||||
|
type = with types; nullOr path;
|
||||||
|
default = null;
|
||||||
|
example = "/run/secrets/caddy.env";
|
||||||
|
description = ''
|
||||||
|
Environment file as defined in {manpage}`launchd.plist(5)`.
|
||||||
|
|
||||||
|
You can use environment variables to pass secrets to the service without adding
|
||||||
|
them to the world-redable nix store.
|
||||||
|
|
||||||
|
```
|
||||||
|
# in configuration.nix
|
||||||
|
services.caddy.environmentFile = "/run/secrets/caddy.env";
|
||||||
|
services.caddy.globalConfig = '''
|
||||||
|
{
|
||||||
|
acme_ca https://acme.zerossl.com/v2/DV90
|
||||||
|
acme_eab {
|
||||||
|
key_id {$EAB_KEY_ID}
|
||||||
|
mac_key {$EAB_MAC_KEY}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
''';
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
# in /run/secrets/caddy.env
|
||||||
|
EAB_KEY_ID=secret
|
||||||
|
EAB_MAC_KEY=secret
|
||||||
|
```
|
||||||
|
|
||||||
|
Find more examples
|
||||||
|
[here](https://caddyserver.com/docs/caddyfile/concepts#environment-variables)
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
serviceConfig = mkOption {
|
||||||
|
type = types.attrs;
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Additional launchd service configuration.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# implementation
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
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`";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
services.caddy.globalConfig = ''
|
||||||
|
${optionalString (cfg.email != null) "email ${cfg.email}"}
|
||||||
|
${optionalString (cfg.acmeCA != null) "acme_ca ${cfg.acmeCA}"}
|
||||||
|
log {
|
||||||
|
${cfg.logFormat}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
|
launchd.user.agents.caddy = {
|
||||||
|
path = [cfg.package];
|
||||||
|
|
||||||
|
# Use script when environment file is provided, otherwise direct command
|
||||||
|
script = mkIf (cfg.environmentFile != null) ''
|
||||||
|
if [ -f "${cfg.environmentFile}" ]; then
|
||||||
|
set -a
|
||||||
|
source ${cfg.environmentFile}
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
exec ${lib.getExe cfg.package} run --config ${cfg.configFile} ${
|
||||||
|
optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"
|
||||||
|
} ${optionalString cfg.resume "--resume"}
|
||||||
|
'';
|
||||||
|
|
||||||
|
command = mkIf (cfg.environmentFile == null) "${lib.getExe cfg.package} run --config ${cfg.configFile} ${
|
||||||
|
optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"
|
||||||
|
} ${optionalString cfg.resume "--resume"}";
|
||||||
|
|
||||||
|
serviceConfig =
|
||||||
|
{
|
||||||
|
KeepAlive = true;
|
||||||
|
RunAtLoad = true;
|
||||||
|
WorkingDirectory = cfg.dataDir;
|
||||||
|
StandardOutPath = "${cfg.logDir}/caddy.log";
|
||||||
|
StandardErrorPath = "${cfg.logDir}/caddy.error.log";
|
||||||
|
}
|
||||||
|
// cfg.serviceConfig
|
||||||
|
// (optionalAttrs (cfg.user != "caddy") {
|
||||||
|
UserName = cfg.user;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
# Note: User management is handled differently in Darwin
|
||||||
|
# Users should be created manually or through other means
|
||||||
|
|
||||||
|
system.activationScripts.caddy = mkIf cfg.enable ''
|
||||||
|
# Create data directory
|
||||||
|
if [ ! -d "${cfg.dataDir}" ]; then
|
||||||
|
mkdir -p "${cfg.dataDir}"
|
||||||
|
chown ${cfg.user}:${cfg.group} "${cfg.dataDir}"
|
||||||
|
chmod 750 "${cfg.dataDir}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create log directory
|
||||||
|
if [ ! -d "${cfg.logDir}" ]; then
|
||||||
|
mkdir -p "${cfg.logDir}"
|
||||||
|
chown ${cfg.user}:${cfg.group} "${cfg.logDir}"
|
||||||
|
chmod 750 "${cfg.logDir}"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
69
modules/darwin/caddy/vhost-options.nix
Normal file
69
modules/darwin/caddy/vhost-options.nix
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
{cfg}: {
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
name,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit (lib) literalExpression mkOption types;
|
||||||
|
in {
|
||||||
|
options = {
|
||||||
|
hostName = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = name;
|
||||||
|
description = "Canonical hostname for the server.";
|
||||||
|
};
|
||||||
|
|
||||||
|
serverAliases = mkOption {
|
||||||
|
type = with types; listOf str;
|
||||||
|
default = [];
|
||||||
|
example = [
|
||||||
|
"www.example.org"
|
||||||
|
"example.org"
|
||||||
|
];
|
||||||
|
description = ''
|
||||||
|
Additional names of virtual hosts served by this virtual host configuration.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
listenAddresses = mkOption {
|
||||||
|
type = with types; listOf str;
|
||||||
|
description = ''
|
||||||
|
A list of host interfaces to bind to for this virtual host.
|
||||||
|
'';
|
||||||
|
default = [];
|
||||||
|
example = [
|
||||||
|
"127.0.0.1"
|
||||||
|
"::1"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
logFormat = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
default = ''
|
||||||
|
output file ${cfg.logDir}/access-${lib.replaceStrings ["/" " "] ["_" "_"] config.hostName}.log
|
||||||
|
'';
|
||||||
|
defaultText = ''
|
||||||
|
output file ''${config.services.caddy.logDir}/access-''${hostName}.log
|
||||||
|
'';
|
||||||
|
example = literalExpression ''
|
||||||
|
mkForce '''
|
||||||
|
output discard
|
||||||
|
''';
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Configuration for HTTP request logging (also known as access logs). See
|
||||||
|
<https://caddyserver.com/docs/caddyfile/directives/log#log>
|
||||||
|
for details.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
extraConfig = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
default = "";
|
||||||
|
description = ''
|
||||||
|
Additional lines of configuration appended to this virtual host in the
|
||||||
|
automatically generated `Caddyfile`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
487
modules/nixos/caddy/default.nix
Normal file
487
modules/nixos/caddy/default.nix
Normal file
@@ -0,0 +1,487 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.caddy;
|
||||||
|
|
||||||
|
certs = config.security.acme.certs;
|
||||||
|
virtualHosts = attrValues cfg.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
|
||||||
|
sslCertDir = config.security.acme.certs.${hostOpts.useACMEHost}.directory;
|
||||||
|
in
|
||||||
|
''
|
||||||
|
${hostOpts.hostName} ${concatStringsSep " " hostOpts.serverAliases} {
|
||||||
|
${optionalString (
|
||||||
|
hostOpts.listenAddresses != [ ]
|
||||||
|
) "bind ${concatStringsSep " " hostOpts.listenAddresses}"}
|
||||||
|
${optionalString (
|
||||||
|
hostOpts.useACMEHost != null
|
||||||
|
) "tls ${sslCertDir}/cert.pem ${sslCertDir}/key.pem"}
|
||||||
|
log {
|
||||||
|
${hostOpts.logFormat}
|
||||||
|
}
|
||||||
|
|
||||||
|
${hostOpts.extraConfig}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
|
settingsFormat = pkgs.formats.json { };
|
||||||
|
|
||||||
|
configFile =
|
||||||
|
if cfg.settings != { } then
|
||||||
|
settingsFormat.generate "caddy.json" cfg.settings
|
||||||
|
else
|
||||||
|
let
|
||||||
|
Caddyfile = pkgs.writeTextDir "Caddyfile" ''
|
||||||
|
{
|
||||||
|
${cfg.globalConfig}
|
||||||
|
}
|
||||||
|
${cfg.extraConfig}
|
||||||
|
${concatMapStringsSep "\n" mkVHostConf virtualHosts}
|
||||||
|
'';
|
||||||
|
|
||||||
|
Caddyfile-formatted = pkgs.runCommand "Caddyfile-formatted" { } ''
|
||||||
|
mkdir -p $out
|
||||||
|
cp --no-preserve=mode ${Caddyfile}/Caddyfile $out/Caddyfile
|
||||||
|
${lib.getExe cfg.package} fmt --overwrite $out/Caddyfile
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
"${
|
||||||
|
if pkgs.stdenv.buildPlatform == pkgs.stdenv.hostPlatform then Caddyfile-formatted else Caddyfile
|
||||||
|
}/Caddyfile";
|
||||||
|
|
||||||
|
etcConfigFile = "caddy/caddy_config";
|
||||||
|
|
||||||
|
configPath = "/etc/${etcConfigFile}";
|
||||||
|
|
||||||
|
mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix lib;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
(mkRemovedOptionModule [
|
||||||
|
"services"
|
||||||
|
"caddy"
|
||||||
|
"agree"
|
||||||
|
] "this option is no longer necessary for Caddy 2")
|
||||||
|
(mkRenamedOptionModule [ "services" "caddy" "ca" ] [ "services" "caddy" "acmeCA" ])
|
||||||
|
(mkRenamedOptionModule [ "services" "caddy" "config" ] [ "services" "caddy" "extraConfig" ])
|
||||||
|
];
|
||||||
|
|
||||||
|
# interface
|
||||||
|
options.services.caddy = {
|
||||||
|
enable = mkEnableOption "Caddy web server";
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
default = "caddy";
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
User account under which caddy runs.
|
||||||
|
|
||||||
|
::: {.note}
|
||||||
|
If left as the default value this user will automatically be created
|
||||||
|
on system activation, otherwise you are responsible for
|
||||||
|
ensuring the user exists before the Caddy service starts.
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
group = mkOption {
|
||||||
|
default = "caddy";
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
Group under which caddy runs.
|
||||||
|
|
||||||
|
::: {.note}
|
||||||
|
If left as the default value this group will automatically be created
|
||||||
|
on system activation, otherwise you are responsible for
|
||||||
|
ensuring the group exists before the Caddy service starts.
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
package = mkPackageOption pkgs "caddy" { };
|
||||||
|
|
||||||
|
dataDir = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "/var/lib/caddy";
|
||||||
|
description = ''
|
||||||
|
The data directory for caddy.
|
||||||
|
|
||||||
|
::: {.note}
|
||||||
|
If left as the default value this directory will automatically be created
|
||||||
|
before the Caddy server starts, otherwise you are responsible for ensuring
|
||||||
|
the directory exists with appropriate ownership and permissions.
|
||||||
|
|
||||||
|
Caddy v2 replaced `CADDYPATH` with XDG directories.
|
||||||
|
See <https://caddyserver.com/docs/conventions#file-locations>.
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
logDir = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "/var/log/caddy";
|
||||||
|
description = ''
|
||||||
|
Directory for storing Caddy access logs.
|
||||||
|
|
||||||
|
::: {.note}
|
||||||
|
If left as the default value this directory will automatically be created
|
||||||
|
before the Caddy server starts, otherwise the sysadmin is responsible for
|
||||||
|
ensuring the directory exists with appropriate ownership and permissions.
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
logFormat = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
default = ''
|
||||||
|
level ERROR
|
||||||
|
'';
|
||||||
|
example = literalExpression ''
|
||||||
|
mkForce "level INFO";
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Configuration for the default logger. See
|
||||||
|
<https://caddyserver.com/docs/caddyfile/options#log>
|
||||||
|
for details.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
configFile = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = configFile;
|
||||||
|
defaultText = "A Caddyfile automatically generated by values from services.caddy.*";
|
||||||
|
example = literalExpression ''
|
||||||
|
pkgs.writeText "Caddyfile" '''
|
||||||
|
example.com
|
||||||
|
|
||||||
|
root * /var/www/wordpress
|
||||||
|
php_fastcgi unix//run/php/php-version-fpm.sock
|
||||||
|
file_server
|
||||||
|
''';
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Override the configuration file used by Caddy. By default,
|
||||||
|
NixOS generates one automatically.
|
||||||
|
|
||||||
|
The configuration file is exposed at {file}`${configPath}`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
adapter = mkOption {
|
||||||
|
default =
|
||||||
|
if ((cfg.configFile != configFile) || (builtins.baseNameOf cfg.configFile) == "Caddyfile") then
|
||||||
|
"caddyfile"
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
defaultText = literalExpression ''
|
||||||
|
if ((cfg.configFile != configFile) || (builtins.baseNameOf cfg.configFile) == "Caddyfile") then "caddyfile" else null
|
||||||
|
'';
|
||||||
|
example = literalExpression "nginx";
|
||||||
|
type = with types; nullOr str;
|
||||||
|
description = ''
|
||||||
|
Name of the config adapter to use.
|
||||||
|
See <https://caddyserver.com/docs/config-adapters>
|
||||||
|
for the full list.
|
||||||
|
|
||||||
|
If `null` is specified, the `--adapter` argument is omitted when
|
||||||
|
starting or restarting Caddy. Notably, this allows specification of a
|
||||||
|
configuration file in Caddy's native JSON format, as long as the
|
||||||
|
filename does not start with `Caddyfile` (in which case the `caddyfile`
|
||||||
|
adapter is implicitly enabled). See
|
||||||
|
<https://caddyserver.com/docs/command-line#caddy-run> for details.
|
||||||
|
|
||||||
|
::: {.note}
|
||||||
|
Any value other than `null` or `caddyfile` is only valid when providing
|
||||||
|
your own `configFile`.
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
resume = mkOption {
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
description = ''
|
||||||
|
Use saved config, if any (and prefer over any specified configuration passed with `--config`).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
globalConfig = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
default = "";
|
||||||
|
example = ''
|
||||||
|
debug
|
||||||
|
servers {
|
||||||
|
protocol {
|
||||||
|
experimental_http3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Additional lines of configuration appended to the global config section
|
||||||
|
of the `Caddyfile`.
|
||||||
|
|
||||||
|
Refer to <https://caddyserver.com/docs/caddyfile/options#global-options>
|
||||||
|
for details on supported values.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
extraConfig = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
default = "";
|
||||||
|
example = ''
|
||||||
|
example.com {
|
||||||
|
encode gzip
|
||||||
|
log
|
||||||
|
root /srv/http
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Additional lines of configuration appended to the automatically
|
||||||
|
generated `Caddyfile`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
virtualHosts = mkOption {
|
||||||
|
type = with types; attrsOf (submodule (import ./vhost-options.nix { inherit cfg; }));
|
||||||
|
default = { };
|
||||||
|
example = literalExpression ''
|
||||||
|
{
|
||||||
|
"hydra.example.com" = {
|
||||||
|
serverAliases = [ "www.hydra.example.com" ];
|
||||||
|
extraConfig = '''
|
||||||
|
encode gzip
|
||||||
|
root * /srv/http
|
||||||
|
''';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Declarative specification of virtual hosts served by Caddy.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
acmeCA = mkOption {
|
||||||
|
default = null;
|
||||||
|
example = "https://acme-v02.api.letsencrypt.org/directory";
|
||||||
|
type = with types; nullOr str;
|
||||||
|
description = ''
|
||||||
|
::: {.note}
|
||||||
|
Sets the [`acme_ca` option](https://caddyserver.com/docs/caddyfile/options#acme-ca)
|
||||||
|
in the global options block of the resulting Caddyfile.
|
||||||
|
:::
|
||||||
|
|
||||||
|
The URL to the ACME CA's directory. It is strongly recommended to set
|
||||||
|
this to `https://acme-staging-v02.api.letsencrypt.org/directory` for
|
||||||
|
Let's Encrypt's [staging endpoint](https://letsencrypt.org/docs/staging-environment/)
|
||||||
|
while testing or in development.
|
||||||
|
|
||||||
|
Value `null` should be prefered for production setups,
|
||||||
|
as it omits the `acme_ca` option to enable
|
||||||
|
[automatic issuer fallback](https://caddyserver.com/docs/automatic-https#issuer-fallback).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
email = mkOption {
|
||||||
|
default = null;
|
||||||
|
type = with types; nullOr str;
|
||||||
|
description = ''
|
||||||
|
Your email address. Mainly used when creating an ACME account with your
|
||||||
|
CA, and is highly recommended in case there are problems with your
|
||||||
|
certificates.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
enableReload = mkOption {
|
||||||
|
default = true;
|
||||||
|
type = types.bool;
|
||||||
|
description = ''
|
||||||
|
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.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
settings = mkOption {
|
||||||
|
type = settingsFormat.type;
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Structured configuration for Caddy to generate a Caddy JSON configuration file.
|
||||||
|
See <https://caddyserver.com/docs/json/> for available options.
|
||||||
|
|
||||||
|
::: {.warning}
|
||||||
|
Using a [Caddyfile](https://caddyserver.com/docs/caddyfile) instead of a JSON config is highly recommended by upstream.
|
||||||
|
There are only very few exception to this.
|
||||||
|
|
||||||
|
Please use a Caddyfile via {option}`services.caddy.configFile`, {option}`services.caddy.virtualHosts` or
|
||||||
|
{option}`services.caddy.extraConfig` with {option}`services.caddy.globalConfig` instead.
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: {.note}
|
||||||
|
Takes presence over most `services.caddy.*` options, such as {option}`services.caddy.configFile` and {option}`services.caddy.virtualHosts`, if specified.
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
environmentFile = mkOption {
|
||||||
|
type = with types; nullOr path;
|
||||||
|
default = null;
|
||||||
|
example = "/run/secrets/caddy.env";
|
||||||
|
description = ''
|
||||||
|
Environment file as defined in {manpage}`systemd.exec(5)`.
|
||||||
|
|
||||||
|
You can use environment variables to pass secrets to the service without adding
|
||||||
|
them to the world-redable nix store.
|
||||||
|
|
||||||
|
```
|
||||||
|
# in configuration.nix
|
||||||
|
services.caddy.environmentFile = "/run/secrets/caddy.env";
|
||||||
|
services.caddy.globalConfig = '''
|
||||||
|
{
|
||||||
|
acme_ca https://acme.zerossl.com/v2/DV90
|
||||||
|
acme_eab {
|
||||||
|
key_id {$EAB_KEY_ID}
|
||||||
|
mac_key {$EAB_MAC_KEY}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
''';
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
# in /run/secrets/caddy.env
|
||||||
|
EAB_KEY_ID=secret
|
||||||
|
EAB_MAC_KEY=secret
|
||||||
|
```
|
||||||
|
|
||||||
|
Find more examples
|
||||||
|
[here](https://caddyserver.com/docs/caddyfile/concepts#environment-variables)
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# implementation
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
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`";
|
||||||
|
}
|
||||||
|
]
|
||||||
|
++ map (
|
||||||
|
name:
|
||||||
|
mkCertOwnershipAssertion {
|
||||||
|
cert = config.security.acme.certs.${name};
|
||||||
|
groups = config.users.groups;
|
||||||
|
services = [ config.systemd.services.caddy ];
|
||||||
|
}
|
||||||
|
) vhostCertNames;
|
||||||
|
|
||||||
|
services.caddy.globalConfig = ''
|
||||||
|
${optionalString (cfg.email != null) "email ${cfg.email}"}
|
||||||
|
${optionalString (cfg.acmeCA != null) "acme_ca ${cfg.acmeCA}"}
|
||||||
|
log {
|
||||||
|
${cfg.logFormat}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
|
# https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes
|
||||||
|
boot.kernel.sysctl."net.core.rmem_max" = mkDefault 2500000;
|
||||||
|
boot.kernel.sysctl."net.core.wmem_max" = mkDefault 2500000;
|
||||||
|
|
||||||
|
systemd.packages = [ cfg.package ];
|
||||||
|
systemd.services.caddy = {
|
||||||
|
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;
|
||||||
|
startLimitBurst = 10;
|
||||||
|
reloadTriggers = optional cfg.enableReload cfg.configFile;
|
||||||
|
restartTriggers = optional (!cfg.enableReload) cfg.configFile;
|
||||||
|
|
||||||
|
serviceConfig =
|
||||||
|
let
|
||||||
|
runOptions = ''--config ${configPath} ${
|
||||||
|
optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"
|
||||||
|
}'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Override the `ExecStart` line from upstream's systemd unit file by our own:
|
||||||
|
# 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 = [
|
||||||
|
""
|
||||||
|
''${lib.getExe cfg.package} run ${runOptions} ${optionalString cfg.resume "--resume"}''
|
||||||
|
];
|
||||||
|
# Validating the configuration before applying it ensures we’ll get a proper error that will be reported when switching to the configuration
|
||||||
|
ExecReload = [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
++ lib.optional cfg.enableReload "${lib.getExe cfg.package} reload ${runOptions} --force";
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
ReadWritePaths = [ cfg.dataDir ];
|
||||||
|
StateDirectory = mkIf (cfg.dataDir == "/var/lib/caddy") [ "caddy" ];
|
||||||
|
LogsDirectory = mkIf (cfg.logDir == "/var/log/caddy") [ "caddy" ];
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartPreventExitStatus = 1;
|
||||||
|
RestartSec = "5s";
|
||||||
|
EnvironmentFile = optional (cfg.environmentFile != null) cfg.environmentFile;
|
||||||
|
|
||||||
|
# TODO: attempt to upstream these options
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateDevices = true;
|
||||||
|
ProtectHome = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users = optionalAttrs (cfg.user == "caddy") {
|
||||||
|
caddy = {
|
||||||
|
group = cfg.group;
|
||||||
|
uid = config.ids.uids.caddy;
|
||||||
|
home = cfg.dataDir;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
users.groups = optionalAttrs (cfg.group == "caddy") {
|
||||||
|
caddy.gid = config.ids.gids.caddy;
|
||||||
|
};
|
||||||
|
|
||||||
|
security.acme.certs =
|
||||||
|
let
|
||||||
|
certCfg = map (
|
||||||
|
certName:
|
||||||
|
nameValuePair certName {
|
||||||
|
group = mkDefault cfg.group;
|
||||||
|
reloadServices = [ "caddy.service" ];
|
||||||
|
}
|
||||||
|
) vhostCertNames;
|
||||||
|
in
|
||||||
|
listToAttrs certCfg;
|
||||||
|
|
||||||
|
environment.etc.${etcConfigFile}.source = cfg.configFile;
|
||||||
|
};
|
||||||
|
}
|
||||||
88
modules/nixos/caddy/vhost-options.nix
Normal file
88
modules/nixos/caddy/vhost-options.nix
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
{ cfg }:
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
name,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
inherit (lib) literalExpression mkOption types;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
|
||||||
|
hostName = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = name;
|
||||||
|
description = "Canonical hostname for the server.";
|
||||||
|
};
|
||||||
|
|
||||||
|
serverAliases = mkOption {
|
||||||
|
type = with types; listOf str;
|
||||||
|
default = [ ];
|
||||||
|
example = [
|
||||||
|
"www.example.org"
|
||||||
|
"example.org"
|
||||||
|
];
|
||||||
|
description = ''
|
||||||
|
Additional names of virtual hosts served by this virtual host configuration.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
listenAddresses = mkOption {
|
||||||
|
type = with types; listOf str;
|
||||||
|
description = ''
|
||||||
|
A list of host interfaces to bind to for this virtual host.
|
||||||
|
'';
|
||||||
|
default = [ ];
|
||||||
|
example = [
|
||||||
|
"127.0.0.1"
|
||||||
|
"::1"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
useACMEHost = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
A host of an existing Let's Encrypt certificate to use.
|
||||||
|
This is mostly useful if you use DNS challenges but Caddy does not
|
||||||
|
currently support your provider.
|
||||||
|
|
||||||
|
*Note that this option does not create any certificates, nor
|
||||||
|
does it add subdomains to existing ones – you will need to create them
|
||||||
|
manually using [](#opt-security.acme.certs).*
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
logFormat = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
default = ''
|
||||||
|
output file ${cfg.logDir}/access-${lib.replaceStrings [ "/" " " ] [ "_" "_" ] config.hostName}.log
|
||||||
|
'';
|
||||||
|
defaultText = ''
|
||||||
|
output file ''${config.services.caddy.logDir}/access-''${hostName}.log
|
||||||
|
'';
|
||||||
|
example = literalExpression ''
|
||||||
|
mkForce '''
|
||||||
|
output discard
|
||||||
|
''';
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Configuration for HTTP request logging (also known as access logs). See
|
||||||
|
<https://caddyserver.com/docs/caddyfile/directives/log#log>
|
||||||
|
for details.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
extraConfig = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
default = "";
|
||||||
|
description = ''
|
||||||
|
Additional lines of configuration appended to this virtual host in the
|
||||||
|
automatically generated `Caddyfile`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user