From 7a7057492c598938b79f8f631464ffe048533da5 Mon Sep 17 00:00:00 2001 From: arcnmx Date: Fri, 20 Sep 2024 13:41:26 -0700 Subject: [PATCH] chore(minecraft): module cleanup --- .../bedrock.nix} | 195 ++-------------- modules/nixos/minecraft/common.nix | 221 ++++++++++++++++++ .../katsink.nix} | 56 +++-- modules/nixos/monitoring/source/promtail.nix | 51 ++++ modules/system/exports/minecraft-java.nix | 61 +++++ nixos/kyuuto/mount.nix | 10 +- nixos/minecraft/katsink.nix | 34 ++- systems/minecraft/default.nix | 4 + tree.nix | 1 + 9 files changed, 426 insertions(+), 207 deletions(-) rename modules/nixos/{minecraft-bedrock.nix => minecraft/bedrock.nix} (58%) create mode 100644 modules/nixos/minecraft/common.nix rename modules/nixos/{minecraft-katsink.nix => minecraft/katsink.nix} (72%) create mode 100644 modules/system/exports/minecraft-java.nix diff --git a/modules/nixos/minecraft-bedrock.nix b/modules/nixos/minecraft/bedrock.nix similarity index 58% rename from modules/nixos/minecraft-bedrock.nix rename to modules/nixos/minecraft/bedrock.nix index 532fb636..c5710081 100644 --- a/modules/nixos/minecraft-bedrock.nix +++ b/modules/nixos/minecraft/bedrock.nix @@ -1,120 +1,3 @@ -let - allowListModule = { - config, - name, - gensokyo-zone, - lib, - ... - }: let - inherit (gensokyo-zone.Std) UInt; - inherit (lib.options) mkOption; - inherit (lib.modules) mkOptionDefault; - inherit (builtins) typeOf; - in { - options = with lib.types; { - name = mkOption { - type = str; - default = name; - }; - xuid = mkOption { - type = oneOf [int str]; - }; - permission = mkOption { - type = enum ["visitor" "member" "operator"]; - default = "member"; - }; - settings = mkOption { - type = attrsOf str; - }; - permissionSettings = mkOption { - type = attrsOf str; - }; - }; - config = let - xuid = - { - string = toString (UInt.FromHex config.xuid); - int = toString config.xuid; - } - .${typeOf config.xuid}; - in { - settings = { - name = mkOptionDefault config.name; - xuid = mkOptionDefault xuid; - # TODO: ignoresPlayerLimit = true/false - }; - permissionSettings = { - xuid = mkOptionDefault xuid; - permission = mkOptionDefault config.permission; - }; - }; - }; - packModule = { - config, - lib, - ... - }: let - inherit (lib.options) mkOption mkEnableOption; - inherit (lib.modules) mkIf mkOptionDefault; - inherit (lib.strings) splitString; - inherit (builtins) typeOf; - in { - options = with lib.types; { - enable = - mkEnableOption "pack" - // { - default = true; - }; - package = mkOption { - type = nullOr package; - default = null; - }; - packDir = mkOption { - type = str; - }; - packType = mkOption { - type = enum ["resource_packs" "behavior_packs"]; - }; - packId = mkOption { - type = str; - }; - version = mkOption { - type = oneOf [str (listOf str)]; - }; - settings = mkOption { - type = attrsOf (oneOf [str (listOf str)]); - }; - }; - config = { - packId = mkIf (config.package != null && config.package ? minecraft-bedrock.pack.pack_id) ( - mkOptionDefault - config.package.minecraft-bedrock.pack.pack_id - ); - packType = mkIf (config.package != null && config.package ? minecraft-bedrock.pack.type) ( - mkOptionDefault - config.package.minecraft-bedrock.pack.type - ); - version = mkIf (config.package != null && config.package ? minecraft-bedrock.pack.version) ( - mkOptionDefault - config.package.minecraft-bedrock.pack.version - ); - packDir = mkIf (config.package != null && config.package ? minecraft-bedrock.pack.dir) ( - mkOptionDefault - config.package.minecraft-bedrock.pack.dir - ); - settings = { - pack_id = mkOptionDefault config.packId; - version = - mkOptionDefault - { - string = splitString "." config.version; - list = config.version; - } - .${typeOf config.version}; - }; - }; - }; -in { config, gensokyo-zone, @@ -128,23 +11,9 @@ in inherit (lib.modules) mkIf mkMerge mkOptionDefault; inherit (lib.attrsets) filterAttrs mapAttrsToList; inherit (lib.lists) optional; - inherit (lib.strings) concatStringsSep; - inherit (lib.trivial) boolToString; inherit (lib.meta) getExe; - inherit (builtins) toJSON; + inherit (config.lib.minecraft) mkAllowPlayerType mkPackType writeServerProperties writeAllowList writePermissions writePacks; cfg = config.services.minecraft-bedrock-server; - - cfgToString = v: - if builtins.isBool v - then boolToString v - else toString v; - - serverPropertiesFile = pkgs.writeText "server.properties" ('' - # server.properties managed by NixOS configuration - '' - + concatStringsSep "\n" (mapAttrsToList - (n: v: "${n}=${cfgToString v}") - cfg.serverProperties)); in { options.services.minecraft-bedrock-server = with lib.types; { enable = mkOption { @@ -221,13 +90,7 @@ in }; allowPlayers = mkOption { - type = nullOr (attrsOf (submoduleWith { - modules = [allowListModule]; - specialArgs = { - inherit gensokyo-zone; - nixosConfig = config; - }; - })); + type = nullOr (attrsOf (mkAllowPlayerType {})); default = null; }; @@ -240,13 +103,7 @@ in }; packs = mkOption { - type = attrsOf (submoduleWith { - modules = [packModule]; - specialArgs = { - inherit gensokyo-zone; - nixosConfig = config; - }; - }); + type = attrsOf (mkPackType {}); default = {}; }; }; @@ -279,28 +136,16 @@ in player-movement-duration-threshold-in-ms = 500; correct-player-movement = false; }; - allowList = let - allowPlayers = mapAttrsToList (_: allow: allow.settings) cfg.allowPlayers; - allowListJson = pkgs.writeText "minecraft-bedrock-server-allowlist.json" ( - toJSON allowPlayers - ); - in - mkOptionDefault ( - if cfg.allowPlayers != null - then allowListJson - else null - ); - permissions = let - permissions = mapAttrsToList (_: allow: allow.permissionSettings) cfg.allowPlayers; - permissionsJson = pkgs.writeText "minecraft-bedrock-server-permissions.json" ( - toJSON permissions - ); - in - mkOptionDefault ( - if cfg.allowPlayers != null - then permissionsJson - else null - ); + allowList = mkOptionDefault ( + if cfg.allowPlayers != null + then writeAllowList cfg.allowPlayers + else null + ); + permissions = mkOptionDefault ( + if cfg.allowPlayers != null + then writePermissions cfg.allowPlayers + else null + ); }; conf.users.users.${cfg.user} = { inherit (cfg) group; @@ -334,13 +179,11 @@ in ] ++ optional (cfg.permissions == null) "permissions.json"); mkWorldPacks = type: let - enabledPacks = filterAttrs (_: pack: pack.enable && pack.packType == "${type}_packs") cfg.packs; - jsonName = "world_${type}_packs.json"; - packsJson = mapAttrsToList (_: pack: pack.settings) enabledPacks; - packsJsonPath = pkgs.writeText jsonName (toJSON packsJson); + enabledPacks = filterAttrs (_: pack: pack.enable && pack.packType == type) cfg.packs; + packsJsonPath = writePacks { inherit type; } enabledPacks; in mkIf (enabledPacks != {}) [ - "${packsJsonPath}:${cfg.dataDir}/worlds/${cfg.serverProperties.level-name}/${jsonName}" + "${packsJsonPath}:${cfg.dataDir}/worlds/${cfg.serverProperties.level-name}/${packsJsonPath.name}" ]; mapWorldPacks = packs: let enabledPacks = filterAttrs (_: pack: pack.enable && pack.package != null) packs; @@ -350,8 +193,8 @@ in in mapAttrsToList mapPackPath enabledPacks; packsPaths = mkMerge [ - (mkWorldPacks "behavior") - (mkWorldPacks "resource") + (mkWorldPacks "behavior_packs") + (mkWorldPacks "resource_packs") (mapWorldPacks cfg.packs) ]; in @@ -376,7 +219,7 @@ in preStart = '' mkdir -p behavior_packs ln -sf ${cfg.package}/var/lib/minecraft-bedrock/behavior_packs/* behavior_packs/ - cp -f ${serverPropertiesFile} server.properties + cp -f ${writeServerProperties cfg.serverProperties} server.properties chmod +w server.properties ''; }; diff --git a/modules/nixos/minecraft/common.nix b/modules/nixos/minecraft/common.nix new file mode 100644 index 00000000..8137106e --- /dev/null +++ b/modules/nixos/minecraft/common.nix @@ -0,0 +1,221 @@ +let + allowListModule = { + config, + name, + gensokyo-zone, + lib, + ... + }: let + inherit (gensokyo-zone.Std) UInt; + inherit (gensokyo-zone.lib) json; + inherit (lib.options) mkOption; + inherit (lib.modules) mkOptionDefault; + inherit (builtins) typeOf; + in { + options = with lib.types; { + name = mkOption { + type = str; + default = name; + }; + xuid = mkOption { + type = nullOr (oneOf [int str]); + }; + uuid = mkOption { + type = nullOr str; + }; + permission = mkOption { + type = enum ["visitor" "member" "operator"]; + default = "member"; + }; + permissionLevel = mkOption { + type = ints.between 0 4; + description = "1=mod, 2=gm, 3=admin, 4=owner"; + default = 0; + }; + settings = mkOption { + type = json.types.attrs; + }; + whitelistSettings = mkOption { + type = json.types.attrs; + }; + permissionSettings = mkOption { + type = json.types.attrs; + }; + opsSettings = mkOption { + type = json.types.attrs; + }; + }; + config = let + xuid = + { + string = toString (UInt.FromHex config.xuid); + int = toString config.xuid; + } + .${typeOf config.xuid}; + in { + settings = { + name = mkOptionDefault config.name; + xuid = mkOptionDefault xuid; + # TODO: ignoresPlayerLimit = true/false + }; + whitelistSettings = { + name = mkOptionDefault config.name; + uuid = mkOptionDefault config.uuid; + }; + permissionSettings = { + xuid = mkOptionDefault xuid; + permission = mkOptionDefault config.permission; + }; + opsSettings = { + name = mkOptionDefault config.name; + uuid = mkOptionDefault config.uuid; + level = mkOptionDefault config.permissionLevel; + bypassesPlayerLimit = mkOptionDefault true; + }; + }; + }; + packModule = { + config, + lib, + ... + }: let + inherit (lib.options) mkOption mkEnableOption; + inherit (lib.modules) mkIf mkOptionDefault; + inherit (lib.strings) splitString; + inherit (builtins) typeOf; + in { + options = with lib.types; { + enable = + mkEnableOption "pack" + // { + default = true; + }; + package = mkOption { + type = nullOr package; + default = null; + }; + packDir = mkOption { + type = str; + }; + packType = mkOption { + type = enum ["resource_packs" "behavior_packs"]; + }; + packId = mkOption { + type = str; + }; + version = mkOption { + type = oneOf [str (listOf str)]; + }; + settings = mkOption { + type = attrsOf (oneOf [str (listOf str)]); + }; + }; + config = { + packId = mkIf (config.package != null && config.package ? minecraft-bedrock.pack.pack_id) ( + mkOptionDefault + config.package.minecraft-bedrock.pack.pack_id + ); + packType = mkIf (config.package != null && config.package ? minecraft-bedrock.pack.type) ( + mkOptionDefault + config.package.minecraft-bedrock.pack.type + ); + version = mkIf (config.package != null && config.package ? minecraft-bedrock.pack.version) ( + mkOptionDefault + config.package.minecraft-bedrock.pack.version + ); + packDir = mkIf (config.package != null && config.package ? minecraft-bedrock.pack.dir) ( + mkOptionDefault + config.package.minecraft-bedrock.pack.dir + ); + settings = { + pack_id = mkOptionDefault config.packId; + version = + mkOptionDefault + { + string = splitString "." config.version; + list = config.version; + } + .${typeOf config.version}; + }; + }; + }; +in + { + config, + gensokyo-zone, + lib, + pkgs, + ... + }: let + inherit (lib.attrsets) mapAttrsToList filterAttrs; + inherit (lib.strings) concatStringsSep; + inherit (lib.trivial) boolToString; + inherit (builtins) toJSON; + inherit (config.lib) minecraft; + in { + config.lib.minecraft = { + inherit allowListModule packModule; + mkAllowPlayerType = { + modules ? [], + specialArgs ? {}, + }: + lib.types.submoduleWith { + modules = modules ++ [minecraft.allowListModule]; + specialArgs = + { + inherit gensokyo-zone; + nixosConfig = config; + } + // specialArgs; + }; + writeAllowList = allowPlayers: let + allowList = mapAttrsToList (_: allow: allow.settings) allowPlayers; + in + pkgs.writeText "allowlist.json" (toJSON allowList); + writeWhiteList = allowPlayers: let + allowList = mapAttrsToList (_: allow: allow.whitelistSettings) allowPlayers; + in + pkgs.writeText "whitelist.json" (toJSON allowList); + writePermissions = allowPlayers: let + permissions = mapAttrsToList (_: allow: allow.permissionSettings) allowPlayers; + in + pkgs.writeText "permissions.json" (toJSON permissions); + writeOps = allowPlayers: let + ops = filterAttrs (_: player: player.permissionLevel > 0) allowPlayers; + permissions = mapAttrsToList (_: allow: allow.opsSettings) ops; + in + pkgs.writeText "ops.json" (toJSON permissions); + mkPackType = { + modules ? [], + specialArgs ? {}, + }: + lib.types.submoduleWith { + modules = modules ++ [minecraft.packModule]; + specialArgs = + { + inherit gensokyo-zone; + nixosConfig = config; + } + // specialArgs; + }; + writePacks = {type}: packs: let + packsSettings = mapAttrsToList (_: pack: pack.settings) packs; + in + pkgs.writeText "world_${type}.json" (toJSON packsSettings); + writeServerProperty = let + cfgToString = v: + if builtins.isBool v + then boolToString v + else toString v; + in + n: v: "${n}=${cfgToString v}"; + writeServerProperties = serverProperties: let + inherit (config.lib.minecraft) writeServerProperty; + lines = mapAttrsToList writeServerProperty serverProperties; + in + pkgs.writeText "server.properties" '' + # server.properties managed by NixOS configuration + ${concatStringsSep "\n" lines} + ''; + }; + } diff --git a/modules/nixos/minecraft-katsink.nix b/modules/nixos/minecraft/katsink.nix similarity index 72% rename from modules/nixos/minecraft-katsink.nix rename to modules/nixos/minecraft/katsink.nix index 28fdca8d..01410726 100644 --- a/modules/nixos/minecraft-katsink.nix +++ b/modules/nixos/minecraft/katsink.nix @@ -1,15 +1,17 @@ { config, + gensokyo-zone, lib, pkgs, ... }: let + inherit (gensokyo-zone.lib) mapOptionDefaults; inherit (lib.options) mkOption mkEnableOption mkPackageOption; - inherit (lib.modules) mkIf mkMerge mkOptionDefault; - inherit (lib.strings) concatStringsSep; + inherit (lib.modules) mkIf mkMerge mkAfter mkOptionDefault; + inherit (lib.strings) escapeShellArgs; inherit (lib.meta) getExe; + inherit (config.lib.minecraft) mkAllowPlayerType writeWhiteList writeOps; cfg = config.services.minecraft-katsink-server; - in { options.services.minecraft-katsink-server = with lib.types; { enable = mkEnableOption "kat-kitchen-sink"; @@ -35,7 +37,7 @@ in { argsFiles = mkOption { type = listOf str; - default = [ "user_jvm_args.txt" ]; + default = ["user_jvm_args.txt"]; }; jvmOpts = mkOption { @@ -52,10 +54,21 @@ in { type = str; default = cfg.user; }; + + serverProperties = mkOption { + type = attrsOf (oneOf [bool int str float]); + }; + + allowPlayers = mkOption { + type = nullOr (attrsOf (mkAllowPlayerType {})); + default = null; + }; }; config = let confService.services.minecraft-katsink-server = { + serverProperties = mapOptionDefaults { + }; }; conf.users = mkIf (cfg.user == "minecraft-bedrock") { users.${cfg.user} = { @@ -69,10 +82,9 @@ in { }; conf.systemd.services.minecraft-katsink-server = let - execStart = concatStringsSep " " ([ - "${getExe cfg.jre.package}" - ] ++ map (argsFile: "@${argsFile}") cfg.argsFiles - ++ cfg.jvmOpts); + execStartArgs = + map (argsFile: "@${argsFile}") cfg.argsFiles + ++ cfg.jvmOpts; execStop = pkgs.writeShellScriptBin "minecraft-katsink-stop" '' echo /stop > ${config.systemd.sockets.minecraft-katsink-server.socketConfig.ListenFIFO} @@ -82,7 +94,6 @@ in { sleep 1s done ''; - in { description = "Minecraft Kat Kitchen Server"; wantedBy = ["multi-user.target"]; @@ -95,24 +106,29 @@ in { cfg.argsFiles ]; + path = [cfg.jre.package]; + script = mkAfter '' + exec java ${escapeShellArgs execStartArgs} + ''; + serviceConfig = { - ExecStart = [execStart]; + BindReadOnlyPaths = mkIf (cfg.allowPlayers != null) [ + "${writeWhiteList cfg.allowPlayers}:${cfg.dataDir}/whitelist.json" + "${writeOps cfg.allowPlayers}:${cfg.dataDir}/ops.json" + ]; ExecStop = "${getExe execStop} $MAINPID"; - Restart = "on-failure"; + Restart = "always"; User = cfg.user; WorkingDirectory = cfg.dataDir; - /*LogFilterPatterns = [ - "~.*minecraft:trial_chambers/chamber/end" - "~Running AutoCompaction" - ];*/ + RuntimeDirectory = "minecraft-katsink"; StandardInput = "socket"; StandardOutput = "journal"; StandardError = "journal"; # Hardening - CapabilityBoundingSet = [ "" ]; - DeviceAllow = [ "" ]; + CapabilityBoundingSet = [""]; + DeviceAllow = [""]; LockPersonality = true; PrivateDevices = true; PrivateTmp = true; @@ -125,7 +141,7 @@ in { ProtectKernelModules = true; ProtectKernelTunables = true; ProtectProc = "invisible"; - RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; + RestrictAddressFamilies = ["AF_INET" "AF_INET6" "AF_UNIX"]; RestrictNamespaces = true; RestrictRealtime = true; RestrictSUIDSGID = true; @@ -134,9 +150,9 @@ in { }; }; conf.systemd.sockets.minecraft-katsink-server = { - bindsTo = [ "minecraft-katsink-server.service" ]; + bindsTo = ["minecraft-katsink-server.service"]; socketConfig = { - ListenFIFO = "/run/minecraft-katsink.stdin"; + ListenFIFO = "/run/minecraft-katsink/stdin"; SocketMode = "0660"; SocketUser = mkOptionDefault cfg.user; SocketGroup = mkOptionDefault cfg.group; diff --git a/modules/nixos/monitoring/source/promtail.nix b/modules/nixos/monitoring/source/promtail.nix index 71df55e6..cfcce811 100644 --- a/modules/nixos/monitoring/source/promtail.nix +++ b/modules/nixos/monitoring/source/promtail.nix @@ -59,6 +59,57 @@ in { target_label = "priority"; } ]; + pipeline_stages = let + minecraftServer = [ + { + match = { + selector = ''{unit="minecraft-katsink-server.service"}''; + pipeline_name = "minecraft-log4j"; + stages = [ + { + decolorize = {}; + } + { + multiline = { + firstline = ''^(\[[^\]]+\]|[0-9A-Z]+:)''; + max_wait_time = "2s"; + max_lines = 512; + }; + } + { + regex.expression = concatStringsSep " " [ + ''^\[(?P