From 04c830db01dc5ca25290716ae4e6049bb8074e9b Mon Sep 17 00:00:00 2001 From: gabriel-doriath-dohler Date: Sun, 2 Jul 2023 23:59:59 +0000 Subject: [PATCH] nixos/mchprs: init module Co-authored-by: Ryan Lahfa --- .../manual/release-notes/rl-2311.section.md | 2 +- nixos/modules/module-list.nix | 1 + nixos/modules/services/games/mchprs.nix | 341 ++++++++++++++++++ 3 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 nixos/modules/services/games/mchprs.nix diff --git a/nixos/doc/manual/release-notes/rl-2311.section.md b/nixos/doc/manual/release-notes/rl-2311.section.md index 17c254d53de2..d785d2239b44 100644 --- a/nixos/doc/manual/release-notes/rl-2311.section.md +++ b/nixos/doc/manual/release-notes/rl-2311.section.md @@ -6,7 +6,7 @@ ## New Services {#sec-release-23.11-new-services} -- Create the first release note entry in this section! +- [MCHPRS](https://github.com/MCHPR/MCHPRS), a multithreaded Minecraft server built for redstone. Available as [services.mchprs](#opt-services.mchprs.enable). - [acme-dns](https://github.com/joohoi/acme-dns), a limited DNS server to handle ACME DNS challenges easily and securely. Available as [services.acme-dns](#opt-services.acme-dns.enable). diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index e728dcb3f192..4cd64af713e5 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -476,6 +476,7 @@ ./services/games/deliantra-server.nix ./services/games/factorio.nix ./services/games/freeciv.nix + ./services/games/mchprs.nix ./services/games/minecraft-server.nix ./services/games/minetest-server.nix ./services/games/openarena.nix diff --git a/nixos/modules/services/games/mchprs.nix b/nixos/modules/services/games/mchprs.nix new file mode 100644 index 000000000000..a65001b0b3e2 --- /dev/null +++ b/nixos/modules/services/games/mchprs.nix @@ -0,0 +1,341 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.mchprs; + settingsFormat = pkgs.formats.toml { }; + + whitelistFile = pkgs.writeText "whitelist.json" + (builtins.toJSON + (mapAttrsToList (n: v: { name = n; uuid = v; }) cfg.whitelist.list)); + + configToml = + (removeAttrs cfg.settings [ "address" "port" ]) // + { + bind_address = cfg.settings.address + ":" + toString cfg.settings.port; + whitelist = cfg.whitelist.enable; + }; + + configTomlFile = settingsFormat.generate "Config.toml" configToml; +in +{ + options = { + services.mchprs = { + enable = mkEnableOption "MCHPRS"; + + declarativeSettings = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Whether to use a declarative configuration for MCHPRS. + ''; + }; + + declarativeWhitelist = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Whether to use a declarative whitelist. + The options {option}`services.mchprs.whitelist.list` + will be applied if and only if set to `true`. + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/mchprs"; + description = mdDoc '' + Directory to store MCHPRS database and other state/data files. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Whether to open ports in the firewall for the server. + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + maxRuntime = mkOption { + type = types.str; + default = "infinity"; + example = "7d"; + description = mdDoc '' + Automatically restart the server after + {option}`services.mchprs.maxRuntime`. + The time span format is described here: + https://www.freedesktop.org/software/systemd/man/systemd.time.html#Parsing%20Time%20Spans. + If `null`, then the server is not restarted automatically. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.mchprs; + defaultText = literalExpression "pkgs.mchprs"; + description = mdDoc "Version of MCHPRS to run."; + }; + + settings = mkOption { + type = types.submodule { + freeformType = settingsFormat.type; + + options = { + port = mkOption { + type = types.port; + default = 25565; + description = mdDoc '' + Port for the server. + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + address = mkOption { + type = types.str; + default = "0.0.0.0"; + description = mdDoc '' + Address for the server. + Please use enclosing square brackets when using ipv6. + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + motd = mkOption { + type = types.str; + default = "Minecraft High Performance Redstone Server"; + description = mdDoc '' + Message of the day. + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + chat_format = mkOption { + type = types.str; + default = "<{username}> {message}"; + description = mdDoc '' + How to format chat message interpolating `username` + and `message` with curly braces. + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + max_players = mkOption { + type = types.ints.positive; + default = 99999; + description = mdDoc '' + Maximum number of simultaneous players. + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + view_distance = mkOption { + type = types.ints.positive; + default = 8; + description = mdDoc '' + Maximal distance (in chunks) between players and loaded chunks. + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + bungeecord = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Enable compatibility with + [BungeeCord](https://github.com/SpigotMC/BungeeCord). + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + schemati = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Mimic the verification and directory layout used by the + Open Redstone Engineers + [Schemati plugin](https://github.com/OpenRedstoneEngineers/Schemati). + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + block_in_hitbox = mkOption { + type = types.bool; + default = true; + description = mdDoc '' + Allow placing blocks inside of players + (hitbox logic is simplified). + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + auto_redpiler = mkOption { + type = types.bool; + default = true; + description = mdDoc '' + Use redpiler automatically. + Only has effect when + {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + }; + }; + default = { }; + + description = mdDoc '' + Configuration for MCHPRS via `Config.toml`. + See https://github.com/MCHPR/MCHPRS/blob/master/README.md for documentation. + ''; + }; + + whitelist = { + enable = mkOption { + type = types.bool; + default = false; + description = mdDoc '' + Whether or not the whitelist (in `whitelist.json`) shoud be enabled. + Only has effect when {option}`services.mchprs.declarativeSettings` is `true`. + ''; + }; + + list = mkOption { + type = + let + minecraftUUID = types.strMatching + "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" // { + description = "Minecraft UUID"; + }; + in + types.attrsOf minecraftUUID; + default = { }; + example = literalExpression '' + { + username1 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; + username2 = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"; + }; + ''; + description = mdDoc '' + Whitelisted players, only has an effect when + {option}`services.mchprs.declarativeWhitelist` is + `true` and the whitelist is enabled + via {option}`services.mchprs.whitelist.enable`. + This is a mapping from Minecraft usernames to UUIDs. + You can use to get a + Minecraft UUID for a username. + ''; + }; + }; + }; + }; + + config = mkIf cfg.enable { + users.users.mchprs = { + description = "MCHPRS service user"; + home = cfg.dataDir; + createHome = true; + isSystemUser = true; + group = "mchprs"; + }; + users.groups.mchprs = { }; + + systemd.services.mchprs = { + description = "MCHPRS Service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + ExecStart = "${lib.getExe cfg.package}"; + Restart = "always"; + RuntimeMaxSec = cfg.maxRuntime; + User = "mchprs"; + WorkingDirectory = cfg.dataDir; + + StandardOutput = "journal"; + StandardError = "journal"; + + # Hardening + CapabilityBoundingSet = [ "" ]; + DeviceAllow = [ "" ]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + UMask = "0077"; + }; + + preStart = + (if cfg.declarativeSettings then '' + if [ -e .declarativeSettings ]; then + + # Settings were declarative before, no need to back up anything + cp -f ${configTomlFile} Config.toml + + else + + # Declarative settings for the first time, backup stateful files + cp -b --suffix=.stateful ${configTomlFile} Config.toml + + echo "Autogenerated file that implies that this server configuration is managed declaratively by NixOS" \ + > .declarativeSettings + + fi + '' else '' + if [ -e .declarativeSettings ]; then + rm .declarativeSettings + fi + '') + (if cfg.declarativeWhitelist then '' + if [ -e .declarativeWhitelist ]; then + + # Whitelist was declarative before, no need to back up anything + ln -sf ${whitelistFile} whitelist.json + + else + + # Declarative whitelist for the first time, backup stateful files + ln -sb --suffix=.stateful ${whitelistFile} whitelist.json + + echo "Autogenerated file that implies that this server's whitelist is managed declaratively by NixOS" \ + > .declarativeWhitelist + + fi + '' else '' + if [ -e .declarativeWhitelist ]; then + rm .declarativeWhitelist + fi + ''); + }; + + networking.firewall = mkIf (cfg.declarativeSettings && cfg.openFirewall) { + allowedUDPPorts = [ cfg.settings.port ]; + allowedTCPPorts = [ cfg.settings.port ]; + }; + }; + + meta.maintainers = with maintainers; [ gdd ]; +}