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 "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.anubis} { 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 # MTA-STS Setup for mailserver 122 "mta-sts.pyrox.dev" = { 123 extraConfig = '' 124 header Content-Type text/plain; charset=utf-8 125 respond /.well-known/mta-sts.txt <<END 126 version: STSv1 127 mode: enforce 128 mx: mail.pyrox.dev 129 mx:mail2.pyrox.dev 130 max_age: 2419200 131 END 200 132 ''; 133 }; 134 135 # Yourmother.website 136 "yourmother.website" = { 137 extraConfig = '' 138 header Content-Type text/html 139 respond 200 { 140 body `<!DOCTYPE html> 141 <html> 142 <head> 143 <meta http-equiv="Refresh" content="0; url=https://youtube.com/watch?v=oHg5SJYRHA0" /> 144 </head> 145 </html>` 146 } 147 ''; 148 }; 149 150 # OpenPGP WKD stuff 151 "openpgpkey.pyrox.dev" = { 152 serverAliases = [ "openpgpkey.thehedgehog.me" ]; 153 extraConfig = '' 154 respond /.well-known/openpgpkey/{labels.1}.{labels.0}/policy 200 155 header Access-Control-Allow-Origin * 156 header /.well-known/openpgpkey/{labels.1}.{labels.0}/hu/* Content-Type application/octet-stream 157 file_server { 158 fs blog-repo 159 } 160 ''; 161 }; 162 163 # Metrics 164 ":6899" = { 165 extraConfig = '' 166 metrics /metrics 167 ''; 168 }; 169 # SIMPLE HOSTS 170 171 # Forgejo 172 ${pns.git.extUrl} = { 173 extraConfig = '' 174 reverse_proxy ${marvin}:${toString pns.git.anubis} { 175 header_up X-Real-Ip {remote_host} 176 header_up X-Http-Version {http.request.proto} 177 } 178 ''; 179 }; 180 181 # Grafana 182 ${pns.grafana.extUrl} = { 183 extraConfig = '' 184 reverse_proxy ${marvin}:${toString pns.grafana.anubis} { 185 header_up X-Real-Ip {remote_host} 186 header_up X-Http-Version {http.request.proto} 187 } 188 ''; 189 }; 190 191 # Miniflux 192 ${pns.miniflux.extUrl} = { 193 extraConfig = '' 194 reverse_proxy ${marvin}:${toString pns.miniflux.anubis} { 195 header_up X-Real-Ip {remote_host} 196 header_up X-Http-Version {http.request.proto} 197 } 198 ''; 199 }; 200 201 # Nextcloud 202 ${pns.nextcloud.extUrl} = { 203 extraConfig = '' 204 reverse_proxy ${marvin}:${toString pns.nextcloud.anubis} { 205 header_up X-Real-Ip {remote_host} 206 header_up X-Http-Version {http.request.proto} 207 } 208 ''; 209 }; 210 211 # Nextcloud-Office(Collabora) 212 ${pns.nextcloud-office.extUrl} = { 213 extraConfig = '' 214 reverse_proxy ${marvin}:${toString pns.nextcloud-office.anubis} { 215 header_up X-Real-Ip {remote_host} 216 header_up X-Http-Version {http.request.proto} 217 } 218 ''; 219 }; 220 221 # Planka 222 ${pns.planka.extUrl} = { 223 extraConfig = '' 224 reverse_proxy ${marvin}:${toString pns.planka.anubis} { 225 header_up X-Real-Ip {remote_host} 226 header_up X-Http-Version {http.request.proto} 227 } 228 ''; 229 }; 230 231 # Pingvin Share 232 ${pns.pingvin-share.extUrl} = { 233 extraConfig = '' 234 reverse_proxy /api/* ${marvin}:${toString pns.pingvin-share.be-anubis} { 235 header_up X-Real-IP {remote_host} 236 header_up X-Http-Version {http.request.proto} 237 } 238 reverse_proxy /* ${marvin}:${toString pns.pingvin-share.anubis} { 239 header_up X-Real-IP {remote_host} 240 header_up X-Http-Version {http.request.proto} 241 } 242 ''; 243 }; 244 # Tangled Services 245 ${pns.tangled-knot.extUrl} = { 246 extraConfig = '' 247 reverse_proxy ${marvin}:${toString pns.tangled-knot.port} 248 ''; 249 }; 250 ${pns.tangled-spindle.extUrl} = { 251 extraConfig = '' 252 reverse_proxy ${marvin}:${toString pns.tangled-spindle.port} 253 ''; 254 }; 255 256 # Simple Tailscale Hosts 257 258 # Deemix 259 "${pns.deemix.tsHost}.${tsNet}" = { 260 extraConfig = '' 261 bind tailscale/${pns.deemix.tsHost} 262 tailscale_auth 263 reverse_proxy ${marvin}:${toString pns.deemix.port} 264 ''; 265 }; 266 # Pinchflat 267 "${pns.pinchflat.tsHost}.${tsNet}" = { 268 extraConfig = '' 269 bind tailscale/${pns.pinchflat.tsHost} 270 tailscale_auth 271 reverse_proxy ${marvin}:${toString pns.pinchflat.port} 272 ''; 273 }; 274 # "mail.pyrox.dev:80" = { 275 # extraConfig = '' 276 # reverse_proxy ${marvin}:${mail.intHTTP} 277 # ''; 278 # }; 279 }; 280 # Mail Config 281 globalConfig = '' 282 filesystem blog-repo git ${marvin}:${toString pns.git.port}/pyrox/new-blog { 283 ref refs/heads/pages 284 refresh_period 10m 285 } 286 layer4 { 287 :22 { 288 @a ssh 289 route @a { 290 proxy ${marvinIP}:2222 291 } 292 } 293 } 294 ''; 295 # TODO: Move the below section to global options once stalwart is working 296 # extraConfig = '' 297 # layer4 { 298 # 0.0.0.0:465 { 299 # route { 300 # proxy { 301 # proxy_protocol v2 302 # upstream ${marvinIP}:mail.intSMTPS} 303 # } 304 # } 305 # } 306 # 0.0.0.0:993 { 307 # route { 308 # proxy { 309 # proxy_protocol v2 310 # upstream ${marvinIP}:mail.intIMAPS} 311 # } 312 # } 313 # } 314 # 0.0.0.0:4190 { 315 # route { 316 # proxy { 317 # proxy_protocol v2 318 # upstream ${marvinIP}:mail.intManageSieve} 319 # } 320 # } 321 # } 322 # } 323 # ''; 324 }; 325 systemd.services.caddy.serviceConfig.CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; 326 systemd.services.caddy.serviceConfig.AmbientCapabilities = "CAP_NET_BIND_SERVICE"; 327}