#!/usr/bin/env bash set -euo pipefail [[ "$#" -eq 1 && "$1" =~ ^[a-z_][a-z0-9_-]*$ ]] || { echo "usage: setup-user.sh PUBLIC_PROXY_USER" >&2 exit 2 } user=toad public_proxy_user="$1" home_dir=/home/toad id -u "${public_proxy_user}" >/dev/null version="$(podman version --format '{{.Client.Version}}')" major="${version%%.*}" [[ "${major}" =~ ^[0-9]+$ && "${major}" -ge 5 ]] || { echo "toad requires Podman 5 or newer, found ${version}" >&2 exit 1 } id -u "${user}" >/dev/null 2>&1 || useradd --create-home --shell /usr/sbin/nologin "${user}" install -d -o "${user}" -g "${user}" -m 0750 \ "${home_dir}" \ "${home_dir}/.config" \ "${home_dir}/.config/containers/systemd" \ "${home_dir}/.config/systemd/user/sockets.target.wants" \ "${home_dir}/.local/share/containers" \ /data/toad install -d -o "${user}" -g "${user}" -m 0770 /data/toad-ingress setfacl -m "u:${public_proxy_user}:rwx,d:u:${public_proxy_user}:rwx" /data/toad-ingress semanage fcontext -a -t container_file_t '/data/toad-ingress(/.*)?' 2>/dev/null || semanage fcontext -m -t container_file_t '/data/toad-ingress(/.*)?' for shared_file in /data/toad/gateway.Caddyfile /data/toad/ingress.env; do semanage fcontext -a -t container_file_t "${shared_file}" 2>/dev/null || semanage fcontext -m -t container_file_t "${shared_file}" done restorecon -RF /data/toad-ingress loginctl enable-linger "${user}" uid="$(id -u "${user}")" runtime="/run/user/${uid}" install -d -o "${user}" -g "${user}" -m 0700 "${runtime}" systemctl start "user@${uid}.service" for _ in $(seq 1 25); do [[ -S "${runtime}/bus" ]] && break sleep 0.2 done [[ -S "${runtime}/bus" ]] || { echo "toad user bus did not start" >&2 exit 1 } socket_unit=/usr/lib/systemd/user/podman.socket [[ -f "${socket_unit}" ]] || socket_unit=/lib/systemd/user/podman.socket [[ -f "${socket_unit}" ]] || { echo "missing podman.socket user unit" >&2 exit 1 } ln -sf "${socket_unit}" "${home_dir}/.config/systemd/user/sockets.target.wants/podman.socket" chown -h "${user}:${user}" "${home_dir}/.config/systemd/user/sockets.target.wants/podman.socket" runuser -u "${user}" -- env \ HOME="${home_dir}" \ USER="${user}" \ LOGNAME="${user}" \ XDG_RUNTIME_DIR="${runtime}" \ DBUS_SESSION_BUS_ADDRESS="unix:path=${runtime}/bus" \ systemctl --user daemon-reload runuser -u "${user}" -- env \ HOME="${home_dir}" \ USER="${user}" \ LOGNAME="${user}" \ XDG_RUNTIME_DIR="${runtime}" \ DBUS_SESSION_BUS_ADDRESS="unix:path=${runtime}/bus" \ systemctl --user start podman.socket cat >/usr/local/bin/toad <<'WRAPPER' #!/usr/bin/env bash set -euo pipefail [[ "${EUID}" -eq 0 ]] || { echo "toad host wrapper must run as root" >&2 exit 1 } uid="$(id -u toad)" cd /home/toad exec runuser -u toad -- env \ HOME=/home/toad \ USER=toad \ LOGNAME=toad \ XDG_RUNTIME_DIR="/run/user/${uid}" \ DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/${uid}/bus" \ podman exec toad /toad "$@" WRAPPER chmod 0755 /usr/local/bin/toad