Session principal resolution chain: GSH_PRINCIPAL → BASCULE_DISPLAY_NAME → derive from DID → whoami() GSH_DID → BASCULE_USER_DID → whoami() .gshrc Windows identity detection: Entra-joined: whoami /upn → tking@guildhouse.dev → DID Domain-joined: USERNAME@USERDNSDOMAIN → DID Local: USERNAME only (no DID) Governed sessions (Bascule) override with authenticated identity. Non-WSL2 environments fall back silently. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
205 lines
6.9 KiB
Bash
Executable file
205 lines
6.9 KiB
Bash
Executable file
#!/bin/bash
|
|
# build-wsl2-image.sh — Configure WSL2 instance as governed jumphost
|
|
#
|
|
# Usage:
|
|
# ./scripts/build-wsl2-image.sh # Configure current instance
|
|
# ./scripts/build-wsl2-image.sh --export # Configure + export hint
|
|
#
|
|
# Idempotent: safe to run multiple times. No sudo required for gsh setup.
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
GSH_DIR="$(dirname "$SCRIPT_DIR")"
|
|
CORPUS_CID="sha256:dev-jumphost"
|
|
CORPUS_DIR="$HOME/.gsh/corpus/$CORPUS_CID"
|
|
|
|
echo "=== Guildhouse WSL2 Jumphost Builder ==="
|
|
echo ""
|
|
|
|
# ─── Step 1: System packages ─────────────────────────────
|
|
echo "[1/7] Checking system packages..."
|
|
|
|
MISSING=""
|
|
for pkg in curl git jq openssh-client ca-certificates; do
|
|
dpkg -s "$pkg" &>/dev/null || MISSING="$MISSING $pkg"
|
|
done
|
|
|
|
if [ -n "$MISSING" ]; then
|
|
echo " Missing:$MISSING"
|
|
if sudo -n true 2>/dev/null; then
|
|
sudo apt-get update -qq
|
|
sudo apt-get install -y -qq $MISSING 2>/dev/null
|
|
else
|
|
echo " Run: sudo apt-get install -y$MISSING"
|
|
fi
|
|
else
|
|
echo " All present"
|
|
fi
|
|
|
|
# ─── Step 2: kubectl ─────────────────────────────────────
|
|
echo "[2/7] kubectl..."
|
|
|
|
if command -v kubectl &>/dev/null; then
|
|
echo " $(kubectl version --client 2>/dev/null | head -1)"
|
|
else
|
|
echo " Installing kubectl..."
|
|
curl -sLo "$HOME/.local/bin/kubectl" "https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
|
|
chmod +x "$HOME/.local/bin/kubectl"
|
|
echo " Installed: $(kubectl version --client 2>/dev/null | head -1)"
|
|
fi
|
|
|
|
# ─── Step 3: helm ────────────────────────────────────────
|
|
echo "[3/7] helm..."
|
|
|
|
if command -v helm &>/dev/null; then
|
|
echo " $(helm version --short 2>/dev/null)"
|
|
else
|
|
echo " Installing helm..."
|
|
curl -fsSL https://get.helm.sh/helm-v3.17.3-linux-amd64.tar.gz | tar xz -C /tmp linux-amd64/helm
|
|
mv /tmp/linux-amd64/helm "$HOME/.local/bin/helm"
|
|
chmod +x "$HOME/.local/bin/helm"
|
|
rm -rf /tmp/linux-amd64
|
|
echo " Installed: $(helm version --short 2>/dev/null)"
|
|
fi
|
|
|
|
# ─── Step 4: gsh binary ─────────────────────────────────
|
|
echo "[4/7] gsh..."
|
|
|
|
if [ -f "$GSH_DIR/target/release/gsh" ]; then
|
|
mkdir -p "$HOME/.local/bin"
|
|
cp "$GSH_DIR/target/release/gsh" "$HOME/.local/bin/gsh"
|
|
chmod +x "$HOME/.local/bin/gsh"
|
|
echo " Installed to ~/.local/bin/gsh"
|
|
elif command -v gsh &>/dev/null; then
|
|
echo " Already on PATH: $(which gsh)"
|
|
else
|
|
echo " ERROR: gsh binary not found. Build first: cd ~/projects/gsh && cargo build --release"
|
|
exit 1
|
|
fi
|
|
|
|
# Ensure ~/.local/bin on PATH
|
|
if ! echo "$PATH" | grep -q "$HOME/.local/bin"; then
|
|
export PATH="$HOME/.local/bin:$PATH"
|
|
fi
|
|
|
|
# ─── Step 5: Corpus directory ────────────────────────────
|
|
echo "[5/7] Corpus..."
|
|
|
|
mkdir -p "$CORPUS_DIR"
|
|
|
|
for tool in kubectl helm; do
|
|
REAL_PATH=$(which $tool 2>/dev/null || true)
|
|
if [ -n "$REAL_PATH" ]; then
|
|
# Symlink to the real binary (simpler than wrapper scripts)
|
|
ln -sf "$REAL_PATH" "$CORPUS_DIR/$tool" 2>/dev/null || true
|
|
echo " $tool → $REAL_PATH"
|
|
fi
|
|
done
|
|
|
|
echo " Corpus at: $CORPUS_DIR"
|
|
|
|
# ─── Step 6: SSH config ─────────────────────────────────
|
|
echo "[6/7] SSH config..."
|
|
|
|
mkdir -p "$HOME/.ssh"
|
|
chmod 700 "$HOME/.ssh"
|
|
|
|
# Generate SSH key if none exists
|
|
if [ ! -f "$HOME/.ssh/id_ed25519" ]; then
|
|
ssh-keygen -t ed25519 -f "$HOME/.ssh/id_ed25519" -N "" -q
|
|
echo " Generated SSH key: ~/.ssh/id_ed25519"
|
|
fi
|
|
|
|
# Add gsh SSH aliases if not present
|
|
add_ssh_host() {
|
|
local name="$1" host="$2" port="$3"
|
|
if ! grep -q "Host $name" "$HOME/.ssh/config" 2>/dev/null; then
|
|
cat >> "$HOME/.ssh/config" << EOF
|
|
|
|
Host $name
|
|
HostName $host
|
|
Port $port
|
|
User \$(whoami)
|
|
StrictHostKeyChecking no
|
|
UserKnownHostsFile /dev/null
|
|
EOF
|
|
echo " Added: $name → $host:$port"
|
|
else
|
|
echo " Exists: $name"
|
|
fi
|
|
}
|
|
|
|
touch "$HOME/.ssh/config"
|
|
chmod 600 "$HOME/.ssh/config"
|
|
add_ssh_host "dev.gsh" "127.0.0.1" "2223"
|
|
add_ssh_host "stg.gsh" "178.104.110.197" "30222"
|
|
|
|
# ─── Step 7: Environment + shell config ──────────────────
|
|
echo "[7/7] Environment..."
|
|
|
|
# .gshrc — governed shell defaults
|
|
cat > "$HOME/.gshrc" << 'GSHRC'
|
|
# Guildhouse Governed Shell environment
|
|
# Sourced by .bashrc
|
|
|
|
export GSAP_CORPUS_CID="sha256:dev-jumphost"
|
|
export GSH_CORPUS_DIR="$HOME/.gsh/corpus"
|
|
export PATH="$HOME/.local/bin:$PATH"
|
|
|
|
# Detect Windows principal (WSL2 interop)
|
|
if command -v cmd.exe >/dev/null 2>&1; then
|
|
_UPN=$(cmd.exe /c "whoami /upn" 2>/dev/null | tr -d '\r')
|
|
if [ -n "$_UPN" ] && echo "$_UPN" | grep -q "@"; then
|
|
_UPN_LC=$(echo "$_UPN" | tr 'A-Z' 'a-z')
|
|
export GSH_PRINCIPAL="$_UPN_LC"
|
|
export GSH_DID="did:web:$(echo "$_UPN_LC" | cut -d@ -f2)/user/$(echo "$_UPN_LC" | cut -d@ -f1)"
|
|
else
|
|
_WIN_USER=$(cmd.exe /c "echo %USERNAME%" 2>/dev/null | tr -d '\r')
|
|
[ -n "$_WIN_USER" ] && export GSH_PRINCIPAL="$_WIN_USER"
|
|
fi
|
|
unset _UPN _UPN_LC _WIN_USER
|
|
fi
|
|
GSHRC
|
|
echo " Created ~/.gshrc"
|
|
|
|
# Source from .bashrc if not already
|
|
if ! grep -q "gshrc" "$HOME/.bashrc" 2>/dev/null; then
|
|
echo '' >> "$HOME/.bashrc"
|
|
echo '# Guildhouse governed shell' >> "$HOME/.bashrc"
|
|
echo '[ -f ~/.gshrc ] && source ~/.gshrc' >> "$HOME/.bashrc"
|
|
echo " Added .gshrc to .bashrc"
|
|
fi
|
|
|
|
# ─── Done ────────────────────────────────────────────────
|
|
echo ""
|
|
echo "=== Jumphost configured ==="
|
|
echo ""
|
|
echo " gsh: $(which gsh 2>/dev/null || echo '~/.local/bin/gsh')"
|
|
echo " kubectl: $(which kubectl)"
|
|
echo " helm: $(which helm)"
|
|
echo " corpus: $CORPUS_DIR/"
|
|
echo ""
|
|
echo "Connect:"
|
|
echo " ssh dev.gsh — governed shell (local Docker Desktop)"
|
|
echo " ssh stg.gsh — governed shell (Hetzner staging)"
|
|
echo " gsh — local ungoverned shell"
|
|
echo ""
|
|
|
|
# ─── Optional: Export hint ───────────────────────────────
|
|
if [ "${1:-}" = "--export" ]; then
|
|
echo "=== Export ==="
|
|
echo ""
|
|
echo "Clean build artifacts to reduce size:"
|
|
echo " rm -rf ~/projects/gsh/target/debug"
|
|
echo " rm -rf ~/projects/substrate-project/substrate/target/debug"
|
|
echo " sudo apt-get clean"
|
|
echo ""
|
|
|
|
DISTRO_NAME=$(hostname 2>/dev/null || echo "gsh-jumphost")
|
|
echo "From PowerShell:"
|
|
echo " wsl --export $DISTRO_NAME gsh-jumphost.tar"
|
|
echo ""
|
|
echo "Import on another machine:"
|
|
echo " wsl --import gsh-jumphost C:\\WSL\\gsh-jumphost gsh-jumphost.tar"
|
|
echo " wsl -d gsh-jumphost"
|
|
fi
|