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