1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8let
9 inherit (lib)
10 getExe
11 mkIf
12 mkOption
13 mkEnableOption
14 types
15 ;
16
17 cfg = config.services.mollysocket;
18 configuration = format.generate "mollysocket.conf" cfg.settings;
19 format = pkgs.formats.toml { };
20 package = pkgs.writeShellScriptBin "mollysocket" ''
21 MOLLY_CONF=${configuration} exec ${getExe pkgs.mollysocket} "$@"
22 '';
23in
24{
25 options.services.mollysocket = {
26 enable = mkEnableOption ''
27 [MollySocket](https://github.com/mollyim/mollysocket) for getting Signal
28 notifications via UnifiedPush
29 '';
30
31 settings = mkOption {
32 default = { };
33 description = ''
34 Configuration for MollySocket. Available options are listed
35 [here](https://github.com/mollyim/mollysocket#configuration).
36 '';
37 type = types.submodule {
38 freeformType = format.type;
39 options = {
40 host = mkOption {
41 default = "127.0.0.1";
42 description = "Listening address of the web server";
43 type = types.str;
44 };
45
46 port = mkOption {
47 default = 8020;
48 description = "Listening port of the web server";
49 type = types.port;
50 };
51
52 allowed_endpoints = mkOption {
53 default = [ "*" ];
54 description = "List of UnifiedPush servers";
55 example = [ "https://ntfy.sh" ];
56 type = with types; listOf str;
57 };
58
59 allowed_uuids = mkOption {
60 default = [ "*" ];
61 description = "UUIDs of Signal accounts that may use this server";
62 example = [ "abcdef-12345-tuxyz-67890" ];
63 type = with types; listOf str;
64 };
65 };
66 };
67 };
68
69 environmentFile = mkOption {
70 default = null;
71 description = ''
72 Environment file (see {manpage}`systemd.exec(5)` "EnvironmentFile="
73 section for the syntax) passed to the service. This option can be
74 used to safely include secrets in the configuration.
75 '';
76 example = "/run/secrets/mollysocket";
77 type = with types; nullOr path;
78 };
79
80 logLevel = mkOption {
81 default = "info";
82 description = "Set the {env}`RUST_LOG` environment variable";
83 example = "debug";
84 type = types.str;
85 };
86 };
87
88 config = mkIf cfg.enable {
89 environment.systemPackages = [
90 package
91 ];
92
93 # see https://github.com/mollyim/mollysocket/blob/main/mollysocket.service
94 systemd.services.mollysocket = {
95 description = "MollySocket";
96 wantedBy = [ "multi-user.target" ];
97 after = [ "network-online.target" ];
98 wants = [ "network-online.target" ];
99 environment.RUST_LOG = cfg.logLevel;
100 serviceConfig = {
101 EnvironmentFile = cfg.environmentFile;
102 ExecStart = "${getExe package} server";
103 KillSignal = "SIGINT";
104 Restart = "on-failure";
105 StateDirectory = "mollysocket";
106 TimeoutStopSec = 5;
107 WorkingDirectory = "/var/lib/mollysocket";
108
109 # hardening
110 DevicePolicy = "closed";
111 DynamicUser = true;
112 LockPersonality = true;
113 MemoryDenyWriteExecute = true;
114 NoNewPrivileges = true;
115 PrivateDevices = true;
116 PrivateTmp = true;
117 PrivateUsers = true;
118 ProcSubset = "pid";
119 ProtectClock = true;
120 ProtectControlGroups = true;
121 ProtectHome = true;
122 ProtectHostname = true;
123 ProtectKernelLogs = true;
124 ProtectKernelModules = true;
125 ProtectKernelTunables = true;
126 ProtectProc = "invisible";
127 ProtectSystem = "strict";
128 RemoveIPC = true;
129 RestrictAddressFamilies = [
130 "AF_INET"
131 "AF_INET6"
132 ];
133 RestrictNamespaces = true;
134 RestrictRealtime = true;
135 RestrictSUIDSGID = true;
136 SystemCallArchitectures = "native";
137 SystemCallFilter = [
138 "@system-service"
139 "~@resources"
140 "~@privileged"
141 ];
142 UMask = "0077";
143 };
144 };
145 };
146
147 meta.maintainers = with lib.maintainers; [ dotlambda ];
148}