Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix image build to override root homedir in /etc/passwd #1027

Merged
merged 5 commits into from
May 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ RUN for dir in \

WORKDIR /runner

# NB: this appears to be necessary for container builds based on this container, since we can't rely on the entrypoint
# script to run during a build to fix up /etc/passwd. This envvar value, and the fact that all user homedirs are
# set to /home/runner is an implementation detail that may change with future versions of runner and should not be
# assumed by other code or tools.
ENV HOME=/home/runner

ADD utils/entrypoint.sh /bin/entrypoint
Expand Down
73 changes: 53 additions & 20 deletions utils/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,27 +1,60 @@
#!/usr/bin/env bash

# In OpenShift, containers are run as a random high number uid
# that doesn't exist in /etc/passwd, but Ansible module utils
# require a named user. So if we're in OpenShift, we need to make
# one before Ansible runs.
if [[ (`id -u` -ge 500 || -z "${CURRENT_UID}") ]]; then

# Only needed for RHEL 8. Try deleting this conditional (not the code)
# sometime in the future. Seems to be fixed on Fedora 32
# If we are running in rootless podman, this file cannot be overwritten
ROOTLESS_MODE=$(cat /proc/self/uid_map | head -n1 | awk '{ print $2; }')
if [[ "$ROOTLESS_MODE" -eq "0" ]]; then
cat << EOF > /etc/passwd
root:x:0:0:root:/root:/bin/bash
runner:x:`id -u`:`id -g`:,,,:/home/runner:/bin/bash
EOF
fi
# We need to fix a number of problems here that manifest under different container runtimes, as well as tweak some
# things to simplify runner's containerized launch behavior. Since runner currently always expects to bind-mount its
# callback plugins under ~/.ansible, it must have prior knowledge of the user's homedir before the container is launched
# in order to know where to mount in the callback dir. In all cases, we must get a consistent answer from $HOME
# and anything that queries /etc/passwd for a homedir (eg, `~root`), or lots of things (including parts of Ansible
# core itself) will be broken.

# If we're running as a legit default user that has an entry in /etc/passwd and a valid homedir, we're all good.

# If the username/uid we're running under is not represented in /etc/passwd or the current user's homedir is something
# other than /home/runner (eg, the container was run with --user and some dynamic unmapped UID from the host with
# primary GID 0), we need to correct that in order for ansible-runner's callbacks to function properly. Some things
# (eg podman/cri-o today) already create an /etc/passwd entry on the fly in this case, but they set the homedir to
# WORKDIR, which causes potential collisions with mounted/mapped volumes. For consistency, we'll
# just always set the current user's homedir to `/home/runner`, which we've already configured in a way
# that should always work with known container runtimes (eg, ug+rwx and all dirs owned by the root group).

# If current user is not listed in /etc/passwd, add an entry with username==uid, primary gid 0, and homedir /home/runner

# If current user is in /etc/passwd but $HOME != `/home/runner`, rewrite that user's homedir in /etc/passwd to
# /home/runner and export HOME=/home/runner for this session only. All new sessions (eg podman exec) should
# automatically set HOME to the value in /etc/passwd going forward.

# Ideally in the future, we can come up with a better way for the outer runner to dynamically inject its callbacks, or
# rely on the inner runner's copy. This would allow us to restore the typical POSIX user homedir conventions.

# if any of this business fails, we probably want to fail fast
if [ -n "$EP_DEBUG" ]; then
set -eux
echo 'hello from entrypoint'
else
set -e
fi

# current user might not exist in /etc/passwd at all
if ! $(whoami &> /dev/null) || ! getent passwd $(whoami || id -u) &> /dev/null ; then
if [ -n "$EP_DEBUG" ]; then
echo "adding missing uid $(id -u) into /etc/passwd"
fi
echo "$(id -u):x:$(id -u):0:container user $(id -u):/home/runner:/bin/bash" >> /etc/passwd
export HOME=/home/runner
fi

cat <<EOF > /etc/group
root:x:0:runner
runner:x:`id -g`:
EOF
MYHOME=`getent passwd $(whoami) | cut -d: -f6`

if [ "$MYHOME" != "$HOME" ] || [ "$MYHOME" != "/home/runner" ]; then
if [ -n "$EP_DEBUG" ]; then
echo "replacing homedir for user $(whoami)"
fi
# sed -i wants to create a tempfile next to the original, which won't work with /etc permissions in many cases,
# so just do it in memory and overwrite the existing file if we succeeded
NEWPW=$(sed -r "s/(^$(whoami):(.*:){4})(.*:)/\1\/home\/runner:/g" /etc/passwd)
echo "$NEWPW" > /etc/passwd
# ensure the envvar matches what we just set in /etc/passwd for this session; future sessions set automatically
export HOME=/home/runner
fi

if [[ -n "${LAUNCHED_BY_RUNNER}" ]]; then
Expand Down