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