workspaces-pipelines/ssh_router.sh
Pallav Vasa 3a4773ebce Revert "feat: pass private key as env variable SSH_KEY"
This reverts commit 73f25ab8d24962dab50447ac0a50bb6849becac7.
2025-05-23 15:15:45 +00:00

216 lines
7.2 KiB
Bash
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
set -euo pipefail
PERSON="${1:?Usage: $0 <person>}"
WORKSPACE="${SSH_ORIGINAL_COMMAND:-}"
IMAGE="localhost/workspaces:latest"
DEV_USER="devuser"
XDG_RUNTIME_DIR="/run/user/$(id -u)"
LOG_FILE="/tmp/.ssh-router-${PERSON}.log"
# ─────────────────────────────────────────────
# ANSI colors & emojis
readonly C_RESET='\033[0m'
readonly C_INFO='\033[1;34m' # blue
readonly C_WARN='\033[1;33m' # yellow
readonly C_ERROR='\033[1;31m' # red
log() {
local level="${1^^}"
shift
local icon color
case "$level" in
INFO) icon="" color="$C_INFO" ;;
WARN) icon="⚠️" color="$C_WARN" ;;
ERROR) icon="❌" color="$C_ERROR" ;;
*) icon="🔹" color="$C_RESET" ;;
esac
local ts
ts="$(date '+%Y-%m-%d %H:%M:%S')"
printf '%b%s [%s] %s%b\n' \
"$color" "$icon" "$ts" "[$level] $*" "$C_RESET" >>"$LOG_FILE"
}
# ─────────────────────────────────────────────
# Check for interactive TTY
if [[ ! -t 0 ]]; then
log ERROR "No TTY allocated—refusing to run without an interactive terminal"
echo "Error: No TTY. Use 'ssh -t'" >&2
exit 1
fi
# ─────────────────────────────────────────────
# Default WORKSPACE if empty
if [[ -z "$WORKSPACE" ]]; then
WORKSPACE="$PERSON"
log INFO "Defaulted WORKSPACE → $WORKSPACE"
fi
TMUX_SESSION="${WORKSPACE}|workspace"
# ─────────────────────────────────────────────
# Ensure Podman socket is up
ensure_podman() {
local sock="$XDG_RUNTIME_DIR/podman/podman.sock"
if [[ ! -S "$sock" ]]; then
log INFO "Starting podman.socket for user $(id -un)"
systemctl --user start podman.socket || {
log ERROR "Failed to start podman.socket"
exit 1
}
sleep 1
fi
[[ -S "$sock" ]] || {
log ERROR "Podman socket still missing"
exit 1
}
}
ensure_podman
# ─────────────────────────────────────────────
# Ensure IMAGE is present
ensure_image() {
if ! podman image exists "$IMAGE"; then
log WARN "Image $IMAGE not found—pulling"
podman pull --tls-verify=false "$IMAGE" || {
log ERROR "Failed to pull $IMAGE"
exit 1
}
log INFO "Pulled $IMAGE"
fi
}
ensure_image
# ─────────────────────────────────────────────
# Disallow file transfers
case "${SSH_ORIGINAL_COMMAND:-}" in
*scp* | *sftp* | *rsync* | *tar*)
log ERROR "File transfers are disabled"
exit 1
;;
esac
# ─────────────────────────────────────────────
# Generate per-user gitconfig
generate_gitconfig() {
local ws="$1"
local access="$HOME/access.yml"
local template="$HOME/gitconfig.template"
local userdir="$HOME/secrets/$ws"
local name email
name=$(yq -r ".\"$ws\".name" "$access" 2>/dev/null || echo)
email=$(yq -r ".\"$ws\".email" "$access" 2>/dev/null || echo)
if [[ -z "$name" || -z "$email" ]]; then
log ERROR "Missing name/email for '$ws' in $access"
exit 1
fi
mkdir -p "$userdir"
GIT_NAME="$name" GIT_EMAIL="$email" \
envsubst <"$template" >"$userdir/gitconfig"
log INFO ".gitconfig created → $userdir/gitconfig"
}
# ─────────────────────────────────────────────
# Start container if absent or stopped
start_container_if_needed() {
if ! podman container exists "$WORKSPACE"; then
log INFO "Creating container '$WORKSPACE'"
generate_gitconfig "$PERSON"
podman run -dit \
--name "$WORKSPACE" \
--userns=keep-id:uid=1000,gid=1000 \
--group-add keep-groups \
--user "$DEV_USER" \
--hostname "$WORKSPACE" \
--label auto-cleanup=true \
-v "/mnt/private/workspaces-data/public/$WORKSPACE:/app/public" \
-v "/mnt/private/workspaces-data/private/$WORKSPACE:/app/private" \
-v "$HOME/secrets/$WORKSPACE/gitconfig:/home/$DEV_USER/.gitconfig:ro,Z" \
-v "$HOME/secrets/$WORKSPACE/id_ed25519:/home/$DEV_USER/.ssh/id_ed25519:ro,Z" \
-v "$HOME/secrets/$WORKSPACE/id_ed25519.pub:/home/$DEV_USER/.ssh/id_ed25519.pub:ro,Z" \
--entrypoint "/home/$DEV_USER/start.sh" \
"$IMAGE" "$TMUX_SESSION"
elif ! podman inspect -f '{{.State.Running}}' "$WORKSPACE" | grep -q true; then
log INFO "Starting existing container '$WORKSPACE'"
podman start "$WORKSPACE" >/dev/null
fi
sleep 1
}
# ─────────────────────────────────────────────
# Detach logic: stop container when devuser has left
check_devuser_attached() {
local clients
clients=$(podman exec "$WORKSPACE" tmux list-clients -t "$TMUX_SESSION" -F "#{client_user}" 2>/dev/null)
if grep -q "^${DEV_USER}\$" <<<"$clients"; then
log INFO "devuser still attached—keeping container running"
else
log INFO "devuser detached—stopping container"
podman stop "$WORKSPACE" >/dev/null
fi
}
# ─────────────────────────────────────────────
# Determine access mode (rw|ro) or exit
get_access_mode() {
local yaml="$HOME/access.yml" user="$PERSON" ws="$WORKSPACE"
[[ ! "$ws" =~ ^[A-Za-z0-9._-]+$ ]] && {
log ERROR "Invalid workspace name"
exit 1
}
if [[ "$user" == "$ws" ]]; then
echo rw
elif yq -e '.["'"$user"'"].rw[]?' "$yaml" | grep -qx "$ws"; then
echo rw
elif yq -e '.["'"$user"'"].ro[]?' "$yaml" | grep -qx "$ws"; then
echo ro
else
log ERROR "$user has no access to $ws"
exit 1
fi
}
MODE="$(get_access_mode)"
# ─────────────────────────────────────────────
# Main dispatch
case "$MODE" in
rw)
start_container_if_needed
log INFO "$PERSON attaching to workspace '$WORKSPACE'"
# Try to attach to session; if it fails, create and attach
if ! podman exec -it -e TERM="$TERM" --user "$DEV_USER" "$WORKSPACE" \
tmux attach -t "$TMUX_SESSION"; then
log WARN "tmux session not found, creating new session"
podman exec -it --user "$DEV_USER" "$WORKSPACE" \
tmux new-session -d -s "$TMUX_SESSION" 2>/dev/null || true
podman exec -it -e TERM="$TERM" --user "$DEV_USER" "$WORKSPACE" \
tmux attach -t "$TMUX_SESSION"
fi
log INFO "$PERSON detached from '$WORKSPACE'"
check_devuser_attached
;;
ro)
if podman inspect -f '{{.State.Running}}' "$WORKSPACE" 2>/dev/null | grep -q true; then
log INFO "$PERSON viewing workspace '$WORKSPACE'"
podman exec -it -e TERM="$TERM" --user "$DEV_USER" "$WORKSPACE" \
tmux attach -r -t "$TMUX_SESSION"
log INFO "$PERSON stopped viewing '$WORKSPACE'"
else
log ERROR "Workspace '$WORKSPACE' is not running"
exit 1
fi
;;
*)
log ERROR "Unknown access mode: '$MODE'"
exit 1
;;
esac