From 0da77d6559ccc96ab56aaf9adec3e1b01b8090ad Mon Sep 17 00:00:00 2001 From: L-Nafaryus Date: Thu, 25 Jan 2024 18:09:52 +0500 Subject: [PATCH] catarina: split services to modules --- .secrets | 2 +- flake.nix | 4 +- nixosConfigurations/catarina/default.nix | 110 +------- nixosConfigurations/catarina/hardware.nix | 2 +- .../catarina/services/gitea.nix | 81 ++++++ .../catarina/services/papermc.nix | 165 ++++++++++++ nixosModules/papermc/default.nix | 237 ++++++++++++++++++ 7 files changed, 495 insertions(+), 106 deletions(-) create mode 100644 nixosConfigurations/catarina/services/gitea.nix create mode 100644 nixosConfigurations/catarina/services/papermc.nix create mode 100644 nixosModules/papermc/default.nix diff --git a/.secrets b/.secrets index d4a686b..3d189e2 160000 --- a/.secrets +++ b/.secrets @@ -1 +1 @@ -Subproject commit d4a686b321770dbe16130e31966e87143440469e +Subproject commit 3d189e205d5fc4194726010e5f31178235c1c046 diff --git a/flake.nix b/flake.nix index 8297634..0574c1e 100644 --- a/flake.nix +++ b/flake.nix @@ -18,7 +18,6 @@ outputs = inputs @ { self, nixpkgs, home-manager, crane, nixgl, simple-nixos-mailserver, sops-nix, ... }: { - lib = import ./lib {}; nixosConfigurations = { @@ -41,6 +40,7 @@ self.nixosModules.spoofdpi simple-nixos-mailserver.nixosModules.mailserver sops-nix.nixosModules.sops + self.nixosModules.papermc ]; specialArgs = { inherit inputs self; }; }; @@ -50,6 +50,8 @@ bonfire = import ./nixosModules/bonfire.nix; spoofdpi = import ./nixosModules/spoofdpi { inherit self; }; + + papermc = import ./nixosModules/papermc { inherit self; }; }; templates = { diff --git a/nixosConfigurations/catarina/default.nix b/nixosConfigurations/catarina/default.nix index 855ba62..a2c4fd4 100644 --- a/nixosConfigurations/catarina/default.nix +++ b/nixosConfigurations/catarina/default.nix @@ -2,7 +2,11 @@ rec { system.stateVersion = "23.11"; - imports = [ ./hardware.nix ./users.nix ]; + imports = [ + ./hardware.nix ./users.nix + ./services/papermc.nix + ./services/gitea.nix + ]; # Nix settings nix = { @@ -47,7 +51,6 @@ rec { autoSuspend = false; }; desktopManager.gnome.enable = true; - windowManager.awesome.enable = true; }; services.printing.enable = true; @@ -100,14 +103,14 @@ rec { certs = { "elnafo.ru" = { domain = "elnafo.ru"; - extraDomainNames = [ "*.elnafo.ru" ]; + extraDomainNames = [ "www.elnafo.ru" "vcs.elnafo.ru" "media.elnafo.ru" "mc.elnafo.ru" "map.mc.elnafo.ru"]; dnsProvider = "webnames"; credentialsFile = config.sops.secrets."dns".path; webroot = null; }; }; }; - + services.nginx = { enable = true; @@ -131,12 +134,6 @@ rec { globalRedirect = "elnafo.ru"; }; - "vcs.elnafo.ru" = { - forceSSL = true; - useACMEHost = "elnafo.ru"; - locations."/".proxyPass = "http://127.0.0.1:3001"; - }; - "media.elnafo.ru" = { forceSSL = true; useACMEHost = "elnafo.ru"; @@ -144,81 +141,6 @@ rec { locations."/".proxyPass = "http://127.0.0.1:8096"; }; }; - - - }; - - services.postgresql = { - enable = true; - authentication = '' - # Type Database DB-User Auth-Method Ident-Map(optional) - local git all ident map=gitea-users - ''; - identMap = '' - # MapName System-User DB-User - gitea-users git git - ''; - ensureDatabases = [ "git" ]; - }; - - services.gitea = { - enable = true; - - user = "git"; - group = "gitea"; - stateDir = "/var/lib/gitea"; - - settings = { - server = { - DOMAIN = "vcs.elnafo.ru"; - ROOT_URL = "https://vcs.elnafo.ru/"; - HTTP_ADDRESS = "127.0.0.1"; - HTTP_PORT = 3001; - }; - - session.COOKIE_SECURE = true; - - mailer = { - ENABLED = true; - FROM = "git@elnafo.ru"; - PROTOCOL = "smtps"; - SMTP_ADDR = "smtp.elnafo.ru"; - SMTP_PORT = 465; - USER = "git"; - USE_CLIENT_CERT = true; - CLIENT_CERT_FILE = "${config.security.acme.certs."elnafo.ru".directory}/cert.pem"; - CLIENT_KEY_FILE = "${config.security.acme.certs."elnafo.ru".directory}/key.pem"; - }; - - service.DISABLE_REGISTRATION = true; - - other = { - SHOW_FOOTER_VERSION = false; - SHOW_FOOTER_TEMPLATE_LOAD_TIME = false; - }; - }; - - mailerPasswordFile = config.sops.secrets."gitea/mail".path; - - database = { - type = "postgres"; - passwordFile = config.sops.secrets."database/git".path; - name = "git"; - user = "git"; - }; - - lfs.enable = true; - - appName = "Elnafo VCS"; - }; - - users.users.${services.gitea.user} = { - description = "Gitea Service"; - home = services.gitea.stateDir; - useDefaultShell = true; - group = services.gitea.group; - extraGroups = [ "nginx" ]; - isSystemUser = true; }; mailserver = { @@ -238,24 +160,6 @@ rec { openFirewall = true; }; - services.minecraft-server = { - enable = true; - eula = true; - declarative = true; - openFirewall = true; - serverProperties = { - server-port = 25565; - gamemode = "survival"; - motd = "NixOS Minecraft Server"; - max-players = 10; - level-seed = "66666666"; - enable-status = true; - enforce-secure-profile = false; - difficulty = "normal"; - online-mode = false; - }; - }; - services.spoofdpi.enable = true; # Packages diff --git a/nixosConfigurations/catarina/hardware.nix b/nixosConfigurations/catarina/hardware.nix index 2f7be7c..f3a6a44 100644 --- a/nixosConfigurations/catarina/hardware.nix +++ b/nixosConfigurations/catarina/hardware.nix @@ -109,7 +109,7 @@ firewall = { enable = true; - allowedTCPPorts = [ 80 443 3001 ]; + allowedTCPPorts = [ 80 443 3001 25600 ]; }; interfaces.enp9s0.ipv4.addresses = [ { diff --git a/nixosConfigurations/catarina/services/gitea.nix b/nixosConfigurations/catarina/services/gitea.nix new file mode 100644 index 0000000..1a1fbaf --- /dev/null +++ b/nixosConfigurations/catarina/services/gitea.nix @@ -0,0 +1,81 @@ +{ config, lib, pkgs, ... }: +{ + services.postgresql = { + enable = true; + authentication = '' + # Type Database DB-User Auth-Method Ident-Map(optional) + local git all ident map=gitea-users + ''; + identMap = '' + # MapName System-User DB-User + gitea-users git git + ''; + ensureDatabases = [ "git" ]; + }; + + services.gitea = { + enable = true; + + user = "git"; + group = "gitea"; + stateDir = "/var/lib/gitea"; + + settings = { + server = { + DOMAIN = "vcs.elnafo.ru"; + ROOT_URL = "https://vcs.elnafo.ru/"; + HTTP_ADDRESS = "127.0.0.1"; + HTTP_PORT = 3001; + }; + + session.COOKIE_SECURE = true; + + mailer = { + ENABLED = true; + FROM = "git@elnafo.ru"; + PROTOCOL = "smtps"; + SMTP_ADDR = "smtp.elnafo.ru"; + SMTP_PORT = 465; + USER = "git"; + USE_CLIENT_CERT = true; + CLIENT_CERT_FILE = "${config.security.acme.certs."elnafo.ru".directory}/cert.pem"; + CLIENT_KEY_FILE = "${config.security.acme.certs."elnafo.ru".directory}/key.pem"; + }; + + service.DISABLE_REGISTRATION = true; + + other = { + SHOW_FOOTER_VERSION = false; + SHOW_FOOTER_TEMPLATE_LOAD_TIME = false; + }; + }; + + mailerPasswordFile = config.sops.secrets."gitea/mail".path; + + database = { + type = "postgres"; + passwordFile = config.sops.secrets."database/git".path; + name = "git"; + user = "git"; + }; + + lfs.enable = true; + + appName = "Elnafo VCS"; + }; + + users.users.${config.services.gitea.user} = { + description = "Gitea Service"; + home = config.services.gitea.stateDir; + useDefaultShell = true; + group = config.services.gitea.group; + extraGroups = [ "nginx" ]; + isSystemUser = true; + }; + + services.nginx.virtualHosts."vcs.elnafo.ru" = { + forceSSL = true; + useACMEHost = "elnafo.ru"; + locations."/".proxyPass = "http://127.0.0.1:3001"; + }; +} diff --git a/nixosConfigurations/catarina/services/papermc.nix b/nixosConfigurations/catarina/services/papermc.nix new file mode 100644 index 0000000..a5a9785 --- /dev/null +++ b/nixosConfigurations/catarina/services/papermc.nix @@ -0,0 +1,165 @@ +{ config, lib, pkgs, ... }: +let + inherit (pkgs) stdenv fetchurl; + + playerlist = [ + { + name = "L_Nafaryus"; + uuid = "02c47438-79eb-3938-b5e0-d7c03cb5709f"; + level = 4; + } + { + name = "AfroPriest"; + uuid = "6fa9251d-11a5-33ad-ada3-312f0632eab1"; + level = 3; + } + ]; + + operators = lib.filter (player: player.level > 0) playerlist; + whitelist = map (player: removeAttrs player [ "level" ]) playerlist; + + # Plugins + + passky = stdenv.mkDerivation rec { + pname = "Passky"; + version = "2.1.1"; + src = fetchurl { + url = "https://hangarcdn.papermc.io/plugins/Black1_TV/Passky/versions/${version}/PAPER/Passky-${version}.jar"; + hash = "sha256-D5NpFrkGLgZNMS5WlMRM3Uv07hPsI9Hdsii2whTAZ2o="; + }; + meta.homepage = "https://hangar.papermc.io/Black1_TV/Passky"; + phases = [ "installPhase" ]; + installPhase = '' + mkdir -p $out/bin + cp $src $out/bin/${pname}.jar + ''; + }; + + grimAnticheat = stdenv.mkDerivation rec { + pname = "GrimAC"; + version = "2.3.46"; + src = fetchurl { + url = "https://hangarcdn.papermc.io/plugins/GrimAnticheat/GrimAnticheat/versions/${version}/PAPER/grimac-${version}.jar"; + hash = "sha256-tG8pBDMU4N/Ijn5RfdsQrtY4/gEhN1wEDCopqOSIqB4="; + }; + meta.homepage = "https://hangar.papermc.io/GrimAnticheat/GrimAnticheat"; + phases = [ "installPhase" ]; + installPhase = '' + mkdir -p $out/bin + cp $src $out/bin/${pname}.jar + ''; + }; + + viaVersion = stdenv.mkDerivation rec { + pname = "ViaVersion"; + version = "4.9.2"; + src = fetchurl { + url = "https://hangarcdn.papermc.io/plugins/ViaVersion/ViaVersion/versions/${version}/PAPER/ViaVersion-${version}.jar"; + hash = "sha256-dvcyqCpIjArKCnUAD/L+lG/5gRQ9fLMKcl/+o8sLmYs="; + }; + meta.homepage = "https://hangar.papermc.io/ViaVersion/ViaVersion"; + phases = [ "installPhase" ]; + installPhase = '' + mkdir -p $out/bin + cp $src $out/bin/${pname}.jar + ''; + }; + + directionHUD = stdenv.mkDerivation rec { + pname = "DirectionHUD"; + version = "1.2.2"; + src = fetchurl { + url = "https://hangarcdn.papermc.io/plugins/other/DirectionHUD/versions/${version}%2B1.18-1.20.2/PAPER/directionhud-spigot-${version}%2B1.18-1.20.2.jar"; + hash = "sha256-F+86Q58+3VoqNoD8P38bu8u1Hx8Si0lxNXZnF/R4hAg="; + }; + meta.homepage = "https://hangar.papermc.io/other/DirectionHUD"; + phases = [ "installPhase" ]; + installPhase = '' + mkdir -p $out/bin + cp $src $out/bin/${pname}.jar + ''; + }; + + miniMOTD = stdenv.mkDerivation rec { + pname = "MiniMOTD"; + version = "2.0.14"; + src = fetchurl { + url = "https://hangarcdn.papermc.io/plugins/jmp/MiniMOTD/versions/${version}/PAPER/minimotd-bukkit-${version}.jar"; + hash = "sha256-d7l/pZGxteS2A9c9PIZASDTACGev8HY5SHZRvcxBc5A="; + }; + meta.homepage = "https://hangar.papermc.io/jmp/MiniMOTD"; + phases = [ "installPhase" ]; + installPhase = '' + mkdir -p $out/bin + cp $src $out/bin/${pname}.jar + ''; + }; + + skinRestorer = stdenv.mkDerivation rec { + pname = "SkinRestorer"; + version = "15.0.2"; + src = fetchurl { + url = "https://github.com/SkinsRestorer/SkinsRestorerX/releases/download/${version}/SkinsRestorer.jar"; + hash = "sha256-fhAegFtl22xKXMi5MbsXCYOjbfqOlQTnILoEJxCDbkc="; + }; + meta.homepage = "https://hangar.papermc.io/SRTeam/SkinsRestorer"; + phases = [ "installPhase" ]; + installPhase = '' + mkdir -p $out/bin + cp $src $out/bin/${pname}.jar + ''; + }; + + squaremap = stdenv.mkDerivation rec { + pname = "squaremap"; + version = "1.2.2"; + src = fetchurl { + url = "https://hangarcdn.papermc.io/plugins/jmp/squaremap/versions/${version}/PAPER/squaremap-paper-mc1.20.2-${version}.jar"; + hash = "sha256-Z8AWzZLlZavF8YYs1kslhtCvzq5fZ7O97mTx3hCgj78="; + }; + meta.homepage = "https://hangar.papermc.io/jmp/squaremap"; + phases = [ "installPhase" ]; + installPhase = '' + mkdir -p $out/bin + cp $src $out/bin/${pname}.jar + ''; + }; + + plugins = [ + passky grimAnticheat viaVersion directionHUD miniMOTD skinRestorer squaremap + ]; + +in { + services.papermc = { + enable = true; + eula = true; + openFirewall = true; + serverProperties = { + server-port = 25565; + gamemode = "survival"; + motd = "NixOS Paper Server"; + max-players = 10; + level-seed = "66666666"; + enable-status = true; + enforce-secure-profile = false; + difficulty = "normal"; + online-mode = false; + enable-rcon = true; + "rcon.port" = 25600; + white-list = true; + }; + rconPasswordFile = config.sops.secrets."papermc/rcon".path; + whitelist = whitelist; + ops = operators; + extraPreStart = '' + mkdir -p ${builtins.concatStringsSep " " (map (v: "plugins/${v.pname}") plugins)} + '' + builtins.concatStringsSep "\n" (map (v: "ln -s ${v.outPath}/bin/${v.pname}.jar plugins/") plugins) + ; + }; + + services.nginx.virtualHosts."map.mc.elnafo.ru" = { + forceSSL = true; + useACMEHost = "elnafo.ru"; + locations."/".proxyPass = "http://127.0.0.1:8088"; + }; +} diff --git a/nixosModules/papermc/default.nix b/nixosModules/papermc/default.nix new file mode 100644 index 0000000..be4c69a --- /dev/null +++ b/nixosModules/papermc/default.nix @@ -0,0 +1,237 @@ +{ self, ... }: +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.services.papermc; + + eulaFile = builtins.toFile "eula.txt" '' + # eula.txt managed by NixOS Configuration + eula=true + ''; + + whitelistFile = pkgs.writeText "whitelist.json" + (builtins.toJSON cfg.whitelist); + + opsFile = pkgs.writeText "whitelist.json" + (builtins.toJSON cfg.ops); + + cfgToString = v: if builtins.isBool v then boolToString v else toString v; + + serverPropertiesFile = let + serverProperties' = if (cfg.rconPasswordFile == null) then cfg.serverProperties else + (removeAttrs cfg.serverProperties [ "rcon.password" ]); + in pkgs.writeText "server.properties" ('' + # server.properties managed by NixOS configuration + '' + concatStringsSep "\n" (mapAttrsToList + (n: v: "${n}=${cfgToString v}") serverProperties') + + lib.optionalString (cfg.rconPasswordFile != null) "\nrcon.password=#rconpass#"); + + stopScript = pkgs.writeShellScript "minecraft-server-stop" '' + echo stop > ${config.systemd.sockets.papermc.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 + ''; + + defaultServerPort = 25565; + + serverPort = cfg.serverProperties.server-port or defaultServerPort; + + rconPort = if cfg.serverProperties.enable-rcon or false + then cfg.serverProperties."rcon.port" or 25575 + else null; + + queryPort = if cfg.serverProperties.enable-query or false + then cfg.serverProperties."query.port" or 25565 + else null; + +in { + options.services.papermc = { + enable = mkEnableOption "Enables the PaperMC service."; + + openFirewall = mkOption rec { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether to open ports in the firewall for the server. + ''; + }; + + eula = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether you agree to [Mojangs EULA](https://account.mojang.com/documents/minecraft_eula). + This option must be set to `true` to run Minecraft server. + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/papermc"; + description = lib.mdDoc '' + Directory to store Minecraft database and other state/data files. + ''; + }; + + whitelist = mkOption { + type = types.listOf types.attrs; + default = {}; + description = lib.mdDoc '' + This is a mapping from Minecraft usernames to UUIDs. + ''; + }; + + ops = mkOption { + type = types.listOf types.attrs; + default = {}; + }; + + serverProperties = mkOption { + type = with types; attrsOf (oneOf [ bool int str ]); + default = { + "rcon.password" = mkIf (cfg.rconPasswordFile != null) "#rconpass#"; + }; + example = literalExpression '' + { + server-port = 43000; + difficulty = 3; + gamemode = 1; + max-players = 5; + motd = "NixOS Minecraft server!"; + white-list = true; + enable-rcon = true; + "rcon.password" = "hunter2"; + } + ''; + description = lib.mdDoc '' + Minecraft server properties for the server.properties file. See + + for documentation on these values. + ''; + }; + + rconPasswordFile = mkOption { + type = types.nullOr types.str; + default = null; + example = "/var/lib/secrets/papermc/rconpw"; + }; + + package = mkPackageOption pkgs "papermc" { + example = "papermc_6_6_6"; + }; + + jvmOpts = mkOption { + type = types.separatedString " "; + default = "-Xmx2048M -Xms2048M"; + # Example options from https://minecraft.gamepedia.com/Tutorials/Server_startup_script + example = "-Xms4092M -Xmx4092M -XX:+UseG1GC -XX:+CMSIncrementalPacing " + + "-XX:+CMSClassUnloadingEnabled -XX:ParallelGCThreads=2 " + + "-XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=10"; + description = lib.mdDoc "JVM options for the Minecraft server."; + }; + + extraPreStart = mkOption { + type = types.lines; + default = ''''; + }; + }; + + config = mkIf cfg.enable { + users.users.papermc = { + description = "Minecraft server service user"; + home = cfg.dataDir; + createHome = true; + isSystemUser = true; + group = "papermc"; + }; + users.groups.papermc = {}; + + systemd.sockets.papermc = { + bindsTo = [ "papermc.service" ]; + socketConfig = { + ListenFIFO = "/run/papermc.stdin"; + SocketMode = "0660"; + SocketUser = "papermc"; + SocketGroup = "papermc"; + RemoveOnStop = true; + FlushPending = true; + }; + }; + + systemd.services.papermc = { + description = "PaperMC Service"; + wantedBy = [ "multi-user.target" ]; + requires = [ "papermc.socket" ]; + after = [ "network.target" "papermc.socket" ]; + + serviceConfig = { + ExecStart = "${cfg.package}/bin/minecraft-server ${cfg.jvmOpts}"; + ExecStop = "${stopScript} $MAINPID"; + Restart = "always"; + User = "papermc"; + WorkingDirectory = cfg.dataDir; + + 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" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + UMask = "0077"; + }; + + preStart = let + replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret"; + in '' + ln -sf ${eulaFile} eula.txt + + cp -b --suffix=.stateful ${whitelistFile} whitelist.json + cp -b --suffix=.stateful ${opsFile} ops.json + cp -b --suffix=.stateful ${serverPropertiesFile} server.properties + + chmod +w whitelist.json ops.json server.properties + + ${lib.optionalString (cfg.rconPasswordFile != null) '' + ${replaceSecretBin} '#rconpass#' '${cfg.rconPasswordFile}' server.properties + ''} + '' + cfg.extraPreStart; + }; + + networking.firewall = mkIf cfg.openFirewall ({ + allowedUDPPorts = [ serverPort ]; + allowedTCPPorts = [ serverPort ] + ++ optional (queryPort != null) queryPort + ++ optional (rconPort != null) rconPort; + }); + + assertions = [ + { assertion = cfg.eula; + message = "You must agree to Mojangs EULA to run minecraft-server." + + " Read https://account.mojang.com/documents/minecraft_eula and" + + " set `services.minecraft-server.eula` to `true` if you agree."; + } + ]; + }; +}