{ pkgs, self, ... }: let pns = self.lib.data.services; inherit (self.lib.data) mail; marvin = "http://${self.lib.data.hosts.marvin.ts.ip4}"; marvinIP = self.lib.data.hosts.marvin.ts.ip4; inherit (self.lib.data) tsNet; in { services.caddy = { enable = true; package = pkgs.caddy.withPlugins { plugins = [ "github.com/caddy-dns/desec@v1.0.1" "github.com/greenpau/caddy-security@v1.1.31" "github.com/tailscale/caddy-tailscale@v0.0.0-20251016213337-01d084e119cb" "github.com/mholt/caddy-l4@v0.0.0-20251001194302-2e3e6cf60b25" "github.com/mohammed90/caddy-git-fs@v0.0.0-20240805164056-529acecd1830" ]; hash = "sha256-kvChIK67UKn5vMFMcLszSl5AfW1BNHTRm1aXX5t5Wyc="; }; email = "pyrox@pyrox.dev"; virtualHosts = { "mail.pyrox.dev" = { }; # Redirect old domains -> pyrox.dev "blog.pyrox.dev" = { serverAliases = [ "www.pyrox.dev" "thehedgehog.me" ]; extraConfig = '' redir https://pyrox.dev{uri} permanent ''; }; "pyrox.dev" = { extraConfig = '' route { header /.well-known/matrix/* Access-Control-Allow-Origin * reverse_proxy /.well-known/matrix/* http://100.123.15.72:6922 redir /.well-known/carddav https://cloud.pyrox.dev/.well-known/carddav temporary redir /.well-known/caldav https://cloud.pyrox.dev/.well-known/caldav temporary header /.well-known/openpgpkey/* Access-Control-Allow-Origin * header /.well-known/openpgpkey/hu/* application/octet-stream respond /.well-known/openpgpkey/*/policy 200 header /.well-known/fursona Content-Type application/json header { X-Content-Type-Options nosniff Permissions-Policy accelerometer=(), autoplay=(), camera=(), cross-origin-isolated=(), unload=(), +Permissions-Policy display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), +Permissions-Policy gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), +Permissions-Policy payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), +Permissions-Policy sync-xhr=(self), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), +Permissions-Policy clipboard-write=(), gamepad=(), hid=(), idle-detection=(), interest-cohort=(), serial=() X-Frame-Options SAMEORIGIN Referrer-Policy origin -Server } file_server { fs blog-repo hide .git precompressed br gzip } } ''; }; # Authentication ${pns.pocket-id.extUrl} = { extraConfig = '' reverse_proxy ${marvin}:${toString pns.pocket-id.port} ''; }; # Vaultwarden ${pns.vaultwarden.extUrl} = { extraConfig = '' header / { Strict-Transport-Security "max-age=31536000;" X-XSS-Protection "0" X-Frame-Options "DENY" X-Robots-Tag "noindex, nofollow" X-Content-Type-Options "nosniff" -Server -X-Powered-By -Last-Modified } reverse_proxy ${marvin}:${toString pns.vaultwarden.anubis} { header_up X-Real-IP {remote_host} header_up X-Http-Version {http.request.proto} } ''; }; # Cinny + Conduit ${pns.matrix-server.extUrl} = { extraConfig = '' handle /_matrix/* { reverse_proxy ${marvin}:${toString pns.matrix-server.port} } handle { root * /var/www/cinny/dist/ try_files {path} / index.html file_server } ''; }; # Jellyfin ${pns.jellyfin.extUrl} = { extraConfig = '' @blocked not remote_ip 100.64.0.0/10 private_ranges reverse_proxy ${marvin}:${toString pns.jellyfin.port} handle /metrics* { respond @blocked "Access Denied" 403 } ''; }; # Yourmother.website "yourmother.website" = { extraConfig = '' header Content-Type text/html respond 200 { body ` ` } ''; }; # OpenPGP WKD stuff "openpgpkey.pyrox.dev" = { serverAliases = [ "openpgpkey.thehedgehog.me" ]; extraConfig = '' respond /.well-known/openpgpkey/{labels.1}.{labels.0}/policy 200 header Access-Control-Allow-Origin * header /.well-known/openpgpkey/{labels.1}.{labels.0}/hu/* Content-Type application/octet-stream file_server { fs blog-repo } ''; }; # Metrics ":6899" = { extraConfig = '' metrics /metrics ''; }; # SIMPLE HOSTS # Forgejo ${pns.git.extUrl} = { extraConfig = '' reverse_proxy ${marvin}:${toString pns.git.anubis} { header_up X-Real-Ip {remote_host} header_up X-Http-Version {http.request.proto} } ''; }; # Grafana ${pns.grafana.extUrl} = { extraConfig = '' reverse_proxy ${marvin}:${toString pns.grafana.anubis} { header_up X-Real-Ip {remote_host} header_up X-Http-Version {http.request.proto} } ''; }; # Miniflux ${pns.miniflux.extUrl} = { extraConfig = '' reverse_proxy ${marvin}:${toString pns.miniflux.anubis} { header_up X-Real-Ip {remote_host} header_up X-Http-Version {http.request.proto} } ''; }; # Nextcloud ${pns.nextcloud.extUrl} = { extraConfig = '' reverse_proxy ${marvin}:${toString pns.nextcloud.anubis} { header_up X-Real-Ip {remote_host} header_up X-Http-Version {http.request.proto} } ''; }; # Nextcloud-Office(Collabora) ${pns.nextcloud-office.extUrl} = { extraConfig = '' reverse_proxy ${marvin}:${toString pns.nextcloud-office.anubis} { header_up X-Real-Ip {remote_host} header_up X-Http-Version {http.request.proto} } ''; }; # Planka ${pns.planka.extUrl} = { extraConfig = '' reverse_proxy ${marvin}:${toString pns.planka.anubis} { header_up X-Real-Ip {remote_host} header_up X-Http-Version {http.request.proto} } ''; }; # Immich ${pns.immich.extUrl} = { extraConfig = '' @public path /share /share/* handle @public { reverse_proxy ${marvin}:${toString pns.immich.pubProxy} } reverse_proxy ${marvin}:${toString pns.immich.port} ''; }; # Tangled Services ${pns.tangled-knot.extUrl} = { extraConfig = '' reverse_proxy ${marvin}:${toString pns.tangled-knot.port} ''; }; ${pns.tangled-spindle.extUrl} = { extraConfig = '' reverse_proxy ${marvin}:${toString pns.tangled-spindle.port} ''; }; # Simple Tailscale Hosts # Deemix "${pns.deemix.tsHost}.${tsNet}" = { extraConfig = '' bind tailscale/${pns.deemix.tsHost} tailscale_auth reverse_proxy ${marvin}:${toString pns.deemix.port} ''; }; # Pinchflat "${pns.pinchflat.tsHost}.${tsNet}" = { extraConfig = '' bind tailscale/${pns.pinchflat.tsHost} tailscale_auth reverse_proxy ${marvin}:${toString pns.pinchflat.port} ''; }; "http://mail.pyrox.dev" = { serverAliases = [ "http://mta-sts.pyrox.dev" "http://autodiscover.pyrox.dev" "http://autoconfig.pyrox.dev" "http://dav.pyrox.dev" ]; extraConfig = '' reverse_proxy 127.0.0.1:${toString mail.intHTTP} { transport http { proxy_protocol v2 } } ''; }; }; # Mail Config globalConfig = '' filesystem blog-repo git ${marvin}:${toString pns.git.port}/pyrox/new-blog { ref refs/heads/pages refresh_period 10m } servers :80 { listener_wrappers { layer4 { @maildomains http host mail.pyrox.dev mta-sts.pyrox.dev autoconfig.pyrox.dev autodiscover.pyrox.dev dav.pyrox.dev route @maildomains { subroute { @a http route @a { proxy { proxy_protocol v2 upstream 127.0.0.1:${toString mail.intHTTP} } } } } } http_redirect } } servers :443 { listener_wrappers { layer4 { @maildomains tls sni mail.pyrox.dev mta-sts.pyrox.dev autoconfig.pyrox.dev autodiscover.pyrox.dev dav.pyrox.dev route @maildomains { proxy { proxy_protocol v2 upstream 127.0.0.1:${toString mail.intHTTPS} } } } tls } } layer4 { :22 { @a ssh route @a { proxy { upstream ${marvinIP}:2222 } } } :25 { route { proxy { proxy_protocol v2 upstream 127.0.0.1:40025 } } } :143 { route { proxy { proxy_protocol v2 upstream 127.0.0.1:${toString mail.intIMAP} } } } :465 { route { proxy { proxy_protocol v2 upstream 127.0.0.1:${toString mail.intSMTPS} } } } :587 { route { proxy { proxy_protocol v2 upstream 127.0.0.1:${toString mail.intSMTP} } } } :993 { route { proxy { proxy_protocol v2 upstream 127.0.0.1:${toString mail.intIMAPS} } } } :4190 { route { proxy { proxy_protocol v2 upstream 127.0.0.1:${toString mail.intManageSieve} } } } } ''; }; systemd.services.caddy.serviceConfig.CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; systemd.services.caddy.serviceConfig.AmbientCapabilities = "CAP_NET_BIND_SERVICE"; }