617 lines
19 KiB
Nix
Executable File
617 lines
19 KiB
Nix
Executable File
{
|
|
config,
|
|
lib,
|
|
namespace,
|
|
...
|
|
}:
|
|
with lib;
|
|
let
|
|
cfg = config.${namespace}.services.traefik;
|
|
|
|
# Process extraServices into service configurations
|
|
extraServiceConfigs =
|
|
let
|
|
makeService =
|
|
service:
|
|
nameValuePair service.name {
|
|
loadBalancer.servers = [
|
|
{
|
|
url = service.url;
|
|
}
|
|
];
|
|
};
|
|
in
|
|
listToAttrs (map makeService cfg.extraServices);
|
|
|
|
# Process extraRouters into router configurations
|
|
extraRouterConfigs =
|
|
let
|
|
makeRouter =
|
|
router:
|
|
nameValuePair router.subdomain {
|
|
entryPoints = router.entryPoints;
|
|
rule = "Host(`${router.subdomain}.${domain}`)";
|
|
service = router.service;
|
|
middlewares = router.middlewares ++ [
|
|
"crowdsec"
|
|
"whitelist-geoblock"
|
|
];
|
|
tls.certResolver = "letsencrypt";
|
|
};
|
|
in
|
|
listToAttrs (map makeRouter cfg.extraRouters);
|
|
|
|
# Process reverseProxies into service and router configurations
|
|
reverseProxyServiceConfigs =
|
|
let
|
|
makeService = reverseProxy: nameValuePair reverseProxy.service.name reverseProxy.service.config;
|
|
in
|
|
listToAttrs (map makeService cfg.reverseProxies);
|
|
|
|
reverseProxyRouterConfigs =
|
|
let
|
|
makeRouter = reverseProxy: nameValuePair reverseProxy.router.subdomain reverseProxy.router.config;
|
|
in
|
|
listToAttrs (map makeRouter cfg.reverseProxies);
|
|
|
|
domain = "mjallen.dev";
|
|
serverIp = "10.0.1.3";
|
|
|
|
# Forward services
|
|
authUrl = "http://${serverIp}:9000/outpost.goauthentik.io";
|
|
|
|
authentikUrl = "http://${serverIp}:9000";
|
|
cacheUrl = "http://${serverIp}:9012";
|
|
cloudUrl = "http:/10.0.1.3:9200";
|
|
# cloudUrl = "http://${config.containers.nextcloud.localAddress}:80";
|
|
hassUrl = "http://10.0.1.4:8123";
|
|
immichUrl = "http://${serverIp}:${toString config.services.immich.port}";
|
|
jellyfinUrl = "http://${serverIp}:8096";
|
|
jellyseerrUrl = "http://10.0.1.3:${toString config.services.jellyseerr.port}";
|
|
lubeloggerUrl = "http://${serverIp}:6754";
|
|
# onlyofficeUrl = "http://${config.containers.nextcloud.localAddress}:${toString config.containers.nextcloud.config.services.onlyoffice.port}";
|
|
onlyofficeUrl = "http://10.0.1.3:9980";
|
|
openWebUIUrl = "http://${serverIp}:8888";
|
|
paperlessUrl = "http://${serverIp}:${toString config.services.paperless.port}";
|
|
|
|
# Plugins
|
|
traefikPlugins = {
|
|
bouncer = {
|
|
moduleName = "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin";
|
|
version = "v1.4.5";
|
|
};
|
|
geoblock = {
|
|
moduleName = "github.com/PascalMinder/geoblock";
|
|
version = "v0.2.5";
|
|
};
|
|
};
|
|
|
|
# Ports
|
|
httpPort = 80;
|
|
httpsPort = 443;
|
|
traefikPort = 8080;
|
|
metricsPort = 8082;
|
|
|
|
forwardPorts = [
|
|
httpPort
|
|
httpsPort
|
|
traefikPort
|
|
metricsPort
|
|
];
|
|
|
|
# misc
|
|
letsEncryptEmail = "jalle008@proton.me";
|
|
dataDir = "/media/nas/main/appdata/traefik";
|
|
authentikAddress = "http://${serverIp}:9000/outpost.goauthentik.io/auth/traefik";
|
|
in
|
|
{
|
|
imports = [ ./options.nix ];
|
|
|
|
config = mkIf cfg.enable {
|
|
sops = {
|
|
secrets = {
|
|
"jallen-nas/traefik/crowdsec/lapi-key" = {
|
|
sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml");
|
|
owner = config.users.users.traefik.name;
|
|
group = config.users.users.traefik.group;
|
|
restartUnits = [ "traefik.service" ];
|
|
};
|
|
|
|
"jallen-nas/traefik/crowdsec/capi-machine-id" = {
|
|
sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml");
|
|
owner = config.users.users.traefik.name;
|
|
group = config.users.users.traefik.group;
|
|
restartUnits = [ "traefik.service" ];
|
|
};
|
|
|
|
"jallen-nas/traefik/crowdsec/capi-password" = {
|
|
sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml");
|
|
owner = config.users.users.traefik.name;
|
|
group = config.users.users.traefik.group;
|
|
restartUnits = [ "traefik.service" ];
|
|
};
|
|
"jallen-nas/traefik/cloudflare-dns-api-token" = {
|
|
sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml");
|
|
owner = config.users.users.traefik.name;
|
|
group = config.users.users.traefik.group;
|
|
restartUnits = [ "traefik.service" ];
|
|
};
|
|
"jallen-nas/traefik/cloudflare-zone-api-token" = {
|
|
sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml");
|
|
owner = config.users.users.traefik.name;
|
|
group = config.users.users.traefik.group;
|
|
restartUnits = [ "traefik.service" ];
|
|
};
|
|
"jallen-nas/traefik/cloudflare-api-key" = {
|
|
sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml");
|
|
owner = config.users.users.traefik.name;
|
|
group = config.users.users.traefik.group;
|
|
restartUnits = [ "traefik.service" ];
|
|
};
|
|
"jallen-nas/traefik/cloudflare-email" = {
|
|
sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml");
|
|
owner = config.users.users.traefik.name;
|
|
group = config.users.users.traefik.group;
|
|
restartUnits = [ "traefik.service" ];
|
|
};
|
|
};
|
|
templates = {
|
|
"traefik.env" = {
|
|
content = ''
|
|
CLOUDFLARE_DNS_API_TOKEN=${config.sops.placeholder."jallen-nas/traefik/cloudflare-dns-api-token"}
|
|
CLOUDFLARE_ZONE_API_TOKEN=${config.sops.placeholder."jallen-nas/traefik/cloudflare-zone-api-token"}
|
|
CLOUDFLARE_API_KEY=${config.sops.placeholder."jallen-nas/traefik/cloudflare-api-key"}
|
|
CLOUDFLARE_EMAIL=${config.sops.placeholder."jallen-nas/traefik/cloudflare-email"}
|
|
'';
|
|
owner = config.users.users.traefik.name;
|
|
group = config.users.users.traefik.group;
|
|
restartUnits = [ "traefik.service" ];
|
|
};
|
|
};
|
|
};
|
|
|
|
networking.firewall = {
|
|
allowedTCPPorts = forwardPorts;
|
|
allowedUDPPorts = forwardPorts;
|
|
};
|
|
|
|
services.traefik = {
|
|
enable = true;
|
|
dataDir = dataDir;
|
|
group = "jallen-nas"; # group;
|
|
environmentFiles = [ config.sops.templates."traefik.env".path ];
|
|
|
|
staticConfigOptions = {
|
|
entryPoints = {
|
|
web = {
|
|
address = ":${toString httpPort}";
|
|
asDefault = true;
|
|
http.redirections.entrypoint = {
|
|
to = "websecure";
|
|
scheme = "https";
|
|
};
|
|
};
|
|
|
|
websecure = {
|
|
address = ":${toString httpsPort}";
|
|
asDefault = true;
|
|
http.tls.certResolver = "letsencrypt";
|
|
};
|
|
|
|
metrics = {
|
|
address = ":${toString metricsPort}"; # Port for metrics
|
|
};
|
|
};
|
|
|
|
log = {
|
|
level = "INFO";
|
|
};
|
|
|
|
metrics = {
|
|
prometheus = {
|
|
entryPoint = "metrics";
|
|
addEntryPointsLabels = true;
|
|
addServicesLabels = true;
|
|
buckets = [
|
|
0.1
|
|
0.3
|
|
1.2
|
|
5.0
|
|
]; # Response time buckets
|
|
};
|
|
};
|
|
|
|
certificatesResolvers.letsencrypt.acme = {
|
|
email = letsEncryptEmail;
|
|
storage = "${config.services.traefik.dataDir}/acme.json";
|
|
dnsChallenge = {
|
|
provider = "cloudflare";
|
|
resolvers = [
|
|
"1.1.1.1:53"
|
|
"8.8.8.8:53"
|
|
];
|
|
};
|
|
};
|
|
|
|
api.dashboard = true;
|
|
# Access the Traefik dashboard on <Traefik IP>:8080 of your server
|
|
api.insecure = true;
|
|
|
|
experimental = {
|
|
plugins = traefikPlugins;
|
|
};
|
|
};
|
|
|
|
dynamicConfigOptions = {
|
|
# udp = {
|
|
# services = {
|
|
# wireguard.loadBalancer.servers = [
|
|
# {
|
|
# url = "localhost:51820";
|
|
# }
|
|
# ];
|
|
# };
|
|
# routers = {
|
|
# wireguard = {
|
|
# entryPoints = [ "websecure" ];
|
|
# service = "wireguard";
|
|
# };
|
|
# };
|
|
# };
|
|
|
|
http = {
|
|
|
|
serversTransports = {
|
|
internal-https = {
|
|
insecureSkipVerify = true;
|
|
};
|
|
attich1 = {
|
|
serverName = "localhost";
|
|
disableHTTP2 = true;
|
|
};
|
|
};
|
|
|
|
middlewares = {
|
|
authentik = {
|
|
forwardAuth = {
|
|
tls.insecureSkipVerify = true;
|
|
address = authentikAddress;
|
|
trustForwardHeader = true;
|
|
authResponseHeaders = [
|
|
"X-authentik-username"
|
|
"X-authentik-groups"
|
|
"X-authentik-email"
|
|
"X-authentik-name"
|
|
"X-authentik-uid"
|
|
"X-authentik-jwt"
|
|
"X-authentik-meta-jwks"
|
|
"X-authentik-meta-outpost"
|
|
"X-authentik-meta-provider"
|
|
"X-authentik-meta-app"
|
|
"X-authentik-meta-version"
|
|
];
|
|
};
|
|
};
|
|
crowdsec = {
|
|
plugin = {
|
|
bouncer = {
|
|
enabled = true;
|
|
crowdsecLapiKeyFile = config.sops.secrets."jallen-nas/traefik/crowdsec/lapi-key".path;
|
|
crowdsecLapiScheme = "http";
|
|
crowdsecLapiHost = "localhost:8181";
|
|
crowdsecLapiPath = "/";
|
|
crowdsecLapiTLSInsecureVerify = false;
|
|
crowdsecCapiMachineIdFile = config.sops.secrets."jallen-nas/traefik/crowdsec/capi-machine-id".path;
|
|
crowdsecCapiPasswordFile = config.sops.secrets."jallen-nas/traefik/crowdsec/capi-password".path;
|
|
crowdsecCapiScenarios = [ ];
|
|
};
|
|
};
|
|
};
|
|
whitelist-geoblock = {
|
|
plugin = {
|
|
geoblock = {
|
|
silentStartUp = false;
|
|
allowLocalRequests = true;
|
|
logLocalRequests = false;
|
|
logAllowedRequests = false;
|
|
logApiRequests = false;
|
|
api = "https://get.geojs.io/v1/ip/country/{ip}";
|
|
apiTimeoutMs = 500;
|
|
cacheSize = 25;
|
|
forceMonthlyUpdate = true;
|
|
allowUnknownCountries = false;
|
|
unknownCountryApiResponse = "nil";
|
|
blackListMode = false;
|
|
countries = [
|
|
"CA"
|
|
"US"
|
|
];
|
|
};
|
|
};
|
|
};
|
|
internal-ipallowlist = {
|
|
ipAllowList = {
|
|
sourceRange = [
|
|
"127.0.0.1/32"
|
|
"10.0.1.0/24"
|
|
];
|
|
};
|
|
};
|
|
collabora-headers = {
|
|
headers = {
|
|
customRequestHeaders = {
|
|
Upgrade = "websocket";
|
|
Connection = "Upgrade";
|
|
X-Forwarded-Proto = "https";
|
|
X-Forwarded-Host = "office.mjallen.dev";
|
|
};
|
|
customResponseHeaders = {
|
|
X-Frame-Options = "";
|
|
Content-Security-Policy = "frame-ancestors https://cloud.mjallen.dev";
|
|
};
|
|
referrerPolicy = "no-referrer";
|
|
stsSeconds = "15552000";
|
|
stsPreload = "true";
|
|
stsIncludeSubdomains = "true";
|
|
forceSTSHeader = "true";
|
|
browserXssFilter = "true";
|
|
};
|
|
};
|
|
onlyoffice-headers = {
|
|
headers = {
|
|
customResponseHeaders = {
|
|
X-Robots-Tag = "none";
|
|
Strict-Transport-Security = "max-age=63072000";
|
|
X-Forwarded-Proto = "https";
|
|
};
|
|
browserXssFilter = "true";
|
|
contentTypeNosniff = "true";
|
|
stsIncludeSubdomains = "true";
|
|
stsPreload = "true";
|
|
stsSeconds = "31536000";
|
|
forceSTSHeader = "true";
|
|
accessControlMaxAge = "15552000";
|
|
accesscontrolalloworiginlist = "*";
|
|
};
|
|
};
|
|
};
|
|
|
|
services = {
|
|
auth.loadBalancer.servers = [
|
|
{
|
|
url = authUrl;
|
|
}
|
|
];
|
|
|
|
gitea.loadBalancer.servers = [
|
|
{
|
|
url = "http://10.0.1.3:3000";
|
|
}
|
|
];
|
|
|
|
actual.loadBalancer.servers = [
|
|
{
|
|
url = "http://10.0.1.3:3333";
|
|
}
|
|
];
|
|
|
|
matrix.loadBalancer.servers = [
|
|
{
|
|
url = "http://10.0.1.3:8448";
|
|
}
|
|
];
|
|
|
|
authentik.loadBalancer.servers = [
|
|
{
|
|
url = authentikUrl;
|
|
}
|
|
];
|
|
cache.loadBalancer = {
|
|
servers = [
|
|
{
|
|
url = cacheUrl;
|
|
}
|
|
];
|
|
serversTransport = "attich1";
|
|
};
|
|
chat.loadBalancer.servers = [
|
|
{
|
|
url = openWebUIUrl;
|
|
}
|
|
];
|
|
cloud.loadBalancer = {
|
|
servers = [
|
|
{
|
|
url = cloudUrl;
|
|
}
|
|
];
|
|
};
|
|
hass.loadBalancer.servers = [
|
|
{
|
|
url = hassUrl;
|
|
}
|
|
];
|
|
immich.loadBalancer.servers = [
|
|
{
|
|
url = immichUrl;
|
|
}
|
|
];
|
|
jellyfin.loadBalancer.servers = [
|
|
{
|
|
url = jellyfinUrl;
|
|
}
|
|
];
|
|
jellyseerr.loadBalancer.servers = [
|
|
{
|
|
url = jellyseerrUrl;
|
|
}
|
|
];
|
|
lubelogger.loadBalancer.servers = [
|
|
{
|
|
url = lubeloggerUrl;
|
|
}
|
|
];
|
|
onlyoffice.loadBalancer = {
|
|
servers = [
|
|
{
|
|
url = onlyofficeUrl;
|
|
}
|
|
];
|
|
passHostHeader = true;
|
|
};
|
|
paperless.loadBalancer.servers = [
|
|
{
|
|
url = paperlessUrl;
|
|
}
|
|
];
|
|
}
|
|
// extraServiceConfigs
|
|
// reverseProxyServiceConfigs;
|
|
|
|
routers = {
|
|
auth = {
|
|
entryPoints = [ "websecure" ];
|
|
rule = "HostRegexp(`{subdomain:[a-z]+}.mjallen.dev`) && PathPrefix(`/outpost.goauthentik.io/`)";
|
|
service = "auth";
|
|
middlewares = [
|
|
"crowdsec"
|
|
"whitelist-geoblock"
|
|
];
|
|
priority = 15;
|
|
tls.certResolver = "letsencrypt";
|
|
};
|
|
|
|
gitea = {
|
|
entryPoints = [ "websecure" ];
|
|
rule = "Host(`gitea.${domain}`)";
|
|
service = "gitea";
|
|
middlewares = [
|
|
"crowdsec"
|
|
"whitelist-geoblock"
|
|
];
|
|
tls.certResolver = "letsencrypt";
|
|
};
|
|
|
|
actual = {
|
|
entryPoints = [ "websecure" ];
|
|
rule = "Host(`actual.${domain}`)";
|
|
service = "actual";
|
|
middlewares = [
|
|
"crowdsec"
|
|
"whitelist-geoblock"
|
|
];
|
|
tls.certResolver = "letsencrypt";
|
|
};
|
|
|
|
matrix = {
|
|
entryPoints = [ "websecure" ];
|
|
rule = "Host(`matrix.${domain}`)";
|
|
service = "matrix";
|
|
middlewares = [
|
|
"crowdsec"
|
|
"whitelist-geoblock"
|
|
];
|
|
tls.certResolver = "letsencrypt";
|
|
};
|
|
|
|
authentik = {
|
|
entryPoints = [ "websecure" ];
|
|
rule = "Host(`authentik.${domain}`)";
|
|
service = "authentik";
|
|
middlewares = [
|
|
"crowdsec"
|
|
"whitelist-geoblock"
|
|
];
|
|
tls.certResolver = "letsencrypt";
|
|
};
|
|
cache = {
|
|
entryPoints = [ "websecure" ];
|
|
rule = "Host(`cache.${domain}`)";
|
|
service = "cache";
|
|
middlewares = [ ];
|
|
priority = 10;
|
|
tls.certResolver = "letsencrypt";
|
|
};
|
|
cloud = {
|
|
entryPoints = [ "websecure" ];
|
|
rule = "Host(`cloud.${domain}`)";
|
|
service = "cloud";
|
|
middlewares = [
|
|
"crowdsec"
|
|
"whitelist-geoblock"
|
|
];
|
|
tls.certResolver = "letsencrypt";
|
|
};
|
|
hass = {
|
|
entryPoints = [ "websecure" ];
|
|
rule = "Host(`hass.${domain}`)";
|
|
service = "hass";
|
|
middlewares = [
|
|
"crowdsec"
|
|
"whitelist-geoblock"
|
|
"authentik"
|
|
];
|
|
priority = 10;
|
|
tls.certResolver = "letsencrypt";
|
|
};
|
|
immich = {
|
|
entryPoints = [ "websecure" ];
|
|
rule = "Host(`immich.${domain}`)";
|
|
service = "immich";
|
|
middlewares = [
|
|
"crowdsec"
|
|
"whitelist-geoblock"
|
|
];
|
|
tls.certResolver = "letsencrypt";
|
|
};
|
|
jellyfin = {
|
|
entryPoints = [ "websecure" ];
|
|
rule = "Host(`jellyfin.${domain}`)";
|
|
service = "jellyfin";
|
|
middlewares = [
|
|
"crowdsec"
|
|
"whitelist-geoblock"
|
|
];
|
|
tls.certResolver = "letsencrypt";
|
|
};
|
|
jellyseerr = {
|
|
entryPoints = [ "websecure" ];
|
|
rule = "Host(`jellyseerr.${domain}`)";
|
|
service = "jellyseerr";
|
|
middlewares = [
|
|
"crowdsec"
|
|
"whitelist-geoblock"
|
|
];
|
|
tls.certResolver = "letsencrypt";
|
|
};
|
|
lubelogger = {
|
|
entryPoints = [ "websecure" ];
|
|
rule = "Host(`lubelogger.${domain}`)";
|
|
service = "lubelogger";
|
|
middlewares = [
|
|
"crowdsec"
|
|
"whitelist-geoblock"
|
|
];
|
|
tls.certResolver = "letsencrypt";
|
|
};
|
|
onlyoffice = {
|
|
entryPoints = [ "websecure" ];
|
|
rule = "Host(`office.${domain}`)";
|
|
service = "onlyoffice";
|
|
middlewares = [
|
|
"crowdsec"
|
|
"whitelist-geoblock"
|
|
# "onlyoffice-headers"
|
|
"collabora-headers"
|
|
];
|
|
tls.certResolver = "letsencrypt";
|
|
};
|
|
}
|
|
// extraRouterConfigs
|
|
// reverseProxyRouterConfigs;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
}
|