fix: fall back to AdminPassword from PalWorldSettings.ini when ADMIN_PASSWORD is unset - #931
fix: fall back to AdminPassword from PalWorldSettings.ini when ADMIN_PASSWORD is unset#931Cabecinha84 wants to merge 2 commits into
Conversation
Container side REST API calls authenticate with ADMIN_PASSWORD. With DISABLE_GENERATE_SETTINGS=true the .ini is the source of truth and nothing populates ADMIN_PASSWORD from it, so auto reboot, backups and the graceful shutdown in term_handler fail with "Unauthorized" for anyone who sets the admin password only in the file. Read AdminPassword from PalWorldSettings.ini when ADMIN_PASSWORD is unset. ADMIN_PASSWORD keeps precedence, so nothing changes for existing setups. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds an INI-based admin password fallback so internal REST API and RCON operations authenticate when settings generation is disabled.
Changes:
- Resolves
AdminPasswordfrom the environment or INI file. - Applies the resolved password to REST API and RCON clients.
- Adds documentation and unit tests.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
scripts/helper_functions.sh |
Implements password resolution and REST API integration. |
scripts/start.sh |
Logs fallback usage and configures RCON credentials. |
tests/test-admin-password.sh |
Tests password resolution behavior. |
.github/workflows/unit-test.yml |
Runs the new tests. |
docusaurus/docs/getting-started/configuration/game-settings.md |
Documents fallback behavior. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| default: | ||
| address: "127.0.0.1:${RCON_PORT}" | ||
| password: "${ADMIN_PASSWORD}" | ||
| password: "$(get_admin_password)" |
There was a problem hiding this comment.
Confirmed, and fixed in 52e2184.
I reproduced it against a YAML parser before changing anything: in a double quoted scalar pa\ssw0rd fails to parse, tab\there is silently rewritten with a real tab, and a password holding " breaks the mapping. Worth noting the same hole already existed on the ${ADMIN_PASSWORD} line this PR replaces, so this is not new behaviour, but the fallback is a good moment to close it.
The password is now written as a YAML single quoted scalar, which processes no escape sequences at all and only needs a literal quote doubled:
password: 'pa\ss"w0rd''x'yaml_single_quoted() in helper_functions.sh does the doubling, start.sh renders the scalar before the heredoc, and tests/test-admin-password.sh covers backslashes, double quotes and single quotes. Round tripping the generated rcon.yaml through a parser returns the password unchanged.
Follow up on the review of thijsvanloef#931, four issues in the code this PR adds: * rcon.yaml took the password in a YAML double quoted scalar, so a backslash makes the file unparseable for rcon-cli and a \t or \n is silently rewritten. A single quoted scalar processes no escapes, only a literal quote is doubled. This also closes the same hole on the ADMIN_PASSWORD path, which predates this PR. * AdminPassword = "value", with spaces around the assignment, was not matched. A hand edited .ini is exactly the case this fallback exists for. * A commented out line was matched, so an old ;OptionSettings=(AdminPassword=…) kept above the live one won. * The bash regex only ever matched the first occurrence, so an empty AdminPassword="" ahead of the real one made the function give up and no fallback happened at all. Reading the file line by line, skipping comments and keeping the last non empty value covers the last three. Tests cover each case and fail without this change. ShellCheck clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GuKEQvBLi2mqFTDbxVARF7
|
Pushed 52e2184 after a self review of the code this PR adds. Copilot's
The last two are the ones that bothered me most. A bash regex only ever matches the first occurrence, so Scope is unchanged: only the function this PR introduces and the one line it already touched. ShellCheck 0.10 is clean on every script, and @thijsvanloef two things when you get a chance:
|
What this fixes
Every container side REST API call authenticates with
admin:${ADMIN_PASSWORD}(
helper_functions.shREST_API()). That is the only source of the credential.When
DISABLE_GENERATE_SETTINGS=true,compile-settings.shnever runs, so the.inibecomes the source of truth and nothing populates
ADMIN_PASSWORDfrom it. There is no.ini→ env path in the image. For anyone who sets the admin password only inPalWorldSettings.ini— which is exactly what theDISABLE_GENERATE_SETTINGSdocs tellusers to do —
ADMIN_PASSWORDstays empty and every REST call returnsUnauthorized.Two user visible consequences:
Auto reboot never runs.
auto_reboot.shgets a 401 onget_player_count(whichreturns
0with rc 0, so the players-online guard passes silently), thenshutdown_serverrefuses to shut down becausesave_server401s — "Do not shutdown ifnot able to save". The cron job exits 1 a couple of seconds after it starts, with no
error beyond supercronic's own line:
No graceful shutdown ever saves.
init.shterm_handlercalls the sameshutdown_server; when it fails it falls through tokill -SIGTERM "$(pidof PalServer-Linux-Shipping)"under a# Does not savecomment. Sodocker stop, restarts and redeploys all discard everything since the last autosave.Same root cause for
rcon.yaml, which is written with an empty password.The change
get_admin_password()returnsADMIN_PASSWORDwhen it is set, and otherwise readsAdminPasswordfromPalWorldSettings.ini.REST_API()and thercon.yamlheredoc use it.ADMIN_PASSWORDkeeps precedence, so behaviour is unchanged for every setup that sets it.DISABLE_GENERATE_SETTINGS=falsethe.iniis generated fromADMIN_PASSWORD, so thefallback is a no-op there too.
term_handlerininit.sh— the parentprocess, whose environment nothing else can reach.
compile-settings.shwrite (AdminPassword="…") ismatched; an unquoted hand edited value falls back to today's behaviour.
start.shlogs a line when the fallback applies, so it is visible in the container log.Deliberately out of scope:
RESTAPIPort/RESTAPIEnabledhave the same env vs.inisplitbut are not part of this bug, so they are untouched.
Testing
tests/test-admin-password.sh(new, wired intounit-test.yml) covers: value read from thefile, special characters preserved,
CRLFstripped, emptyAdminPasswordrejected, missingfile rejected,
ADMIN_PASSWORDprecedence,.iniused whenADMIN_PASSWORDis empty, and anunset
ADMIN_PASSWORDtolerated..shellcheckrc(external-sources=true) on every script in the repo.DISABLE_GENERATE_SETTINGS=trueand the password set only in the
.ini:curl -u "admin:$ADMIN_PASSWORD" …/v1/api/playersreturns401 while the same call with the
.inipassword returns 200, and the nightlyauto_reboot.shjobexits 1 every night. With this patch applied,
REST_API()builds the call with the.inipassword(checked with a stubbed
curl), and withADMIN_PASSWORDset it is unchanged.tr, no GNU only flags, nothing architecture specific.AI disclosure
Per
AI_GUIDELINES.md: this patch was written with the help of an AI assistant. The failure wasdiagnosed on a live production server first, the diff was reviewed by hand, and the tests and
ShellCheck run were executed locally rather than assumed.