#!/usr/bin/env bash # HIY git-shell — SSH authorized_keys command= override # # Install at: /usr/local/bin/hiy-git-shell (or set HIY_GIT_SHELL in .env) # Each authorized_keys entry is written by HIY in this form: # # command="/usr/local/bin/hiy-git-shell ", # no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty # # OpenSSH sets SSH_ORIGINAL_COMMAND to what the developer actually ran, e.g.: # git-receive-pack '/myapp' # This script validates the push and exec's git-receive-pack on the bare repo. set -euo pipefail USER_ID="${1:-}" API_URL="${2:-http://localhost:3000}" TOKEN="${3:-}" REPOS_DIR="${4:-/data/repos}" if [ -z "$USER_ID" ] || [ -z "$TOKEN" ]; then echo "hiy: internal configuration error — contact your administrator." >&2 exit 1 fi ORIG="${SSH_ORIGINAL_COMMAND:-}" # Only git-receive-pack (push) is supported; reject everything else. if [[ "$ORIG" != git-receive-pack* ]]; then echo "hiy: only 'git push' is supported on this host." >&2 exit 1 fi # Parse the app name from: git-receive-pack '/myapp' or git-receive-pack 'myapp' APP_NAME=$(echo "$ORIG" | sed "s/git-receive-pack '\\///;s/git-receive-pack '//;s/'.*//") APP_NAME="${APP_NAME##/}" # strip leading slash if still present APP_NAME="${APP_NAME%/}" # strip trailing slash if [ -z "$APP_NAME" ]; then echo "hiy: could not parse app name from: $ORIG" >&2 exit 1 fi # Ask HIY whether this user may push to this app. RESPONSE=$(curl -sf \ -H "X-Hiy-Token: $TOKEN" \ "${API_URL}/internal/git/auth?user_id=${USER_ID}&app=${APP_NAME}" \ 2>/dev/null) || { echo "hiy: push denied (cannot reach server or access denied for '${APP_NAME}')." >&2 exit 1 } APP_ID=$(echo "$RESPONSE" | python3 -c \ "import sys, json; print(json.load(sys.stdin)['app_id'])" 2>/dev/null) if [ -z "$APP_ID" ]; then echo "hiy: push denied for '${APP_NAME}'." >&2 exit 1 fi REPO_PATH="${REPOS_DIR}/${APP_ID}.git" if [ ! -d "$REPO_PATH" ]; then echo "hiy: repository not found for app '${APP_NAME}' (expected ${REPO_PATH})." >&2 exit 1 fi exec git-receive-pack "$REPO_PATH"