Skip to content

Commit

Permalink
Fix image build to override root homedir in /etc/passwd (#1027) (#1062)
Browse files Browse the repository at this point in the history
* override root homedir in /etc/passwd

* using only `HOME` envvar to override homedir causes mismatches with anything that asks for it a different way (eg, `echo ~root`)
* kicking the can down the road on "why are we overriding /root as root's homedir anyway?"

* more dynamic homedir handling

* just make everybody's home /home/runner

* restore ENV HOME override for builds

* comment wording

(cherry picked from commit c181daa)
  • Loading branch information
nitzmahone committed May 5, 2022
1 parent aec5d42 commit 927109f
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 20 deletions.
4 changes: 4 additions & 0 deletions Dockerfile
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
@@ -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

0 comments on commit 927109f

Please sign in to comment.