My Nix Configuration
1{ pkgs, self, ... }: 2let 3 pns = self.lib.data.services; 4 mail = self.lib.data.mail; 5 marvin = "http://${self.lib.data.hosts.marvin.ts.ip4}"; 6 marvinIP = self.lib.data.hosts.marvin.ts.ip4; 7 tsNet = self.lib.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-20251016213337-01d084e119cb" 17 "github.com/mholt/caddy-l4@v0.0.0-20251001194302-2e3e6cf60b25" 18 "github.com/mohammed90/caddy-git-fs@v0.0.0-20240805164056-529acecd1830" 19 ]; 20 hash = "sha256-kvChIK67UKn5vMFMcLszSl5A"; 21 }; 22 email = "pyrox@pyrox.dev"; 23 virtualHosts = { 24 "mail.pyrox.dev" = { }; 25 # Redirect old domains -> pyrox.dev 26 "blog.pyrox.dev" = { 27 serverAliases = [ 28 "www.pyrox.dev" 29 "thehedgehog.me" 30 ]; 31 extraConfig = '' 32 redir https://pyrox.dev{uri} permanent 33 ''; 34 }; 35 "pyrox.dev" = { 36 extraConfig = '' 37 route { 38 header /.well-known/matrix/* Access-Control-Allow-Origin * 39 reverse_proxy /.well-known/matrix/* http://100.123.15.72:6922 40 redir /.well-known/carddav https://cloud.pyrox.dev/.well-known/carddav temporary 41 redir /.well-known/caldav https://cloud.pyrox.dev/.well-known/caldav temporary 42 header /.well-known/openpgpkey/* Access-Control-Allow-Origin * 43 header /.well-known/openpgpkey/hu/* application/octet-stream 44 respond /.well-known/openpgpkey/*/policy 200 45 header /.well-known/fursona Content-Type application/json 46 header { 47 X-Content-Type-Options nosniff 48 Permissions-Policy accelerometer=(), autoplay=(), camera=(), cross-origin-isolated=(), unload=(), 49 +Permissions-Policy display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), 50 +Permissions-Policy gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), 51 +Permissions-Policy payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), 52 +Permissions-Policy sync-xhr=(self), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), 53 +Permissions-Policy clipboard-write=(), gamepad=(), hid=(), idle-detection=(), interest-cohort=(), serial=() 54 X-Frame-Options SAMEORIGIN 55 Referrer-Policy origin 56 -Server 57 } 58 file_server { 59 fs blog-repo 60 hide .git 61 precompressed br gzip 62 } 63 } 64 ''; 65 }; 66 67 # Authentication 68 ${pns.pocket-id.extUrl} = { 69 extraConfig = '' 70 reverse_proxy / ${marvin}:${toString pns.pocket-id.port} { 71 header_up X-Real-IP {remote_host} 72 header_up X-Http-Version {http.request.proto} 73 } 74 ''; 75 }; 76 77 # Vaultwarden 78 ${pns.vaultwarden.extUrl} = { 79 extraConfig = '' 80 header / { 81 Strict-Transport-Security "max-age=31536000;" 82 X-XSS-Protection "0" 83 X-Frame-Options "DENY" 84 X-Robots-Tag "noindex, nofollow" 85 X-Content-Type-Options "nosniff" 86 -Server 87 -X-Powered-By 88 -Last-Modified 89 } 90 reverse_proxy ${marvin}:${toString pns.vaultwarden.anubis} { 91 header_up X-Real-IP {remote_host} 92 header_up X-Http-Version {http.request.proto} 93 } 94 ''; 95 }; 96 97 # Cinny + Conduit 98 ${pns.matrix-server.extUrl} = { 99 extraConfig = '' 100 handle /_matrix/* { 101 reverse_proxy ${marvin}:${toString pns.matrix-server.port} 102 } 103 handle { 104 root * /var/www/cinny/dist/ 105 try_files {path} / index.html 106 file_server 107 } 108 ''; 109 }; 110 # Jellyfin 111 ${pns.jellyfin.extUrl} = { 112 extraConfig = '' 113 @blocked not remote_ip 100.64.0.0/10 private_ranges 114 reverse_proxy ${marvin}:${toString pns.jellyfin.port} 115 handle /metrics* { 116 respond @blocked "Access Denied" 403 117 } 118 ''; 119 }; 120 121 # Yourmother.website 122 "yourmother.website" = { 123 extraConfig = '' 124 header Content-Type text/html 125 respond 200 { 126 body `<!DOCTYPE html> 127 <html> 128 <head> 129 <meta http-equiv="Refresh" content="0; url=https://youtube.com/watch?v=oHg5SJYRHA0" /> 130 </head> 131 </html>` 132 } 133 ''; 134 }; 135 136 # OpenPGP WKD stuff 137 "openpgpkey.pyrox.dev" = { 138 serverAliases = [ "openpgpkey.thehedgehog.me" ]; 139 extraConfig = '' 140 respond /.well-known/openpgpkey/{labels.1}.{labels.0}/policy 200 141 header Access-Control-Allow-Origin * 142 header /.well-known/openpgpkey/{labels.1}.{labels.0}/hu/* Content-Type application/octet-stream 143 file_server { 144 fs blog-repo 145 } 146 ''; 147 }; 148 149 # Metrics 150 ":6899" = { 151 extraConfig = '' 152 metrics /metrics 153 ''; 154 }; 155 # SIMPLE HOSTS 156 157 # Forgejo 158 ${pns.git.extUrl} = { 159 extraConfig = '' 160 reverse_proxy ${marvin}:${toString pns.git.anubis} { 161 header_up X-Real-Ip {remote_host} 162 header_up X-Http-Version {http.request.proto} 163 } 164 ''; 165 }; 166 167 # Grafana 168 ${pns.grafana.extUrl} = { 169 extraConfig = '' 170 reverse_proxy ${marvin}:${toString pns.grafana.anubis} { 171 header_up X-Real-Ip {remote_host} 172 header_up X-Http-Version {http.request.proto} 173 } 174 ''; 175 }; 176 177 # Miniflux 178 ${pns.miniflux.extUrl} = { 179 extraConfig = '' 180 reverse_proxy ${marvin}:${toString pns.miniflux.anubis} { 181 header_up X-Real-Ip {remote_host} 182 header_up X-Http-Version {http.request.proto} 183 } 184 ''; 185 }; 186 187 # Nextcloud 188 ${pns.nextcloud.extUrl} = { 189 extraConfig = '' 190 reverse_proxy ${marvin}:${toString pns.nextcloud.anubis} { 191 header_up X-Real-Ip {remote_host} 192 header_up X-Http-Version {http.request.proto} 193 } 194 ''; 195 }; 196 197 # Nextcloud-Office(Collabora) 198 ${pns.nextcloud-office.extUrl} = { 199 extraConfig = '' 200 reverse_proxy ${marvin}:${toString pns.nextcloud-office.anubis} { 201 header_up X-Real-Ip {remote_host} 202 header_up X-Http-Version {http.request.proto} 203 } 204 ''; 205 }; 206 207 # Planka 208 ${pns.planka.extUrl} = { 209 extraConfig = '' 210 reverse_proxy ${marvin}:${toString pns.planka.anubis} { 211 header_up X-Real-Ip {remote_host} 212 header_up X-Http-Version {http.request.proto} 213 } 214 ''; 215 }; 216 217 # Pingvin Share 218 ${pns.pingvin-share.extUrl} = { 219 extraConfig = '' 220 reverse_proxy /api/* ${marvin}:${toString pns.pingvin-share.be-anubis} { 221 header_up X-Real-IP {remote_host} 222 header_up X-Http-Version {http.request.proto} 223 } 224 reverse_proxy /* ${marvin}:${toString pns.pingvin-share.anubis} { 225 header_up X-Real-IP {remote_host} 226 header_up X-Http-Version {http.request.proto} 227 } 228 ''; 229 }; 230 # Tangled Services 231 ${pns.tangled-knot.extUrl} = { 232 extraConfig = '' 233 reverse_proxy ${marvin}:${toString pns.tangled-knot.port} 234 ''; 235 }; 236 ${pns.tangled-spindle.extUrl} = { 237 extraConfig = '' 238 reverse_proxy ${marvin}:${toString pns.tangled-spindle.port} 239 ''; 240 }; 241 242 # Simple Tailscale Hosts 243 244 # Deemix 245 "${pns.deemix.tsHost}.${tsNet}" = { 246 extraConfig = '' 247 bind tailscale/${pns.deemix.tsHost} 248 tailscale_auth 249 reverse_proxy ${marvin}:${toString pns.deemix.port} 250 ''; 251 }; 252 # Pinchflat 253 "${pns.pinchflat.tsHost}.${tsNet}" = { 254 extraConfig = '' 255 bind tailscale/${pns.pinchflat.tsHost} 256 tailscale_auth 257 reverse_proxy ${marvin}:${toString pns.pinchflat.port} 258 ''; 259 }; 260 261 "http://mail.pyrox.dev" = { 262 serverAliases = [ 263 "http://mta-sts.pyrox.dev" 264 "http://autodiscover.pyrox.dev" 265 "http://autoconfig.pyrox.dev" 266 "http://dav.pyrox.dev" 267 ]; 268 extraConfig = '' 269 reverse_proxy 127.0.0.1:${toString mail.intHTTP} { 270 transport http { 271 proxy_protocol v2 272 } 273 } 274 275 ''; 276 }; 277 }; 278 # Mail Config 279 globalConfig = '' 280 filesystem blog-repo git ${marvin}:${toString pns.git.port}/pyrox/new-blog { 281 ref refs/heads/pages 282 refresh_period 10m 283 } 284 servers :80 { 285 listener_wrappers { 286 layer4 { 287 @maildomains http host mail.pyrox.dev mta-sts.pyrox.dev autoconfig.pyrox.dev autodiscover.pyrox.dev dav.pyrox.dev 288 route @maildomains { 289 subroute { 290 @a http 291 route @a { 292 proxy { 293 proxy_protocol v2 294 upstream 127.0.0.1:${toString mail.intHTTP} 295 } 296 } 297 } 298 } 299 } 300 http_redirect 301 } 302 } 303 servers :443 { 304 listener_wrappers { 305 layer4 { 306 @maildomains tls sni mail.pyrox.dev mta-sts.pyrox.dev autoconfig.pyrox.dev autodiscover.pyrox.dev dav.pyrox.dev 307 route @maildomains { 308 proxy { 309 proxy_protocol v2 310 upstream 127.0.0.1:${toString mail.intHTTPS} 311 } 312 } 313 } 314 tls 315 } 316 } 317 layer4 { 318 :22 { 319 @a ssh 320 route @a { 321 proxy { 322 upstream ${marvinIP}:2222 323 } 324 } 325 } 326 :25 { 327 route { 328 proxy { 329 proxy_protocol v2 330 upstream 127.0.0.1:40025 331 } 332 } 333 } 334 :143 { 335 route { 336 proxy { 337 proxy_protocol v2 338 upstream 127.0.0.1:${toString mail.intIMAP} 339 } 340 } 341 } 342 :465 { 343 route { 344 proxy { 345 proxy_protocol v2 346 upstream 127.0.0.1:${toString mail.intSMTPS} 347 } 348 } 349 } 350 :587 { 351 route { 352 proxy { 353 proxy_protocol v2 354 upstream 127.0.0.1:${toString mail.intSMTP} 355 } 356 } 357 } 358 :993 { 359 route { 360 proxy { 361 proxy_protocol v2 362 upstream 127.0.0.1:${toString mail.intIMAPS} 363 } 364 } 365 } 366 :4190 { 367 route { 368 proxy { 369 proxy_protocol v2 370 upstream 127.0.0.1:${toString mail.intManageSieve} 371 } 372 } 373 } 374 } 375 ''; 376 }; 377 systemd.services.caddy.serviceConfig.CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; 378 systemd.services.caddy.serviceConfig.AmbientCapabilities = "CAP_NET_BIND_SERVICE"; 379}