nix/modules/networking/caddy.nix
2025-01-19 17:56:26 +01:00

206 lines
5.4 KiB
Nix

{
config,
pkgs,
lib,
...
}:
with lib;
let
cfg = config.my.networking.caddy;
in
{
options.my.networking.caddy = {
enable = lib.mkEnableOption "Enable caddy as reverse proxy";
domainsList = lib.mkOption {
type = lib.types.listOf (lib.types.attrsOf lib.types.str);
description = ''
A list of sets, each containing three parameters of type string: domain, email, and cloudflareApiKeyFile.
'';
default = [
{
domain = "example.com";
email = "user@domain.com";
cloudflareApiKeyFile = "/path/to/cloudflare/api/key";
}
];
};
dynamicdnsDomains = lib.mkOption {
type = lib.types.listOf (lib.types.attrsOf lib.types.str);
description = ''
A list of domains to update with the dynamicdns plugin.
'';
default = [
{
domain = "example.com";
cloudflareApiEnvName = "CLOUDFLARE_API_TOKEN_MY_DOMAIN";
}
];
};
configEnvFile = lib.mkOption {
type = lib.types.path;
description = ''
Path to the environment file that contains the secrets like Cloudflare API key.
In order to use the dynamicdns plugin, you need to set "cloudflareApiEnvName" for each domain in the dynamicdnsDomains list.
'';
default = "";
};
};
config = lib.mkIf cfg.enable {
# Insted on relying on caddy to provide TLS, we use certbot to get a certificate
# https://aottr.dev/posts/2024/08/homelab-setting-up-caddy-reverse-proxy-with-ssl-on-nixos/
security.acme = {
acceptTerms = true;
# TESTING ONLY!
# defaults.server = "https://acme-staging-v02.api.letsencrypt.org/directory";
certs = lib.mkMerge (
map (domainConfig: {
"${domainConfig.domain}" = {
group = config.services.caddy.group;
email = domainConfig.email;
domain = domainConfig.domain;
extraDomainNames = [ "*.${domainConfig.domain}" ];
dnsProvider = "cloudflare";
dnsResolver = "1.1.1.1:53";
dnsPropagationCheck = true;
environmentFile = domainConfig.cloudflareApiKeyFile;
};
}) cfg.domainsList
);
};
services.caddy = {
enable = true;
# Waiting for https://github.com/NixOS/nixpkgs/issues/14671 to be released
package = pkgs.callPackage ../../packages/caddy.nix {
externalPlugins = [
{
name = "cloudflare";
repo = "github.com/caddy-dns/cloudflare";
version = "master";
}
{
name = "dynamicdns";
repo = "github.com/mholt/caddy-dynamicdns";
version = "7c818ab3fc3485a72a346f85c77810725f19f9cf";
}
];
vendorHash = "sha256-vkJw/92zXt5S2eUxRSjtwn1nqU/f+WHPEG8AD4Z342I=";
};
globalConfig =
''
admin :2024
servers {
metrics
}
''
+ lib.concatStringsSep "\n" (
map (dynamicdnsDomain: ''
dynamic_dns {
provider cloudflare {env.${dynamicdnsDomain.cloudflareApiEnvName}}
domains {
${dynamicdnsDomain.domain} @
}
dynamic_domains
}
'') cfg.dynamicdnsDomains
);
extraConfig = lib.concatStringsSep "\n" (
map (
domainConfig:
let
certPath = config.security.acme.certs."${domainConfig.domain}".directory;
in
''
(cloudflare_${domainConfig.domain}) {
tls ${certPath}/cert.pem ${certPath}/key.pem {
protocols tls1.3
}
}
''
) cfg.domainsList
);
};
systemd.services.caddy.serviceConfig = {
AmbientCapabilities = "CAP_NET_BIND_SERVICE";
EnvironmentFile = cfg.configEnvFile;
};
# By default, the module create a custom user but it lacks permission to read caddy files
systemd.services.promtail.serviceConfig = {
Group = lib.mkForce config.services.caddy.group;
User = lib.mkForce config.services.caddy.user;
};
services.promtail = {
enable = true;
configuration = {
server.http_listen_port = 9080;
server.grpc_listen_port = 0;
clients = [ { url = "http://metrics.internal:3100/loki/api/v1/push"; } ];
scrape_configs = [
{
job_name = "journal";
journal = {
max_age = "12h";
labels = {
job = "systemd-journal";
};
};
relabel_configs = [
{
source_labels = [ "__journal__systemd_unit" ];
regex = "(.*)\\.service";
target_label = "service";
}
{
source_labels = [ "__journal__hostname" ];
target_label = "hostname";
}
];
}
{
job_name = "caddy";
static_configs = [
{
targets = [ "localhost" ];
labels = {
job = "caddylogs";
__path__ = "${config.services.caddy.logDir}/*.log";
};
}
];
}
];
};
};
networking.firewall.allowedTCPPorts = [
80
443
2024
];
networking.firewall.allowedUDPPorts = [
80
443
];
};
}