fix(nixos): address Nix review findings
Render Palworld secrets through its documented settings file and remove stale RCON configuration. Harden generated commands, centralize shared modules, and codify the reviewed Nix conventions.
This commit is contained in:
@@ -62,18 +62,6 @@
|
||||
description = "Start the Palworld server after the guest reaches network readiness.";
|
||||
};
|
||||
|
||||
rcon = {
|
||||
enable = lib.mkEnableOption "Palworld RCON" // {
|
||||
default = true;
|
||||
};
|
||||
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 25575;
|
||||
description = "TCP port for Palworld RCON.";
|
||||
};
|
||||
};
|
||||
|
||||
passwordFile = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
description = "Runtime file containing the Palworld server password.";
|
||||
@@ -89,65 +77,120 @@
|
||||
let
|
||||
service = config.modules.services.games.palworld;
|
||||
savedDir = "${service.installDir}/Pal/Saved";
|
||||
defaultSettingsFile = "${service.installDir}/DefaultPalWorldSettings.ini";
|
||||
settingsFile = "${savedDir}/Config/LinuxServer/PalWorldSettings.ini";
|
||||
renderSettingsScript = pkgs.writeText "render-palworld-settings.py" ''
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
default_path, settings_path, server_name, password_path, admin_password_path = sys.argv[1:]
|
||||
default_path = Path(default_path)
|
||||
settings_path = Path(settings_path)
|
||||
|
||||
source_path = settings_path if settings_path.exists() else default_path
|
||||
text = source_path.read_text(encoding="utf-8")
|
||||
|
||||
def read_secret(path):
|
||||
return Path(path).read_text(encoding="utf-8").rstrip("\r\n")
|
||||
|
||||
def set_string_setting(text, name, value):
|
||||
assignment = f"{name}={json.dumps(value, ensure_ascii=False)}"
|
||||
pattern = re.compile(rf'(?<![A-Za-z0-9_]){re.escape(name)}="(?:[^"\\]|\\.)*"')
|
||||
text, replacements = pattern.subn(lambda _match: assignment, text, count=1)
|
||||
if replacements:
|
||||
return text
|
||||
|
||||
marker = "OptionSettings=("
|
||||
if marker not in text:
|
||||
raise ValueError(f"{source_path} has no OptionSettings declaration")
|
||||
return text.replace(marker, f"{marker}{assignment},", 1)
|
||||
|
||||
settings = {
|
||||
"ServerName": server_name,
|
||||
"ServerPassword": read_secret(password_path),
|
||||
"AdminPassword": read_secret(admin_password_path),
|
||||
}
|
||||
for name, value in settings.items():
|
||||
text = set_string_setting(text, name, value)
|
||||
|
||||
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
descriptor, temporary_name = tempfile.mkstemp(
|
||||
prefix=f".{settings_path.name}.",
|
||||
dir=settings_path.parent,
|
||||
)
|
||||
temporary_path = Path(temporary_name)
|
||||
try:
|
||||
os.fchmod(descriptor, 0o600)
|
||||
with os.fdopen(descriptor, "w", encoding="utf-8", newline="\n") as settings_file:
|
||||
settings_file.write(text)
|
||||
os.replace(temporary_path, settings_path)
|
||||
finally:
|
||||
temporary_path.unlink(missing_ok=True)
|
||||
'';
|
||||
startScript = pkgs.writeShellScript "palworld-start" ''
|
||||
set -eu
|
||||
|
||||
${pkgs.coreutils}/bin/install -d \
|
||||
${lib.getExe' pkgs.coreutils "install"} -d \
|
||||
-m 0770 \
|
||||
-- \
|
||||
${lib.escapeShellArg service.stateDir} \
|
||||
${lib.escapeShellArg (builtins.dirOf savedDir)}
|
||||
|
||||
if [ -L ${lib.escapeShellArg savedDir} ]; then
|
||||
current_target="$(${pkgs.coreutils}/bin/readlink ${lib.escapeShellArg savedDir})"
|
||||
current_target="$(${lib.getExe' pkgs.coreutils "readlink"} -- ${lib.escapeShellArg savedDir})"
|
||||
if [ "$current_target" != ${lib.escapeShellArg service.stateDir} ]; then
|
||||
${pkgs.coreutils}/bin/rm ${lib.escapeShellArg savedDir}
|
||||
${lib.getExe' pkgs.coreutils "rm"} -- ${lib.escapeShellArg savedDir}
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -d ${lib.escapeShellArg savedDir} ] && [ ! -L ${lib.escapeShellArg savedDir} ]; then
|
||||
if ${pkgs.findutils}/bin/find ${lib.escapeShellArg service.stateDir} -mindepth 1 -maxdepth 1 | ${pkgs.gnugrep}/bin/grep -q .; then
|
||||
${pkgs.coreutils}/bin/rm -rf ${lib.escapeShellArg savedDir}
|
||||
if ${lib.getExe' pkgs.findutils "find"} -- ${lib.escapeShellArg service.stateDir} -mindepth 1 -maxdepth 1 | ${lib.getExe pkgs.gnugrep} -q .; then
|
||||
${lib.getExe' pkgs.coreutils "rm"} -rf -- ${lib.escapeShellArg savedDir}
|
||||
else
|
||||
${pkgs.coreutils}/bin/cp -a ${lib.escapeShellArg savedDir}/. ${lib.escapeShellArg service.stateDir}/
|
||||
${pkgs.coreutils}/bin/rm -rf ${lib.escapeShellArg savedDir}
|
||||
${lib.getExe' pkgs.coreutils "cp"} -a -- ${lib.escapeShellArg savedDir}/. ${lib.escapeShellArg service.stateDir}/
|
||||
${lib.getExe' pkgs.coreutils "rm"} -rf -- ${lib.escapeShellArg savedDir}
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -e ${lib.escapeShellArg savedDir} ]; then
|
||||
${pkgs.coreutils}/bin/ln -s ${lib.escapeShellArg service.stateDir} ${lib.escapeShellArg savedDir}
|
||||
${lib.getExe' pkgs.coreutils "ln"} -s -- ${lib.escapeShellArg service.stateDir} ${lib.escapeShellArg savedDir}
|
||||
fi
|
||||
|
||||
password="$(${pkgs.coreutils}/bin/tr -d '\n' < ${lib.escapeShellArg service.passwordFile})"
|
||||
admin_password="$(${pkgs.coreutils}/bin/tr -d '\n' < ${lib.escapeShellArg service.adminPasswordFile})"
|
||||
${lib.getExe pkgs.python3} \
|
||||
${renderSettingsScript} \
|
||||
${lib.escapeShellArg defaultSettingsFile} \
|
||||
${lib.escapeShellArg settingsFile} \
|
||||
${lib.escapeShellArg service.serverName} \
|
||||
${lib.escapeShellArg service.passwordFile} \
|
||||
${lib.escapeShellArg service.adminPasswordFile}
|
||||
|
||||
exec ./PalServer.sh \
|
||||
"$@" \
|
||||
"-serverpassword=$password" \
|
||||
"-adminpassword=$admin_password"
|
||||
exec ./PalServer.sh "$@"
|
||||
'';
|
||||
in
|
||||
{
|
||||
modules.services.steamcmd = {
|
||||
enable = true;
|
||||
inherit (service) steamDir installDir stateDir;
|
||||
serviceName = "palworld-${lib.toLower service.serverName}";
|
||||
serviceName = "palworld";
|
||||
appId = 2394010;
|
||||
inherit (service) autoStart;
|
||||
executable = "${startScript}";
|
||||
arguments = [
|
||||
"-useperfthreads"
|
||||
"-NoAsyncLoadingThread"
|
||||
"-UseMultithreadForDS"
|
||||
"-port=${toString service.port}"
|
||||
"-publicport=${toString service.port}"
|
||||
"-servername=${service.serverName}"
|
||||
"-players=${toString service.maxPlayers}"
|
||||
]
|
||||
++ lib.optional service.publicLobby "-publiclobby"
|
||||
++ lib.optionals (service.publicIP != null) [ "-publicip=${service.publicIP}" ]
|
||||
++ lib.optional service.rcon.enable "-rcon";
|
||||
++ lib.optionals service.publicLobby (
|
||||
[
|
||||
"-publiclobby"
|
||||
"-publicport=${toString service.port}"
|
||||
]
|
||||
++ lib.optionals (service.publicIP != null) [ "-publicip=${service.publicIP}" ]
|
||||
);
|
||||
udpPorts = [ service.port ];
|
||||
tcpPorts = [ service.port ];
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user