1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.teeworlds;
7 register = cfg.register;
8
9 teeworldsConf = pkgs.writeText "teeworlds.cfg" ''
10 sv_port ${toString cfg.port}
11 sv_register ${if cfg.register then "1" else "0"}
12 ${optionalString (cfg.name != null) "sv_name ${cfg.name}"}
13 ${optionalString (cfg.motd != null) "sv_motd ${cfg.motd}"}
14 ${optionalString (cfg.password != null) "password ${cfg.password}"}
15 ${optionalString (cfg.rconPassword != null) "sv_rcon_password ${cfg.rconPassword}"}
16 ${concatStringsSep "\n" cfg.extraOptions}
17 '';
18
19in
20{
21 options = {
22 services.teeworlds = {
23 enable = mkEnableOption (lib.mdDoc "Teeworlds Server");
24
25 openPorts = mkOption {
26 type = types.bool;
27 default = false;
28 description = lib.mdDoc "Whether to open firewall ports for Teeworlds";
29 };
30
31 name = mkOption {
32 type = types.nullOr types.str;
33 default = null;
34 description = lib.mdDoc ''
35 Name of the server. Defaults to 'unnamed server'.
36 '';
37 };
38
39 register = mkOption {
40 type = types.bool;
41 example = true;
42 default = false;
43 description = lib.mdDoc ''
44 Whether the server registers as public server in the global server list. This is disabled by default because of privacy.
45 '';
46 };
47
48 motd = mkOption {
49 type = types.nullOr types.str;
50 default = null;
51 description = lib.mdDoc ''
52 Set the server message of the day text.
53 '';
54 };
55
56 password = mkOption {
57 type = types.nullOr types.str;
58 default = null;
59 description = lib.mdDoc ''
60 Password to connect to the server.
61 '';
62 };
63
64 rconPassword = mkOption {
65 type = types.nullOr types.str;
66 default = null;
67 description = lib.mdDoc ''
68 Password to access the remote console. If not set, a randomly generated one is displayed in the server log.
69 '';
70 };
71
72 port = mkOption {
73 type = types.port;
74 default = 8303;
75 description = lib.mdDoc ''
76 Port the server will listen on.
77 '';
78 };
79
80 extraOptions = mkOption {
81 type = types.listOf types.str;
82 default = [];
83 description = lib.mdDoc ''
84 Extra configuration lines for the {file}`teeworlds.cfg`. See [Teeworlds Documentation](https://www.teeworlds.com/?page=docs&wiki=server_settings).
85 '';
86 example = [ "sv_map dm1" "sv_gametype dm" ];
87 };
88 };
89 };
90
91 config = mkIf cfg.enable {
92 networking.firewall = mkIf cfg.openPorts {
93 allowedUDPPorts = [ cfg.port ];
94 };
95
96 systemd.services.teeworlds = {
97 description = "Teeworlds Server";
98 wantedBy = [ "multi-user.target" ];
99 after = [ "network.target" ];
100
101 serviceConfig = {
102 DynamicUser = true;
103 ExecStart = "${pkgs.teeworlds}/bin/teeworlds_srv -f ${teeworldsConf}";
104
105 # Hardening
106 CapabilityBoundingSet = false;
107 PrivateDevices = true;
108 PrivateUsers = true;
109 ProtectHome = true;
110 ProtectKernelLogs = true;
111 ProtectKernelModules = true;
112 ProtectKernelTunables = true;
113 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
114 RestrictNamespaces = true;
115 SystemCallArchitectures = "native";
116 };
117 };
118 };
119}