at master 3.8 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8let 9 inherit (lib) 10 concatStringsSep 11 isBool 12 mapAttrs 13 mkEnableOption 14 mkIf 15 mkOption 16 mkPackageOption 17 mkRenamedOptionModule 18 types 19 ; 20 21 cfg = config.services.redlib; 22 23 args = concatStringsSep " " ([ 24 "--port ${toString cfg.port}" 25 "--address ${cfg.address}" 26 ]); 27 28 boolToString' = b: if b then "on" else "off"; 29in 30{ 31 imports = [ 32 (mkRenamedOptionModule 33 [ 34 "services" 35 "libreddit" 36 ] 37 [ 38 "services" 39 "redlib" 40 ] 41 ) 42 ]; 43 44 options = { 45 services.redlib = { 46 enable = mkEnableOption "Private front-end for Reddit"; 47 48 package = mkPackageOption pkgs "redlib" { }; 49 50 address = mkOption { 51 default = "0.0.0.0"; 52 example = "127.0.0.1"; 53 type = types.str; 54 description = "The address to listen on"; 55 }; 56 57 port = mkOption { 58 default = 8080; 59 example = 8000; 60 type = types.port; 61 description = "The port to listen on"; 62 }; 63 64 openFirewall = mkOption { 65 type = types.bool; 66 default = false; 67 description = "Open ports in the firewall for the redlib web interface"; 68 }; 69 70 settings = lib.mkOption { 71 type = lib.types.submodule { 72 freeformType = 73 with types; 74 attrsOf ( 75 nullOr (oneOf [ 76 bool 77 int 78 str 79 ]) 80 ); 81 options = { }; 82 }; 83 default = { }; 84 description = '' 85 See [GitHub](https://github.com/redlib-org/redlib/tree/main?tab=readme-ov-file#configuration) for available settings. 86 ''; 87 }; 88 }; 89 }; 90 91 config = mkIf cfg.enable { 92 systemd.packages = [ cfg.package ]; 93 systemd.services.redlib = { 94 wantedBy = [ "default.target" ]; 95 environment = mapAttrs (_: v: if isBool v then boolToString' v else toString v) cfg.settings; 96 serviceConfig = { 97 # Hardening 98 LockPersonality = true; 99 MemoryDenyWriteExecute = true; 100 NoNewPrivileges = true; 101 PrivateDevices = true; 102 PrivateIPC = true; 103 PrivateTmp = true; 104 ProcSubset = "pid"; 105 ProtectClock = true; 106 ProtectControlGroups = true; 107 ProtectHome = true; 108 ProtectHostname = true; 109 ProtectKernelLogs = true; 110 ProtectKernelModules = true; 111 ProtectKernelTunables = true; 112 ProtectProc = "invisible"; 113 ProtectSystem = "full"; 114 RemoveIPC = true; 115 RestrictAddressFamilies = [ 116 "AF_INET" 117 "AF_INET6" 118 ]; 119 RestrictNamespaces = true; 120 RestrictRealtime = true; 121 RestrictSUIDSGID = true; 122 SystemCallArchitectures = "native"; 123 SystemCallFilter = [ 124 "~@mount" 125 "~@swap" 126 "~@resources" 127 "~@reboot" 128 "~@raw-io" 129 "~@obsolete" 130 "~@module" 131 "~@debug" 132 "~@cpu-emulation" 133 "~@clock" 134 "~@privileged" 135 ]; 136 UMask = "0027"; 137 138 ExecStart = [ 139 "" 140 "${lib.getExe cfg.package} ${args}" 141 ]; 142 } 143 // ( 144 if (cfg.port < 1024) then 145 { 146 AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; 147 CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; 148 } 149 else 150 { 151 # A private user cannot have process capabilities on the host's user 152 # namespace and thus CAP_NET_BIND_SERVICE has no effect. 153 PrivateUsers = true; 154 CapabilityBoundingSet = false; 155 } 156 ); 157 }; 158 159 networking.firewall = mkIf cfg.openFirewall { 160 allowedTCPPorts = [ cfg.port ]; 161 }; 162 }; 163 164 meta = { 165 maintainers = with lib.maintainers; [ Guanran928 ]; 166 }; 167}