I deploy-bastion ergonomics: HTTP_AS_ROOT, auto GIT_SSH_COMMAND, safe.directory, chown soft-fail
Caught while wiring up an HTTP-driven deploy that runs the user's
canonical deploy.sh from a CGI:
- HTTP_AS_ROOT=1 — opt out of busybox httpd's `-u USER` drop so the
CGI (and the deploy.sh it runs) keep root supplementary groups.
Required because busybox httpd does setuid/setgid but not
setgroups; dropping to agent loses the dockerhost group and the
CGI can't reach /var/run/docker.sock. Bastion already has the
socket = host root, so this doesn't widen the envelope.
- chown -R …/.ssh — make it best-effort. With ssh creds mounted
read-only (id_rsa, known_hosts), the chown -R failed under
`set -e` and killed boot. The dir + the file we wrote are what
matter; anything bind-mounted in is the caller's business.
- git config --system --add safe.directory '*' — silence
'detected dubious ownership' when the CGI runs as root over a
host-uid-owned repo (standard bind-mount-into-bastion case).
- GIT_SSH_COMMAND auto-export — when a key is mounted at
/home/agent/.ssh/id_rsa, the wrapper sets git's ssh invocation
to point at it explicitly. Required because setting HOME alone
doesn't make root-uid ssh follow ~/.ssh/{id_rsa,known_hosts}.
This commit is contained in:
parent
0262f677c1
commit
c90206045f
|
|
@ -4,6 +4,12 @@ set -eu
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo " docker-bastion starting"
|
echo " docker-bastion starting"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
|
|
||||||
|
# Trust every git repo regardless of UID — the bastion is a single-tenant
|
||||||
|
# isolated container, and ownership-mismatch on a bind-mounted repo
|
||||||
|
# (host uid != bastion uid) is the normal case here, not an attack vector.
|
||||||
|
# Without this, git refuses with "fatal: detected dubious ownership".
|
||||||
|
git config --system --add safe.directory '*' 2>/dev/null || true
|
||||||
echo "Date: $(date)"
|
echo "Date: $(date)"
|
||||||
echo "Hostname: $(hostname)"
|
echo "Hostname: $(hostname)"
|
||||||
echo "OpenSSH: $(/usr/sbin/sshd -V 2>&1 | head -1 || echo n/a)"
|
echo "OpenSSH: $(/usr/sbin/sshd -V 2>&1 | head -1 || echo n/a)"
|
||||||
|
|
@ -87,7 +93,12 @@ if [ "$file_keys" -eq 0 ] && [ "$dir_keys" -eq 0 ]; then
|
||||||
echo " until then every SSH attempt will fail with 'publickey denied'."
|
echo " until then every SSH attempt will fail with 'publickey denied'."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
chown -R "${SSH_USER}:${SSH_USER}" "$(dirname "$AUTH_FILE")"
|
# chown -R is best-effort: if the caller bind-mounts read-only files into
|
||||||
|
# /home/agent/.ssh/ (e.g. an id_rsa for git push from FORCE_COMMAND), chown
|
||||||
|
# on those will fail with "Read-only file system" — which under `set -e`
|
||||||
|
# previously killed the boot script. The dir itself + the file we just
|
||||||
|
# wrote are what matter; everything else is the caller's business.
|
||||||
|
chown -R "${SSH_USER}:${SSH_USER}" "$(dirname "$AUTH_FILE")" 2>/dev/null || true
|
||||||
chmod 700 "$(dirname "$AUTH_FILE")"
|
chmod 700 "$(dirname "$AUTH_FILE")"
|
||||||
chmod 600 "$AUTH_FILE"
|
chmod 600 "$AUTH_FILE"
|
||||||
|
|
||||||
|
|
@ -117,6 +128,15 @@ cat > /etc/bastion/force-command <<'WRAPPER'
|
||||||
# right place. sshd sets this already; busybox httpd's CGI doesn't, so
|
# right place. sshd sets this already; busybox httpd's CGI doesn't, so
|
||||||
# without this fix `git push` complains about /root/.config/git/* perms.
|
# without this fix `git push` complains about /root/.config/git/* perms.
|
||||||
export HOME=/home/agent
|
export HOME=/home/agent
|
||||||
|
|
||||||
|
# If a deploy SSH key is mounted at the conventional location, point git
|
||||||
|
# at it explicitly. Setting HOME alone isn't enough when the CGI runs as
|
||||||
|
# root (HTTP_AS_ROOT=1) — ssh ignores HOME-based ~/.ssh/ lookup in that
|
||||||
|
# path and silently uses /root/.ssh/, which is empty.
|
||||||
|
if [ -f /home/agent/.ssh/id_rsa ]; then
|
||||||
|
export GIT_SSH_COMMAND="ssh -o IdentityFile=/home/agent/.ssh/id_rsa -o UserKnownHostsFile=/home/agent/.ssh/known_hosts -o StrictHostKeyChecking=accept-new"
|
||||||
|
fi
|
||||||
|
|
||||||
exec sh -c "$(cat /etc/bastion/force-command.cmd)"
|
exec sh -c "$(cat /etc/bastion/force-command.cmd)"
|
||||||
WRAPPER
|
WRAPPER
|
||||||
chmod 0755 /etc/bastion/force-command
|
chmod 0755 /etc/bastion/force-command
|
||||||
|
|
@ -196,7 +216,21 @@ CGI
|
||||||
chmod 0755 /var/www/cgi-bin/run
|
chmod 0755 /var/www/cgi-bin/run
|
||||||
# -c CONFFILE = auth + content-type rules; httpd reads it as root before
|
# -c CONFFILE = auth + content-type rules; httpd reads it as root before
|
||||||
# dropping to -u USER. CGI scripts then run as USER.
|
# dropping to -u USER. CGI scripts then run as USER.
|
||||||
|
#
|
||||||
|
# Set HTTP_AS_ROOT=1 to skip the -u drop, so httpd (and the CGI it
|
||||||
|
# spawns) run as root. Use this when FORCE_COMMAND is a deploy-style
|
||||||
|
# script that needs full filesystem write + docker-socket access +
|
||||||
|
# arbitrary chown — busybox httpd's `-u USER` drop does setuid/setgid
|
||||||
|
# but not setgroups, so supplementary groups (e.g. dockerhost for the
|
||||||
|
# mounted /var/run/docker.sock) don't reach the CGI even when the user
|
||||||
|
# is a member on paper. Bastion already has socket = host root, so
|
||||||
|
# this doesn't enlarge the trust envelope.
|
||||||
|
if [ "${HTTP_AS_ROOT:-0}" = "1" ]; then
|
||||||
|
echo " HTTP_AS_ROOT=1 — httpd + CGI run as root"
|
||||||
|
httpd -f -p "${HTTP_PORT}" -h /var/www -c /etc/bastion/httpd.conf &
|
||||||
|
else
|
||||||
httpd -f -p "${HTTP_PORT}" -h /var/www -u "${SSH_USER}" -c /etc/bastion/httpd.conf &
|
httpd -f -p "${HTTP_PORT}" -h /var/www -u "${SSH_USER}" -c /etc/bastion/httpd.conf &
|
||||||
|
fi
|
||||||
HTTP_PID=$!
|
HTTP_PID=$!
|
||||||
echo " httpd PID ${HTTP_PID}, endpoint: /cgi-bin/run (basic auth)"
|
echo " httpd PID ${HTTP_PID}, endpoint: /cgi-bin/run (basic auth)"
|
||||||
|
|
||||||
|
|
@ -217,7 +251,12 @@ printf 'Content-Type: text/plain\r\nCache-Control: no-cache\r\nX-Accel-Buffering
|
||||||
exec /etc/bastion/force-command 2>&1
|
exec /etc/bastion/force-command 2>&1
|
||||||
CGI
|
CGI
|
||||||
chmod 0755 /var/www/cgi-bin/run
|
chmod 0755 /var/www/cgi-bin/run
|
||||||
|
if [ "${HTTP_AS_ROOT:-0}" = "1" ]; then
|
||||||
|
echo " HTTP_AS_ROOT=1 — httpd + CGI run as root"
|
||||||
|
httpd -f -p "${HTTP_PORT}" -h /var/www &
|
||||||
|
else
|
||||||
httpd -f -p "${HTTP_PORT}" -h /var/www -u "${SSH_USER}" &
|
httpd -f -p "${HTTP_PORT}" -h /var/www -u "${SSH_USER}" &
|
||||||
|
fi
|
||||||
HTTP_PID=$!
|
HTTP_PID=$!
|
||||||
echo " httpd PID ${HTTP_PID}, endpoint: /cgi-bin/run (bearer token)"
|
echo " httpd PID ${HTTP_PID}, endpoint: /cgi-bin/run (bearer token)"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue