summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hosts/surtr/borg.nix50
-rw-r--r--hosts/surtr/default.nix2
-rw-r--r--hosts/surtr/matrix/default.nix4
-rw-r--r--modules/borgsnap/borgsnap/borgsnap/__main__.py4
-rw-r--r--modules/coturn.nix369
-rw-r--r--modules/zfssnap/default.nix34
6 files changed, 444 insertions, 19 deletions
diff --git a/hosts/surtr/borg.nix b/hosts/surtr/borg.nix
new file mode 100644
index 00000000..b9fe53d7
--- /dev/null
+++ b/hosts/surtr/borg.nix
@@ -0,0 +1,50 @@
1{ lib, config, ... }:
2
3with lib;
4
5{
6 config = {
7 services.borgsnap = {
8 enable = true;
9 target = "borg.vidhar:.";
10
11 extraConfig = mkForce {
12 daily = "31";
13 monthly = "-1";
14 };
15
16 sshConfig = ''
17 Include /etc/ssh/ssh_config
18
19 ControlMaster auto
20 ControlPath /var/lib/borg/.borgssh-master-%r@%n:%p
21 ControlPersist yes
22
23 Host borg.vidhar
24 HostName vidhar.yggdrasil.li
25 User borg
26 IdentityFile ${config.sops.secrets."append.borg.vidhar".path}
27 IdentitiesOnly yes
28
29 BatchMode yes
30 ServerAliveInterval 10
31 ServerAliveCountMax 30
32 '';
33 };
34
35 sops.secrets."append.borg.vidhar" = {
36 format = "binary";
37 sopsFile = ../vidhar/borg/jotnar/surtr;
38 owner = "borg";
39 group = "borg";
40 mode = "0400";
41 };
42
43 users.users.borg = {
44 useDefaultShell = true;
45 isSystemUser = true;
46 group = "borg";
47 };
48 users.groups.borg = {};
49 };
50}
diff --git a/hosts/surtr/default.nix b/hosts/surtr/default.nix
index f616d749..cebb2b6c 100644
--- a/hosts/surtr/default.nix
+++ b/hosts/surtr/default.nix
@@ -2,7 +2,7 @@
2{ 2{
3 imports = with flake.nixosModules.systemProfiles; [ 3 imports = with flake.nixosModules.systemProfiles; [
4 tmpfs-root qemu-guest openssh rebuild-machines zfs 4 tmpfs-root qemu-guest openssh rebuild-machines zfs
5 ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql.nix ./prometheus ./email ./vpn 5 ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql.nix ./prometheus ./email ./vpn ./borg.nix
6 ]; 6 ];
7 7
8 config = { 8 config = {
diff --git a/hosts/surtr/matrix/default.nix b/hosts/surtr/matrix/default.nix
index 46c2f338..f5a411ac 100644
--- a/hosts/surtr/matrix/default.nix
+++ b/hosts/surtr/matrix/default.nix
@@ -228,10 +228,6 @@ with lib;
228 "turn.synapse.li" = { 228 "turn.synapse.li" = {
229 zone = "synapse.li"; 229 zone = "synapse.li";
230 certCfg = { 230 certCfg = {
231 server = "https://acme.zerossl.com/v2/DV90";
232 extraLegoFlags = [
233 "--cert.timeout" "300"
234 ];
235 postRun = '' 231 postRun = ''
236 ${pkgs.systemd}/bin/systemctl try-restart coturn.service 232 ${pkgs.systemd}/bin/systemctl try-restart coturn.service
237 ''; 233 '';
diff --git a/modules/borgsnap/borgsnap/borgsnap/__main__.py b/modules/borgsnap/borgsnap/borgsnap/__main__.py
index 91144780..ad46a7bf 100644
--- a/modules/borgsnap/borgsnap/borgsnap/__main__.py
+++ b/modules/borgsnap/borgsnap/borgsnap/__main__.py
@@ -246,7 +246,9 @@ def create(*, snapshot, target, archive_prefix, dry_run):
246 env['BORG_FILES_CACHE_SUFFIX'] = basename 246 env['BORG_FILES_CACHE_SUFFIX'] = basename
247 archive_name = _archive_name(snapshot, target, archive_prefix) 247 archive_name = _archive_name(snapshot, target, archive_prefix)
248 target_host, _, target_path = target.rpartition(':') 248 target_host, _, target_path = target.rpartition(':')
249 *parents_init, _ = list(Path(target_path).parents) 249 parents_init = list()
250 if Path(target_path).parents:
251 *parents_init, _ = list(Path(target_path).parents)
250 backup_patterns = [*(map(lambda p: Path('.backup') / f'{target_host}:{p}', [Path(target_path), *parents_init])), Path('.backup') / target_host, Path('.backup')] 252 backup_patterns = [*(map(lambda p: Path('.backup') / f'{target_host}:{p}', [Path(target_path), *parents_init])), Path('.backup') / target_host, Path('.backup')]
251 for pattern_file in backup_patterns: 253 for pattern_file in backup_patterns:
252 if (dir / pattern_file).is_file(): 254 if (dir / pattern_file).is_file():
diff --git a/modules/coturn.nix b/modules/coturn.nix
new file mode 100644
index 00000000..faa4b5a2
--- /dev/null
+++ b/modules/coturn.nix
@@ -0,0 +1,369 @@
1{ config, lib, pkgs, ... }:
2with lib;
3let
4 cfg = config.services.coturn;
5 pidfile = "/run/turnserver/turnserver.pid";
6 configFile = pkgs.writeText "turnserver.conf" ''
7listening-port=${toString cfg.listening-port}
8tls-listening-port=${toString cfg.tls-listening-port}
9alt-listening-port=${toString cfg.alt-listening-port}
10alt-tls-listening-port=${toString cfg.alt-tls-listening-port}
11${concatStringsSep "\n" (map (x: "listening-ip=${x}") cfg.listening-ips)}
12${concatStringsSep "\n" (map (x: "relay-ip=${x}") cfg.relay-ips)}
13min-port=${toString cfg.min-port}
14max-port=${toString cfg.max-port}
15${lib.optionalString cfg.lt-cred-mech "lt-cred-mech"}
16${lib.optionalString cfg.no-auth "no-auth"}
17${lib.optionalString cfg.use-auth-secret "use-auth-secret"}
18${lib.optionalString (cfg.static-auth-secret != null) ("static-auth-secret=${cfg.static-auth-secret}")}
19${lib.optionalString (cfg.static-auth-secret-file != null) ("static-auth-secret=#static-auth-secret#")}
20realm=${cfg.realm}
21${lib.optionalString cfg.no-udp "no-udp"}
22${lib.optionalString cfg.no-tcp "no-tcp"}
23${lib.optionalString cfg.no-tls "no-tls"}
24${lib.optionalString cfg.no-dtls "no-dtls"}
25${lib.optionalString cfg.no-udp-relay "no-udp-relay"}
26${lib.optionalString cfg.no-tcp-relay "no-tcp-relay"}
27${lib.optionalString (cfg.cert != null) "cert=${cfg.cert}"}
28${lib.optionalString (cfg.pkey != null) "pkey=${cfg.pkey}"}
29${lib.optionalString (cfg.dh-file != null) ("dh-file=${cfg.dh-file}")}
30no-stdout-log
31syslog
32pidfile=${pidfile}
33${lib.optionalString cfg.secure-stun "secure-stun"}
34${lib.optionalString cfg.no-cli "no-cli"}
35cli-ip=${cfg.cli-ip}
36cli-port=${toString cfg.cli-port}
37${lib.optionalString (cfg.cli-password != null) ("cli-password=${cfg.cli-password}")}
38${cfg.extraConfig}
39'';
40in {
41 disabledModules = [ "services/networking/coturn.nix" ];
42
43 options = {
44 services.coturn = {
45 enable = mkEnableOption (lib.mdDoc "coturn TURN server");
46 listening-port = mkOption {
47 type = types.int;
48 default = 3478;
49 description = lib.mdDoc ''
50 TURN listener port for UDP and TCP.
51 Note: actually, TLS and DTLS sessions can connect to the
52 "plain" TCP and UDP port(s), too - if allowed by configuration.
53 '';
54 };
55 tls-listening-port = mkOption {
56 type = types.int;
57 default = 5349;
58 description = lib.mdDoc ''
59 TURN listener port for TLS.
60 Note: actually, "plain" TCP and UDP sessions can connect to the TLS and
61 DTLS port(s), too - if allowed by configuration. The TURN server
62 "automatically" recognizes the type of traffic. Actually, two listening
63 endpoints (the "plain" one and the "tls" one) are equivalent in terms of
64 functionality; but we keep both endpoints to satisfy the RFC 5766 specs.
65 For secure TCP connections, we currently support SSL version 3 and
66 TLS version 1.0, 1.1 and 1.2.
67 For secure UDP connections, we support DTLS version 1.
68 '';
69 };
70 alt-listening-port = mkOption {
71 type = types.int;
72 default = cfg.listening-port + 1;
73 defaultText = literalExpression "listening-port + 1";
74 description = lib.mdDoc ''
75 Alternative listening port for UDP and TCP listeners;
76 default (or zero) value means "listening port plus one".
77 This is needed for RFC 5780 support
78 (STUN extension specs, NAT behavior discovery). The TURN Server
79 supports RFC 5780 only if it is started with more than one
80 listening IP address of the same family (IPv4 or IPv6).
81 RFC 5780 is supported only by UDP protocol, other protocols
82 are listening to that endpoint only for "symmetry".
83 '';
84 };
85 alt-tls-listening-port = mkOption {
86 type = types.int;
87 default = cfg.tls-listening-port + 1;
88 defaultText = literalExpression "tls-listening-port + 1";
89 description = lib.mdDoc ''
90 Alternative listening port for TLS and DTLS protocols.
91 '';
92 };
93 listening-ips = mkOption {
94 type = types.listOf types.str;
95 default = [];
96 example = [ "203.0.113.42" "2001:DB8::42" ];
97 description = lib.mdDoc ''
98 Listener IP addresses of relay server.
99 If no IP(s) specified in the config file or in the command line options,
100 then all IPv4 and IPv6 system IPs will be used for listening.
101 '';
102 };
103 relay-ips = mkOption {
104 type = types.listOf types.str;
105 default = [];
106 example = [ "203.0.113.42" "2001:DB8::42" ];
107 description = lib.mdDoc ''
108 Relay address (the local IP address that will be used to relay the
109 packets to the peer).
110 Multiple relay addresses may be used.
111 The same IP(s) can be used as both listening IP(s) and relay IP(s).
112
113 If no relay IP(s) specified, then the turnserver will apply the default
114 policy: it will decide itself which relay addresses to be used, and it
115 will always be using the client socket IP address as the relay IP address
116 of the TURN session (if the requested relay address family is the same
117 as the family of the client socket).
118 '';
119 };
120 min-port = mkOption {
121 type = types.int;
122 default = 49152;
123 description = lib.mdDoc ''
124 Lower bound of UDP relay endpoints
125 '';
126 };
127 max-port = mkOption {
128 type = types.int;
129 default = 65535;
130 description = lib.mdDoc ''
131 Upper bound of UDP relay endpoints
132 '';
133 };
134 lt-cred-mech = mkOption {
135 type = types.bool;
136 default = false;
137 description = lib.mdDoc ''
138 Use long-term credential mechanism.
139 '';
140 };
141 no-auth = mkOption {
142 type = types.bool;
143 default = false;
144 description = lib.mdDoc ''
145 This option is opposite to lt-cred-mech.
146 (TURN Server with no-auth option allows anonymous access).
147 If neither option is defined, and no users are defined,
148 then no-auth is default. If at least one user is defined,
149 in this file or in command line or in usersdb file, then
150 lt-cred-mech is default.
151 '';
152 };
153 use-auth-secret = mkOption {
154 type = types.bool;
155 default = false;
156 description = lib.mdDoc ''
157 TURN REST API flag.
158 Flag that sets a special authorization option that is based upon authentication secret.
159 This feature can be used with the long-term authentication mechanism, only.
160 This feature purpose is to support "TURN Server REST API", see
161 "TURN REST API" link in the project's page
162 https://github.com/coturn/coturn/
163
164 This option is used with timestamp:
165
166 usercombo -> "timestamp:userid"
167 turn user -> usercombo
168 turn password -> base64(hmac(secret key, usercombo))
169
170 This allows TURN credentials to be accounted for a specific user id.
171 If you don't have a suitable id, the timestamp alone can be used.
172 This option is just turning on secret-based authentication.
173 The actual value of the secret is defined either by option static-auth-secret,
174 or can be found in the turn_secret table in the database.
175 '';
176 };
177 static-auth-secret = mkOption {
178 type = types.nullOr types.str;
179 default = null;
180 description = lib.mdDoc ''
181 'Static' authentication secret value (a string) for TURN REST API only.
182 If not set, then the turn server
183 will try to use the 'dynamic' value in turn_secret table
184 in user database (if present). The database-stored value can be changed on-the-fly
185 by a separate program, so this is why that other mode is 'dynamic'.
186 '';
187 };
188 static-auth-secret-file = mkOption {
189 type = types.nullOr types.str;
190 default = null;
191 description = lib.mdDoc ''
192 Path to the file containing the static authentication secret.
193 '';
194 };
195 realm = mkOption {
196 type = types.str;
197 default = config.networking.hostName;
198 defaultText = literalExpression "config.networking.hostName";
199 example = "example.com";
200 description = lib.mdDoc ''
201 The default realm to be used for the users when no explicit
202 origin/realm relationship was found in the database, or if the TURN
203 server is not using any database (just the commands-line settings
204 and the userdb file). Must be used with long-term credentials
205 mechanism or with TURN REST API.
206 '';
207 };
208 cert = mkOption {
209 type = types.nullOr types.str;
210 default = null;
211 example = "/var/lib/acme/example.com/fullchain.pem";
212 description = lib.mdDoc ''
213 Certificate file in PEM format.
214 '';
215 };
216 pkey = mkOption {
217 type = types.nullOr types.str;
218 default = null;
219 example = "/var/lib/acme/example.com/key.pem";
220 description = lib.mdDoc ''
221 Private key file in PEM format.
222 '';
223 };
224 dh-file = mkOption {
225 type = types.nullOr types.str;
226 default = null;
227 description = lib.mdDoc ''
228 Use custom DH TLS key, stored in PEM format in the file.
229 '';
230 };
231 secure-stun = mkOption {
232 type = types.bool;
233 default = false;
234 description = lib.mdDoc ''
235 Require authentication of the STUN Binding request.
236 By default, the clients are allowed anonymous access to the STUN Binding functionality.
237 '';
238 };
239 no-cli = mkOption {
240 type = types.bool;
241 default = false;
242 description = lib.mdDoc ''
243 Turn OFF the CLI support.
244 '';
245 };
246 cli-ip = mkOption {
247 type = types.str;
248 default = "127.0.0.1";
249 description = lib.mdDoc ''
250 Local system IP address to be used for CLI server endpoint.
251 '';
252 };
253 cli-port = mkOption {
254 type = types.int;
255 default = 5766;
256 description = lib.mdDoc ''
257 CLI server port.
258 '';
259 };
260 cli-password = mkOption {
261 type = types.nullOr types.str;
262 default = null;
263 description = lib.mdDoc ''
264 CLI access password.
265 For the security reasons, it is recommended to use the encrypted
266 for of the password (see the -P command in the turnadmin utility).
267 '';
268 };
269 no-udp = mkOption {
270 type = types.bool;
271 default = false;
272 description = lib.mdDoc "Disable UDP client listener";
273 };
274 no-tcp = mkOption {
275 type = types.bool;
276 default = false;
277 description = lib.mdDoc "Disable TCP client listener";
278 };
279 no-tls = mkOption {
280 type = types.bool;
281 default = false;
282 description = lib.mdDoc "Disable TLS client listener";
283 };
284 no-dtls = mkOption {
285 type = types.bool;
286 default = false;
287 description = lib.mdDoc "Disable DTLS client listener";
288 };
289 no-udp-relay = mkOption {
290 type = types.bool;
291 default = false;
292 description = lib.mdDoc "Disable UDP relay endpoints";
293 };
294 no-tcp-relay = mkOption {
295 type = types.bool;
296 default = false;
297 description = lib.mdDoc "Disable TCP relay endpoints";
298 };
299 extraConfig = mkOption {
300 type = types.lines;
301 default = "";
302 description = lib.mdDoc "Additional configuration options";
303 };
304 };
305 };
306
307 config = mkIf cfg.enable (mkMerge ([
308 { assertions = [
309 { assertion = cfg.static-auth-secret != null -> cfg.static-auth-secret-file == null ;
310 message = "static-auth-secret and static-auth-secret-file cannot be set at the same time";
311 }
312 ];}
313
314 {
315 users.users.turnserver =
316 { uid = config.ids.uids.turnserver;
317 group = "turnserver";
318 description = "coturn TURN server user";
319 };
320 users.groups.turnserver =
321 { gid = config.ids.gids.turnserver;
322 members = [ "turnserver" ];
323 };
324
325 systemd.services.coturn = let
326 runConfig = "/run/coturn/turnserver.cfg";
327 in {
328 description = "coturn TURN server";
329 after = [ "network-online.target" ];
330 wants = [ "network-online.target" ];
331 wantedBy = [ "multi-user.target" ];
332
333 unitConfig = {
334 Documentation = "man:coturn(1) man:turnadmin(1) man:turnserver(1)";
335 };
336
337 script = ''
338 cat ${configFile} > ${runConfig}
339 ${optionalString (cfg.static-auth-secret-file != null) ''
340 ${pkgs.replace-secret}/bin/replace-secret \
341 "#static-auth-secret#" \
342 ${cfg.static-auth-secret-file} \
343 ${runConfig}
344 '' }
345 chmod 640 ${runConfig}
346
347 exec ${pkgs.coturn}/bin/turnserver -c ${runConfig}
348 '';
349 serviceConfig = {
350 Type = "simple";
351 RuntimeDirectory = "turnserver";
352 User = "turnserver";
353 Group = "turnserver";
354 AmbientCapabilities =
355 mkIf (
356 cfg.listening-port < 1024 ||
357 cfg.alt-listening-port < 1024 ||
358 cfg.tls-listening-port < 1024 ||
359 cfg.alt-tls-listening-port < 1024 ||
360 cfg.min-port < 1024
361 ) "cap_net_bind_service";
362 Restart = "on-abort";
363 };
364 };
365 systemd.tmpfiles.rules = [
366 "d /run/coturn 0700 turnserver turnserver - -"
367 ];
368 }]));
369}
diff --git a/modules/zfssnap/default.nix b/modules/zfssnap/default.nix
index f6f32852..23041c36 100644
--- a/modules/zfssnap/default.nix
+++ b/modules/zfssnap/default.nix
@@ -27,19 +27,27 @@ in {
27 enable = mkEnableOption "zfssnap service"; 27 enable = mkEnableOption "zfssnap service";
28 28
29 config = mkOption { 29 config = mkOption {
30 type = with types; attrsOf (attrsOf str); 30 type = types.submodule {
31 default = { 31 options = {
32 keep = { 32 keep = mkOption {
33 within = "15m"; 33 type = with types; attrsOf str;
34 "5m" = "48"; 34 default = {
35 "15m" = "32"; 35 within = "15m";
36 hourly = "48"; 36 "5m" = "48";
37 "4h" = "24"; 37 "15m" = "32";
38 "12h" = "12"; 38 hourly = "48";
39 daily = "62"; 39 "4h" = "24";
40 halfweekly = "32"; 40 "12h" = "12";
41 weekly = "24"; 41 daily = "62";
42 monthly = "-1"; 42 halfweekly = "32";
43 weekly = "24";
44 monthly = "-1";
45 };
46 };
47 exec = mkOption {
48 type = with types; attrsOf str;
49 default = {};
50 };
43 }; 51 };
44 }; 52 };
45 }; 53 };