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}