This repository has no description
11 kB
396 lines
1{ config, pkgs, ... }:
2{
3 users.users.download = {
4 isSystemUser = true;
5 uid = 1001;
6 group = "download";
7 };
8 users.groups.download.members = [ "transmission" ];
9 services.transmission = {
10 enable = true;
11 home = "/var/state/transmission";
12 group = "download";
13 downloadDirPermissions = "775";
14 settings = {
15 download-dir = "/var/download/transmission";
16 incomplete-dir-enabled = false;
17 rpc-bind-address = "::";
18 message-level = 1;
19 umask = 18;
20 rpc-whitelist-enabled = false;
21 rpc-host-whitelist-enabled = false;
22 };
23 };
24 systemd.services.transmission-watcher = {
25 wantedBy = [ "multi-user.target" ];
26 startAt = "*:0/5";
27 path = [
28 pkgs.curl
29 pkgs.systemdMinimal
30 ];
31 script = ''
32 set -efu -o pipefail
33 if ! curl -SsfL http://transmission.r; then
34 systemctl restart transmission*
35 fi
36 '';
37 };
38
39 security.acme.defaults.email = "spam@krebsco.de";
40 security.acme.acceptTerms = true;
41 security.acme.certs."yellow.r".server = config.krebs.ssl.acmeURL;
42 security.acme.certs."jelly.r".server = config.krebs.ssl.acmeURL;
43 security.acme.certs."radar.r".server = config.krebs.ssl.acmeURL;
44 security.acme.certs."sonar.r".server = config.krebs.ssl.acmeURL;
45 security.acme.certs."transmission.r".server = config.krebs.ssl.acmeURL;
46 services.nginx = {
47 enable = true;
48 package = pkgs.nginx.override {
49 modules = with pkgs.nginxModules; [
50 fancyindex
51 ];
52 };
53 virtualHosts."yellow.r" = {
54 serverAliases = [ "flix.r" ];
55 default = true;
56 enableACME = true;
57 addSSL = true;
58 locations."/" = {
59 root = "/var/download";
60 extraConfig = ''
61 fancyindex on;
62 fancyindex_footer "/fancy.html";
63 include ${pkgs.nginx}/conf/mime.types;
64 include ${pkgs.writeText "extrMime" ''
65 types {
66 video/webm mkv;
67 }
68 ''};
69 create_full_put_path on;
70 '';
71 };
72 locations."/chatty" = {
73 proxyPass = "http://localhost:3000";
74 extraConfig = ''
75 rewrite /chatty/(.*) /$1 break;
76 proxy_set_header Host $host;
77 '';
78 };
79 locations."= /fancy.html".extraConfig = ''
80 alias ${pkgs.writeText "nginx_footer" ''
81 <div id="mydiv">
82 <!-- Include a header DIV with the same name as the draggable DIV, followed by "header" -->
83 <div id="mydivheader">Click here to move</div>
84 <iframe src="/chatty/index.html"></iframe>
85 </div>
86 <style>
87 #mydiv {
88 position: absolute;
89 z-index: 9;
90 background-color: #f1f1f1;
91 border: 1px solid #d3d3d3;
92 text-align: center;
93 }
94
95 #mydivheader {
96 padding: 10px;
97 cursor: move;
98 z-index: 10;
99 background-color: #2196F3;
100 color: #fff;
101 }
102 </style>
103 <script>
104 // Make the DIV element draggable:
105 dragElement(document.getElementById("mydiv"));
106
107 function dragElement(elmnt) {
108 var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
109 if (document.getElementById(elmnt.id + "header")) {
110 // if present, the header is where you move the DIV from:
111 document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
112 } else {
113 // otherwise, move the DIV from anywhere inside the DIV:
114 elmnt.onmousedown = dragMouseDown;
115 }
116
117 function dragMouseDown(e) {
118 e = e || window.event;
119 e.preventDefault();
120 // get the mouse cursor position at startup:
121 pos3 = e.clientX;
122 pos4 = e.clientY;
123 document.onmouseup = closeDragElement;
124 // call a function whenever the cursor moves:
125 document.onmousemove = elementDrag;
126 }
127
128 function elementDrag(e) {
129 e = e || window.event;
130 e.preventDefault();
131 // calculate the new cursor position:
132 pos1 = pos3 - e.clientX;
133 pos2 = pos4 - e.clientY;
134 pos3 = e.clientX;
135 pos4 = e.clientY;
136 // set the element's new position:
137 elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
138 elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
139 }
140
141 function closeDragElement() {
142 // stop moving when mouse button is released:
143 document.onmouseup = null;
144 document.onmousemove = null;
145 }
146 }
147 </script>
148 ''};
149 '';
150 };
151 virtualHosts."jelly.r" = {
152 enableACME = true;
153 addSSL = true;
154 locations."/".extraConfig = ''
155 proxy_pass http://localhost:8096/;
156 proxy_set_header Accept-Encoding "";
157 '';
158 };
159 virtualHosts."transmission.r" = {
160 enableACME = true;
161 addSSL = true;
162 locations."/" = {
163 proxyWebsockets = true;
164 proxyPass = "http://128.0.0.1:9091";
165 };
166 };
167 virtualHosts."radar.r" = {
168 enableACME = true;
169 addSSL = true;
170 locations."/" = {
171 proxyWebsockets = true;
172 proxyPass = "http://localhost:7878";
173 };
174 };
175 virtualHosts."sonar.r" = {
176 enableACME = true;
177 addSSL = true;
178 locations."/" = {
179 proxyWebsockets = true;
180 proxyPass = "http://localhost:8989";
181 # sonar.r now has forced authentication
182 extraConfig = ''
183 proxy_set_header Authorization "Basic a3JlYnM6YWlkc2JhbGxz";
184 '';
185 };
186 };
187 };
188
189 services.samba = {
190 enable = true;
191 enableNmbd = false;
192 settings.global = {
193 "hosts allow" = "42::/16 10.243.0.0/16 10.244.0.0/16 fdcc:c5da:5295:c853:d499::/80";
194 "use sendfile" = "true";
195 "disable netbios" = "true";
196 "mangled names" = "illegal";
197 "socket options" = "TCP_NODELAY IPTOS_LOWDELAY SO_RCVBUF=65536 SO_SNDBUF=65536";
198 "load printers" = "false";
199 "disable spoolss" = "true";
200 "printcap name" = "/dev/null";
201 "map to guest" = "Bad User";
202 "max log size" = "50";
203 "dns proxy" = "no";
204 "security" = "user";
205 "syslog only" = "yes";
206 };
207 shares.public = {
208 comment = "Warez";
209 path = "/var/download";
210 public = "yes";
211 "only guest" = "yes";
212 "create mask" = "0644";
213 "directory mask" = "2777";
214 writable = "no";
215 printable = "no";
216 };
217 };
218
219 systemd.services.bruellwuerfel =
220 let
221 bruellwuerfelSrc = pkgs.fetchFromGitHub {
222 owner = "krebs";
223 repo = "bruellwuerfel";
224 rev = "dc73adf69249fb63a4b024f1f3fbc9e541b27015";
225 sha256 = "078jp1gbavdp8lnwa09xa5m6bbbd05fi4x5ldkkgin5z04hwlhmd";
226 };
227 in
228 {
229 wantedBy = [ "multi-user.target" ];
230 after = [ "network-online.target" ];
231 environment = {
232 IRC_CHANNEL = "#flix";
233 IRC_NICK = "bruelli";
234 IRC_SERVER = "irc.r";
235 IRC_HISTORY_FILE = "/tmp/bruelli.history";
236 };
237 serviceConfig = {
238 ExecStart = "${pkgs.deno}/bin/deno run -A ${bruellwuerfelSrc}/src/index.ts";
239 };
240 };
241
242 krebs.iptables = {
243 enable = true;
244 tables.filter.INPUT.rules = [
245 {
246 predicate = "-p tcp --dport 80";
247 target = "ACCEPT";
248 } # nginx web dir
249 {
250 predicate = "-p tcp --dport 443";
251 target = "ACCEPT";
252 } # nginx web dir
253 {
254 predicate = "-p tcp --dport 9091";
255 target = "ACCEPT";
256 } # transmission-web
257 {
258 predicate = "-p tcp --dport 51413";
259 target = "ACCEPT";
260 } # transmission-traffic
261 {
262 predicate = "-p udp --dport 51413";
263 target = "ACCEPT";
264 } # transmission-traffic
265 {
266 predicate = "-p tcp --dport 8096";
267 target = "ACCEPT";
268 } # jellyfin
269 {
270 predicate = "-p tcp --dport 8920";
271 target = "ACCEPT";
272 } # jellyfin
273 {
274 predicate = "-p udp --dport 1900";
275 target = "ACCEPT";
276 } # jellyfin
277 {
278 predicate = "-p udp --dport 7359";
279 target = "ACCEPT";
280 } # jellyfin
281
282 # smbd
283 {
284 predicate = "-p tcp --dport 445";
285 target = "ACCEPT";
286 }
287 {
288 predicate = "-p tcp --dport 111";
289 target = "ACCEPT";
290 }
291 {
292 predicate = "-p udp --dport 111";
293 target = "ACCEPT";
294 }
295 {
296 predicate = "-p tcp --dport 2049";
297 target = "ACCEPT";
298 }
299 {
300 predicate = "-p udp --dport 2049";
301 target = "ACCEPT";
302 }
303 {
304 predicate = "-p tcp --dport 4000:4002";
305 target = "ACCEPT";
306 }
307 {
308 predicate = "-p udp --dport 4000:4002";
309 target = "ACCEPT";
310 }
311 ];
312
313 tables.nat.PREROUTING.rules = [
314 # transmission rpc port
315 {
316 predicate = "-i retiolum -p tcp --dport 9091";
317 target = "DNAT --to-destination fdb4:3310:947::2";
318 v4 = false;
319 }
320 ];
321 tables.filter.FORWARD.policy = "ACCEPT"; # we need this so we can forward into the the transmission network namespace
322 };
323
324 systemd.services.flix-index = {
325 wantedBy = [ "multi-user.target" ];
326 path = [
327 pkgs.coreutils
328 pkgs.findutils
329 pkgs.inotify-tools
330 ];
331 serviceConfig = {
332 Restart = "always";
333 ExecStart = pkgs.writers.writeDash "flix-index" ''
334 set -efu
335
336 DIR=/var/download
337 cd "$DIR"
338 while inotifywait -rq -e create -e move -e delete "$DIR"; do
339 find . -type f > "$DIR"/index.tmp
340 mv "$DIR"/index.tmp "$DIR"/index
341 done
342 '';
343 };
344 };
345
346 services.jellyfin = {
347 enable = true;
348 group = "download";
349 };
350
351 # request managment
352 services.jellyseerr = {
353 enable = true;
354 openFirewall = true;
355 package = pkgs.jellyseerr.overrideAttrs (_: {
356 dontCheckForBrokenSymlinks = true;
357 });
358 };
359
360 # movies
361 services.radarr = {
362 enable = true;
363 openFirewall = true;
364 user = "download";
365 group = "download";
366 };
367
368 # shows
369 services.sonarr = {
370 enable = true;
371 openFirewall = true;
372 user = "download";
373 group = "download";
374 };
375 # sonarr needs unsecure packages
376 nixpkgs.config.permittedInsecurePackages = [
377 "aspnetcore-runtime-wrapped-6.0.36"
378 "aspnetcore-runtime-6.0.36"
379 "dotnet-sdk-wrapped-6.0.428"
380 "dotnet-sdk-6.0.428"
381 ];
382
383 # indexers
384 services.prowlarr = {
385 enable = true;
386 openFirewall = true;
387 };
388
389 # subtitles
390 services.bazarr = {
391 enable = true;
392 openFirewall = true;
393 user = "download";
394 group = "download";
395 };
396}