REWRITE
This commit is contained in:
+52
-222
@@ -65,6 +65,38 @@ in
|
||||
default = "admin@zakobar.com";
|
||||
description = "Email for Let's Encrypt ACME registration.";
|
||||
};
|
||||
|
||||
virtualHosts = lib.mkOption {
|
||||
type = lib.types.listOf (lib.types.submodule {
|
||||
options = {
|
||||
subdomain = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Subdomain under homeyConfig.domain (e.g. \"mealie\" → mealie.zakobar.com).";
|
||||
};
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
description = "Host port to reverse-proxy to.";
|
||||
};
|
||||
auth = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Prepend Authelia forward_auth to this vhost.";
|
||||
};
|
||||
extraConfig = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
description = "Replaces the auto-generated 'reverse_proxy localhost:<port>' for HTTPS. Empty = use default.";
|
||||
};
|
||||
extraHttpConfig = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
description = "Replaces the auto-generated cfProxy for the HTTP loopback vhost. Empty = use default.";
|
||||
};
|
||||
};
|
||||
});
|
||||
default = [];
|
||||
description = "Virtual hosts to generate. Each service module contributes its own entries.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
@@ -89,229 +121,27 @@ in
|
||||
acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
|
||||
'';
|
||||
|
||||
# Each virtual host.
|
||||
# Each virtual host is generated from homey.caddy.virtualHosts entries.
|
||||
# Each service module contributes its own entries to that list.
|
||||
#
|
||||
# Each service gets two vhost entries:
|
||||
# - "host" (no scheme) → Caddy handles HTTPS + auto cert (for LAN access)
|
||||
# - "http://host" → plain HTTP for cloudflared on loopback (no redirect)
|
||||
#
|
||||
# Caddy auto-redirects HTTP→HTTPS only when no explicit http:// vhost exists.
|
||||
# By defining http:// explicitly we suppress that redirect so cloudflared
|
||||
# (which talks plain HTTP on port 80) gets a direct response.
|
||||
virtualHosts = {
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Authelia — public, no auth gate (it IS the auth gate)
|
||||
# ------------------------------------------------------------------
|
||||
"auth.${domain}" = {
|
||||
extraConfig = ''
|
||||
reverse_proxy localhost:9091
|
||||
'';
|
||||
};
|
||||
"http://auth.${domain}" = {
|
||||
extraConfig = cfProxy 9091;
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Gitea — no forward_auth; git HTTP clients can't handle SSO redirects.
|
||||
# Access control is handled by Gitea itself (LDAP auth + private repos).
|
||||
# ------------------------------------------------------------------
|
||||
"git.${domain}" = {
|
||||
extraConfig = ''
|
||||
reverse_proxy localhost:3000
|
||||
'';
|
||||
};
|
||||
"http://git.${domain}" = {
|
||||
extraConfig = cfProxy 3000;
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Nextcloud — public auth (Nextcloud manages its own users + LDAP)
|
||||
# ------------------------------------------------------------------
|
||||
"nextcloud.${domain}" = {
|
||||
extraConfig = ''
|
||||
# Redirect CardDAV/CalDAV discovery
|
||||
redir /.well-known/carddav /remote.php/dav/ 301
|
||||
redir /.well-known/caldav /remote.php/dav/ 301
|
||||
|
||||
# Large uploads (5 GB)
|
||||
request_body {
|
||||
max_size 5GB
|
||||
}
|
||||
|
||||
reverse_proxy localhost:8080 {
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
}
|
||||
'';
|
||||
};
|
||||
"http://nextcloud.${domain}" = {
|
||||
extraConfig = ''
|
||||
redir /.well-known/carddav /remote.php/dav/ 301
|
||||
redir /.well-known/caldav /remote.php/dav/ 301
|
||||
request_body {
|
||||
max_size 5GB
|
||||
}
|
||||
reverse_proxy localhost:8080 {
|
||||
header_up X-Forwarded-Proto https
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# phpLDAPadmin — two_factor, admins only (enforced by authelia policy)
|
||||
# ------------------------------------------------------------------
|
||||
"ldapadmin.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
reverse_proxy localhost:8081
|
||||
'';
|
||||
};
|
||||
"http://ldapadmin.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
${cfProxy 8081}
|
||||
'';
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Jellyfin — no forward_auth; Jellyfin has its own login UI and
|
||||
# native app clients can't handle SSO redirects.
|
||||
# ------------------------------------------------------------------
|
||||
"jellyfin.${domain}" = {
|
||||
extraConfig = ''
|
||||
reverse_proxy localhost:8096
|
||||
'';
|
||||
};
|
||||
"http://jellyfin.${domain}" = {
|
||||
extraConfig = cfProxy 8096;
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Transmission — two_factor, admins only (enforced by authelia policy)
|
||||
# ------------------------------------------------------------------
|
||||
"torrent.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
reverse_proxy localhost:9092
|
||||
'';
|
||||
# NOTE: transmission is bound to 9092 to avoid clash with authelia on 9091.
|
||||
};
|
||||
"http://torrent.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
${cfProxy 9092}
|
||||
'';
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Uptime Kuma — two_factor, admins only (enforced by authelia policy)
|
||||
# ------------------------------------------------------------------
|
||||
"uptime.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
reverse_proxy localhost:3001
|
||||
'';
|
||||
};
|
||||
"http://uptime.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
${cfProxy 3001}
|
||||
'';
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Ntfy — no forward_auth; ntfy has its own token/password auth so the
|
||||
# mobile app can connect without Authelia SSO complications.
|
||||
# ------------------------------------------------------------------
|
||||
"ntfy.${domain}" = {
|
||||
extraConfig = ''
|
||||
reverse_proxy localhost:2586
|
||||
'';
|
||||
};
|
||||
"http://ntfy.${domain}" = {
|
||||
extraConfig = cfProxy 2586;
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Eurovision Vote — one_factor for all authenticated users.
|
||||
# /admin/* is restricted to group:admins by Authelia access_control.
|
||||
# Caddy passes Remote-User → X-Remote-User so Django auto-logs in
|
||||
# the SSO-authenticated user via RemoteUserMiddleware.
|
||||
# ------------------------------------------------------------------
|
||||
"eurovision-vote.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
reverse_proxy localhost:8007 {
|
||||
header_up X-Remote-User {http.request.header.Remote-User}
|
||||
}
|
||||
'';
|
||||
};
|
||||
"http://eurovision-vote.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
reverse_proxy localhost:8007 {
|
||||
header_up X-Forwarded-Proto https
|
||||
header_up X-Remote-User {http.request.header.Remote-User}
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Paperless — one_factor for all authenticated users (authelia policy).
|
||||
# Authelia sets Remote-User; Caddy copies it to the upstream request;
|
||||
# Paperless trusts HTTP_REMOTE_USER for automatic login (no separate
|
||||
# Paperless login page shown).
|
||||
# ------------------------------------------------------------------
|
||||
"paperless.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
reverse_proxy localhost:8083
|
||||
'';
|
||||
};
|
||||
"http://paperless.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
${cfProxy 8083}
|
||||
'';
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Mealie — no forward_auth; LDAP handles auth via Mealie's login page.
|
||||
# ------------------------------------------------------------------
|
||||
"mealie.${domain}" = {
|
||||
extraConfig = ''
|
||||
reverse_proxy localhost:9093
|
||||
'';
|
||||
};
|
||||
"http://mealie.${domain}" = {
|
||||
extraConfig = cfProxy 9093;
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Grafana — two_factor, admins only (enforced by authelia policy).
|
||||
# After Authelia verifies the user, Caddy maps the Remote-User header
|
||||
# to X-WEBAUTH-USER so Grafana's proxy auth auto-signs the user in.
|
||||
# ------------------------------------------------------------------
|
||||
"grafana.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
reverse_proxy localhost:3002 {
|
||||
header_up X-WEBAUTH-USER {http.request.header.Remote-User}
|
||||
}
|
||||
'';
|
||||
};
|
||||
"http://grafana.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
reverse_proxy localhost:3002 {
|
||||
header_up X-Forwarded-Proto https
|
||||
header_up X-WEBAUTH-USER {http.request.header.Remote-User}
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
# Each entry produces two Caddy vhosts:
|
||||
# - "subdomain.domain" → HTTPS (LAN access + Let's Encrypt cert)
|
||||
# - "http://subdomain.domain" → plain HTTP for cloudflared loopback
|
||||
virtualHosts = lib.listToAttrs (
|
||||
lib.concatMap (vh:
|
||||
let
|
||||
d = "${vh.subdomain}.${domain}";
|
||||
authSnip = lib.optionalString vh.auth autheliaForwardAuth;
|
||||
httpsBody = if vh.extraConfig != "" then vh.extraConfig
|
||||
else "reverse_proxy localhost:${toString vh.port}\n";
|
||||
httpBody = if vh.extraHttpConfig != "" then vh.extraHttpConfig
|
||||
else cfProxy vh.port;
|
||||
in [
|
||||
{ name = d; value.extraConfig = "${authSnip}${httpsBody}"; }
|
||||
{ name = "http://${d}"; value.extraConfig = "${authSnip}${httpBody}"; }
|
||||
]
|
||||
) cfg.virtualHosts
|
||||
);
|
||||
};
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user