Working NixOS port: all core services operational
- Fix Caddy cfProxy helper for cloudflared http:// vhosts (X-Forwarded-Proto) - Fix Authelia LDAP bind (readonly user ACL + password sync) - Add gitea-admin-setup oneshot service to survive rebuilds - Update Authelia forward_auth with header_up X-Forwarded-Proto https - Update TODO.org with completed tasks and LDAP config details - Remove old Helm/k8s artifacts (Chart.yaml, templates/, values/, scripts) - Add result to .gitignore Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+78
-22
@@ -3,7 +3,7 @@
|
||||
# Caddy reverse proxy.
|
||||
#
|
||||
# Features:
|
||||
# - DNS-01 ACME via Cloudflare API → real wildcard cert for *.home.zakobar.com
|
||||
# - DNS-01 ACME via Cloudflare API → real wildcard cert for *.zakobar.com
|
||||
# - forward_auth to Authelia for protected vhosts
|
||||
# - Plain reverse_proxy for public vhosts (authelia itself, nextcloud)
|
||||
# - Listens on :80 (redirect) and :443 (TLS)
|
||||
@@ -23,11 +23,23 @@ let
|
||||
# under the hood to produce a fixed-output derivation.
|
||||
caddyWithCloudflare = pkgs.caddy.withPlugins {
|
||||
plugins = [
|
||||
"github.com/caddy-dns/cloudflare@v0.2.2-0.20250724223520-f589a18c0f5d"
|
||||
# v0.2.4 tag points to commit a8737d0 which includes the fix for
|
||||
# cfut_/cfat_ token format validation (PR #123).
|
||||
"github.com/caddy-dns/cloudflare@v0.2.4"
|
||||
];
|
||||
hash = "sha256-2Fb2fgM7YhWk9kBnnNGb85MJkAkgzXiI1fb6eK3ykIE=";
|
||||
hash = "sha256-pRrLBlYRaAyMYwPXeTy4WqWNRu/L9K6Mn2src11dGh8=";
|
||||
};
|
||||
|
||||
# Reverse-proxy snippet for cloudflared http:// vhosts.
|
||||
# Cloudflare terminates TLS; cloudflared connects to Caddy over plain HTTP.
|
||||
# We must override X-Forwarded-Proto so upstream services (especially
|
||||
# Authelia) know the client is actually on HTTPS.
|
||||
cfProxy = port: ''
|
||||
reverse_proxy localhost:${toString port} {
|
||||
header_up X-Forwarded-Proto https
|
||||
}
|
||||
'';
|
||||
|
||||
# Reusable Authelia forward_auth snippet
|
||||
# Returns a Caddyfile snippet block that applies forward_auth.
|
||||
# copy_headers makes Authelia's Remote-* headers available downstream.
|
||||
@@ -35,6 +47,9 @@ let
|
||||
forward_auth localhost:9091 {
|
||||
uri /api/verify?rd=https://auth.${domain}
|
||||
copy_headers Remote-User Remote-Name Remote-Groups Remote-Email
|
||||
# Always tell Authelia the scheme is https (cloudflared terminates TLS
|
||||
# externally; Caddy's http:// vhosts are only for the tunnel loopback).
|
||||
header_up X-Forwarded-Proto https
|
||||
# On auth failure, redirect to the authelia login page
|
||||
@goauth status 401
|
||||
handle_response @goauth {
|
||||
@@ -77,7 +92,15 @@ in
|
||||
acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
|
||||
'';
|
||||
|
||||
# Each virtual host
|
||||
# Each virtual host.
|
||||
#
|
||||
# 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 = {
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
@@ -88,21 +111,25 @@ in
|
||||
reverse_proxy localhost:9091
|
||||
'';
|
||||
};
|
||||
"http://auth.${domain}" = {
|
||||
extraConfig = cfProxy 9091;
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Gitea — protected behind one_factor Authelia
|
||||
# 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 = ''
|
||||
${autheliaForwardAuth}
|
||||
reverse_proxy localhost:3000
|
||||
'';
|
||||
};
|
||||
"http://git.${domain}" = {
|
||||
extraConfig = cfProxy 3000;
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Nextcloud — public auth (Nextcloud manages its own users + LDAP)
|
||||
# Authelia is not gating nextcloud directly because NC has its own
|
||||
# login flow. We still want HTTPS.
|
||||
# ------------------------------------------------------------------
|
||||
"nextcloud.${domain}" = {
|
||||
extraConfig = ''
|
||||
@@ -118,6 +145,18 @@ in
|
||||
reverse_proxy localhost:8080
|
||||
'';
|
||||
};
|
||||
"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
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# phpLDAPadmin — two_factor, admins only (enforced by authelia policy)
|
||||
@@ -128,16 +167,25 @@ in
|
||||
reverse_proxy localhost:8081
|
||||
'';
|
||||
};
|
||||
"http://ldapadmin.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
${cfProxy 8081}
|
||||
'';
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Jellyfin — one_factor (added when enabled)
|
||||
# Jellyfin — no forward_auth; Jellyfin has its own login UI and
|
||||
# native app clients can't handle SSO redirects.
|
||||
# ------------------------------------------------------------------
|
||||
"jellyfin.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
reverse_proxy localhost:8096
|
||||
'';
|
||||
};
|
||||
"http://jellyfin.${domain}" = {
|
||||
extraConfig = cfProxy 8096;
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Transmission — two_factor, admins only (enforced by authelia policy)
|
||||
@@ -149,6 +197,12 @@ in
|
||||
'';
|
||||
# NOTE: transmission is bound to 9092 to avoid clash with authelia on 9091.
|
||||
};
|
||||
"http://torrent.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
${cfProxy 9092}
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
@@ -163,18 +217,20 @@ in
|
||||
# -----------------------------------------------------------------------
|
||||
systemd.services.caddy = {
|
||||
serviceConfig = {
|
||||
EnvironmentFile = "/run/caddy-secrets.env";
|
||||
ExecStartPre = [
|
||||
(pkgs.writeShellScript "caddy-inject-cf-token" ''
|
||||
install -m 0600 /dev/null /run/caddy-secrets.env
|
||||
printf 'CLOUDFLARE_API_TOKEN=%s\n' \
|
||||
"$(cat ${config.sops.secrets."cloudflare/api_token".path})" \
|
||||
> /run/caddy-secrets.env
|
||||
'')
|
||||
];
|
||||
ExecStopPost = [
|
||||
(pkgs.writeShellScript "caddy-cleanup-env" ''
|
||||
rm -f /run/caddy-secrets.env
|
||||
# LoadCredential stages the sops-decrypted secret into a
|
||||
# per-invocation directory ($CREDENTIALS_DIRECTORY) before any
|
||||
# Exec* step. ExecStart then reads the file contents and exports
|
||||
# CLOUDFLARE_API_TOKEN before exec-ing caddy, so there is no
|
||||
# intermediate env file and no ordering race with EnvironmentFile.
|
||||
LoadCredential = "cloudflare_api_token:${config.sops.secrets."cloudflare/api_token".path}";
|
||||
# Systemd requires clearing ExecStart= before setting a new value for
|
||||
# non-oneshot services. The empty string resets the list; the second
|
||||
# entry is the actual start command.
|
||||
ExecStart = lib.mkForce [
|
||||
""
|
||||
(pkgs.writeShellScript "caddy-start" ''
|
||||
export CLOUDFLARE_API_TOKEN=$(cat "$CREDENTIALS_DIRECTORY/cloudflare_api_token")
|
||||
exec ${caddyWithCloudflare}/bin/caddy run --environ --config /etc/caddy/caddy_config --adapter caddyfile
|
||||
'')
|
||||
];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user