My Nix Configuration
1{ pkgs, self, ... }: 2let 3 pns = self.lib.data.services; 4 inherit (self.lib.data) mail; 5 marvin = "http://${self.lib.data.hosts.marvin.ts.ip4}"; 6 marvinIP = self.lib.data.hosts.marvin.ts.ip4; 7 inherit (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-kvChIK67UKn5vMFMcLszSl5AfW1BNHTRm1aXX5t5Wyc="; 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 ''; 72 }; 73 74 # Vaultwarden 75 ${pns.vaultwarden.extUrl} = { 76 extraConfig = '' 77 header / { 78 Strict-Transport-Security "max-age=31536000;" 79 X-XSS-Protection "0" 80 X-Frame-Options "DENY" 81 X-Robots-Tag "noindex, nofollow" 82 X-Content-Type-Options "nosniff" 83 -Server 84 -X-Powered-By 85 -Last-Modified 86 } 87 reverse_proxy ${marvin}:${toString pns.vaultwarden.anubis} { 88 header_up X-Real-IP {remote_host} 89 header_up X-Http-Version {http.request.proto} 90 } 91 ''; 92 }; 93 94 # Cinny + Conduit 95 ${pns.matrix-server.extUrl} = { 96 extraConfig = '' 97 handle /_matrix/* { 98 reverse_proxy ${marvin}:${toString pns.matrix-server.port} 99 } 100 handle { 101 root * /var/www/cinny/dist/ 102 try_files {path} / index.html 103 file_server 104 } 105 ''; 106 }; 107 # Jellyfin 108 ${pns.jellyfin.extUrl} = { 109 extraConfig = '' 110 @blocked not remote_ip 100.64.0.0/10 private_ranges 111 reverse_proxy ${marvin}:${toString pns.jellyfin.port} 112 handle /metrics* { 113 respond @blocked "Access Denied" 403 114 } 115 ''; 116 }; 117 118 # Yourmother.website 119 "yourmother.website" = { 120 extraConfig = '' 121 header Content-Type text/html 122 respond 200 { 123 body `<!DOCTYPE html> 124 <html> 125 <head> 126 <meta http-equiv="Refresh" content="0; url=https://youtube.com/watch?v=oHg5SJYRHA0" /> 127 </head> 128 </html>` 129 } 130 ''; 131 }; 132 133 # OpenPGP WKD stuff 134 "openpgpkey.pyrox.dev" = { 135 serverAliases = [ "openpgpkey.thehedgehog.me" ]; 136 extraConfig = '' 137 respond /.well-known/openpgpkey/{labels.1}.{labels.0}/policy 200 138 header Access-Control-Allow-Origin * 139 header /.well-known/openpgpkey/{labels.1}.{labels.0}/hu/* Content-Type application/octet-stream 140 file_server { 141 fs blog-repo 142 } 143 ''; 144 }; 145 146 # Metrics 147 ":6899" = { 148 extraConfig = '' 149 metrics /metrics 150 ''; 151 }; 152 # SIMPLE HOSTS 153 154 # Forgejo 155 ${pns.git.extUrl} = { 156 extraConfig = '' 157 reverse_proxy ${marvin}:${toString pns.git.anubis} { 158 header_up X-Real-Ip {remote_host} 159 header_up X-Http-Version {http.request.proto} 160 } 161 ''; 162 }; 163 164 # Grafana 165 ${pns.grafana.extUrl} = { 166 extraConfig = '' 167 reverse_proxy ${marvin}:${toString pns.grafana.anubis} { 168 header_up X-Real-Ip {remote_host} 169 header_up X-Http-Version {http.request.proto} 170 } 171 ''; 172 }; 173 174 # Miniflux 175 ${pns.miniflux.extUrl} = { 176 extraConfig = '' 177 reverse_proxy ${marvin}:${toString pns.miniflux.anubis} { 178 header_up X-Real-Ip {remote_host} 179 header_up X-Http-Version {http.request.proto} 180 } 181 ''; 182 }; 183 184 # Nextcloud 185 ${pns.nextcloud.extUrl} = { 186 extraConfig = '' 187 reverse_proxy ${marvin}:${toString pns.nextcloud.anubis} { 188 header_up X-Real-Ip {remote_host} 189 header_up X-Http-Version {http.request.proto} 190 } 191 ''; 192 }; 193 194 # Nextcloud-Office(Collabora) 195 ${pns.nextcloud-office.extUrl} = { 196 extraConfig = '' 197 reverse_proxy ${marvin}:${toString pns.nextcloud-office.anubis} { 198 header_up X-Real-Ip {remote_host} 199 header_up X-Http-Version {http.request.proto} 200 } 201 ''; 202 }; 203 204 # Planka 205 ${pns.planka.extUrl} = { 206 extraConfig = '' 207 reverse_proxy ${marvin}:${toString pns.planka.anubis} { 208 header_up X-Real-Ip {remote_host} 209 header_up X-Http-Version {http.request.proto} 210 } 211 ''; 212 }; 213 214 # Pingvin Share 215 ${pns.pingvin-share.extUrl} = { 216 extraConfig = '' 217 reverse_proxy /api/* ${marvin}:${toString pns.pingvin-share.be-anubis} { 218 header_up X-Real-IP {remote_host} 219 header_up X-Http-Version {http.request.proto} 220 } 221 reverse_proxy /* ${marvin}:${toString pns.pingvin-share.anubis} { 222 header_up X-Real-IP {remote_host} 223 header_up X-Http-Version {http.request.proto} 224 } 225 ''; 226 }; 227 # Tangled Services 228 ${pns.tangled-knot.extUrl} = { 229 extraConfig = '' 230 reverse_proxy ${marvin}:${toString pns.tangled-knot.port} 231 ''; 232 }; 233 ${pns.tangled-spindle.extUrl} = { 234 extraConfig = '' 235 reverse_proxy ${marvin}:${toString pns.tangled-spindle.port} 236 ''; 237 }; 238 239 # Simple Tailscale Hosts 240 241 # Deemix 242 "${pns.deemix.tsHost}.${tsNet}" = { 243 extraConfig = '' 244 bind tailscale/${pns.deemix.tsHost} 245 tailscale_auth 246 reverse_proxy ${marvin}:${toString pns.deemix.port} 247 ''; 248 }; 249 # Pinchflat 250 "${pns.pinchflat.tsHost}.${tsNet}" = { 251 extraConfig = '' 252 bind tailscale/${pns.pinchflat.tsHost} 253 tailscale_auth 254 reverse_proxy ${marvin}:${toString pns.pinchflat.port} 255 ''; 256 }; 257 258 "http://mail.pyrox.dev" = { 259 serverAliases = [ 260 "http://mta-sts.pyrox.dev" 261 "http://autodiscover.pyrox.dev" 262 "http://autoconfig.pyrox.dev" 263 "http://dav.pyrox.dev" 264 ]; 265 extraConfig = '' 266 reverse_proxy 127.0.0.1:${toString mail.intHTTP} { 267 transport http { 268 proxy_protocol v2 269 } 270 } 271 272 ''; 273 }; 274 }; 275 # Mail Config 276 globalConfig = '' 277 filesystem blog-repo git ${marvin}:${toString pns.git.port}/pyrox/new-blog { 278 ref refs/heads/pages 279 refresh_period 10m 280 } 281 servers :80 { 282 listener_wrappers { 283 layer4 { 284 @maildomains http host mail.pyrox.dev mta-sts.pyrox.dev autoconfig.pyrox.dev autodiscover.pyrox.dev dav.pyrox.dev 285 route @maildomains { 286 subroute { 287 @a http 288 route @a { 289 proxy { 290 proxy_protocol v2 291 upstream 127.0.0.1:${toString mail.intHTTP} 292 } 293 } 294 } 295 } 296 } 297 http_redirect 298 } 299 } 300 servers :443 { 301 listener_wrappers { 302 layer4 { 303 @maildomains tls sni mail.pyrox.dev mta-sts.pyrox.dev autoconfig.pyrox.dev autodiscover.pyrox.dev dav.pyrox.dev 304 route @maildomains { 305 proxy { 306 proxy_protocol v2 307 upstream 127.0.0.1:${toString mail.intHTTPS} 308 } 309 } 310 } 311 tls 312 } 313 } 314 layer4 { 315 :22 { 316 @a ssh 317 route @a { 318 proxy { 319 upstream ${marvinIP}:2222 320 } 321 } 322 } 323 :25 { 324 route { 325 proxy { 326 proxy_protocol v2 327 upstream 127.0.0.1:40025 328 } 329 } 330 } 331 :143 { 332 route { 333 proxy { 334 proxy_protocol v2 335 upstream 127.0.0.1:${toString mail.intIMAP} 336 } 337 } 338 } 339 :465 { 340 route { 341 proxy { 342 proxy_protocol v2 343 upstream 127.0.0.1:${toString mail.intSMTPS} 344 } 345 } 346 } 347 :587 { 348 route { 349 proxy { 350 proxy_protocol v2 351 upstream 127.0.0.1:${toString mail.intSMTP} 352 } 353 } 354 } 355 :993 { 356 route { 357 proxy { 358 proxy_protocol v2 359 upstream 127.0.0.1:${toString mail.intIMAPS} 360 } 361 } 362 } 363 :4190 { 364 route { 365 proxy { 366 proxy_protocol v2 367 upstream 127.0.0.1:${toString mail.intManageSieve} 368 } 369 } 370 } 371 } 372 ''; 373 }; 374 systemd.services.caddy.serviceConfig.CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; 375 systemd.services.caddy.serviceConfig.AmbientCapabilities = "CAP_NET_BIND_SERVICE"; 376}