#!/bin/bash
# Privileged sandbox launcher for left4me script overlays.
#
# Invoked via sudo by the web user with two arguments:
#   <overlay_id>   numeric overlay id; bind-mounts /var/lib/left4me/overlays/<id>
#                  read-write at /overlay inside the sandbox.
#   <script_path>  absolute path to a bash file already written by the web app;
#                  bind-mounted read-only at /script.sh inside the sandbox.
#
# The script runs under bubblewrap inside a transient systemd scope so we get
# cgroup-v2 limits (memory / tasks / cpu) and a wallclock kill via
# RuntimeMaxSec. The sandbox drops to the unprivileged l4d2-sandbox UID;
# host filesystems are exposed read-only except /overlay (rw) and tmpfs
# /tmp + /run. Network namespace is *not* unshared — scripts must reach the
# public internet to download workshop / l4d2center / cedapug content.
set -euo pipefail

[[ $# -eq 2 ]] || { echo "usage: $0 <overlay_id> <script>" >&2; exit 64; }

OVERLAY_ID=$1
SCRIPT=$2

[[ "$OVERLAY_ID" =~ ^[0-9]+$ ]] || { echo "bad overlay id" >&2; exit 64; }
OVERLAY_DIR=/var/lib/left4me/overlays/$OVERLAY_ID
[[ -d $OVERLAY_DIR ]] || { echo "no overlay dir at $OVERLAY_DIR" >&2; exit 65; }
[[ -f $SCRIPT ]] || { echo "no script at $SCRIPT" >&2; exit 65; }

if [[ "${LEFT4ME_SCRIPT_SANDBOX_DRY_RUN:-}" == "1" ]]; then
    echo "DRY RUN: overlay_id=$OVERLAY_ID script=$SCRIPT overlay_dir=$OVERLAY_DIR"
    exit 0
fi

# Make sure the sandbox UID owns the overlay dir so the script can write there.
# Idempotent: a no-op when the dir is already l4d2-sandbox-owned (re-run case),
# and corrects the ownership the first time the dir was created by the web app
# under the left4me UID. Group-readable so the gameserver process (left4me)
# can read the overlay contents via the kernel-overlayfs lowerdir at runtime.
chown -R l4d2-sandbox:l4d2-sandbox "$OVERLAY_DIR"
chmod 0755 "$OVERLAY_DIR"

# UID/GID drop happens via systemd-run --uid/--gid before bwrap is invoked.
# bwrap then runs unprivileged as l4d2-sandbox; --unshare-user-try gives it
# the user-namespace context it needs for bind-mounts as a regular user.
exec systemd-run --quiet --scope --collect \
    --uid=l4d2-sandbox --gid=l4d2-sandbox \
    -p MemoryMax=4G -p MemorySwapMax=0 -p TasksMax=512 \
    -p CPUQuota=200% -p RuntimeMaxSec=3600 \
    -- bwrap \
        --die-with-parent --new-session \
        --unshare-user-try \
        --unshare-pid --unshare-ipc --unshare-uts --unshare-cgroup \
        --proc /proc --dev /dev --tmpfs /tmp --tmpfs /run \
        --ro-bind /usr /usr --ro-bind /lib /lib --ro-bind /lib64 /lib64 \
        --symlink usr/bin /bin --symlink usr/sbin /sbin \
        --ro-bind /etc/resolv.conf /etc/resolv.conf \
        --ro-bind /etc/ssl /etc/ssl \
        --ro-bind /etc/ca-certificates /etc/ca-certificates \
        --ro-bind /etc/nsswitch.conf /etc/nsswitch.conf \
        --ro-bind /etc/alternatives /etc/alternatives \
        --bind "$OVERLAY_DIR" /overlay \
        --chdir /overlay \
        --setenv HOME /tmp --setenv PATH /usr/bin:/usr/sbin \
        --setenv OVERLAY /overlay \
        --ro-bind "$SCRIPT" /script.sh \
        /bin/bash /script.sh
