at master 11 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 cfg = config.services.mchprs; 9 settingsFormat = pkgs.formats.toml { }; 10 11 whitelistFile = pkgs.writeText "whitelist.json" ( 12 builtins.toJSON ( 13 lib.mapAttrsToList (n: v: { 14 name = n; 15 uuid = v; 16 }) cfg.whitelist.list 17 ) 18 ); 19 20 configToml = 21 (removeAttrs cfg.settings [ 22 "address" 23 "port" 24 ]) 25 // { 26 bind_address = cfg.settings.address + ":" + toString cfg.settings.port; 27 whitelist = cfg.whitelist.enable; 28 }; 29 30 configTomlFile = settingsFormat.generate "Config.toml" configToml; 31in 32{ 33 options = { 34 services.mchprs = { 35 enable = lib.mkEnableOption "MCHPRS, a Minecraft server"; 36 37 declarativeSettings = lib.mkOption { 38 type = lib.types.bool; 39 default = false; 40 description = '' 41 Whether to use a declarative configuration for MCHPRS. 42 ''; 43 }; 44 45 declarativeWhitelist = lib.mkOption { 46 type = lib.types.bool; 47 default = false; 48 description = '' 49 Whether to use a declarative whitelist. 50 The options {option}`services.mchprs.whitelist.list` 51 will be applied if and only if set to `true`. 52 ''; 53 }; 54 55 dataDir = lib.mkOption { 56 type = lib.types.path; 57 default = "/var/lib/mchprs"; 58 description = '' 59 Directory to store MCHPRS database and other state/data files. 60 ''; 61 }; 62 63 openFirewall = lib.mkOption { 64 type = lib.types.bool; 65 default = false; 66 description = '' 67 Whether to open ports in the firewall for the server. 68 Only has effect when 69 {option}`services.mchprs.declarativeSettings` is `true`. 70 ''; 71 }; 72 73 maxRuntime = lib.mkOption { 74 type = lib.types.str; 75 default = "infinity"; 76 example = "7d"; 77 description = '' 78 Automatically restart the server after 79 {option}`services.mchprs.maxRuntime`. 80 The {manpage}`systemd.time(7)` time span format is described here: 81 <https://www.freedesktop.org/software/systemd/man/systemd.time.html#Parsing%20Time%20Spans>. 82 If `null`, then the server is not restarted automatically. 83 ''; 84 }; 85 86 package = lib.mkPackageOption pkgs "mchprs" { }; 87 88 settings = lib.mkOption { 89 type = lib.types.submodule { 90 freeformType = settingsFormat.type; 91 92 options = { 93 port = lib.mkOption { 94 type = lib.types.port; 95 default = 25565; 96 description = '' 97 Port for the server. 98 Only has effect when 99 {option}`services.mchprs.declarativeSettings` is `true`. 100 ''; 101 }; 102 103 address = lib.mkOption { 104 type = lib.types.str; 105 default = "0.0.0.0"; 106 description = '' 107 Address for the server. 108 Please use enclosing square brackets when using ipv6. 109 Only has effect when 110 {option}`services.mchprs.declarativeSettings` is `true`. 111 ''; 112 }; 113 114 motd = lib.mkOption { 115 type = lib.types.str; 116 default = "Minecraft High Performance Redstone Server"; 117 description = '' 118 Message of the day. 119 Only has effect when 120 {option}`services.mchprs.declarativeSettings` is `true`. 121 ''; 122 }; 123 124 chat_format = lib.mkOption { 125 type = lib.types.str; 126 default = "<{username}> {message}"; 127 description = '' 128 How to format chat message interpolating `username` 129 and `message` with curly braces. 130 Only has effect when 131 {option}`services.mchprs.declarativeSettings` is `true`. 132 ''; 133 }; 134 135 max_players = lib.mkOption { 136 type = lib.types.ints.positive; 137 default = 99999; 138 description = '' 139 Maximum number of simultaneous players. 140 Only has effect when 141 {option}`services.mchprs.declarativeSettings` is `true`. 142 ''; 143 }; 144 145 view_distance = lib.mkOption { 146 type = lib.types.ints.positive; 147 default = 8; 148 description = '' 149 Maximal distance (in chunks) between players and loaded chunks. 150 Only has effect when 151 {option}`services.mchprs.declarativeSettings` is `true`. 152 ''; 153 }; 154 155 bungeecord = lib.mkOption { 156 type = lib.types.bool; 157 default = false; 158 description = '' 159 Enable compatibility with 160 [BungeeCord](https://github.com/SpigotMC/BungeeCord). 161 Only has effect when 162 {option}`services.mchprs.declarativeSettings` is `true`. 163 ''; 164 }; 165 166 schemati = lib.mkOption { 167 type = lib.types.bool; 168 default = false; 169 description = '' 170 Mimic the verification and directory layout used by the 171 Open Redstone Engineers 172 [Schemati plugin](https://github.com/OpenRedstoneEngineers/Schemati). 173 Only has effect when 174 {option}`services.mchprs.declarativeSettings` is `true`. 175 ''; 176 }; 177 178 block_in_hitbox = lib.mkOption { 179 type = lib.types.bool; 180 default = true; 181 description = '' 182 Allow placing blocks inside of players 183 (hitbox logic is simplified). 184 Only has effect when 185 {option}`services.mchprs.declarativeSettings` is `true`. 186 ''; 187 }; 188 189 auto_redpiler = lib.mkOption { 190 type = lib.types.bool; 191 default = true; 192 description = '' 193 Use redpiler automatically. 194 Only has effect when 195 {option}`services.mchprs.declarativeSettings` is `true`. 196 ''; 197 }; 198 }; 199 }; 200 default = { }; 201 202 description = '' 203 Configuration for MCHPRS via `Config.toml`. 204 See <https://github.com/MCHPR/MCHPRS/blob/master/README.md> for documentation. 205 ''; 206 }; 207 208 whitelist = { 209 enable = lib.mkOption { 210 type = lib.types.bool; 211 default = false; 212 description = '' 213 Whether or not the whitelist (in `whitelist.json`) shoud be enabled. 214 Only has effect when {option}`services.mchprs.declarativeSettings` is `true`. 215 ''; 216 }; 217 218 list = lib.mkOption { 219 type = 220 let 221 minecraftUUID = 222 lib.types.strMatching "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" 223 // { 224 description = "Minecraft UUID"; 225 }; 226 in 227 lib.types.attrsOf minecraftUUID; 228 default = { }; 229 example = lib.literalExpression '' 230 { 231 username1 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; 232 username2 = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"; 233 }; 234 ''; 235 description = '' 236 Whitelisted players, only has an effect when 237 {option}`services.mchprs.declarativeWhitelist` is 238 `true` and the whitelist is enabled 239 via {option}`services.mchprs.whitelist.enable`. 240 This is a mapping from Minecraft usernames to UUIDs. 241 You can use <https://mcuuid.net/> to get a 242 Minecraft UUID for a username. 243 ''; 244 }; 245 }; 246 }; 247 }; 248 249 config = lib.mkIf cfg.enable { 250 users.users.mchprs = { 251 description = "MCHPRS service user"; 252 home = cfg.dataDir; 253 createHome = true; 254 isSystemUser = true; 255 group = "mchprs"; 256 }; 257 users.groups.mchprs = { }; 258 259 systemd.services.mchprs = { 260 description = "MCHPRS Service"; 261 wantedBy = [ "multi-user.target" ]; 262 after = [ "network.target" ]; 263 264 serviceConfig = { 265 ExecStart = "${lib.getExe cfg.package}"; 266 Restart = "always"; 267 RuntimeMaxSec = cfg.maxRuntime; 268 User = "mchprs"; 269 WorkingDirectory = cfg.dataDir; 270 271 StandardOutput = "journal"; 272 StandardError = "journal"; 273 274 # Hardening 275 CapabilityBoundingSet = [ "" ]; 276 DeviceAllow = [ "" ]; 277 LockPersonality = true; 278 MemoryDenyWriteExecute = true; 279 PrivateDevices = true; 280 PrivateTmp = true; 281 PrivateUsers = true; 282 ProtectClock = true; 283 ProtectControlGroups = true; 284 ProtectHome = true; 285 ProtectHostname = true; 286 ProtectKernelLogs = true; 287 ProtectKernelModules = true; 288 ProtectKernelTunables = true; 289 ProtectProc = "invisible"; 290 RestrictAddressFamilies = [ 291 "AF_INET" 292 "AF_INET6" 293 ]; 294 RestrictNamespaces = true; 295 RestrictRealtime = true; 296 RestrictSUIDSGID = true; 297 SystemCallArchitectures = "native"; 298 UMask = "0077"; 299 }; 300 301 preStart = 302 ( 303 if cfg.declarativeSettings then 304 '' 305 if [ -e .declarativeSettings ]; then 306 307 # Settings were declarative before, no need to back up anything 308 cp -f ${configTomlFile} Config.toml 309 310 else 311 312 # Declarative settings for the first time, backup stateful files 313 cp -b --suffix=.stateful ${configTomlFile} Config.toml 314 315 echo "Autogenerated file that implies that this server configuration is managed declaratively by NixOS" \ 316 > .declarativeSettings 317 318 fi 319 '' 320 else 321 '' 322 if [ -e .declarativeSettings ]; then 323 rm .declarativeSettings 324 fi 325 '' 326 ) 327 + ( 328 if cfg.declarativeWhitelist then 329 '' 330 if [ -e .declarativeWhitelist ]; then 331 332 # Whitelist was declarative before, no need to back up anything 333 ln -sf ${whitelistFile} whitelist.json 334 335 else 336 337 # Declarative whitelist for the first time, backup stateful files 338 ln -sb --suffix=.stateful ${whitelistFile} whitelist.json 339 340 echo "Autogenerated file that implies that this server's whitelist is managed declaratively by NixOS" \ 341 > .declarativeWhitelist 342 343 fi 344 '' 345 else 346 '' 347 if [ -e .declarativeWhitelist ]; then 348 rm .declarativeWhitelist 349 fi 350 '' 351 ); 352 }; 353 354 networking.firewall = lib.mkIf (cfg.declarativeSettings && cfg.openFirewall) { 355 allowedUDPPorts = [ cfg.settings.port ]; 356 allowedTCPPorts = [ cfg.settings.port ]; 357 }; 358 }; 359 360 meta.maintainers = with lib.maintainers; [ gdd ]; 361}