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 /api/* ${marvin}:${toString pns.pocket-id.be-port} {
70 header_up X-Real-IP {remote_host}
71 header_up X-Http-Version {http.request.proto}
72 }
73 reverse_proxy /.well-known/* ${marvin}:${toString pns.pocket-id.be-port} {
74 header_up X-Real-IP {remote_host}
75 header_up X-Http-Version {http.request.proto}
76 }
77 reverse_proxy /* ${marvin}:${toString pns.pocket-id.port} {
78 header_up X-Real-IP {remote_host}
79 header_up X-Http-Version {http.request.proto}
80 }
81 '';
82 };
83
84 # Vaultwarden
85 ${pns.vaultwarden.extUrl} = {
86 extraConfig = ''
87 header / {
88 Strict-Transport-Security "max-age=31536000;"
89 X-XSS-Protection "0"
90 X-Frame-Options "DENY"
91 X-Robots-Tag "noindex, nofollow"
92 X-Content-Type-Options "nosniff"
93 -Server
94 -X-Powered-By
95 -Last-Modified
96 }
97 reverse_proxy ${marvin}:${toString pns.vaultwarden.anubis} {
98 header_up X-Real-IP {remote_host}
99 header_up X-Http-Version {http.request.proto}
100 }
101 '';
102 };
103
104 # Cinny + Conduit
105 ${pns.matrix-server.extUrl} = {
106 extraConfig = ''
107 handle /_matrix/* {
108 reverse_proxy ${marvin}:${toString pns.matrix-server.port}
109 }
110 handle {
111 root * /var/www/cinny/dist/
112 try_files {path} / index.html
113 file_server
114 }
115 '';
116 };
117 # Jellyfin
118 ${pns.jellyfin.extUrl} = {
119 extraConfig = ''
120 @blocked not remote_ip 100.64.0.0/10 private_ranges
121 reverse_proxy ${marvin}:${toString pns.jellyfin.port}
122 handle /metrics* {
123 respond @blocked "Access Denied" 403
124 }
125 '';
126 };
127
128 # MTA-STS Setup for mailserver
129 "mta-sts.pyrox.dev" = {
130 extraConfig = ''
131 header Content-Type text/plain; charset=utf-8
132 respond /.well-known/mta-sts.txt <<END
133 version: STSv1
134 mode: enforce
135 mx: mail.pyrox.dev
136 mx:mail2.pyrox.dev
137 max_age: 2419200
138 END 200
139 '';
140 };
141
142 # Yourmother.website
143 "yourmother.website" = {
144 extraConfig = ''
145 header Content-Type text/html
146 respond 200 {
147 body `<!DOCTYPE html>
148 <html>
149 <head>
150 <meta http-equiv="Refresh" content="0; url=https://youtube.com/watch?v=oHg5SJYRHA0" />
151 </head>
152 </html>`
153 }
154 '';
155 };
156
157 # OpenPGP WKD stuff
158 "openpgpkey.pyrox.dev" = {
159 serverAliases = [ "openpgpkey.thehedgehog.me" ];
160 extraConfig = ''
161 respond /.well-known/openpgpkey/{labels.1}.{labels.0}/policy 200
162 header Access-Control-Allow-Origin *
163 header /.well-known/openpgpkey/{labels.1}.{labels.0}/hu/* Content-Type application/octet-stream
164 file_server {
165 fs blog-repo
166 }
167 '';
168 };
169
170 # Metrics
171 ":6899" = {
172 extraConfig = ''
173 metrics /metrics
174 '';
175 };
176 # SIMPLE HOSTS
177
178 # Forgejo
179 ${pns.git.extUrl} = {
180 extraConfig = ''
181 reverse_proxy ${marvin}:${toString pns.git.anubis} {
182 header_up X-Real-Ip {remote_host}
183 header_up X-Http-Version {http.request.proto}
184 }
185 '';
186 };
187
188 # Grafana
189 ${pns.grafana.extUrl} = {
190 extraConfig = ''
191 reverse_proxy ${marvin}:${toString pns.grafana.anubis} {
192 header_up X-Real-Ip {remote_host}
193 header_up X-Http-Version {http.request.proto}
194 }
195 '';
196 };
197
198 # Miniflux
199 ${pns.miniflux.extUrl} = {
200 extraConfig = ''
201 reverse_proxy ${marvin}:${toString pns.miniflux.anubis} {
202 header_up X-Real-Ip {remote_host}
203 header_up X-Http-Version {http.request.proto}
204 }
205 '';
206 };
207
208 # Nextcloud
209 ${pns.nextcloud.extUrl} = {
210 extraConfig = ''
211 reverse_proxy ${marvin}:${toString pns.nextcloud.anubis} {
212 header_up X-Real-Ip {remote_host}
213 header_up X-Http-Version {http.request.proto}
214 }
215 '';
216 };
217
218 # Nextcloud-Office(Collabora)
219 ${pns.nextcloud-office.extUrl} = {
220 extraConfig = ''
221 reverse_proxy ${marvin}:${toString pns.nextcloud-office.anubis} {
222 header_up X-Real-Ip {remote_host}
223 header_up X-Http-Version {http.request.proto}
224 }
225 '';
226 };
227
228 # Planka
229 ${pns.planka.extUrl} = {
230 extraConfig = ''
231 reverse_proxy ${marvin}:${toString pns.planka.anubis} {
232 header_up X-Real-Ip {remote_host}
233 header_up X-Http-Version {http.request.proto}
234 }
235 '';
236 };
237
238 # Pingvin Share
239 ${pns.pingvin-share.extUrl} = {
240 extraConfig = ''
241 reverse_proxy /api/* ${marvin}:${toString pns.pingvin-share.be-anubis} {
242 header_up X-Real-IP {remote_host}
243 header_up X-Http-Version {http.request.proto}
244 }
245 reverse_proxy /* ${marvin}:${toString pns.pingvin-share.anubis} {
246 header_up X-Real-IP {remote_host}
247 header_up X-Http-Version {http.request.proto}
248 }
249 '';
250 };
251
252 # Simple Tailscale Hosts
253
254 # Deemix
255 "${pns.deemix.tsHost}.${tsNet}" = {
256 extraConfig = ''
257 bind tailscale/${pns.deemix.tsHost}
258 tailscale_auth
259 reverse_proxy ${marvin}:${toString pns.deemix.port}
260 '';
261 };
262 # Pinchflat
263 "${pns.pinchflat.tsHost}.${tsNet}" = {
264 extraConfig = ''
265 bind tailscale/${pns.pinchflat.tsHost}
266 tailscale_auth
267 reverse_proxy ${marvin}:${toString pns.pinchflat.port}
268 '';
269 };
270 # "mail.pyrox.dev:80" = {
271 # extraConfig = ''
272 # reverse_proxy ${marvin}:${mail.intHTTP}
273 # '';
274 # };
275 };
276 # Mail Config
277 globalConfig = ''
278 filesystem blog-repo git ${marvin}:${toString pns.git.port}/pyrox/new-blog {
279 ref refs/heads/pages
280 refresh_period 10m
281 }
282 '';
283 # layer4 {
284 # 0.0.0.0:465 {
285 # route {
286 # proxy {
287 # proxy_protocol v2
288 # upstream ${marvinIP}:${mail.intSMTPS}
289 # }
290 # }
291 # }
292 # 0.0.0.0:993 {
293 # route {
294 # proxy {
295 # proxy_protocol v2
296 # upstream ${marvinIP}:${mail.intIMAPS}
297 # }
298 # }
299 # }
300 # 0.0.0.0:4190 {
301 # route {
302 # proxy {
303 # proxy_protocol v2
304 # upstream ${marvinIP}:${mail.intManageSieve}
305 # }
306 # }
307 # }
308 # }
309 };
310 systemd.services.caddy.serviceConfig.CapabilityBoundingSet = "CAP_NET_BIND_SERVICE";
311 systemd.services.caddy.serviceConfig.AmbientCapabilities = "CAP_NET_BIND_SERVICE";
312}