skillguard/hetzner-deployment/ssh_context.sh

164 lines
5 KiB
Bash
Raw Permalink Normal View History

#!/usr/bin/env bash
set -euo pipefail
# =============================================================================
# ssh_context.sh
# Wird von den Deploy-Skripten gesourced.
# Stellt Target-Aufloesung und SSH-Setup bereit.
#
# Targets werden NICHT mehr hartkodiert, sondern aus Konfigurationsdateien unter
# targets/<target>.env geladen. Neue Targets legt man interaktiv mit ./init.sh an.
#
# Der SSH-Key (id_ed25519_forgejo) ist mit einer Passphrase verschluesselt.
# setup_ssh() laedt ihn in einen temporaeren ssh-agent; dabei wird die
# Passphrase einmalig abgefragt (Quelle: VaultWarden). Eine falsche Passphrase
# kann den Key nicht entschluesseln -> Abbruch, keine SSH/SFTP-Verbindung.
# Ein separates Passwort-Secret ist dadurch nicht mehr noetig.
# =============================================================================
# --- Pfade ---
CONTEXT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TARGETS_DIR="${CONTEXT_DIR}/targets"
# --- Hilfsfunktion: verfuegbare Targets auflisten ---
# Eine einzige Quelle fuer die Target-Liste (keine duplizierten Strings mehr).
list_targets() {
local found=""
local f name
if [ -d "${TARGETS_DIR}" ]; then
for f in "${TARGETS_DIR}"/*.env; do
[ -e "$f" ] || continue
name="$(basename "$f" .env)"
# 'example' ist nur eine Vorlage und kein echtes Target.
[ "$name" = "example" ] && continue
found="${found:+${found}, }${name}"
done
fi
if [ -n "$found" ]; then
echo "Verfuegbare Targets: ${found}"
else
echo "Keine Targets konfiguriert. Lege eines mit ./init.sh an."
fi
}
# --- Target-Validierung ---
TARGET="${1:-}"
if [ -z "$TARGET" ]; then
echo "FEHLER: Target ist erforderlich."
echo "Verwendung: $0 <target>"
list_targets
exit 1
fi
# Target-Name auf sichere Zeichen beschraenken, bevor daraus ein Pfad gebaut
# wird (verhindert Path-Traversal wie '../' und das Laden beliebiger Dateien).
if ! printf '%s' "$TARGET" | grep -Eq '^[A-Za-z0-9._-]+$'; then
echo "FEHLER: Ungueltiger Target-Name '${TARGET}'."
echo "Erlaubt sind nur Buchstaben, Zahlen sowie '.', '_' und '-'."
list_targets
exit 1
fi
# --- Target-Aufloesung (Config aus targets/<target>.env laden) ---
TARGET_FILE="${TARGETS_DIR}/${TARGET}.env"
if [ ! -f "${TARGET_FILE}" ]; then
echo "FEHLER: Unbekanntes Target '${TARGET}'."
list_targets
exit 1
fi
# shellcheck disable=SC1090
source "${TARGET_FILE}"
# Pflichtfelder pruefen, damit kaputte Configs frueh auffallen.
for _var in SSH_HOST SFTP_HOST DB_NAME GIT_REMOTE DOMAIN; do
if [ -z "${!_var:-}" ]; then
echo "FEHLER: '${_var}' fehlt oder ist leer in ${TARGET_FILE}."
exit 1
fi
done
unset _var
export TARGET SSH_HOST SFTP_HOST DB_NAME GIT_REMOTE DOMAIN
echo "Target: ${TARGET} (${DOMAIN})"
# --- SSH-Setup ---
setup_ssh() {
WORKSPACE_SSH_DIR="/home/runner/workspace/.ssh"
KEY_PATH="${WORKSPACE_SSH_DIR}/id_ed25519_forgejo"
local FORGEJO_SSH_PORT="2222"
SFTP_PORT="2022"
mkdir -p "${WORKSPACE_SSH_DIR}"
# SSH-Config fuer den aktuellen Host erstellen/aktualisieren
local TEMP_CONFIG
TEMP_CONFIG=$(mktemp)
if [ -f "${WORKSPACE_SSH_DIR}/config" ]; then
# Bestehende Eintraege fuer diesen Host entfernen
awk -v host="${SSH_HOST}" '
/^Host / { skip = ($2 == host); }
!skip { print; }
' "${WORKSPACE_SSH_DIR}/config" > "${TEMP_CONFIG}"
else
touch "${TEMP_CONFIG}"
fi
cat >> "${TEMP_CONFIG}" <<EOF
Host ${SSH_HOST}
HostName ${SSH_HOST}
Port ${FORGEJO_SSH_PORT}
User git
IdentityFile ${KEY_PATH}
StrictHostKeyChecking no
EOF
mv "${TEMP_CONFIG}" "${WORKSPACE_SSH_DIR}/config"
# Git speichert keine 0600-Rechte -> nach Checkout/Reset liegt der Key auf 0644,
# was ssh-add ablehnt. Rechte hier korrigieren und Fehler sichtbar machen.
if ! chmod 600 "${KEY_PATH}"; then
echo "FEHLER: Konnte Rechte auf ${KEY_PATH} nicht auf 600 setzen."
exit 1
fi
chmod 644 "${WORKSPACE_SSH_DIR}/config"
# Symlinks ins Home-Verzeichnis
mkdir -p ~/.ssh
ln -sf "${WORKSPACE_SSH_DIR}/config" ~/.ssh/config
ln -sf "${KEY_PATH}" ~/.ssh/id_ed25519_forgejo
ln -sf "${KEY_PATH}.pub" ~/.ssh/id_ed25519_forgejo.pub
# --- Verschluesselten Key in ssh-agent laden (Passphrase-Abfrage) ---
if [ ! -f "${KEY_PATH}" ]; then
echo "FEHLER: SSH-Key nicht gefunden: ${KEY_PATH}"
exit 1
fi
# ssh-add fragt die Passphrase ueber das Terminal ab.
if [ ! -t 0 ]; then
echo "FEHLER: Kein interaktives Terminal. Skript muss manuell ausgefuehrt werden."
exit 1
fi
# Frischen ssh-agent starten und beim Script-Ende automatisch beenden,
# damit der entschluesselte Key nur waehrend der Laufzeit im Speicher liegt.
eval "$(ssh-agent -s)" >/dev/null
trap 'ssh-agent -k >/dev/null 2>&1 || true' EXIT
echo "Lade SSH-Key in den Agent (Passphrase erforderlich; siehe VaultWarden)..."
if ! ssh-add "${KEY_PATH}"; then
echo "FEHLER: SSH-Key konnte nicht geladen werden (falsche Passphrase?)."
exit 1
fi
echo "SSH-Key geladen."
echo "SSH-Setup fuer ${SSH_HOST} abgeschlossen."
# Exportiere Pfade fuer andere Skripte
export WORKSPACE_SSH_DIR KEY_PATH SFTP_PORT
}