154 lines
5.7 KiB
Shell
154 lines
5.7 KiB
Shell
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
if [[ "$(id -u)" -ne 0 ]]; then
|
|
echo "INSTALL_REQUIRES_ROOT" >&2
|
|
exit 1
|
|
fi
|
|
|
|
SOURCE_DIR="${1:-}"
|
|
AUTHORIZED_KEY_FILE="${2:-}"
|
|
if [[ -z "$SOURCE_DIR" || ! -f "$SOURCE_DIR/remote/zhulan_guard.py" ]]; then
|
|
echo "SOURCE_DIR_INVALID" >&2
|
|
exit 1
|
|
fi
|
|
if [[ -z "$AUTHORIZED_KEY_FILE" || ! -s "$AUTHORIZED_KEY_FILE" ]]; then
|
|
echo "AUTHORIZED_KEY_REQUIRED" >&2
|
|
exit 1
|
|
fi
|
|
if grep -qvE '^ssh-ed25519 [A-Za-z0-9+/=]+( .*)?$' "$AUTHORIZED_KEY_FILE"; then
|
|
echo "AUTHORIZED_KEY_INVALID" >&2
|
|
exit 1
|
|
fi
|
|
|
|
EXPECTED_HOST="${ZHULAN_EXPECTED_HOST:-VM-12-12-ubuntu}"
|
|
EXPECTED_DMI="${ZHULAN_EXPECTED_DMI:-7a500f2d-9aed-4b93-b3b8-59c87c65d031}"
|
|
ACTUAL_HOST="$(hostname)"
|
|
ACTUAL_DMI="$(tr '[:upper:]' '[:lower:]' </sys/class/dmi/id/product_uuid)"
|
|
if [[ "$ACTUAL_HOST" != "$EXPECTED_HOST" || "$ACTUAL_DMI" != "$EXPECTED_DMI" ]]; then
|
|
echo "TARGET_IDENTITY_MISMATCH" >&2
|
|
exit 1
|
|
fi
|
|
|
|
REMOTE_USER="zhulan-codex"
|
|
REMOTE_HOME="/srv/guanghu/zhulan-codex/home"
|
|
REMOTE_ROOT="/srv/guanghu/zhulan-codex"
|
|
WORKSPACE_ROOT="$REMOTE_ROOT/workspaces"
|
|
CANDIDATE_ROOT="$REMOTE_ROOT/candidates"
|
|
POLICY_ROOT="/etc/guanghu/zhulan-codex"
|
|
HOOK_ROOT="$POLICY_ROOT/hooks"
|
|
REQUIREMENTS_ROOT="/etc/codex"
|
|
SSHD_DROPIN="/etc/ssh/sshd_config.d/60-zhulan-codex.conf"
|
|
REPOSITORY_URL="https://guanghulab.com/code/bingshuo/guanghu-ice-heart.git"
|
|
REPOSITORY_DIR="$WORKSPACE_ROOT/guanghu-ice-heart"
|
|
CODEX_VERSION="${ZHULAN_CODEX_VERSION:-0.147.0}"
|
|
SOURCE_COMMIT="${ZHULAN_SOURCE_COMMIT:-}"
|
|
|
|
if [[ ! "$SOURCE_COMMIT" =~ ^[0-9a-f]{40}$ ]]; then
|
|
echo "SOURCE_COMMIT_REQUIRED" >&2
|
|
exit 1
|
|
fi
|
|
|
|
getent passwd "$REMOTE_USER" >/dev/null || useradd \
|
|
--home-dir "$REMOTE_HOME" \
|
|
--create-home \
|
|
--shell /bin/bash \
|
|
--comment "Zhulan Codex Remote restricted developer" \
|
|
"$REMOTE_USER"
|
|
passwd -l "$REMOTE_USER" >/dev/null
|
|
gpasswd -d "$REMOTE_USER" sudo >/dev/null 2>&1 || true
|
|
gpasswd -d "$REMOTE_USER" adm >/dev/null 2>&1 || true
|
|
|
|
install -d -o root -g root -m 0755 "$REMOTE_ROOT"
|
|
install -d -o "$REMOTE_USER" -g "$REMOTE_USER" -m 0700 "$REMOTE_HOME" "$WORKSPACE_ROOT" "$CANDIDATE_ROOT"
|
|
install -d -o "$REMOTE_USER" -g "$REMOTE_USER" -m 0700 "$REMOTE_HOME/.ssh"
|
|
install -d -o "$REMOTE_USER" -g "$REMOTE_USER" -m 0700 "$REMOTE_HOME/.codex"
|
|
{
|
|
printf 'restrict,pty '
|
|
cat "$AUTHORIZED_KEY_FILE"
|
|
} >"$REMOTE_HOME/.ssh/authorized_keys.tmp"
|
|
chown "$REMOTE_USER:$REMOTE_USER" "$REMOTE_HOME/.ssh/authorized_keys.tmp"
|
|
chmod 0600 "$REMOTE_HOME/.ssh/authorized_keys.tmp"
|
|
mv "$REMOTE_HOME/.ssh/authorized_keys.tmp" "$REMOTE_HOME/.ssh/authorized_keys"
|
|
|
|
install -d -o root -g root -m 0755 "$POLICY_ROOT" "$HOOK_ROOT" "$REQUIREMENTS_ROOT"
|
|
install -o root -g root -m 0444 "$SOURCE_DIR/remote/LANE.hdlp" "$POLICY_ROOT/LANE.hdlp"
|
|
install -o root -g root -m 0555 "$SOURCE_DIR/remote/zhulan_guard.py" "$HOOK_ROOT/zhulan_guard.py"
|
|
install -o root -g root -m 0444 "$SOURCE_DIR/remote/requirements.toml" "$REQUIREMENTS_ROOT/requirements.toml"
|
|
|
|
cat >"$SSHD_DROPIN.tmp" <<'EOF'
|
|
Match User zhulan-codex
|
|
AuthenticationMethods publickey
|
|
PasswordAuthentication no
|
|
KbdInteractiveAuthentication no
|
|
PubkeyAuthentication yes
|
|
AllowAgentForwarding no
|
|
AllowTcpForwarding no
|
|
X11Forwarding no
|
|
PermitTunnel no
|
|
PermitTTY yes
|
|
PermitUserEnvironment no
|
|
EOF
|
|
chmod 0644 "$SSHD_DROPIN.tmp"
|
|
mv "$SSHD_DROPIN.tmp" "$SSHD_DROPIN"
|
|
/usr/sbin/sshd -t
|
|
systemctl reload ssh
|
|
|
|
if ! command -v codex >/dev/null 2>&1 || [[ "$(codex --version 2>/dev/null)" != *"$CODEX_VERSION"* ]]; then
|
|
npm install --global "@openai/codex@$CODEX_VERSION"
|
|
fi
|
|
|
|
if [[ ! -d "$REPOSITORY_DIR/.git" ]]; then
|
|
runuser -u "$REMOTE_USER" -- git clone "$REPOSITORY_URL" "$REPOSITORY_DIR"
|
|
fi
|
|
runuser -u "$REMOTE_USER" -- git -C "$REPOSITORY_DIR" fetch --prune origin main
|
|
runuser -u "$REMOTE_USER" -- git -C "$REPOSITORY_DIR" remote set-url --push origin DISABLED_CENTRAL_PUSH
|
|
if [[ ! -d "$CANDIDATE_ROOT/guanghu-ice-heart.git" ]]; then
|
|
runuser -u "$REMOTE_USER" -- git init --bare "$CANDIDATE_ROOT/guanghu-ice-heart.git"
|
|
fi
|
|
if runuser -u "$REMOTE_USER" -- git -C "$REPOSITORY_DIR" remote get-url candidate >/dev/null 2>&1; then
|
|
runuser -u "$REMOTE_USER" -- git -C "$REPOSITORY_DIR" remote set-url candidate "$CANDIDATE_ROOT/guanghu-ice-heart.git"
|
|
else
|
|
runuser -u "$REMOTE_USER" -- git -C "$REPOSITORY_DIR" remote add candidate "$CANDIDATE_ROOT/guanghu-ice-heart.git"
|
|
fi
|
|
install -o "$REMOTE_USER" -g "$REMOTE_USER" -m 0644 "$SOURCE_DIR/remote/AGENTS.md" "$REMOTE_HOME/.codex/AGENTS.md"
|
|
install -d -o "$REMOTE_USER" -g "$REMOTE_USER" -m 0700 "$REPOSITORY_DIR/.zhulan/continuity"
|
|
if ! grep -qxF '/.zhulan/' "$REPOSITORY_DIR/.git/info/exclude"; then
|
|
printf '%s\n' '/.zhulan/' >>"$REPOSITORY_DIR/.git/info/exclude"
|
|
fi
|
|
chown "$REMOTE_USER:$REMOTE_USER" "$REPOSITORY_DIR/.git/info/exclude"
|
|
runuser -u "$REMOTE_USER" -- git -C "$REPOSITORY_DIR" config user.name "Zhulan Remote Candidate"
|
|
runuser -u "$REMOTE_USER" -- git -C "$REPOSITORY_DIR" config user.email "zhulan-remote@localhost"
|
|
|
|
if id -nG "$REMOTE_USER" | tr ' ' '\n' | grep -qx sudo; then
|
|
echo "REMOTE_USER_HAS_SUDO" >&2
|
|
exit 1
|
|
fi
|
|
if sudo -n -u "$REMOTE_USER" sudo -n true >/dev/null 2>&1; then
|
|
echo "REMOTE_USER_CAN_SUDO" >&2
|
|
exit 1
|
|
fi
|
|
|
|
RECEIPT="$REMOTE_ROOT/install-receipt.json"
|
|
cat >"$RECEIPT.tmp" <<EOF
|
|
{
|
|
"schema": "guanghu.zhulan-codex-remote-ssh-install/v1",
|
|
"development_id": "DEV-20260813-005",
|
|
"persona_id": "ICE-GL-ZL-001",
|
|
"runtime_node": "BS-SG-003",
|
|
"front_node_role": "BS-GZ-006_PROXY_ONLY",
|
|
"source_commit": "$SOURCE_COMMIT",
|
|
"codex_version": "$CODEX_VERSION",
|
|
"remote_user": "$REMOTE_USER",
|
|
"workspace": "$REPOSITORY_DIR",
|
|
"sudo": false,
|
|
"central_push_credentials": false,
|
|
"managed_hooks": true,
|
|
"candidate_remote_only": true,
|
|
"health": "PASS"
|
|
}
|
|
EOF
|
|
chown root:root "$RECEIPT.tmp"
|
|
chmod 0444 "$RECEIPT.tmp"
|
|
mv "$RECEIPT.tmp" "$RECEIPT"
|
|
cat "$RECEIPT"
|