1{
2 config,
3 pkgs,
4 lib,
5 ...
6}:
7let
8 cfg = config.services.pufferpanel;
9in
10{
11 options.services.pufferpanel = {
12 enable = lib.mkOption {
13 type = lib.types.bool;
14 default = false;
15 description = ''
16 Whether to enable PufferPanel game management server.
17
18 Note that [PufferPanel templates] and binaries downloaded by PufferPanel
19 expect [FHS environment]. It is possible to set {option}`package` option
20 to use PufferPanel wrapper with FHS environment. For example, to use
21 `Download Game from Steam` and `Download Java` template operations:
22 ```Nix
23 { lib, pkgs, ... }: {
24 services.pufferpanel = {
25 enable = true;
26 extraPackages = with pkgs; [ bash curl gawk gnutar gzip ];
27 package = pkgs.buildFHSEnv {
28 name = "pufferpanel-fhs";
29 runScript = lib.getExe pkgs.pufferpanel;
30 targetPkgs = pkgs': with pkgs'; [ icu openssl zlib ];
31 };
32 };
33 }
34 ```
35
36 [PufferPanel templates]: https://github.com/PufferPanel/templates
37 [FHS environment]: https://wikipedia.org/wiki/Filesystem_Hierarchy_Standard
38 '';
39 };
40
41 package = lib.mkPackageOption pkgs "pufferpanel" { };
42
43 extraGroups = lib.mkOption {
44 type = lib.types.listOf lib.types.str;
45 default = [ ];
46 example = [ "podman" ];
47 description = ''
48 Additional groups for the systemd service.
49 '';
50 };
51
52 extraPackages = lib.mkOption {
53 type = lib.types.listOf lib.types.package;
54 default = [ ];
55 example = lib.literalExpression "[ pkgs.jre ]";
56 description = ''
57 Packages to add to the PATH environment variable. Both the {file}`bin`
58 and {file}`sbin` subdirectories of each package are added.
59 '';
60 };
61
62 environment = lib.mkOption {
63 type = lib.types.attrsOf lib.types.str;
64 default = { };
65 example = lib.literalExpression ''
66 {
67 PUFFER_WEB_HOST = ":8080";
68 PUFFER_DAEMON_SFTP_HOST = ":5657";
69 PUFFER_DAEMON_CONSOLE_BUFFER = "1000";
70 PUFFER_DAEMON_CONSOLE_FORWARD = "true";
71 PUFFER_PANEL_REGISTRATIONENABLED = "false";
72 }
73 '';
74 description = ''
75 Environment variables to set for the service. Secrets should be
76 specified using {option}`environmentFile`.
77
78 Refer to the [PufferPanel source code][] for the list of available
79 configuration options. Variable name is an upper-cased configuration
80 entry name with underscores instead of dots, prefixed with `PUFFER_`.
81 For example, `panel.settings.companyName` entry can be set using
82 {env}`PUFFER_PANEL_SETTINGS_COMPANYNAME`.
83
84 When running with panel enabled (configured with `PUFFER_PANEL_ENABLE`
85 environment variable), it is recommended disable registration using
86 `PUFFER_PANEL_REGISTRATIONENABLED` environment variable (registration is
87 enabled by default). To create the initial administrator user, run
88 {command}`pufferpanel --workDir /var/lib/pufferpanel user add --admin`.
89
90 Some options override corresponding settings set via web interface (e.g.
91 `PUFFER_PANEL_REGISTRATIONENABLED`). Those options can be temporarily
92 toggled or set in settings but do not persist between restarts.
93
94 [PufferPanel source code]: https://github.com/PufferPanel/PufferPanel/blob/master/config/entries.go
95 '';
96 };
97
98 environmentFile = lib.mkOption {
99 type = lib.types.nullOr lib.types.path;
100 default = null;
101 description = ''
102 File to load environment variables from. Loaded variables override
103 values set in {option}`environment`.
104 '';
105 };
106 };
107
108 config = lib.mkIf cfg.enable {
109 systemd.services.pufferpanel = {
110 description = "PufferPanel game management server";
111 wantedBy = [ "multi-user.target" ];
112 after = [ "network.target" ];
113
114 path = cfg.extraPackages;
115 environment = cfg.environment;
116
117 # Note that we export environment variables for service directories if the
118 # value is not set. An empty environment variable is considered to be set.
119 # E.g.
120 # export PUFFER_LOGS=${PUFFER_LOGS-$LOGS_DIRECTORY}
121 # would set PUFFER_LOGS to $LOGS_DIRECTORY if PUFFER_LOGS environment
122 # variable is not defined.
123 script = ''
124 ${lib.concatLines (
125 lib.mapAttrsToList
126 (name: value: ''
127 export ${name}="''${${name}-${value}}"
128 '')
129 {
130 PUFFER_LOGS = "$LOGS_DIRECTORY";
131 PUFFER_DAEMON_DATA_CACHE = "$CACHE_DIRECTORY";
132 PUFFER_DAEMON_DATA_SERVERS = "$STATE_DIRECTORY/servers";
133 PUFFER_DAEMON_DATA_BINARIES = "$STATE_DIRECTORY/binaries";
134 }
135 )}
136 exec ${lib.getExe cfg.package} run --workDir "$STATE_DIRECTORY"
137 '';
138
139 serviceConfig = {
140 Type = "simple";
141 Restart = "always";
142
143 UMask = "0077";
144
145 SupplementaryGroups = cfg.extraGroups;
146
147 StateDirectory = "pufferpanel";
148 StateDirectoryMode = "0700";
149 CacheDirectory = "pufferpanel";
150 CacheDirectoryMode = "0700";
151 LogsDirectory = "pufferpanel";
152 LogsDirectoryMode = "0700";
153
154 EnvironmentFile = cfg.environmentFile;
155
156 # Command "pufferpanel shutdown --pid $MAINPID" sends SIGTERM (code 15)
157 # to the main process and waits for termination. This is essentially
158 # KillMode=mixed we are using here. See
159 # https://freedesktop.org/software/systemd/man/systemd.kill.html#KillMode=
160 KillMode = "mixed";
161
162 DynamicUser = true;
163 ProtectHome = true;
164 ProtectProc = "invisible";
165 ProtectClock = true;
166 ProtectHostname = true;
167 ProtectControlGroups = true;
168 ProtectKernelLogs = true;
169 ProtectKernelModules = true;
170 ProtectKernelTunables = true;
171 PrivateUsers = true;
172 PrivateDevices = true;
173 RestrictRealtime = true;
174 RestrictNamespaces = [
175 "user"
176 "mnt"
177 ]; # allow buildFHSEnv
178 RestrictAddressFamilies = [
179 "AF_INET"
180 "AF_INET6"
181 "AF_UNIX"
182 ];
183 LockPersonality = true;
184 DeviceAllow = [ "" ];
185 DevicePolicy = "closed";
186 CapabilityBoundingSet = [ "" ];
187 };
188 };
189 };
190
191 meta.maintainers = [ lib.maintainers.tie ];
192}