Skip to content

UNOMI-974: Require an explicit admin and health-check password at startup - #850

Merged
sergehuber merged 2 commits into
masterfrom
UNOMI-974-explicit-admin-password
Aug 16, 2026
Merged

UNOMI-974: Require an explicit admin and health-check password at startup#850
sergehuber merged 2 commits into
masterfrom
UNOMI-974-explicit-admin-password

Conversation

@sergehuber

Copy link
Copy Markdown
Contributor

The shipped karaf and health-check accounts fell back to a value carried in the distribution when
the operator set nothing. Both now resolve only from UNOMI_ROOT_PASSWORD and
UNOMI_HEALTHCHECK_PASSWORD, with no fallback.

Removing the fallback is necessary but not sufficient, because of how Karaf resolves properties:
PropertiesLoader and PropertiesLoginModule both substitute with defaultsToEmptyString=true, so an
unset property yields an empty value rather than an unusable account. The account must therefore be
made unusable deliberately. bin/setenv and the Docker entrypoint refuse to start when either
variable is unset, and AuthenticationFilter requires a non-blank password on any Basic credential it
accepts.

That last check runs at each point a Basic credential is consumed rather than once at the top of
filter(). The public paths and every V2 path ignore Authorization entirely, so a single up-front
check would turn a stray or stale header into a 401 on requests that must succeed anonymously.

The shell guards cannot cover every way the JVM is started - karaf.bat calls setenv.bat without
testing errorlevel, as setenv itself documents, and a systemd unit or container command override
skips them too - so the REST-layer requirement is what actually holds. The guards are executed by
the tests rather than grepped, because a check whose text is present but whose condition never
matches would otherwise pass silently.

Documentation, examples, compose files and the setup scripts no longer carry a sample password.
Operators upgrading must set both variables before starting; the 3.0-to-3.1 migration guide covers
it.

Jira: https://issues.apache.org/jira/browse/UNOMI-974

…rtup

The shipped karaf and health-check accounts fell back to a value carried in the distribution when
the operator set nothing. Both now resolve only from UNOMI_ROOT_PASSWORD and
UNOMI_HEALTHCHECK_PASSWORD, with no fallback.

Removing the fallback is necessary but not sufficient, because of how Karaf resolves properties:
PropertiesLoader and PropertiesLoginModule both substitute with defaultsToEmptyString=true, so an
unset property yields an empty value rather than an unusable account. The account must therefore be
made unusable deliberately. bin/setenv and the Docker entrypoint refuse to start when either
variable is unset, and AuthenticationFilter requires a non-blank password on any Basic credential it
accepts.

That last check runs at each point a Basic credential is consumed rather than once at the top of
filter(). The public paths and every V2 path ignore Authorization entirely, so a single up-front
check would turn a stray or stale header into a 401 on requests that must succeed anonymously.

The shell guards cannot cover every way the JVM is started - karaf.bat calls setenv.bat without
testing errorlevel, as setenv itself documents, and a systemd unit or container command override
skips them too - so the REST-layer requirement is what actually holds. The guards are executed by
the tests rather than grepped, because a check whose text is present but whose condition never
matches would otherwise pass silently.

Documentation, examples, compose files and the setup scripts no longer carry a sample password.
Operators upgrading must set both variables before starting; the 3.0-to-3.1 migration guide covers
it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@asf-gitbox-commits
asf-gitbox-commits force-pushed the UNOMI-974-explicit-admin-password branch from 8f0f416 to 4e3092f Compare August 14, 2026 14:32
…ndpoints too

The blank-password refusal added to AuthenticationFilter only covers JAX-RS. Two other surfaces
parse the Basic header themselves and call LoginContext.login() directly against the same karaf
realm, so an empty password still authenticated there: the health check HTTP context and the
GraphQL servlet validator. Both now refuse it.

The health check path was reachable in practice. It split the decoded credential with split(":"),
which discards trailing empty strings, so "health::x" decoded to ["health", "", "x"] and handed
JAAS an empty password. Bounding the split to two parts restores the RFC 7617 rule that the
password is everything after the first colon, and makes the emptiness check meaningful. The same
parser threw out of the servlet on several malformed headers, answering 500 where 401 was meant;
it now returns no credential instead. The scheme is matched case-insensitively (RFC 7235 2.1)
because the previous blind substring(6) accepted "basic " and a stricter check would have started
rejecting those clients.

Both tests stub a realm that accepts any credential. That is deliberate: a rejecting realm answers
"not authenticated" whether or not the guard exists, so only an accepting one can tell "refused
before JAAS" from "JAAS said no". The health check test asserts on the credential the realm was
actually handed, which is the only way to catch the split bug, since the bypass still returned
"authenticated".

extractBasicCredentials is covered exhaustively -- 9 header shapes that must yield no credential
and 12 that must decode to specific values, including the empty, colon-only and non-ASCII cases,
and an assertion that neither element is ever null so the emptiness check cannot throw.

AuthenticationFilter's guard at the ordinary V3 private path had no test: the suite only exercised
the tenants branch and the V2 branch, so deleting that third call site left every test green. Two
tests now cover it.

Documentation that still presented the removed karaf/karaf and health/health pairs as working
defaults is corrected, including the configuration chapter, which contradicted its own REST API
security section. building-and-deploying told readers to run ./bin/karaf with no password step,
which now fails outright; it gains the export step and the Windows caveat.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sergehuber
sergehuber merged commit c46ff23 into master Aug 16, 2026
7 checks passed
@sergehuber
sergehuber deleted the UNOMI-974-explicit-admin-password branch August 16, 2026 06:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant