1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.libreddit;
7
8 args = concatStringsSep " " ([
9 "--port ${toString cfg.port}"
10 "--address ${cfg.address}"
11 ]);
12in
13{
14 options = {
15 services.libreddit = {
16 enable = mkEnableOption "Private front-end for Reddit";
17
18 package = mkPackageOption pkgs "libreddit" { };
19
20 address = mkOption {
21 default = "0.0.0.0";
22 example = "127.0.0.1";
23 type = types.str;
24 description = "The address to listen on";
25 };
26
27 port = mkOption {
28 default = 8080;
29 example = 8000;
30 type = types.port;
31 description = "The port to listen on";
32 };
33
34 openFirewall = mkOption {
35 type = types.bool;
36 default = false;
37 description = "Open ports in the firewall for the libreddit web interface";
38 };
39
40 };
41 };
42
43 config = mkIf cfg.enable {
44 systemd.services.libreddit = {
45 description = "Private front-end for Reddit";
46 wantedBy = [ "multi-user.target" ];
47 after = [ "network.target" ];
48 serviceConfig = {
49 DynamicUser = true;
50 ExecStart = "${lib.getExe cfg.package} ${args}";
51 AmbientCapabilities = lib.mkIf (cfg.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
52 Restart = "on-failure";
53 RestartSec = "2s";
54 # Hardening
55 CapabilityBoundingSet = if (cfg.port < 1024) then [ "CAP_NET_BIND_SERVICE" ] else [ "" ];
56 DeviceAllow = [ "" ];
57 LockPersonality = true;
58 MemoryDenyWriteExecute = true;
59 PrivateDevices = true;
60 # A private user cannot have process capabilities on the host's user
61 # namespace and thus CAP_NET_BIND_SERVICE has no effect.
62 PrivateUsers = (cfg.port >= 1024);
63 ProcSubset = "pid";
64 ProtectClock = true;
65 ProtectControlGroups = true;
66 ProtectHome = true;
67 ProtectHostname = true;
68 ProtectKernelLogs = true;
69 ProtectKernelModules = true;
70 ProtectKernelTunables = true;
71 ProtectProc = "invisible";
72 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
73 RestrictNamespaces = true;
74 RestrictRealtime = true;
75 RestrictSUIDSGID = true;
76 SystemCallArchitectures = "native";
77 SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
78 UMask = "0077";
79 };
80 };
81
82 networking.firewall = mkIf cfg.openFirewall {
83 allowedTCPPorts = [ cfg.port ];
84 };
85 };
86}