1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 ts3 = pkgs.teamspeak_server;
7 cfg = config.services.teamspeak3;
8 user = "teamspeak";
9 group = "teamspeak";
10in
11
12{
13
14 ###### interface
15
16 options = {
17
18 services.teamspeak3 = {
19 enable = mkOption {
20 type = types.bool;
21 default = false;
22 description = ''
23 Whether to run the Teamspeak3 voice communication server daemon.
24 '';
25 };
26
27 dataDir = mkOption {
28 type = types.path;
29 default = "/var/lib/teamspeak3-server";
30 description = ''
31 Directory to store TS3 database and other state/data files.
32 '';
33 };
34
35 logPath = mkOption {
36 type = types.path;
37 default = "/var/log/teamspeak3-server/";
38 description = ''
39 Directory to store log files in.
40 '';
41 };
42
43 voiceIP = mkOption {
44 type = types.nullOr types.str;
45 default = null;
46 example = "[::]";
47 description = ''
48 IP on which the server instance will listen for incoming voice connections. Defaults to any IP.
49 '';
50 };
51
52 defaultVoicePort = mkOption {
53 type = types.port;
54 default = 9987;
55 description = ''
56 Default UDP port for clients to connect to virtual servers - used for first virtual server, subsequent ones will open on incrementing port numbers by default.
57 '';
58 };
59
60 fileTransferIP = mkOption {
61 type = types.nullOr types.str;
62 default = null;
63 example = "[::]";
64 description = ''
65 IP on which the server instance will listen for incoming file transfer connections. Defaults to any IP.
66 '';
67 };
68
69 fileTransferPort = mkOption {
70 type = types.port;
71 default = 30033;
72 description = ''
73 TCP port opened for file transfers.
74 '';
75 };
76
77 queryIP = mkOption {
78 type = types.nullOr types.str;
79 default = null;
80 example = "0.0.0.0";
81 description = ''
82 IP on which the server instance will listen for incoming ServerQuery connections. Defaults to any IP.
83 '';
84 };
85
86 queryPort = mkOption {
87 type = types.port;
88 default = 10011;
89 description = ''
90 TCP port opened for ServerQuery connections using the raw telnet protocol.
91 '';
92 };
93
94 querySshPort = mkOption {
95 type = types.port;
96 default = 10022;
97 description = ''
98 TCP port opened for ServerQuery connections using the SSH protocol.
99 '';
100 };
101
102 queryHttpPort = mkOption {
103 type = types.port;
104 default = 10080;
105 description = ''
106 TCP port opened for ServerQuery connections using the HTTP protocol.
107 '';
108 };
109
110 openFirewall = mkOption {
111 type = types.bool;
112 default = false;
113 description = "Open ports in the firewall for the TeamSpeak3 server.";
114 };
115
116 openFirewallServerQuery = mkOption {
117 type = types.bool;
118 default = false;
119 description = "Open ports in the firewall for the TeamSpeak3 serverquery (administration) system. Requires openFirewall.";
120 };
121
122 };
123
124 };
125
126
127 ###### implementation
128
129 config = mkIf cfg.enable {
130 users.users.teamspeak = {
131 description = "Teamspeak3 voice communication server daemon";
132 group = group;
133 uid = config.ids.uids.teamspeak;
134 home = cfg.dataDir;
135 createHome = true;
136 };
137
138 users.groups.teamspeak = {
139 gid = config.ids.gids.teamspeak;
140 };
141
142 systemd.tmpfiles.rules = [
143 "d '${cfg.logPath}' - ${user} ${group} - -"
144 ];
145
146 networking.firewall = mkIf cfg.openFirewall {
147 allowedTCPPorts = [ cfg.fileTransferPort ] ++ (map (port:
148 mkIf cfg.openFirewallServerQuery port
149 ) [cfg.queryPort cfg.querySshPort cfg.queryHttpPort]);
150 # subsequent vServers will use the incremented voice port, let's just open the next 10
151 allowedUDPPortRanges = [ { from = cfg.defaultVoicePort; to = cfg.defaultVoicePort + 10; } ];
152 };
153
154 systemd.services.teamspeak3-server = {
155 description = "Teamspeak3 voice communication server daemon";
156 after = [ "network.target" ];
157 wantedBy = [ "multi-user.target" ];
158
159 serviceConfig = {
160 ExecStart = ''
161 ${ts3}/bin/ts3server \
162 dbsqlpath=${ts3}/lib/teamspeak/sql/ \
163 logpath=${cfg.logPath} \
164 license_accepted=1 \
165 default_voice_port=${toString cfg.defaultVoicePort} \
166 filetransfer_port=${toString cfg.fileTransferPort} \
167 query_port=${toString cfg.queryPort} \
168 query_ssh_port=${toString cfg.querySshPort} \
169 query_http_port=${toString cfg.queryHttpPort} \
170 ${optionalString (cfg.voiceIP != null) "voice_ip=${cfg.voiceIP}"} \
171 ${optionalString (cfg.fileTransferIP != null) "filetransfer_ip=${cfg.fileTransferIP}"} \
172 ${optionalString (cfg.queryIP != null) "query_ip=${cfg.queryIP}"} \
173 ${optionalString (cfg.queryIP != null) "query_ssh_ip=${cfg.queryIP}"} \
174 ${optionalString (cfg.queryIP != null) "query_http_ip=${cfg.queryIP}"} \
175 '';
176 WorkingDirectory = cfg.dataDir;
177 User = user;
178 Group = group;
179 Restart = "on-failure";
180 };
181 };
182 };
183
184 meta.maintainers = with lib.maintainers; [ arobyn ];
185}