system-manager-wakasu
  1{
  2  libx,
  3  globals,
  4  lib,
  5  pkgs,
  6  config,
  7  ...
  8}:
  9{
 10  age.secrets."gandi.env" = {
 11    file = ../../secrets/rhea/gandi.env.age;
 12    mode = "400";
 13    owner = "traefik";
 14    group = "traefik";
 15  };
 16
 17  users.users.vincent.linger = true;
 18
 19  services = {
 20    traefik = {
 21      enable = true;
 22
 23      staticConfigOptions = {
 24        # Entry points
 25        entryPoints = {
 26          web = {
 27            address = ":80";
 28            http.redirections.entryPoint = {
 29              to = "websecure";
 30              scheme = "https";
 31            };
 32          };
 33          websecure = {
 34            address = ":443";
 35          };
 36        };
 37
 38        # Certificate resolver using Gandi DNS
 39        certificatesResolvers.letsencrypt = {
 40          acme = {
 41            email = "vincent@sbr.pm";
 42            storage = "/var/lib/traefik/acme.json";
 43            dnsChallenge = {
 44              provider = "gandiv5";
 45              delayBeforeCheck = "0s";
 46              resolvers = [
 47                "1.1.1.1:53"
 48                "8.8.8.8:53"
 49              ];
 50            };
 51          };
 52        };
 53      };
 54
 55      # Dynamic configuration using module option
 56      dynamicConfigOptions =
 57        let
 58          # Filter machines that have syncthing configured
 59          syncthingMachines = lib.filterAttrs (
 60            _name: machine: machine ? syncthing && machine.syncthing ? folders
 61          ) globals.machines;
 62
 63          # Generate routers for syncthing hosts
 64          syncthingRouters = lib.mapAttrs' (
 65            name: _machine:
 66            lib.nameValuePair "syncthing-${name}" {
 67              rule = "Host(`syncthing.sbr.pm`) && PathPrefix(`/${name}`) || Host(`s.sbr.pm`) && PathPrefix(`/${name}`)";
 68              service = "syncthing-${name}";
 69              entryPoints = [ "websecure" ];
 70              middlewares = [ "syncthing-${name}-strip" ];
 71              tls = {
 72                certResolver = "letsencrypt";
 73              };
 74            }
 75          ) syncthingMachines;
 76
 77          # Generate services for syncthing hosts
 78          syncthingServices = lib.mapAttrs' (
 79            name: machine:
 80            lib.nameValuePair "syncthing-${name}" {
 81              loadBalancer = {
 82                servers = [
 83                  { url = "http://${builtins.head machine.net.vpn.ips}:8384"; }
 84                ];
 85              };
 86            }
 87          ) syncthingMachines;
 88
 89          # Generate middleware for path stripping
 90          syncthingMiddlewares = lib.mapAttrs' (
 91            name: _machine:
 92            lib.nameValuePair "syncthing-${name}-strip" {
 93              stripPrefix = {
 94                prefixes = [ "/${name}" ];
 95              };
 96            }
 97          ) syncthingMachines;
 98        in
 99        {
100          http = {
101            routers = syncthingRouters // {
102              jellyfin = {
103                rule = "Host(`jellyfin.sbr.pm`)";
104                service = "jellyfin";
105                entryPoints = [ "websecure" ];
106                tls = {
107                  certResolver = "letsencrypt";
108                };
109              };
110              jellyseerr = {
111                rule = "Host(`jellyseerr.sbr.pm`)";
112                service = "jellyseerr";
113                entryPoints = [ "websecure" ];
114                tls = {
115                  certResolver = "letsencrypt";
116                };
117              };
118              sonarr = {
119                rule = "Host(`sonarr.sbr.pm`)";
120                service = "sonarr";
121                entryPoints = [ "websecure" ];
122                tls = {
123                  certResolver = "letsencrypt";
124                };
125              };
126              radarr = {
127                rule = "Host(`radarr.sbr.pm`)";
128                service = "radarr";
129                entryPoints = [ "websecure" ];
130                tls = {
131                  certResolver = "letsencrypt";
132                };
133              };
134              lidarr = {
135                rule = "Host(`lidarr.sbr.pm`)";
136                service = "lidarr";
137                entryPoints = [ "websecure" ];
138                tls = {
139                  certResolver = "letsencrypt";
140                };
141              };
142              bazarr = {
143                rule = "Host(`bazarr.sbr.pm`)";
144                service = "bazarr";
145                entryPoints = [ "websecure" ];
146                tls = {
147                  certResolver = "letsencrypt";
148                };
149              };
150              transmission = {
151                rule = "Host(`transmission.sbr.pm`) || Host(`t.sbr.pm`)";
152                service = "transmission";
153                entryPoints = [ "websecure" ];
154                tls = {
155                  certResolver = "letsencrypt";
156                };
157              };
158            };
159            services = syncthingServices // {
160              jellyfin = {
161                loadBalancer = {
162                  servers = [
163                    { url = "http://localhost:8096"; }
164                  ];
165                };
166              };
167              jellyseerr = {
168                loadBalancer = {
169                  servers = [
170                    { url = "http://localhost:5055"; }
171                  ];
172                };
173              };
174              sonarr = {
175                loadBalancer = {
176                  servers = [
177                    { url = "http://localhost:8989"; }
178                  ];
179                };
180              };
181              radarr = {
182                loadBalancer = {
183                  servers = [
184                    { url = "http://localhost:7878"; }
185                  ];
186                };
187              };
188              lidarr = {
189                loadBalancer = {
190                  servers = [
191                    { url = "http://localhost:8686"; }
192                  ];
193                };
194              };
195              bazarr = {
196                loadBalancer = {
197                  servers = [
198                    { url = "http://localhost:6767"; }
199                  ];
200                };
201              };
202              transmission = {
203                loadBalancer = {
204                  servers = [
205                    { url = "http://localhost:9091"; }
206                  ];
207                };
208              };
209            };
210            middlewares = syncthingMiddlewares;
211          };
212        };
213    };
214
215    wireguard = {
216      enable = true;
217      ips = libx.wg-ips globals.machines.rhea.net.vpn.ips;
218      endpoint = "${globals.net.vpn.endpoint}";
219      endpointPublicKey = "${globals.machines.kerkouane.net.vpn.pubkey}";
220    };
221    # smartd = {
222    #   enable = true;
223    #   devices = [ { device = "/dev/nvme0n1"; } ];
224    # };
225    samba.settings = {
226      settings = {
227        "backup" = {
228          path = "/neo/backup";
229          public = true;
230          browseable = "yes";
231          "read only" = "no";
232          "guest ok" = "yes";
233          writable = true;
234          comment = "backup";
235          "create mask" = "0644";
236          "directory mask" = "0755";
237          "force user" = "vincent";
238          "force group" = "users";
239        };
240        "documents" = {
241          path = "/neo/documents";
242          public = true;
243          browseable = "yes";
244          "read only" = "no";
245          "guest ok" = "yes";
246          writable = true;
247          comment = "documents";
248          "create mask" = "0644";
249          "directory mask" = "0755";
250          "force user" = "vincent";
251          "force group" = "users";
252        };
253        "downloads" = {
254          path = "/neo/downloads";
255          public = true;
256          browseable = "yes";
257          "read only" = "no";
258          "guest ok" = "yes";
259          writable = true;
260          comment = "downloads";
261          "create mask" = "0644";
262          "directory mask" = "0755";
263          "force user" = "vincent";
264          "force group" = "users";
265        };
266        "music" = {
267          path = "/neo/music";
268          public = true;
269          browseable = "yes";
270          "read only" = "no";
271          "guest ok" = "yes";
272          writable = true;
273          comment = "music";
274          "create mask" = "0644";
275          "directory mask" = "0755";
276          "force user" = "vincent";
277          "force group" = "users";
278        };
279        "pictures" = {
280          path = "/neo/pictures";
281          public = true;
282          browseable = "yes";
283          "read only" = "no";
284          "guest ok" = "yes";
285          writable = true;
286          comment = "pictures";
287          "create mask" = "0644";
288          "directory mask" = "0755";
289          "force user" = "vincent";
290          "force group" = "users";
291        };
292        "videos" = {
293          path = "/neo/videos";
294          public = true;
295          browseable = "yes";
296          "read only" = "no";
297          "guest ok" = "yes";
298          writable = true;
299          comment = "videos";
300          "create mask" = "0644";
301          "directory mask" = "0755";
302          "force user" = "vincent";
303          "force group" = "users";
304        };
305      };
306    };
307    nfs.server = {
308      enable = true;
309      exports = ''
310                /neo                      192.168.1.0/24(rw,fsid=0,no_subtree_check) 10.100.0.0/24(rw,fsid=0,no_subtree_check)
311                /neo/backup               192.168.1.0/24(rw,fsid=1,no_subtree_check) 10.100.0.0/24(rw,fsid=1,no_subtree_check)
312                /neo/documents            192.168.1.0/24(rw,fsid=2,no_subtree_check) 10.100.0.0/24(rw,fsid=2,no_subtree_check)
313                /neo/downloads            192.168.1.0/24(rw,fsid=2,no_subtree_check) 10.100.0.0/24(rw,fsid=2,no_subtree_check)
314                /neo/music                192.168.1.0/24(rw,fsid=2,no_subtree_check) 10.100.0.0/24(rw,fsid=2,no_subtree_check)
315                /neo/pictures             192.168.1.0/24(rw,fsid=2,no_subtree_check) 10.100.0.0/24(rw,fsid=2,no_subtree_check)
316                /neo/videos               192.168.1.0/24(rw,fsid=2,no_subtree_check) 10.100.0.0/24(rw,fsid=2,no_subtree_check)
317        			'';
318    };
319    # immich = {
320    #   enable = true;
321    #   user = "vincent";
322    #   group = "users";
323    #   mediaLocation = "/neo/pictures/photos";
324    # };
325    jellyfin = {
326      enable = true;
327      user = "vincent";
328      group = "users";
329      openFirewall = true;
330    };
331    jellyseerr = {
332      enable = true;
333      openFirewall = true;
334    };
335    aria2 = {
336      # FIXME: make sure aria2 runs as user vincent
337      enable = true;
338      openPorts = true;
339      settings = {
340        max-concurrent-downloads = 20;
341        dir = "/neo/downloads";
342      };
343      rpcSecretFile = "${pkgs.writeText "aria" "aria2rpc\n"}"; # FIXME: use secrets for this somehow
344    };
345    transmission = {
346      enable = true;
347      user = "vincent";
348      group = "users";
349      openFirewall = true;
350      package = pkgs.transmission_4;
351      openRPCPort = true; # Open firewall for RPC
352      home = "/neo/torrents";
353      settings = {
354        # Override default settings
355        incomplete-dir-enabled = true;
356        rpc-bind-address = "0.0.0.0"; # Bind to own IP
357        rpc-host-whitelist = "localhost,t.sbr.pm,transmission.sbr.pm,rhea.home,rhea.vpn,rhea.sbr.pm,192.168.1.50,10.100.0.50";
358        rpc-host-whitelist-enabled = true;
359        rpc-whitelist-enabled = true;
360        rpc-whitelist = "127.0.0.1,192.168.1.*,10.100.0.*"; # Whitelist your remote machine (10.0.0.1 in this example)
361        rpc-username = "transmission";
362        rpc-password = "transmission";
363      };
364    };
365    sonarr = {
366      enable = true;
367      user = "vincent";
368      group = "users";
369      openFirewall = true;
370    };
371    radarr = {
372      enable = true;
373      user = "vincent";
374      group = "users";
375      openFirewall = true;
376    };
377    bazarr = {
378      enable = true;
379      user = "vincent";
380      group = "users";
381      openFirewall = true;
382    };
383    prowlarr = {
384      enable = true;
385      openFirewall = true;
386    };
387    readarr = {
388      enable = true;
389      user = "vincent";
390      group = "users";
391      openFirewall = true;
392    };
393    lidarr = {
394      enable = true;
395      user = "vincent";
396      group = "users";
397      openFirewall = true;
398    };
399  };
400
401  security.acme = {
402    acceptTerms = true;
403    email = "vincent@sbr.pm";
404  };
405
406  networking.useDHCP = lib.mkDefault true;
407
408  # Open firewall for Traefik
409  networking.firewall.allowedTCPPorts = [
410    80
411    443
412  ];
413
414  # Environment file for Gandi API key (managed by agenix)
415  systemd.services.traefik.serviceConfig = {
416    EnvironmentFile = config.age.secrets."gandi.env".path;
417  };
418
419  environment.systemPackages = with pkgs; [
420    lm_sensors
421    gnumake
422  ];
423
424}