84 lines
4.0 KiB
Bash
Executable File
84 lines
4.0 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
# Derive the Gitea API hostname from GITEA_API (a full URL) so it can be
|
|
# firewall-allowlisted alongside GITEA_SSH_HOST.
|
|
gitea_api_host=""
|
|
if [ -n "${GITEA_API:-}" ]; then
|
|
gitea_api_host=$(echo "$GITEA_API" | sed -E 's#^[a-zA-Z]+://([^/]+).*#\1#')
|
|
fi
|
|
|
|
# Same for LOKI_URL (also a full URL) - Loki's host needs to be reachable
|
|
# for log-shipper.sh to actually deliver anything.
|
|
loki_host=""
|
|
if [ -n "${LOKI_URL:-}" ]; then
|
|
loki_host=$(echo "$LOKI_URL" | sed -E 's#^[a-zA-Z]+://##; s#[:/].*##')
|
|
fi
|
|
|
|
extra_domains="${GITEA_SSH_HOST:-} ${gitea_api_host} ${loki_host} ${ALLOWED_DOMAINS:-}"
|
|
|
|
if [ "${ENABLE_FIREWALL:-1}" = "1" ]; then
|
|
# dns-proxy.sh must run first: it repoints /etc/resolv.conf at a local
|
|
# dnsmasq that only resolves allowlisted names, so init-firewall.sh's own
|
|
# dig calls (and everything afterward) go through the same restriction.
|
|
EXTRA_ALLOWED_DOMAINS="${extra_domains}" /usr/local/bin/dns-proxy.sh
|
|
EXTRA_ALLOWED_DOMAINS="${extra_domains}" /usr/local/bin/init-firewall.sh
|
|
|
|
# NFLOG groups only exist once init-firewall.sh has run, so ulogd2 (the
|
|
# NFLOG consumer) and the Loki shipper start after it. Both are
|
|
# best-effort visibility on top of the enforcement above, not required
|
|
# for it - log-shipper.sh itself no-ops if LOKI_URL isn't set. Runs as
|
|
# its own unprivileged `ulogd` user (opens the NFLOG socket and output
|
|
# files first, then drops), not root - it's parsing headers from
|
|
# attacker-influenceable packets (whatever a compromised session sends),
|
|
# so a bug in it shouldn't be a path back to root in the container.
|
|
#
|
|
# `ulogd -d` daemonizes immediately and always exits 0 from this shell's
|
|
# point of view, regardless of whether the daemon itself goes on to
|
|
# start successfully - so `set -e` above won't catch a failure here (nor
|
|
# should it: logging is best-effort, not something worth failing the
|
|
# whole container over). Check separately so a failure is at least
|
|
# visible instead of silently producing no logs.
|
|
/usr/sbin/ulogd -d -u ulogd -c /etc/ulogd-sandbox.conf
|
|
sleep 0.5
|
|
if ! pgrep -x ulogd >/dev/null; then
|
|
echo "WARNING: ulogd2 failed to start - connection logging will be unavailable (see /var/log/ulogd.log)" >&2
|
|
fi
|
|
/usr/local/bin/log-shipper.sh &
|
|
fi
|
|
|
|
# A normal login/ssh session always chowns the allocated pty to the logging
|
|
# in user; this container's entrypoint skips that (there's no login/sshd
|
|
# involved), leaving the tty root-owned. nvim's clipboard fallback needs to
|
|
# write to it directly as the sandbox user, so fix the ownership up here,
|
|
# before dropping privileges.
|
|
if [ -t 1 ]; then
|
|
chown "$SANDBOX_USER" "$(tty)" 2>/dev/null || true
|
|
fi
|
|
|
|
# Point git's github.com credential helper at `gh`, which reads GH_TOKEN -
|
|
# only possible now, not at image build time, since that's a runtime
|
|
# secret. Combined with the firewall restricting github.com to port 443
|
|
# only, this is what makes GitHub read-only from inside the sandbox: no
|
|
# write-capable credential for it, and SSH (which could use the forwarded
|
|
# agent's real key) can't reach it at all. Skipped if GH_TOKEN isn't set -
|
|
# github.com HTTPS still works anonymously for public repos either way.
|
|
if [ -n "${GH_TOKEN:-}" ]; then
|
|
gosu "$SANDBOX_USER" gh auth setup-git
|
|
fi
|
|
|
|
# Test harness hook (docker/claude-sandbox/test/): everything above this
|
|
# point is the real startup sequence - DNS proxy, firewall, IPv6 block,
|
|
# logging, privilege-drop prep. Skip execing nvim (which needs a real tty)
|
|
# so the suite can `docker exec` into a live, fully-initialized container
|
|
# and assert against it, exercising the actual code path instead of a
|
|
# reimplementation of it that could drift out of sync.
|
|
if [ "${SANDBOX_TEST_MODE:-0}" = "1" ]; then
|
|
exec gosu "$SANDBOX_USER" sleep infinity
|
|
fi
|
|
|
|
# Drop root -> the sandbox user for the actual session; that user has no
|
|
# sudo/setuid path back to root after this, so it can't re-run
|
|
# init-firewall.sh itself to widen the allowlist mid-session.
|
|
exec gosu "$SANDBOX_USER" nvim "$@"
|