Files
claude-code/files/session-entrypoint.sh
T
claude 1bd0fec23a
Helm Chart Release / release-chart (push) Successful in 12s
Build Session Image / build-push (push) Successful in 2m15s
feat: Alpine ベースの Claude Code リモート実行 Helm チャート初版 (v2.1.259)
gateway(nginx) + manager(python 標準ライブラリ) + セッションごとの使い捨て Pod +
reaper CronJob の構成。ブラウザ(PC/スマホ)からアクセスし、セッション単位で
独立した Alpine コンテナに Git リポジトリ(GitHub/Gitea など)をチェックアウトして
編集・検証・プッシュする。無操作が続いたセッションは Pod ごと自動破棄。

- セキュリティ: セッション NS 分離 / restricted PodSecurity / RBAC はセッション NS の
  pods,services のみの Role / セッション Pod は SA トークン非マウント・非 root・
  readOnlyRootFilesystem・drop ALL caps・seccomp / NetworkPolicy で egress を
  DNS+HTTPS+SSH に限定 / Git ホスト allow-list / ResourceQuota+LimitRange
- ライフサイクル: idleTimeout(Pod 内ウォッチドッグ + manager 回収) / maxLifetime
  (activeDeadlineSeconds) / maxSessions / 予備の孤児掃除 CronJob
- 認証: token(既定) / basic / forward(外部SSO) / none。WebSocket は Cookie トークン
- Claude 認証: ANTHROPIC_API_KEY または CLAUDE_CODE_OAUTH_TOKEN
- CI: helm-release.yaml(未リリース版のみ tag+release+publish) /
  image-update-and-release.yaml(週次 npm 追従) / session-image.yaml(multi-arch build)
- files/ 実体を .Files.Get で ConfigMap 化。helm lint / template 検証済み
2026-09-03 08:10:12 +09:00

231 lines
9.6 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
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.
#!/bin/sh
# ==========================================================================
# claude-code セッションブート(使い捨てコンテナ内で1回だけ実行)
#
# 1. git 認証(SSH 鍵 / HTTPS トークン)を $HOME にセットアップ
# 2. 許可済みリポジトリを $WORKSPACE_DIR へ clone
# 3. `claude` を PATH に用意(無ければ非 root で $HOME にインストール)
# 4. ttyd でブラウザにシェル(+ 任意で claude 自動起動)を提供
# 5. アイドルウォッチドッグ:無通信が続いたら自身を終了 → Pod 破棄
#
# 前提: 非 root(uid=10001) / readOnlyRootFilesystem。
# 書き込み可能なのは $HOME, $WORKSPACE_DIR, /tmp のみ。
# git / ttyd / node は事前ビルドイメージに同梱されている想定
# (files/Dockerfile.session)。未同梱時は下記フォールバックが走る。
# ==========================================================================
set -eu
log() { printf '\033[36m[boot]\033[0m %s\n' "$*"; }
err() { printf '\033[31m[boot:ERROR]\033[0m %s\n' "$*" >&2; }
: "${HOME:=/home/claude}"
: "${WORKSPACE_DIR:=/workspace}"
: "${TTYD_PORT:=7681}"
: "${IDLE_TIMEOUT_SECONDS:=1800}"
: "${AUTOSTART_CLAUDE:=true}"
: "${REPO_REF:=main}"
export HOME
export NPM_CONFIG_CACHE="$HOME/.npm"
export PATH="$HOME/.npm-global/bin:$HOME/.local/bin:$PATH"
mkdir -p "$HOME/.ssh" "$WORKSPACE_DIR" "$HOME/.npm-global"
chmod 700 "$HOME/.ssh"
# --------------------------------------------------------------------------
# 1. git 認証セットアップ
# --------------------------------------------------------------------------
git config --global user.name "${GIT_AUTHOR_NAME:-Claude}"
git config --global user.email "${GIT_AUTHOR_EMAIL:-claude@cafepieters.com}"
git config --global safe.directory '*'
git config --global advice.detachedHead false
git config --global credential.helper ''
ASKPASS_DISABLE() { export GIT_TERMINAL_PROMPT=0; }
ASKPASS_DISABLE
case "${GIT_IDENTITY_TYPE:-}" in
ssh)
if [ ! -r /git-identity/id ]; then err "SSH 鍵がマウントされていません"; exit 1; fi
install -m 600 /git-identity/id "$HOME/.ssh/id_session"
# known_hosts を必須にし、ホスト鍵の TOFU を無効化する
if [ -r /git-identity/known_hosts ]; then
install -m 644 /git-identity/known_hosts "$HOME/.ssh/known_hosts"
else
err "known_hosts がありません(StrictHostKeyChecking を維持するため必須)"; exit 1
fi
cat > "$HOME/.ssh/config" <<EOF
Host *
IdentityFile $HOME/.ssh/id_session
IdentitiesOnly yes
StrictHostKeyChecking yes
UserKnownHostsFile $HOME/.ssh/known_hosts
PasswordAuthentication no
EOF
chmod 600 "$HOME/.ssh/config"
export GIT_SSH_COMMAND="ssh -F $HOME/.ssh/config"
log "SSH 認証を構成しました"
;;
https-token)
if [ ! -r /git-identity/token ]; then err "HTTPS トークンがマウントされていません"; exit 1; fi
_u="${GIT_HTTPS_USERNAME:-x-access-token}"
_t="$(cat /git-identity/token)"
# トークンをプロセス環境にもファイルにも平文で残さず、credential helper で都度供給
cat > "$HOME/.git-credentials" <<EOF
https://$(printf '%s' "$_u" | sed 's/@/%40/g'):$(printf '%s' "$_t" | sed 's/@/%40/g')@$(printf '%s\n' "$REPO_URL" | sed -E 's#^https://([^/]+)/.*#\1#')
EOF
chmod 600 "$HOME/.git-credentials"
git config --global credential.helper "store --file=$HOME/.git-credentials"
unset _t
log "HTTPS トークン認証を構成しました"
;;
*)
log "git 認証なし(公開リポジトリのみ clone 可能)"
;;
esac
# --------------------------------------------------------------------------
# 2. clone
# --------------------------------------------------------------------------
REPO_DIR="$WORKSPACE_DIR/$(basename "${REPO_URL%.git}")"
if [ -d "$REPO_DIR/.git" ]; then
log "既存チェックアウトを再利用: $REPO_DIR"
else
log "clone 中: $REPO_URL ($REPO_REF)"
if ! git clone --filter=blob:none --no-tags "$REPO_URL" "$REPO_DIR"; then
err "clone に失敗しました"; exit 1
fi
fi
cd "$REPO_DIR"
git checkout "$REPO_REF" 2>/dev/null || git checkout -b "$REPO_REF" 2>/dev/null || true
log "作業ディレクトリ: $REPO_DIR ($(git rev-parse --short HEAD 2>/dev/null || echo '?'))"
# --------------------------------------------------------------------------
# 3. claude CLI を用意
# --------------------------------------------------------------------------
if ! command -v claude >/dev/null 2>&1; then
log "claude が未同梱のため $HOME にインストールします(初回は時間がかかります)"
npm config set prefix "$HOME/.npm-global" >/dev/null 2>&1 || true
_ver="${CLAUDE_CODE_VERSION:-}"
_pkg="@anthropic-ai/claude-code${_ver:+@$_ver}"
if ! npm install -g --no-fund --no-audit "$_pkg"; then
err "claude-code のインストールに失敗しました。事前ビルドイメージの利用を推奨します。"
exit 1
fi
fi
log "claude: $(claude --version 2>/dev/null || echo 'unknown')"
# 追加 apk(root 権限がある事前ビルドイメージのみ有効。非 root では警告のみ)
if [ -n "${EXTRA_APK_PACKAGES:-}" ]; then
if [ "$(id -u)" = "0" ]; then
apk add --no-cache $EXTRA_APK_PACKAGES || err "apk add に一部失敗"
else
err "EXTRA_APK_PACKAGES は非 root では無視されます: $EXTRA_APK_PACKAGES"
fi
fi
# --------------------------------------------------------------------------
# 4. 補助コマンド(claude-verify / claude-push)
# --------------------------------------------------------------------------
mkdir -p "$HOME/.local/bin"
cat > "$HOME/.local/bin/claude-verify" <<'EOF'
#!/bin/sh
# セッション設定の検証コマンドを実行する
if [ -z "${VERIFY_COMMAND:-}" ]; then echo "verifyCommand は未設定です"; exit 0; fi
echo "+ $VERIFY_COMMAND"
exec sh -c "$VERIFY_COMMAND"
EOF
chmod +x "$HOME/.local/bin/claude-verify"
cat > "$HOME/.local/bin/claude-push" <<'EOF'
#!/bin/sh
# 検証 → 安全なブランチへ push(デフォルト/保護ブランチ・--force は禁止)
set -eu
if [ "${AUTOPUSH_ENABLED:-false}" != "true" ]; then
echo "autoPush は無効です(values: session.autoPush.enabled)。手動 push は 'git push' を使ってください。" >&2
exit 1
fi
cur="$(git rev-parse --abbrev-ref HEAD)"
prefix="${AUTOPUSH_BRANCH_PREFIX:-claude/}"
protected="${AUTOPUSH_PROTECTED:-main,master,develop,release}"
case ",$protected," in *",$cur,"*)
echo "現在のブランチ '$cur' は保護対象です。作業ブランチへ切り替えてください。" >&2; exit 1;;
esac
case "$cur" in "$prefix"*) target="$cur" ;; *) target="${prefix}${cur}" ;; esac
if [ -n "${VERIFY_COMMAND:-}" ]; then
echo "== verify: $VERIFY_COMMAND"
sh -c "$VERIFY_COMMAND" || { echo "検証に失敗したため push を中止します。" >&2; exit 1; }
fi
if [ -n "$(git status --porcelain)" ]; then
git add -A
git commit -m "${1:-chore: work from claude-code session $SESSION_ID}"
fi
[ "$target" = "$cur" ] || git branch -f "$target" "$cur"
echo "== push origin $target(--force なし)"
exec git push origin "HEAD:$target"
EOF
chmod +x "$HOME/.local/bin/claude-push"
# --------------------------------------------------------------------------
# 5. アイドルウォッチドッグ
# --------------------------------------------------------------------------
idle_watchdog() {
# ttyd への ESTABLISHED 接続が IDLE_TIMEOUT_SECONDS の間ゼロなら Pod を終了させる
last_seen="$(date +%s)"
while true; do
sleep 30
conns=0
if command -v ss >/dev/null 2>&1; then
conns="$(ss -Htn "sport = :$TTYD_PORT" state established 2>/dev/null | wc -l | tr -d ' ')"
elif [ -r /proc/net/tcp ]; then
hexport="$(printf '%04X' "$TTYD_PORT")"
conns="$(awk -v p=":$hexport" '$2 ~ p"$" && $4=="01"{n++} END{print n+0}' /proc/net/tcp)"
fi
now="$(date +%s)"
if [ "${conns:-0}" -gt 0 ]; then
last_seen="$now"
elif [ $(( now - last_seen )) -ge "$IDLE_TIMEOUT_SECONDS" ]; then
err "アイドル ${IDLE_TIMEOUT_SECONDS}s を超過。セッションを終了します。"
kill -TERM "$TTYD_PID" 2>/dev/null || true
exit 0
fi
done
}
# --------------------------------------------------------------------------
# 6. ttyd 起動
# --------------------------------------------------------------------------
if ! command -v ttyd >/dev/null 2>&1; then
err "ttyd が見つかりません。事前ビルドイメージ(files/Dockerfile.session)を利用してください。"
exit 1
fi
SHELL_CMD="/bin/bash"
command -v bash >/dev/null 2>&1 || SHELL_CMD="/bin/sh"
cat > "$HOME/.session-shell" <<EOF
#!/bin/sh
cd "$REPO_DIR" || cd "$WORKSPACE_DIR"
export PATH="$PATH"
echo "── Claude Code セッション $SESSION_ID ──────────────────────────"
echo "リポジトリ : $REPO_URL ($REPO_REF)"
echo "検証コマンド: ${VERIFY_COMMAND:-(未設定)} 補助: claude-verify / claude-push"
echo "─────────────────────────────────────────────────────────────"
if [ "${AUTOSTART_CLAUDE}" = "true" ] && command -v claude >/dev/null 2>&1; then
exec claude
fi
exec $SHELL_CMD -l
EOF
chmod +x "$HOME/.session-shell"
log "ttyd を 0.0.0.0:$TTYD_PORT で起動します"
ttyd --port "$TTYD_PORT" --interface 0.0.0.0 --writable \
--client-option fontSize=14 --client-option 'theme={"background":"#1e1e1e"}' \
--terminal-type xterm-256color \
"$HOME/.session-shell" &
TTYD_PID=$!
idle_watchdog &
wait "$TTYD_PID"