From 75b123776b9931388394f4dfd10be7040ce647d4 Mon Sep 17 00:00:00 2001 From: arcnmx Date: Tue, 17 Sep 2024 12:42:18 -0700 Subject: [PATCH] chore(minecraft): mounts --- .github/workflows/nodes.yml | 51 ++++++++++ ci/systems.json | 18 ++++ docs/network.adoc | 1 + modules/nixos/minecraft-katsink.nix | 150 ++++++++++++++++++++++++++++ nixos/minecraft/katsink.nix | 37 +++++++ systems/aya/lxc.json | 2 + systems/minecraft/default.nix | 4 +- systems/minecraft/lxc.json | 24 ++++- systems/minecraft/nixos.nix | 15 +-- systems/minecraft/proxmox.nix | 7 +- systems/reisen/setup.sh | 55 +++++++--- systems/reisen/systems.json | 18 ++++ tf/cloudflare_records.tf | 7 ++ tf/proxmox_vms.tf | 16 +-- tf/tailscale_devices.tf | 51 ++++++++-- 15 files changed, 402 insertions(+), 54 deletions(-) create mode 100644 modules/nixos/minecraft-katsink.nix create mode 100644 nixos/minecraft/katsink.nix diff --git a/.github/workflows/nodes.yml b/.github/workflows/nodes.yml index 7ece2889..68d57e05 100644 --- a/.github/workflows/nodes.yml +++ b/.github/workflows/nodes.yml @@ -487,6 +487,57 @@ jobs: command: ci-build-cache quiet: false stdin: ${{ runner.temp }}/ci.build.cache + minecraft: + name: nodes-minecraft + runs-on: ubuntu-latest + steps: + - id: checkout + name: git clone + uses: actions/checkout@v4 + with: + submodules: false + - id: nix-install + name: nix install + uses: arcnmx/ci/actions/nix/install@v0.7 + - id: ci-dirty + name: nix test dirty + uses: arcnmx/ci/actions/nix/run@v0.7 + with: + attrs: ci.job.minecraft.run.test + command: ci-build-dirty + quiet: false + stdout: ${{ runner.temp }}/ci.build.dirty + - id: ci-test + name: nix test build + uses: arcnmx/ci/actions/nix/run@v0.7 + with: + attrs: ci.job.minecraft.run.test + command: ci-build-realise + ignore-exit-code: true + quiet: false + stdin: ${{ runner.temp }}/ci.build.dirty + - env: + CI_EXIT_CODE: ${{ steps.ci-test.outputs.exit-code }} + id: ci-summary + name: nix test results + uses: arcnmx/ci/actions/nix/run@v0.7 + with: + attrs: ci.job.minecraft.run.test + command: ci-build-summarise + quiet: false + stdin: ${{ runner.temp }}/ci.build.dirty + stdout: ${{ runner.temp }}/ci.build.cache + - env: + CACHIX_SIGNING_KEY: ${{ secrets.CACHIX_SIGNING_KEY }} + id: ci-cache + if: always() + name: nix test cache + uses: arcnmx/ci/actions/nix/run@v0.7 + with: + attrs: ci.job.minecraft.run.test + command: ci-build-cache + quiet: false + stdin: ${{ runner.temp }}/ci.build.cache packages: name: nodes-packages runs-on: ubuntu-latest diff --git a/ci/systems.json b/ci/systems.json index 5a968740..bafe852a 100644 --- a/ci/systems.json +++ b/ci/systems.json @@ -245,6 +245,24 @@ } } }, + "minecraft": { + "network": { + "hostName": "minecraft", + "networks": { + "int": null, + "local": { + "address4": "10.1.1.51", + "address6": "fd0a::be24:11ff:fec4:66ad", + "macAddress": "BC:24:11:C4:66:AD" + }, + "tail": { + "address4": "100.73.157.122", + "address6": "fd7a:115c:a1e0::1f01:9d7a", + "macAddress": null + } + } + } + }, "nue": { "network": { "hostName": "nue", diff --git a/docs/network.adoc b/docs/network.adoc index 080d1c8b..a708184c 100644 --- a/docs/network.adoc +++ b/docs/network.adoc @@ -32,6 +32,7 @@ aya:: `10.1.1.47` keycloak:: `10.1.1.48` kasen:: `10.1.1.49` sakuya:: `10.1.1.50` +minecraft:: `10.1.1.51` nue:: `10.1.1.62` logistics:: `10.1.1.63` diff --git a/modules/nixos/minecraft-katsink.nix b/modules/nixos/minecraft-katsink.nix new file mode 100644 index 00000000..ac8eadd5 --- /dev/null +++ b/modules/nixos/minecraft-katsink.nix @@ -0,0 +1,150 @@ +{ + config, + lib, + pkgs, + ... +}: let + inherit (lib.options) mkOption mkEnableOption mkPackageOption; + inherit (lib.modules) mkIf mkMerge mkOptionDefault; + inherit (lib.strings) concatStringsSep; + inherit (lib.meta) getExe; + cfg = config.services.minecraft-katsink-server; + +in { + options.services.minecraft-katsink-server = with lib.types; { + enable = mkEnableOption "kat-kitchen-sink"; + + openFirewall = mkOption { + type = bool; + default = false; + }; + port = mkOption { + type = port; + default = 25565; + }; + + jre.package = mkPackageOption pkgs "jre" {}; + + dataDir = mkOption { + type = path; + default = "/var/lib/minecraft-katsink"; + description = '' + Directory to store Minecraft database and other state/data files. + ''; + }; + + argsFiles = mkOption { + type = listOf str; + default = [ "user_jvm_args.txt" ]; + }; + + jvmOpts = mkOption { + type = listOf str; + default = []; + example = ["-Xmx4G"]; + }; + + user = mkOption { + type = str; + default = "minecraft-bedrock"; + }; + group = mkOption { + type = str; + default = cfg.user; + }; + }; + + config = let + confService.services.minecraft-katsink-server = { + }; + conf.users = mkIf (cfg.user == "minecraft-bedrock") { + users.${cfg.user} = { + inherit (cfg) group; + description = "Minecraft server service user"; + home = cfg.dataDir; + createHome = true; + isSystemUser = true; + }; + groups.${cfg.group} = {}; + }; + + conf.systemd.services.minecraft-katsink-server = let + execStart = concatStringsSep " " ([ + "${getExe cfg.jre.package}" + ] ++ map (argsFile: "@${argsFile}") cfg.argsFiles + ++ cfg.jvmOpts); + execStop = pkgs.writeShellScriptBin "minecraft-katsink-stop" '' + echo /stop > ${config.systemd.sockets.minecraft-katsink-server.socketConfig.ListenFIFO} + + # Wait for the PID of the minecraft server to disappear before + # returning, so systemd doesn't attempt to SIGKILL it. + while kill -0 "$1" 2> /dev/null; do + sleep 1s + done + ''; + + in { + description = "Minecraft Kat Kitchen Server"; + wantedBy = ["multi-user.target"]; + requires = ["minecraft-katsink-server.socket"]; + after = ["network.target" "minecraft-katsink-server.socket"]; + + serviceConfig = { + ExecStart = [execStart]; + ExecStop = "${getExe execStop} $MAINPID"; + Restart = "on-failure"; + User = cfg.user; + WorkingDirectory = cfg.dataDir; + /*LogFilterPatterns = [ + "~.*minecraft:trial_chambers/chamber/end" + "~Running AutoCompaction" + ];*/ + + StandardInput = "socket"; + StandardOutput = "journal"; + StandardError = "journal"; + + # Hardening + CapabilityBoundingSet = [ "" ]; + DeviceAllow = [ "" ]; + LockPersonality = 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" "AF_UNIX" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + UMask = "0077"; + }; + }; + conf.systemd.sockets.minecraft-katsink-server = { + bindsTo = [ "minecraft-katsink-server.service" ]; + socketConfig = { + ListenFIFO = "/run/minecraft-katsink.stdin"; + SocketMode = "0660"; + SocketUser = mkOptionDefault cfg.user; + SocketGroup = mkOptionDefault cfg.group; + RemoveOnStop = true; + FlushPending = true; + }; + }; + + conf.networking.firewall = mkIf cfg.openFirewall { + allowedUDPPorts = cfg.port; + }; + in + mkMerge [ + confService + (mkIf cfg.enable conf) + ]; +} diff --git a/nixos/minecraft/katsink.nix b/nixos/minecraft/katsink.nix new file mode 100644 index 00000000..cc8d7d67 --- /dev/null +++ b/nixos/minecraft/katsink.nix @@ -0,0 +1,37 @@ +{ + config, + lib, + ... +}: let + inherit (lib.modules) mkIf mkDefault; + cfg = config.services.minecraft-katsink-server; +in { + services.minecraft-katsink-server = { + enable = mkDefault true; + argsFiles = [ + "user_jvm_args.txt" + "libraries/net/neoforged/neoforge/21.1.54/unix_args.txt" + ]; + }; + users = mkIf cfg.enable { + users.${cfg.user}.uid = 913; + groups.${cfg.group}.gid = config.users.users.${cfg.user}.uid; + }; + + systemd = mkIf cfg.enable { + services.minecraft-katsink-server = { + # TODO: confinement.enable = true; + gensokyo-zone.sharedMounts."minecraft/katsink/kat-kitchen-server" = {config, ...}: { + root = config.rootDir + "/minecraft/katsink"; + path = mkDefault cfg.dataDir; + }; + }; + sockets.minecraft-katsink-server = { + socketConfig.SocketGroup = "admin"; + }; + }; + networking.firewall = mkIf cfg.enable { + interfaces.tailscale0.allowedTCPPorts = [cfg.port]; + interfaces.local.allowedTCPPorts = [cfg.port]; + }; +} diff --git a/systems/aya/lxc.json b/systems/aya/lxc.json index 0fac5294..c0555220 100644 --- a/systems/aya/lxc.json +++ b/systems/aya/lxc.json @@ -4,6 +4,8 @@ "/rpool/shared/nix/store nix/store none bind,create=dir", "/rpool/shared/nix/var nix/var none bind,create=dir", "/rpool/shared/minecraft/bedrock mnt/shared/minecraft/bedrock none bind,optional,create=dir", + "/rpool/shared/minecraft/katsink mnt/shared/minecraft/katsink none bind,optional,create=dir", + "/mnt/kyuuto-data/minecraft mnt/kyuuto-data/minecraft none bind,optional,create=dir", "/dev/net/tun dev/net/tun none bind,optional,create=file" ], "lxc.idmap": [ diff --git a/systems/minecraft/default.nix b/systems/minecraft/default.nix index d55be39f..8bc87cbe 100644 --- a/systems/minecraft/default.nix +++ b/systems/minecraft/default.nix @@ -11,8 +11,8 @@ _: { ]; network.networks = { tail = { - address4 = "100.70.124.79"; - address6 = "fd7a:115c:a1e0::b001:7c4f"; + address4 = "100.73.157.122"; + address6 = "fd7a:115c:a1e0::1f01:9d7a"; }; }; exports = { diff --git a/systems/minecraft/lxc.json b/systems/minecraft/lxc.json index 3cee5afa..6f3e2dd0 100644 --- a/systems/minecraft/lxc.json +++ b/systems/minecraft/lxc.json @@ -1,8 +1,22 @@ { - "lxc": { - "lxc.mount.entry": [ - "/mnt/kyuuto-minecraft mnt/kyuuto-minecraft none bind,optional,create=dir", - "/dev/net/tun dev/net/tun none bind,optional,create=file" - ] + "lxc": { + "lxc.mount.entry": [ + "/rpool/shared/minecraft/katsink mnt/shared/minecraft/katsink none bind,optional,create=dir", + "/mnt/kyuuto-data/minecraft mnt/kyuuto-data/minecraft none bind,optional,create=dir", + "/mnt/kyuuto-minecraft mnt/kyuuto-minecraft none bind,optional,create=dir", + "/dev/net/tun dev/net/tun none bind,optional,create=file" + ], + "lxc.idmap": [ + "u 0 100000 8000", + "g 0 100000 8000", + "u 8000 8000 128", + "g 8000 8000 256", + "u 8128 108128 57406", + "g 8256 108256 57278", + "u 65534 65534 1", + "g 65534 65534 1", + "u 65535 165535 1", + "g 65535 165535 1" + ] } } diff --git a/systems/minecraft/nixos.nix b/systems/minecraft/nixos.nix index 62c4dd9a..825c9854 100644 --- a/systems/minecraft/nixos.nix +++ b/systems/minecraft/nixos.nix @@ -1,20 +1,21 @@ -{meta, pkgs, ...}:{ +{ + meta, + config, + ... +}: { imports = let inherit (meta) nixos; in [ nixos.sops nixos.reisen-ct nixos.tailscale + nixos.minecraft.katsink ]; - environment.systemPackages = with pkgs; [ - jre - tmux + environment.systemPackages = [ + config.services.minecraft-katsink-server.jre.package ]; - networking.firewall.interfaces.tailscale0.allowedTCPPorts = [ 25565 ]; - networking.firewall.interfaces.local.allowedTCPPorts = [ 25565 ]; - sops = { defaultSopsFile = ./secrets.yaml; secrets.tailscale-key.key = "tailscale-key"; diff --git a/systems/minecraft/proxmox.nix b/systems/minecraft/proxmox.nix index ef28f1bc..441c206d 100644 --- a/systems/minecraft/proxmox.nix +++ b/systems/minecraft/proxmox.nix @@ -1,6 +1,6 @@ _: { proxmox = { - vm.id = 106; + vm.id = 109; container = { enable = true; lxc.configJsonFile = ./lxc.json; @@ -8,11 +8,10 @@ _: { network.interfaces = { net0 = { mdns.enable = true; - macAddress = "BC:24:11:C4:66:AB"; - address4 = "dhcp"; + macAddress = "BC:24:11:C4:66:AD"; + address4 = "10.1.1.51/24"; address6 = "auto"; }; - net1.internal.enable = true; }; }; } diff --git a/systems/reisen/setup.sh b/systems/reisen/setup.sh index 88d1546e..a5134b6d 100644 --- a/systems/reisen/setup.sh +++ b/systems/reisen/setup.sh @@ -100,31 +100,51 @@ if [[ ! -d /rpool/caches ]]; then zfs create rpool/caches fi -mkrpool() { - local SHARED_PATH SHARED_MODE SHARED_OWNER SHARED_GROUP - SHARED_PATH=$1 - SHARED_OWNER=$2 - SHARED_GROUP=$3 - SHARED_MODE=$4 +mkzfs() { + local ZFS_PATH ZFS_MODE ZFS_OWNER ZFS_GROUP + ZFS_PATH=$1 + ZFS_OWNER=$2 + ZFS_GROUP=$3 + ZFS_MODE=$4 shift 4 - if [[ ! -d "/rpool/$SHARED_PATH" ]]; then - zfs create "rpool/$SHARED_PATH" + ZFS_NAME=${ZFS_PATH#/} + if [[ $# -gt 0 ]]; then + ZFS_NAME=$1 + shift fi - chmod "$SHARED_MODE" "/rpool/$SHARED_PATH" - chown "$SHARED_OWNER:$SHARED_GROUP" "/rpool/$SHARED_PATH" + + ZFS_ARGS=("$@") + + if [[ $ZFS_NAME != ${ZFS_PATH#/} ]]; then + ZFS_ARGS+=(-o "mountpoint=$ZFS_PATH") + fi + + if [[ ! -d "$ZFS_PATH" ]]; then + zfs create "$ZFS_NAME" ${ZFS_ARGS[@]+"${ZFS_ARGS[@]}"} + fi + chmod "$ZFS_MODE" "$ZFS_PATH" + chown "$ZFS_OWNER:$ZFS_GROUP" "$ZFS_PATH" } mkshared() { local SHARED_PATH=$1 shift - mkrpool "shared/$SHARED_PATH" "$@" + mkzfs "/rpool/shared/$SHARED_PATH" "$@" } mkcache() { - local SHARED_PATH=$1 + local CACHE_PATH=$1 shift - mkrpool "caches/$SHARED_PATH" "$@" + mkzfs "/rpool/caches/$CACHE_PATH" "$@" +} + +mkkyuuto() { + local KYUUTO_PATH KYUUTO_ARGS=() + KYUUTO_NAME=$1 + KYUUTO_ARGS=("$2" "$3" "$4") + shift 4 + mkzfs "/mnt/kyuuto-$KYUUTO_NAME" "${KYUUTO_ARGS[@]}" "kyuuto/$KYUUTO_NAME" "$@" } mkshared nix 0 0 0755 @@ -163,6 +183,15 @@ mkshared zigbee2mqtt 100317 100317 0700 mkshared vaultwarden 100915 100915 0750 mkshared minecraft 100913 100913 0750 mkshared minecraft/bedrock 100913 100913 0750 +mkshared minecraft/katsink 100913 100913 0750 + +mkkyuuto data 0 0 0755 -o compression=on +mkkyuuto data/minecraft 0 8126 0775 +if [[ ! -d /mnt/kyuuto-data/minecraft/simplebackups ]]; then + mkdir -p /mnt/kyuuto-data/minecraft/simplebackups +fi +chown 100913:8126 /mnt/kyuuto-data/minecraft/simplebackups +chmod 0775 /mnt/kyuuto-data/minecraft/simplebackups ln -sf /lib/systemd/system/auth-rpcgss-module.service /etc/systemd/system/ mkdir -p /etc/systemd/system/auth-rpcgss-module.service.d diff --git a/systems/reisen/systems.json b/systems/reisen/systems.json index 26603b66..65652622 100644 --- a/systems/reisen/systems.json +++ b/systems/reisen/systems.json @@ -151,6 +151,24 @@ } } }, + "minecraft": { + "hostName": "minecraft", + "network": { + "networks": { + "int": null, + "local": { + "address4": "10.1.1.51", + "address6": "fd0a::be24:11ff:fec4:66ad", + "macAddress": "BC:24:11:C4:66:AD" + }, + "tail": { + "address4": "100.73.157.122", + "address6": "fd7a:115c:a1e0::1f01:9d7a", + "macAddress": null + } + } + } + }, "reimu": { "hostName": "reimu", "network": { diff --git a/tf/cloudflare_records.tf b/tf/cloudflare_records.tf index 732b73d0..8d3c8445 100644 --- a/tf/cloudflare_records.tf +++ b/tf/cloudflare_records.tf @@ -92,6 +92,13 @@ module "aya_system_records" { ] } +module "minecraft_system_records" { + source = "./system/records" + zone_id = cloudflare_zone.gensokyo-zone_zone.id + zone_zone = cloudflare_zone.gensokyo-zone_zone.zone + net_data = local.systems.minecraft.network +} + module "tewi_system_records" { source = "./system/records" zone_id = cloudflare_zone.gensokyo-zone_zone.id diff --git a/tf/proxmox_vms.tf b/tf/proxmox_vms.tf index be80515b..67bbfc3e 100644 --- a/tf/proxmox_vms.tf +++ b/tf/proxmox_vms.tf @@ -784,15 +784,8 @@ EOT address = "auto" } ipv4 { - address = "dhcp" - } - } - ip_config { - ipv6 { - address = "${cidrhost(local.reisen_int_prefix6, local.proxmox_minecraft_vm_id - local.reisen_int_offset)}/64" - } - ipv4 { - address = "${cidrhost(local.reisen_int_prefix4, local.proxmox_minecraft_vm_id - local.reisen_int_offset)}/24" + address = "10.1.1.51/24" + gateway = "10.1.1.1" } } } @@ -807,11 +800,6 @@ EOT name = "eth0" mac_address = "BC:24:11:C4:66:AD" } - network_interface { - name = "eth9" - mac_address = "BC:24:19:C4:66:AD" - bridge = proxmox_virtual_environment_network_linux_bridge.internal.name - } operating_system { template_file_id = var.proxmox_container_template diff --git a/tf/tailscale_devices.tf b/tf/tailscale_devices.tf index 4e2062b0..cc2cc27f 100644 --- a/tf/tailscale_devices.tf +++ b/tf/tailscale_devices.tf @@ -1,7 +1,8 @@ locals { - tailscale_tag_infra = "tag:infrastructure" - tailscale_tag_genso = "tag:gensokyo" - tailscale_tag_reisen = "tag:reisen" + tailscale_tag_infra = "tag:infrastructure" + tailscale_tag_genso = "tag:gensokyo" + tailscale_tag_reisen = "tag:reisen" + tailscale_tag_minecraft = "tag:minecraft" tailscale_tag_arc = "tag:arc" tailscale_tag_arc_deploy = "tag:arc-deploy" @@ -11,7 +12,14 @@ locals { tailscale_user_arc = "arc@${var.tailscale_tailnet}" tailscale_user_kat = "kat@${var.tailscale_tailnet}" - tailscale_group_admin = "autogroup:admin" + tailscale_group_member = "autogroup:member" + tailscale_group_admin = "autogroup:admin" + + tailscale_tags_genso = [local.tailscale_tag_infra, local.tailscale_tag_genso] + tailscale_tags_reisen = concat(local.tailscale_tags_genso, [local.tailscale_tag_reisen]) + tailscale_tags_arc = [local.tailscale_user_arc, local.tailscale_tag_arc] + tailscale_tags_kat = [local.tailscale_user_kat, local.tailscale_tag_kat] + tailscale_tags_peeps = concat(local.tailscale_tags_arc, local.tailscale_tags_kat) } resource "tailscale_acl" "tailnet" { @@ -20,6 +28,7 @@ resource "tailscale_acl" "tailnet" { "${local.tailscale_tag_infra}" : [local.tailscale_group_admin], "${local.tailscale_tag_reisen}" : [local.tailscale_group_admin, local.tailscale_tag_infra], "${local.tailscale_tag_genso}" : [local.tailscale_group_admin, local.tailscale_tag_arc_deploy, local.tailscale_tag_kat_deploy], + "${local.tailscale_tag_minecraft}" : [local.tailscale_group_admin, local.tailscale_tag_infra], "${local.tailscale_tag_arc}" : [local.tailscale_user_arc, local.tailscale_tag_arc_deploy], "${local.tailscale_tag_arc_deploy}" : [local.tailscale_user_arc], "${local.tailscale_tag_kat}" : [local.tailscale_user_kat, local.tailscale_tag_kat_deploy], @@ -27,10 +36,34 @@ resource "tailscale_acl" "tailnet" { } acls = [ { - # Allow all connections + action = "accept" + src = [local.tailscale_group_admin] + dst = ["*:*"] + }, + { + action = "accept" + src = [local.tailscale_tag_reisen] + dst = ["${local.tailscale_tag_reisen}:*"] + }, + { + action = "accept" + src = concat([local.tailscale_tag_genso], local.tailscale_tags_peeps) + dst = [ + "${local.tailscale_tag_genso}:*", + ] + }, + { action = "accept" src = ["*"] - dst = ["*:*"] + dst = [ + "autogroup:self:*", + "${local.tailscale_tag_minecraft}:19132,19133,25565", + ] + }, + { + action = "accept" + src = [local.tailscale_group_member] + dst = ["autogroup:internet:*"] }, ] # Define users and devices that can use Tailscale SSH. @@ -38,7 +71,7 @@ resource "tailscale_acl" "tailnet" { # Allow all users to SSH into their own devices in check mode. { action = "check", - src = ["autogroup:member"], + src = [local.tailscale_group_member], dst = ["autogroup:self"], users = ["autogroup:nonroot", "root"], }, @@ -51,7 +84,7 @@ resource "tailscale_tailnet_key" "reisen" { ephemeral = false preauthorized = true description = "Reisen VM" - tags = [local.tailscale_tag_infra, local.tailscale_tag_genso, local.tailscale_tag_reisen] + tags = local.tailscale_tags_reisen depends_on = [tailscale_acl.tailnet] } @@ -60,7 +93,7 @@ resource "tailscale_tailnet_key" "gensokyo" { ephemeral = false preauthorized = true description = "Reisen VM" - tags = [local.tailscale_tag_infra, local.tailscale_tag_genso] + tags = local.tailscale_tags_genso depends_on = [tailscale_acl.tailnet] }