My Nix Configuration
1{ pkgs, lib, ... }: 2let 3 pns = lib.py.data.services; 4 mail = lib.py.data.mail; 5 marvin = "http://${lib.py.data.hosts.marvin.ts.ip4}"; 6 marvinIP = lib.py.data.hosts.marvin.ts.ip4; 7 tsNet = lib.py.data.tsNet; 8in 9{ 10 services.caddy = { 11 enable = true; 12 package = pkgs.caddy.withPlugins { 13 plugins = [ 14 "github.com/caddy-dns/desec@v1.0.1" 15 "github.com/greenpau/caddy-security@v1.1.31" 16 "github.com/tailscale/caddy-tailscale@v0.0.0-20250207163903-69a970c84556" 17 "github.com/mholt/caddy-l4@v0.0.0-20250428144642-57989befb7e6" 18 "github.com/mohammed90/caddy-git-fs@v0.0.0-20240805164056-529acecd1830" 19 ]; 20 hash = "sha256-NPMrt7hARdnXP3xh4TRsc+TMpuXZzwY6zdrTw6E3nSM="; 21 }; 22 email = "pyrox@pyrox.dev"; 23 virtualHosts = { 24 # Redirect old domains -> pyrox.dev 25 "blog.pyrox.dev" = { 26 serverAliases = [ 27 "www.pyrox.dev" 28 "thehedgehog.me" 29 ]; 30 extraConfig = '' 31 redir https://pyrox.dev{uri} permanent 32 ''; 33 }; 34 "pyrox.dev" = { 35 extraConfig = '' 36 route { 37 header /.well-known/matrix/* Access-Control-Allow-Origin * 38 reverse_proxy /.well-known/matrix/* http://100.123.15.72:6922 39 redir /.well-known/carddav https://cloud.pyrox.dev/.well-known/carddav temporary 40 redir /.well-known/caldav https://cloud.pyrox.dev/.well-known/caldav temporary 41 header /.well-known/openpgpkey/* Access-Control-Allow-Origin * 42 header /.well-known/openpgpkey/hu/* application/octet-stream 43 respond /.well-known/openpgpkey/*/policy 200 44 header /.well-known/fursona Content-Type application/json 45 header { 46 X-Content-Type-Options nosniff 47 Permissions-Policy accelerometer=(), autoplay=(), camera=(), cross-origin-isolated=(), unload=(), 48 +Permissions-Policy display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), 49 +Permissions-Policy gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), 50 +Permissions-Policy payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), 51 +Permissions-Policy sync-xhr=(self), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), 52 +Permissions-Policy clipboard-write=(), gamepad=(), hid=(), idle-detection=(), interest-cohort=(), serial=() 53 X-Frame-Options SAMEORIGIN 54 Referrer-Policy origin 55 -Server 56 } 57 file_server { 58 fs blog-repo 59 hide .git 60 precompressed br gzip 61 } 62 } 63 ''; 64 }; 65 66 # Authentication 67 ${pns.pocket-id.extUrl} = { 68 extraConfig = '' 69 reverse_proxy / ${marvin}:${toString pns.pocket-id.anubis} { 70 header_up X-Real-IP {remote_host} 71 header_up X-Http-Version {http.request.proto} 72 } 73 ''; 74 }; 75 76 # Vaultwarden 77 ${pns.vaultwarden.extUrl} = { 78 extraConfig = '' 79 header / { 80 Strict-Transport-Security "max-age=31536000;" 81 X-XSS-Protection "0" 82 X-Frame-Options "DENY" 83 X-Robots-Tag "noindex, nofollow" 84 X-Content-Type-Options "nosniff" 85 -Server 86 -X-Powered-By 87 -Last-Modified 88 } 89 reverse_proxy ${marvin}:${toString pns.vaultwarden.anubis} { 90 header_up X-Real-IP {remote_host} 91 header_up X-Http-Version {http.request.proto} 92 } 93 ''; 94 }; 95 96 # Cinny + Conduit 97 ${pns.matrix-server.extUrl} = { 98 extraConfig = '' 99 handle /_matrix/* { 100 reverse_proxy ${marvin}:${toString pns.matrix-server.port} 101 } 102 handle { 103 root * /var/www/cinny/dist/ 104 try_files {path} / index.html 105 file_server 106 } 107 ''; 108 }; 109 # Jellyfin 110 ${pns.jellyfin.extUrl} = { 111 extraConfig = '' 112 @blocked not remote_ip 100.64.0.0/10 private_ranges 113 reverse_proxy ${marvin}:${toString pns.jellyfin.port} 114 handle /metrics* { 115 respond @blocked "Access Denied" 403 116 } 117 ''; 118 }; 119 120 # MTA-STS Setup for mailserver 121 "mta-sts.pyrox.dev" = { 122 extraConfig = '' 123 header Content-Type text/plain; charset=utf-8 124 respond /.well-known/mta-sts.txt <<END 125 version: STSv1 126 mode: enforce 127 mx: mail.pyrox.dev 128 mx:mail2.pyrox.dev 129 max_age: 2419200 130 END 200 131 ''; 132 }; 133 134 # Yourmother.website 135 "yourmother.website" = { 136 extraConfig = '' 137 header Content-Type text/html 138 respond 200 { 139 body `<!DOCTYPE html> 140 <html> 141 <head> 142 <meta http-equiv="Refresh" content="0; url=https://youtube.com/watch?v=oHg5SJYRHA0" /> 143 </head> 144 </html>` 145 } 146 ''; 147 }; 148 149 # OpenPGP WKD stuff 150 "openpgpkey.pyrox.dev" = { 151 serverAliases = [ "openpgpkey.thehedgehog.me" ]; 152 extraConfig = '' 153 respond /.well-known/openpgpkey/{labels.1}.{labels.0}/policy 200 154 header Access-Control-Allow-Origin * 155 header /.well-known/openpgpkey/{labels.1}.{labels.0}/hu/* Content-Type application/octet-stream 156 file_server { 157 fs blog-repo 158 } 159 ''; 160 }; 161 162 # Metrics 163 ":6899" = { 164 extraConfig = '' 165 metrics /metrics 166 ''; 167 }; 168 # SIMPLE HOSTS 169 170 # Forgejo 171 ${pns.git.extUrl} = { 172 extraConfig = '' 173 reverse_proxy ${marvin}:${toString pns.git.anubis} { 174 header_up X-Real-Ip {remote_host} 175 header_up X-Http-Version {http.request.proto} 176 } 177 ''; 178 }; 179 180 # Grafana 181 ${pns.grafana.extUrl} = { 182 extraConfig = '' 183 reverse_proxy ${marvin}:${toString pns.grafana.anubis} { 184 header_up X-Real-Ip {remote_host} 185 header_up X-Http-Version {http.request.proto} 186 } 187 ''; 188 }; 189 190 # Miniflux 191 ${pns.miniflux.extUrl} = { 192 extraConfig = '' 193 reverse_proxy ${marvin}:${toString pns.miniflux.anubis} { 194 header_up X-Real-Ip {remote_host} 195 header_up X-Http-Version {http.request.proto} 196 } 197 ''; 198 }; 199 200 # Nextcloud 201 ${pns.nextcloud.extUrl} = { 202 extraConfig = '' 203 reverse_proxy ${marvin}:${toString pns.nextcloud.anubis} { 204 header_up X-Real-Ip {remote_host} 205 header_up X-Http-Version {http.request.proto} 206 } 207 ''; 208 }; 209 210 # Nextcloud-Office(Collabora) 211 ${pns.nextcloud-office.extUrl} = { 212 extraConfig = '' 213 reverse_proxy ${marvin}:${toString pns.nextcloud-office.anubis} { 214 header_up X-Real-Ip {remote_host} 215 header_up X-Http-Version {http.request.proto} 216 } 217 ''; 218 }; 219 220 # Planka 221 ${pns.planka.extUrl} = { 222 extraConfig = '' 223 reverse_proxy ${marvin}:${toString pns.planka.anubis} { 224 header_up X-Real-Ip {remote_host} 225 header_up X-Http-Version {http.request.proto} 226 } 227 ''; 228 }; 229 230 # Pingvin Share 231 ${pns.pingvin-share.extUrl} = { 232 extraConfig = '' 233 reverse_proxy /api/* ${marvin}:${toString pns.pingvin-share.be-anubis} { 234 header_up X-Real-IP {remote_host} 235 header_up X-Http-Version {http.request.proto} 236 } 237 reverse_proxy /* ${marvin}:${toString pns.pingvin-share.anubis} { 238 header_up X-Real-IP {remote_host} 239 header_up X-Http-Version {http.request.proto} 240 } 241 ''; 242 }; 243 244 # Simple Tailscale Hosts 245 246 # Deemix 247 "${pns.deemix.tsHost}.${tsNet}" = { 248 extraConfig = '' 249 bind tailscale/${pns.deemix.tsHost} 250 tailscale_auth 251 reverse_proxy ${marvin}:${toString pns.deemix.port} 252 ''; 253 }; 254 # Pinchflat 255 "${pns.pinchflat.tsHost}.${tsNet}" = { 256 extraConfig = '' 257 bind tailscale/${pns.pinchflat.tsHost} 258 tailscale_auth 259 reverse_proxy ${marvin}:${toString pns.pinchflat.port} 260 ''; 261 }; 262 # "mail.pyrox.dev:80" = { 263 # extraConfig = '' 264 # reverse_proxy ${marvin}:${mail.intHTTP} 265 # ''; 266 # }; 267 }; 268 # Mail Config 269 globalConfig = '' 270 filesystem blog-repo git ${marvin}:${toString pns.git.port}/pyrox/new-blog { 271 ref refs/heads/pages 272 refresh_period 10m 273 } 274 ''; 275 # layer4 { 276 # 0.0.0.0:465 { 277 # route { 278 # proxy { 279 # proxy_protocol v2 280 # upstream ${marvinIP}:${mail.intSMTPS} 281 # } 282 # } 283 # } 284 # 0.0.0.0:993 { 285 # route { 286 # proxy { 287 # proxy_protocol v2 288 # upstream ${marvinIP}:${mail.intIMAPS} 289 # } 290 # } 291 # } 292 # 0.0.0.0:4190 { 293 # route { 294 # proxy { 295 # proxy_protocol v2 296 # upstream ${marvinIP}:${mail.intManageSieve} 297 # } 298 # } 299 # } 300 # } 301 }; 302 systemd.services.caddy.serviceConfig.CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; 303 systemd.services.caddy.serviceConfig.AmbientCapabilities = "CAP_NET_BIND_SERVICE"; 304}