1{
2 pkgs,
3 config,
4 lib,
5 ...
6}:
7let
8 cfg = config.services.bitmagnet;
9 inherit (lib)
10 mkEnableOption
11 mkIf
12 mkOption
13 mkPackageOption
14 optional
15 ;
16 inherit (lib.types)
17 bool
18 int
19 port
20 str
21 submodule
22 ;
23 inherit (lib.generators) toYAML;
24
25 freeformType = (pkgs.formats.yaml { }).type;
26in
27{
28 options.services.bitmagnet = {
29 enable = mkEnableOption "Bitmagnet service";
30 useLocalPostgresDB = mkOption {
31 description = "Use a local postgresql database, create user and database";
32 type = bool;
33 default = true;
34 };
35 settings = mkOption {
36 description = "Bitmagnet configuration (https://bitmagnet.io/setup/configuration.html).";
37 default = { };
38 type = submodule {
39 inherit freeformType;
40 options = {
41 http_server = mkOption {
42 default = { };
43 description = "HTTP server settings";
44 type = submodule {
45 inherit freeformType;
46 options = {
47 port = mkOption {
48 type = str;
49 default = ":3333";
50 description = "HTTP server listen port";
51 };
52 };
53 };
54 };
55 dht_server = mkOption {
56 default = { };
57 description = "DHT server settings";
58 type = submodule {
59 inherit freeformType;
60 options = {
61 port = mkOption {
62 type = port;
63 default = 3334;
64 description = "DHT listen port";
65 };
66 };
67 };
68 };
69 postgres = mkOption {
70 default = { };
71 description = "PostgreSQL database configuration";
72 type = submodule {
73 inherit freeformType;
74 options = {
75 host = mkOption {
76 type = str;
77 default = "";
78 description = "Address, hostname or Unix socket path of the database server";
79 };
80 name = mkOption {
81 type = str;
82 default = "bitmagnet";
83 description = "Database name to connect to";
84 };
85 user = mkOption {
86 type = str;
87 default = "";
88 description = "User to connect as";
89 };
90 password = mkOption {
91 type = str;
92 default = "";
93 description = "Password for database user";
94 };
95 };
96 };
97 };
98 };
99 };
100 };
101 package = mkPackageOption pkgs "bitmagnet" { };
102 user = mkOption {
103 description = "User running bitmagnet";
104 type = str;
105 default = "bitmagnet";
106 };
107 group = mkOption {
108 description = "Group of user running bitmagnet";
109 type = str;
110 default = "bitmagnet";
111 };
112 openFirewall = mkOption {
113 description = "Open DHT ports in firewall";
114 type = bool;
115 default = false;
116 };
117 };
118 config = mkIf cfg.enable {
119 environment.etc."xdg/bitmagnet/config.yml" = {
120 text = toYAML { } cfg.settings;
121 mode = "0440";
122 user = cfg.user;
123 group = cfg.group;
124 };
125 systemd.services.bitmagnet = {
126 enable = true;
127 wantedBy = [ "multi-user.target" ];
128 after = [
129 "network.target"
130 ]
131 ++ optional cfg.useLocalPostgresDB "postgresql.target";
132 requires = optional cfg.useLocalPostgresDB "postgresql.target";
133 serviceConfig = {
134 Type = "simple";
135 DynamicUser = true;
136 User = cfg.user;
137 Group = cfg.group;
138 ExecStart = "${cfg.package}/bin/bitmagnet worker run --all";
139 Restart = "on-failure";
140 WorkingDirectory = "/var/lib/bitmagnet";
141 StateDirectory = "bitmagnet";
142
143 # Sandboxing (sorted by occurrence in https://www.freedesktop.org/software/systemd/man/systemd.exec.html)
144 ProtectSystem = "strict";
145 ProtectHome = true;
146 PrivateTmp = true;
147 PrivateDevices = true;
148 ProtectHostname = true;
149 ProtectClock = true;
150 ProtectKernelTunables = true;
151 ProtectKernelModules = true;
152 ProtectKernelLogs = true;
153 ProtectControlGroups = true;
154 RestrictAddressFamilies = [
155 "AF_UNIX"
156 "AF_INET"
157 "AF_INET6"
158 ];
159 RestrictNamespaces = true;
160 LockPersonality = true;
161 MemoryDenyWriteExecute = true;
162 RestrictRealtime = true;
163 RestrictSUIDSGID = true;
164 RemoveIPC = true;
165 PrivateMounts = true;
166 };
167 };
168 users.users = mkIf (cfg.user == "bitmagnet") {
169 bitmagnet = {
170 group = cfg.group;
171 isSystemUser = true;
172 };
173 };
174 users.groups = mkIf (cfg.group == "bitmagnet") { bitmagnet = { }; };
175 networking.firewall = mkIf cfg.openFirewall {
176 allowedTCPPorts = [ cfg.settings.dht_server.port ];
177 allowedUDPPorts = [ cfg.settings.dht_server.port ];
178 };
179 services.postgresql = mkIf cfg.useLocalPostgresDB {
180 enable = true;
181 ensureDatabases = [
182 cfg.settings.postgres.name
183 (if (cfg.settings.postgres.user == "") then cfg.user else cfg.settings.postgres.user)
184 ];
185 ensureUsers = [
186 {
187 name = if (cfg.settings.postgres.user == "") then cfg.user else cfg.settings.postgres.user;
188 ensureDBOwnership = true;
189 }
190 ];
191 };
192 };
193
194 meta.maintainers = with lib.maintainers; [ gileri ];
195}