174 lines
5.4 KiB
Nix
174 lines
5.4 KiB
Nix
{ pkgs, lib, config, ... }:
|
|
|
|
with lib;
|
|
let
|
|
cfg = config.cloud.traefik;
|
|
|
|
tlsNoCloudflare = {
|
|
options = "no-cloudflare";
|
|
};
|
|
|
|
# Copied from traefik.nix
|
|
jsonValue = with types;
|
|
let
|
|
valueType = nullOr
|
|
(oneOf [
|
|
bool
|
|
int
|
|
float
|
|
str
|
|
(lazyAttrsOf valueType)
|
|
(listOf valueType)
|
|
]) // {
|
|
description = "JSON value";
|
|
emptyValue.value = { };
|
|
};
|
|
in
|
|
valueType;
|
|
|
|
hostType = with types; submodule {
|
|
options = {
|
|
host = mkOption {
|
|
type = str;
|
|
description = "The host for the router filter";
|
|
};
|
|
path = mkOption {
|
|
type = nullOr str;
|
|
default = null;
|
|
description = "The path for the router filter (exact path is matched)";
|
|
};
|
|
filter = mkOption {
|
|
type = nullOr str;
|
|
default = null;
|
|
description = "The filter syntax for the router. Overrides `host` and `path` if provided";
|
|
};
|
|
localHost = mkOption {
|
|
type = types.nullOr types.str;
|
|
description = "The local host of the service. Must be an IP if protocol is TCP. Default to localhost/127.0.0.1";
|
|
default = null;
|
|
};
|
|
port = mkOption {
|
|
type = types.port;
|
|
description = "The port that the service is listening on";
|
|
};
|
|
entrypoints = mkOption {
|
|
type = listOf (enum [ "http" "https" "smtp-submission" "smtp-submission-ssl" "imap" "wireguard" ]);
|
|
default = [ "https" ];
|
|
description = "The entrypoints that will serve the host";
|
|
};
|
|
middlewares = mkOption {
|
|
type = listOf jsonValue;
|
|
default = [ ];
|
|
description = "The middlewares to be used with the host.";
|
|
};
|
|
protocol = mkOption {
|
|
type = enum [ "http" "tcp" "udp" ];
|
|
default = "http";
|
|
description = "The protocol of the router and service";
|
|
};
|
|
tlsPassthrough = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = "Sets the TCP passthrough value. Defaults to `true` if the connection is tcp";
|
|
};
|
|
noCloudflare = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Bypasses the client cert requirement, enable if you don't route things through cloudflare";
|
|
};
|
|
};
|
|
};
|
|
|
|
# Returns the filter given a host configuration
|
|
filterOfHost = host:
|
|
let
|
|
hostFilter = if host.protocol == "http" then "Host" else "HostSNI";
|
|
in
|
|
if host.filter != null then host.filter
|
|
else if host.path == null then "${hostFilter}(`${host.host}`)"
|
|
else "${hostFilter}(`${host.host}`) && Path(`${host.path}`)";
|
|
|
|
# Turns a host configuration into dynamic traefik configuration
|
|
hostToConfig = name: host: {
|
|
"${host.protocol}" = {
|
|
routers."${name}-router" = (if (host.protocol != "udp") then {
|
|
rule = filterOfHost host;
|
|
tls = { certResolver = "le"; }
|
|
// (if host.protocol == "tcp" then { passthrough = if (host ? tlsPassthrough) then host.tlsPassthrough else true; } else { })
|
|
// (if host.noCloudflare then tlsNoCloudflare else { });
|
|
} else { }) // {
|
|
entryPoints = host.entrypoints;
|
|
service = "${name}-service";
|
|
} // (
|
|
if host.protocol == "http" then
|
|
{ middlewares = lists.imap0 (id: m: "${name}-middleware-${toString id}") host.middlewares; }
|
|
else if host.middlewares == [ ] then
|
|
{ }
|
|
else abort "Cannot have middlewares on non-http routers"
|
|
);
|
|
services."${name}-service".loadBalancer.servers = [
|
|
(
|
|
let
|
|
localhost =
|
|
if isNull host.localHost then
|
|
(
|
|
if host.protocol == "http" then "localhost"
|
|
else "127.0.0.1"
|
|
) else host.localHost;
|
|
in
|
|
if host.protocol == "http" then
|
|
{ url = "http://${localhost}:${toString host.port}"; }
|
|
else { address = "${localhost}:${toString host.port}"; }
|
|
)
|
|
];
|
|
} // (if (host.middlewares != [ ]) then {
|
|
middlewares = builtins.listToAttrs (lists.imap0
|
|
(id: v: {
|
|
name = "${name}-middleware-${toString id}";
|
|
value = v;
|
|
})
|
|
host.middlewares);
|
|
} else { });
|
|
};
|
|
|
|
tlsConfig = {
|
|
tls.options.default = {
|
|
sniStrict = true;
|
|
clientAuth = {
|
|
caFiles = [
|
|
(builtins.fetchurl {
|
|
url = "https://developers.cloudflare.com/ssl/static/authenticated_origin_pull_ca.pem";
|
|
sha256 = "sha256:0hxqszqfzsbmgksfm6k0gp0hsx9k1gqx24gakxqv0391wl6fsky1";
|
|
})
|
|
];
|
|
clientAuthType = "RequireAndVerifyClientCert";
|
|
};
|
|
};
|
|
tls.options.no-cloudflare = {
|
|
sniStrict = true;
|
|
clientAuth = {
|
|
caFiles = [
|
|
(builtins.fetchurl {
|
|
url = "https://developers.cloudflare.com/ssl/static/authenticated_origin_pull_ca.pem";
|
|
sha256 = "sha256:0hxqszqfzsbmgksfm6k0gp0hsx9k1gqx24gakxqv0391wl6fsky1";
|
|
})
|
|
];
|
|
clientAuthType = "VerifyClientCertIfGiven";
|
|
};
|
|
};
|
|
};
|
|
in
|
|
{
|
|
|
|
options.cloud.traefik.hosts = mkOption {
|
|
type = types.attrsOf hostType;
|
|
default = { };
|
|
description = "The HTTP hosts to run on the server";
|
|
};
|
|
|
|
config.cloud.traefik.config = builtins.foldl' attrsets.recursiveUpdate { } [
|
|
(builtins.foldl' attrsets.recursiveUpdate { } (attrsets.mapAttrsToList hostToConfig cfg.hosts))
|
|
tlsConfig
|
|
];
|
|
}
|