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