Skip to content

Allow the credentials to be given as files rather than as env vars - #31

Closed
tigerblue77 wants to merge 2 commits into
ShaneMcC:masterfrom
tigerblue77:upstream/omsa-credentials-from-file
Closed

tigerblue77 wants to merge 2 commits into
ShaneMcC:masterfrom
tigerblue77:upstream/omsa-credentials-from-file

Conversation

@tigerblue77

@tigerblue77 tigerblue77 commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

This is the one that adds rather than fixes, so it is where your judgement matters most and mine matters least. The case for it: OMSA_PASS is readable for the life of the container by anything that can run docker inspect or read /proc/<pid>/environ, which is unavoidable for a value passed as -e and is the one thing a file does not have. Twenty lines, no behaviour change for anyone who keeps using the environment variables, and it is what makes this image deployable under compose or swarm secrets without the password sitting in the clear.

This is the stacked shape you pointed at on #28: it and the run.sh PR touch the same part of the file, so this branch contains that commit too. Merge that one first and this becomes a one-commit diff.

OMSA_PASS stays readable for the life of the container: docker inspect prints it, /proc/<pid>/environ inside the container has it, and anything that dumps the environment on a crash takes it along. That is unavoidable for a value passed as -e, and is the one thing a file does not have.

OMSA_USER_FILE and OMSA_PASS_FILE are added beside the existing variables, not instead of them, so nothing that works today changes. /run/secrets/<name> is where compose and swarm put a secret, so there is no plumbing past naming it.

Setting both an env var and its _FILE is refused rather than one silently winning. An unreadable file stops the container rather than falling through to "please specify OMSA_USER and OMSA_PASS", which would be true and useless.

Only the first line is used, newline stripped -- what echo and docker secret create from a here-string produce. A password containing a newline cannot go through a file; the env var still takes anything.

One consequence worth pointing at rather than leaving to be found: the re-create recipe in the README reads the credentials back out of the running container's environment, which is the very thing this avoids. A container started from a secret has to be given the secret again instead. The README says so beside the new example.

Verification

Run against mocked getent, adduser, chpasswd and init: both credentials from files, either one from a file, both an env var and its _FILE set (refused), an unreadable file (refused), neither given (refused), a password containing a colon (survives -- chpasswd splits on the first one), and the env-var-only path unchanged. shellcheck -x clean.

About the authorship

Per your answer on #28. The commit is authored by Claude and carries my Signed-off-by:, which is how I record this in my own repositories: the agent wrote it, so git log and git blame say so, and the sign-off names the person who read it and stands behind it. That person is me -- I have read every line, the reasoning above is mine, and any question you ask about it gets answered by me and not by a re-prompt.

The branch is based on 487c4eb (the v0.4.0 tag), so it is up to date with master and merges clean.

adduser and chpasswd could both fail without the script noticing, and it went
on to write the role map and exec /sbin/init regardless. The container then
came up healthy, OMSA answered on 1311, and the login simply did not work --
with the reason eight lines above in a log nobody reads when the web UI is
already up.

Both are now checked, and either failing stops the container with a message
saying which one it was.

The "--" on getent and adduser is the same failure reached another way. Without
it a username beginning with "-" is read as an option rather than as a name:
"getent passwd -x" answers "invalid option -- 'x'" and exits 64 rather than
"no such user", so the lookup fails for the wrong reason, and "adduser -x"
then exits 2 on the same complaint. With "--" the name reaches validation and
useradd says what is actually wrong with it ("invalid user name '-x'", exit 3),
which the check above now turns into a clean exit rather than a container that
starts anyway.

printf replaces echo for the same class of reason: echo has no portable way of
being told to stop looking for options, so a username beginning with "-n" or
"-e" is eaten as one instead of reaching chpasswd.

Verified by running the script before and after against mocked getent, adduser,
chpasswd and init. With adduser refusing, before: "INIT REACHED", exit 0, no
account, role map written. After: exit 1, nothing written. Same for chpasswd
refusing. The ordinary path is byte for byte what it was.

Signed-off-by: Tigerblue77 <37409593+tigerblue77@users.noreply.github.com>
OMSA_PASS stays readable for the life of the container: "docker inspect" prints
it, /proc/<pid>/environ inside the container has it, and anything that dumps the
environment on a crash takes it along. That is unavoidable for a value passed as
"-e", and it is the one thing a file does not have.

OMSA_USER_FILE and OMSA_PASS_FILE are added beside the existing variables rather
than instead of them, so nothing that works today changes. "/run/secrets/<name>"
is where compose and swarm put a secret, so no plumbing is needed past naming it.

Setting both an env var and its _FILE is refused rather than one silently
winning, which is the failure that is otherwise found months later by wondering
which of the two the container is actually using. An unreadable file stops the
container rather than falling through to "please specify OMSA_USER and
OMSA_PASS", which would be true and useless.

Only the first line of the file is used, with its newline stripped -- what
"echo" and "docker secret create" from a here-string produce. A password
containing a newline cannot go through a file; the env var still takes anything.

One consequence worth pointing at rather than leaving to be discovered: the
re-create recipe in the README reads the credentials back out of the running
container's environment, which is the very thing this avoids. A container
started from a secret has to be given the secret again instead, and the README
now says so beside the example.

Verified against mocked getent, adduser, chpasswd and init: both credentials
from files, either one from a file, both an env var and its _FILE set, an
unreadable file, neither given, and a password containing a colon (chpasswd
splits on the first one, so it survives). The env-var-only path is unchanged.

Signed-off-by: Tigerblue77 <37409593+tigerblue77@users.noreply.github.com>
@ShaneMcC

Copy link
Copy Markdown
Owner

I generally like this idea, so I've rewritten it and added it (8edc5dc)

I'd prefer commits were marked as authored by you, even if you used Claude or some other AI tooling to do (most of) it - I see the AI as a tool not a code author. Something like Assisted-by (like https://docs.kernel.org/process/coding-assistants.html) would be fine, but not author or signed-off-by.

This was referenced Sep 20, 2026
@tigerblue77

Copy link
Copy Markdown
Contributor Author

No complaint at all — your version is better than mine in the place it matters, and I went looking before saying so.

Refusing a username that begins with - outright is simpler than passing it after an end-of-options marker and letting useradd refuse it, and it closes the same hole in one check instead of four call sites. It also makes the echo into chpasswd safe without needing printf, since the first field can no longer look like an option. And you put the check after the file is read, which is the part that is easy to get wrong — a credential file must not be a way round a validation the environment variable is subject to.

I ran the suite from #35 against d5be4b8: twenty-six cases, fifty assertions, all green. I also probed the _FILE paths by hand — a missing file, an empty one, a directory, a path beginning with -, a variable and its _FILE both set, and a dash-leading username coming from the file rather than the environment. All refused, cleanly, with a message. Nothing to report.

Two things I looked at and am deliberately not raising as issues, so that "I found nothing" is not just silence: head -n 1 "${OMSA_..._FILE}" has no --, but the -r test in front of it makes a path beginning with a dash unreachable; and a password containing a backslash would survive echo under bash and not under a stricter /bin/sh, which is theoretical for this image and would be a printf away if it ever stopped being.

Thanks for taking the idea rather than the patch — that is the better outcome for a file you have to maintain.

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.

3 participants