dotfiles/modules/nixos/services/traefik.nix

135 lines
3.4 KiB
Nix

{
config,
lib,
...
}: let
cfg = config.services.traefik;
routeOptions = lib.types.submodule {
options = {
rule = lib.mkOption {
type = lib.types.str;
example = "Host(`example.com`)";
description = "Traefik routing rule";
};
url = lib.mkOption {
type = lib.types.str;
example = "http://localhost:8096";
description = "Backend service URL";
};
entryPoints = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = ["websecure"];
description = "Entry points for this route";
};
certResolver = lib.mkOption {
type = lib.types.str;
default = "letsencrypt";
description = "Certificate resolver to use";
};
middlewares = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = "Middlewares to apply to this route";
example = ["auth" "compress"];
};
};
};
mkRouter = service: routeCfg:
{
inherit service;
rule = routeCfg.rule;
entryPoints = routeCfg.entryPoints;
tls.certResolver = routeCfg.certResolver;
}
// lib.optionalAttrs (routeCfg.middlewares != []) {
middlewares = routeCfg.middlewares;
};
mkService = name: routeCfg: {
loadBalancer.servers = [
{url = routeCfg.url;}
];
};
dynamicConfigOptions = {
http = {
routers = lib.mapAttrs mkRouter cfg.routes;
services = lib.mapAttrs mkService cfg.routes;
};
};
in {
options.services.traefik = {
postmasterEmail = lib.mkOption {
type = lib.types.str;
example = "email@example.com";
description = "The email address of the postmaster";
};
routes = lib.mkOption {
type = lib.types.attrsOf routeOptions;
default = {};
description = "Simple route definitions for Traefik";
example = lib.literalExpression ''
{
solid-pod = {
rule = "Host(`solid.my.dev`)";
url = "http://localhost:8096";
};
radicle = {
rule = "Host(`radicle.my.dev`)";
url = "http://localhost:8097";
};
}
'';
};
};
config = lib.mkIf (cfg.enable && cfg.routes != {}) {
networking.firewall.allowedTCPPorts = [80 443];
services.traefik = {
inherit dynamicConfigOptions;
staticConfigOptions = {
entryPoints = {
web = {
address = ":80";
asDefault = true;
http.redirections.entrypoint = {
to = "websecure";
scheme = "https";
};
};
websecure = {
address = ":443";
asDefault = true;
http.tls.certResolver = "letsencrypt";
};
};
log = {
level = "DEBUG";
filePath = "${config.services.traefik.dataDir}/traefik.log";
format = "json";
};
certificatesResolvers.letsencrypt.acme = {
email = config.services.traefik.postmasterEmail;
storage = "${config.services.traefik.dataDir}/acme.json";
# dnsChallenge.provider = "cloudflare";
# TODO: Declaratively determine whether to use staging or production
# based on whether we are in testing.
# caServer = "";
httpChallenge.entryPoint = "web";
};
};
};
};
}