Self-host your own digital island
1{ config, pkgs, lib, ... }:
2
3with lib;
4let
5 cfg = config.eilean;
6 domain = config.networking.domain;
7 subdomain = "turn.${domain}";
8 staticAuthSecretFile = "/run/coturn/static-auth-secret";
9in {
10 options.eilean.turn = { enable = mkEnableOption "TURN server"; };
11
12 config = mkIf cfg.turn.enable {
13 security.acme-eon.certs."${subdomain}" = lib.mkIf cfg.acme-eon {
14 group = "turnserver";
15 reloadServices = [ "coturn" ];
16 };
17
18 services.coturn = let
19 certDir = if cfg.acme-eon then
20 config.security.acme-eon.certs.${subdomain}.directory
21 else
22 config.security.acme.certs.${subdomain}.directory;
23 in {
24 enable = true;
25 no-cli = true;
26 no-tcp-relay = true;
27 secure-stun = true;
28 use-auth-secret = true;
29 static-auth-secret-file = staticAuthSecretFile;
30 realm = subdomain;
31 relay-ips = with config.eilean; [ serverIpv4 serverIpv6 ];
32 cert = "${certDir}/fullchain.pem";
33 pkey = "${certDir}/key.pem";
34 };
35
36 systemd.services = {
37 coturn-static-auth-secret-generator = {
38 description = "Generate coturn static auth secret file";
39 script = ''
40 if [ ! -f '${staticAuthSecretFile}' ]; then
41 umask 077
42 DIR="$(dirname '${staticAuthSecretFile}')"
43 mkdir -p "$DIR"
44 tr -dc A-Za-z0-9 </dev/urandom | head -c 32 > '${staticAuthSecretFile}'
45 chown -R ${config.systemd.services.coturn.serviceConfig.User}:${config.systemd.services.coturn.serviceConfig.Group} "$DIR"
46 fi
47 '';
48 serviceConfig.Type = "oneshot";
49 serviceConfig.RemainAfterExit = true;
50 };
51 "coturn" = {
52 after = [ "coturn-static-auth-secret-generator.service" ]
53 ++ lib.lists.optional cfg.acme-eon "acme-eon-${subdomain}.service";
54 requires = [ "coturn-static-auth-secret-generator.service" ];
55 wants = lib.lists.optional cfg.acme-eon "acme-eon-${subdomain}.service";
56 };
57 };
58
59 networking.firewall = with config.services.coturn;
60 let
61 turn-range = {
62 from = min-port;
63 to = max-port;
64 };
65 stun-ports = [
66 listening-port
67 tls-listening-port
68 # these are only used if server has more than one IP address (of the same family
69 #alt-listening-port
70 #alt-tls-listening-port
71 ];
72 in {
73 allowedTCPPorts = stun-ports;
74 allowedTCPPortRanges = [ turn-range ];
75 allowedUDPPorts = stun-ports;
76 allowedUDPPortRanges = [ turn-range ];
77 };
78
79 security.acme.certs.${config.services.coturn.realm} =
80 lib.mkIf (!cfg.acme-eon) {
81 postRun =
82 "systemctl reload nginx.service; systemctl restart coturn.service";
83 group = "turnserver";
84 };
85 services.nginx.enable = lib.mkIf (!cfg.acme-eon) true;
86 services.nginx.virtualHosts = lib.mkIf (!cfg.acme-eon) {
87 "${config.services.coturn.realm}" = {
88 forceSSL = true;
89 enableACME = true;
90 };
91 };
92 users.groups."turnserver".members =
93 lib.mkIf (!cfg.acme-eon) [ config.services.nginx.user ];
94
95 eilean.dns.enable = true;
96 eilean.services.dns.zones.${config.networking.domain}.records = [{
97 name = "turn";
98 type = "CNAME";
99 value = cfg.domainName;
100 }];
101 };
102}