Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ec4378047 | ||
|
|
528a48b667 | ||
|
|
cd7092be17 | ||
|
|
71d8db5b09 | ||
|
|
4913ceace7 |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
trigger: always_on
|
||||
description: Read the CLAUDE.md hierarchy via GEMINI.md before doing anything in this workspace
|
||||
---
|
||||
|
||||
最初に、このディレクトリの `GEMINI.md` を読み、そこに列挙された CLAUDE.md を上から順にすべて読んで従うこと。
|
||||
Read `GEMINI.md` in this directory first, then read and follow every CLAUDE.md it lists, in order.
|
||||
@@ -0,0 +1,34 @@
|
||||
# CHANGELOG 2.1.263-a — 手動リリース
|
||||
|
||||
v2.1.263(自動更新)の上に適用する手動リリース。
|
||||
|
||||
## 追加 / 変更
|
||||
|
||||
- **Service の LoadBalancer 対応(既定を `LoadBalancer` に変更)**
|
||||
- `service.annotations` … LB 実装へのヒント。kube-vip の VIP 指定
|
||||
(`kube-vip.io/loadbalancerIPs` / `kube-vip.io/vipHost`)、MetalLB のプール指定など。
|
||||
- `service.loadBalancerIP` / `service.loadBalancerClass`
|
||||
- `service.loadBalancerSourceRanges` … 接続元 CIDR を制限。Ingress を張らない構成での
|
||||
第一の防御線。
|
||||
- `service.externalTrafficPolicy` … `Local` で実クライアント IP を保持。
|
||||
- `service.nodePort` … `type: NodePort` 時の固定ポート。
|
||||
- `service.labels`
|
||||
- Ingress 機能(`ingress.*`)は変更なし。LoadBalancer と併用できる。
|
||||
- `templates/NOTES.txt` に LoadBalancer の EXTERNAL-IP 確認手順を追加。
|
||||
- `examples/loadbalancer-oauth.values.yaml` を追加
|
||||
(Ingress 無効 + `service.type: LoadBalancer` + kube-vip VIP + `loadBalancerSourceRanges`
|
||||
によるローカルセグメント限定 + `anthropic.authMode: oauth`)。
|
||||
|
||||
## ドキュメント
|
||||
|
||||
- README に **「Claude 認証(API 課金なし / サブスクリプション)」** セクションを追加。
|
||||
`claude setup-token` で発行した長期 OAuth トークンを `anthropic.oauthToken`
|
||||
(`authMode: oauth`)に渡すと、API 従量課金を使わずローカルの Claude Code と同一の
|
||||
挙動で動作する。トークンはセッション NS の Secret に保存し、セッション Pod へ
|
||||
`CLAUDE_CODE_OAUTH_TOKEN` として注入。
|
||||
- README に **「管理画面へのアクセス(LoadBalancer / Ingress)」** セクションを追加。
|
||||
|
||||
## バージョン
|
||||
|
||||
`version` = `2.1.263-a` / `appVersion` = `"2.1.263"`(主アプリ = Claude Code CLI は据え置き、
|
||||
チャートのみ手動リリース)。
|
||||
@@ -0,0 +1,46 @@
|
||||
# CHANGELOG 2.1.263-b — 手動リリース
|
||||
|
||||
v2.1.263-a の上に適用する手動リリース。
|
||||
|
||||
## 背景
|
||||
|
||||
`claude setup-token` / サブスクリプションのアクセストークンは定期的に失効・ローテーション
|
||||
される。単発トークン(`authMode: oauth`)注入では、失効のたびに `helm upgrade` が必要だった。
|
||||
|
||||
## 追加
|
||||
|
||||
- **`anthropic.authMode: credentials`(新既定)**
|
||||
- `~/.claude/.credentials.json` 全体(`accessToken` + `refreshToken` + `expiresAt`)を Secret に
|
||||
格納し、セッション Pod にファイルとしてマウント → 起動時に `$HOME/.claude/.credentials.json`
|
||||
へコピー(書き込み可能)。セッション内 `claude` が `refreshToken` で自己更新する。
|
||||
- `--set-file anthropic.credentialsJson=$HOME/.claude/.credentials.json` で注入。
|
||||
- `anthropic.existingSecretKeyCredentials`(既定 `.credentials.json`)で既存 Secret のキー名を指定可。
|
||||
- **refresher CronJob(`anthropic.refresher`、`credentials` モード時・既定有効)**
|
||||
- `files/token-refresher.py`(Python 標準ライブラリのみ)。Secret 内の `refreshToken` で
|
||||
OAuth トークンエンドポイントを叩き、新しい `accessToken` / `refreshToken` / `expiresAt` を
|
||||
Secret に書き戻す。既定 30 分ごと、残り寿命 1h 未満で更新。
|
||||
- 専用 ServiceAccount + Role(**セッション NS の認証情報 Secret 1 個への `get` / `patch` のみ**、
|
||||
`resourceNames` で限定)+ RoleBinding。
|
||||
- `tokenUrl` / `clientId` は Claude Code CLI の公開値(非公式)。仕様変更時は values で上書き。
|
||||
壊れてもセッション内 `claude` の自己更新は独立して機能する。`enabled: false` で無効化。
|
||||
- Secret に `helm.sh/resource-policy: keep`(`credentials` モード時)。`helm upgrade` で
|
||||
`--set-file` を付けない限り refresher が書いた値を Helm が上書きしない。
|
||||
|
||||
## 変更
|
||||
|
||||
- `anthropic.authMode` の既定を `apiKey` → **`credentials`**。既存の `apiKey` / `oauth` 運用は
|
||||
values で明示指定すれば従来どおり。
|
||||
- `templates/secret-anthropic.yaml` / `deployment.yaml` / `files/manager.py`(`build_pod` の
|
||||
認証情報注入分岐)/ `files/session-entrypoint.sh`(seed 処理)/ `values.schema.json` /
|
||||
`templates/NOTES.txt` / `examples/loadbalancer-oauth.values.yaml` を更新。
|
||||
|
||||
## 修正
|
||||
|
||||
- `values.schema.json` の `$schema` に `https://json-schema.org/draft-07/schema#` を指定して
|
||||
いたため、一部の Helm バージョンで
|
||||
`no schema with key or ref "https://json-schema.org/draft-07/schema#"` エラーが出ていた。
|
||||
`$schema` 行を削除(他チャートと同じ・Helm は draft-07 で検証)。
|
||||
|
||||
## バージョン
|
||||
|
||||
`version` = `2.1.263-b` / `appVersion` = `"2.1.263"`。
|
||||
@@ -0,0 +1,33 @@
|
||||
# CHANGELOG 2.1.263-c — 手動リリース(ホットフィックス)
|
||||
|
||||
## 修正
|
||||
|
||||
- **gateway の nginx が起動時にクラッシュループする不具合**
|
||||
|
||||
```
|
||||
nginx: [emerg] pcre2_compile() failed: missing closing parenthesis
|
||||
in "^/s/(?<sid>[a-f0-9]" in /tmp/nginx/nginx.conf
|
||||
```
|
||||
|
||||
`files/gateway.conf.template` の
|
||||
|
||||
```nginx
|
||||
location ~ ^/s/(?<sid>[a-f0-9]{10})(?<rest>/.*)?$ {
|
||||
```
|
||||
|
||||
で、正規表現中の `{10}` に含まれる `{` `}` を nginx の設定パーサーがブロック区切りと
|
||||
解釈し、`location` の正規表現が `^/s/(?<sid>[a-f0-9]` で途切れていた。
|
||||
nginx は `{` `}` `;` を含む正規表現を **クォート必須** とする。
|
||||
|
||||
修正: 正規表現全体をダブルクォートで囲む。
|
||||
|
||||
```nginx
|
||||
location ~ "^/s/(?<sid>[a-f0-9]{10})(?<rest>/.*)?$" {
|
||||
```
|
||||
|
||||
`envsubst` の置換対象は `${GATEWAY_PORT}` など 6 変数のみで `{10}` には影響しないことを
|
||||
確認済み(レンダリング後のブレース対応もローカル検証)。
|
||||
|
||||
## バージョン
|
||||
|
||||
`version` = `2.1.263-c` / `appVersion` = `"2.1.263"`。
|
||||
@@ -1,5 +1,8 @@
|
||||
# CLAUDE.md — claude-code Helm Chart
|
||||
|
||||
> **共通ルール(要約)** — 詳細は `P:\Claude\CLAUDE.md`(全体共通)と `P:\Claude\HelmChart\CLAUDE.md`(Helm チャート共通: リリースフロー・ワークフロー・Gitea Secrets)を参照。
|
||||
> 作業前に pull / 機能変更時は README 更新 / 作業単位ごとに commit → push まで / コミットに AI クレジットを付けない / pull で入った変更を revert しない / Helm はローカル実行不可(Python のみ可)。
|
||||
|
||||
## リポジトリ概要
|
||||
|
||||
Alpine Linux ベースの軽量コンテナで **Claude Code をリモート実行** するための Helm チャート。
|
||||
@@ -11,6 +14,8 @@ Git リポジトリ(GitHub / Gitea など互換 Git 全般)をチェック
|
||||
- **Helm リポジトリ**: `https://git.cafepieters.com/api/packages/helmchart/helm`
|
||||
- **セッションイメージ**: `git.cafepieters.com/helmchart/claude-code/session:<appVersion>`(CI で publish)
|
||||
|
||||
> **注意**: セッション Pod は Gitea からリポジトリ単体を clone するため、`P:\Claude` の上位階層 CLAUDE.md は届かない。各リポジトリの CLAUDE.md 先頭に「共通ルール(要約)」を置く理由はこれ。
|
||||
|
||||
## アーキテクチャ(要点)
|
||||
|
||||
| コンポーネント | 実体 | 役割 |
|
||||
@@ -28,37 +33,16 @@ Git リポジトリ(GitHub / Gitea など互換 Git 全般)をチェック
|
||||
- ファイル実体は `files/`(`.Files.Get` で ConfigMap 化): `manager.py` / `session-entrypoint.sh`
|
||||
/ `gateway.conf.template` / `gateway-entrypoint.sh` / `Dockerfile.session`。
|
||||
|
||||
## 実行環境について
|
||||
## README 更新時の追加事項
|
||||
|
||||
**Helm はこのマシンにインストールされていない。** ローカルで `helm lint` / `template` は実行できない。
|
||||
テンプレートの検証はステージングのクラスタか CI(`helm-release.yaml` の lint ステップ)で行う。
|
||||
- values の新設キー、挙動変更、セキュリティ影響は README の該当セクションと values リファレンス表へ反映する。
|
||||
- 手動リリース(`-a` 等)時は対応する `CHANGELOG-<version>.md` を追加し、README の「バージョン履歴」にも 1 行追加する(このリポジトリの慣行)。
|
||||
|
||||
## Git 運用ルール(このリポジトリで厳守)
|
||||
|
||||
1. **機能を追加・変更したら、必ず `README.md` を更新してから作業完了とする。**
|
||||
- values の新設キー、挙動変更、セキュリティ影響は README の該当セクションと
|
||||
values リファレンス表へ反映する。
|
||||
2. **変更は必ず Git Commit / Push する。**
|
||||
- 作業単位ごとにコミットし、`origin/main` へ push するまでが作業完了。
|
||||
3. **コミットメッセージに Claude クレジット(`Co-Authored-By` 等)を付けない。**
|
||||
- `user.name = Claude` でコミットされるため、ユーザー名で判別可能。
|
||||
4. `git pull` で取り込んだ変更は常に尊重する。revert して push することは禁止。
|
||||
不適切と思われる場合はユーザーに改善提案を提示し、承認を得てから対応する。
|
||||
|
||||
### Git Commit 情報
|
||||
|
||||
| 項目 | 値 |
|
||||
|------|-----|
|
||||
| user.name | `Claude` |
|
||||
| user.email | `claude@cafepieters.com` |
|
||||
| SSH キー | `P:/Claude/.ssh/id_claude` |
|
||||
| core.sshCommand | `ssh -i P:/Claude/.ssh/id_claude -F P:/Claude/.ssh/config -o StrictHostKeyChecking=no` |
|
||||
|
||||
## リリースフロー
|
||||
## リリースフロー(組織ルールとの差分)
|
||||
|
||||
- **バージョン番号**: `Chart.yaml` の `version` / `appVersion` は
|
||||
**主アプリ = Claude Code CLI(`@anthropic-ai/claude-code` の npm 最新版)と同一**にする。
|
||||
- **ワークフロー(3 本、他 Helm チャートプロジェクトと同じ思想)**
|
||||
- **ワークフロー(3 本)**
|
||||
- `helm-release.yaml` — `main` への push で発火。`helm lint` の後、`Chart.yaml` の version が
|
||||
未リリース(Gitea リリース・Package Registry 双方に存在しない)なら タグ + Gitea リリース +
|
||||
チャート publish。リリース済みなら lint のみで終了(再 publish しない)。
|
||||
@@ -69,20 +53,78 @@ Git リポジトリ(GitHub / Gitea など互換 Git 全般)をチェック
|
||||
- `session-image.yaml` — `files/Dockerfile.session` 等の変更、または上記による `Chart.yaml`
|
||||
更新で発火。セッションイメージを `linux/amd64,linux/arm64` でビルドして
|
||||
Gitea Container Registry(`helmchart/claude-code/session:<appVersion>` + `latest`)へ push。
|
||||
- **手動リリース(臨時)**: 改修作業などで必要な場合はバージョン末尾にアルファベットを付与する
|
||||
(例 `2.1.259` → `2.1.259-a` → `2.1.259-b`)。対応する `CHANGELOG-<version>.md` を追加し、
|
||||
README の「バージョン履歴」にも 1 行追加する。
|
||||
|
||||
## 必要な Gitea Secrets
|
||||
|
||||
| シークレット名 | 用途 |
|
||||
|---|---|
|
||||
| `GITEA_TOKEN` | Gitea API(リリース作成・タグ操作) |
|
||||
| `REGISTRY_USER` | Gitea Package / Container Registry ユーザー名 |
|
||||
| `REGISTRY_TOKEN` | Gitea Package / Container Registry トークン(`docker login` にも使用) |
|
||||
- **手動リリース(臨時)**: 例 `2.1.259` → `2.1.259-a` → `2.1.259-b`。
|
||||
|
||||
## 改修履歴
|
||||
|
||||
### gateway nginx クラッシュループ修正(location 正規表現のクォート)(2026-09-08, v2.1.263-c)
|
||||
|
||||
**症状**: gateway コンテナが起動直後に
|
||||
`nginx: [emerg] pcre2_compile() failed: missing closing parenthesis in "^/s/(?<sid>[a-f0-9]"`
|
||||
で終了し、CrashLoopBackOff。
|
||||
|
||||
**原因**: `files/gateway.conf.template` の
|
||||
`location ~ ^/s/(?<sid>[a-f0-9]{10})(?<rest>/.*)?$ { ... }` で、正規表現中の `{10}` の
|
||||
`{` `}` を nginx の設定パーサーがブロック区切りと解釈し、`location` の regex が
|
||||
`^/s/(?<sid>[a-f0-9]` で切れていた(nginx は `{` `}` `;` を含む正規表現のクォートを必須とする)。
|
||||
|
||||
**修正内容**: `location ~ "^/s/(?<sid>[a-f0-9]{10})(?<rest>/.*)?$" { ... }` と
|
||||
正規表現全体をダブルクォートで囲んだ。envsubst の対象は `${...}` の 6 変数のみで
|
||||
`{10}` には影響しないことを確認済み。
|
||||
|
||||
**対象ファイル**: `files/gateway.conf.template`, `README.md`, `Chart.yaml`, `CHANGELOG-2.1.263-c.md`
|
||||
|
||||
### 認証情報の credentials モード + トークン自動リフレッシュ(2026-09-08, v2.1.263-b)
|
||||
|
||||
**背景**: サブスクリプションのアクセストークンは定期的に失効・ローテーションする。単発
|
||||
トークン注入では失効のたびに `helm upgrade` が必要だった。
|
||||
|
||||
**症状**: あわせて、一部の Helm バージョンで `values.schema.json` の
|
||||
`$schema: https://json-schema.org/draft-07/schema#` が
|
||||
`no schema with key or ref ...` エラーになる不具合。
|
||||
|
||||
**修正内容**:
|
||||
- `anthropic.authMode: credentials`(新既定): `~/.claude/.credentials.json` 全体を Secret に
|
||||
格納 →(a)セッション Pod にファイルとしてマウントし `$HOME/.claude` へコピー、セッション内
|
||||
`claude` が `refreshToken` で自己更新。(b)`anthropic.refresher` CronJob
|
||||
(`files/token-refresher.py`、専用 SA + `resourceNames` 限定 Role で該当 Secret の get/patch のみ)
|
||||
がクラスタ内で `accessToken` を定期更新し Secret を書き戻す。`tokenUrl` / `clientId` は
|
||||
Claude Code CLI の非公式値で values 上書き可。
|
||||
- `manager.py` の `build_pod`: credentials モードは `envFrom` を使わずファイルマウント +
|
||||
`SEED_CREDENTIALS_FILE` env。apiKey/oauth は従来どおり `envFrom`。
|
||||
- Secret に `helm.sh/resource-policy: keep`(credentials 時)。
|
||||
- `values.schema.json` の `$schema` 行を削除(他チャートと同じ。Helm は draft-07 で検証)。
|
||||
|
||||
**対象ファイル**: `values.yaml`, `values.schema.json`, `templates/secret-anthropic.yaml`,
|
||||
`templates/deployment.yaml`, `templates/configmap-manager.yaml`, `templates/token-refresher.yaml`(新規),
|
||||
`templates/NOTES.txt`, `files/manager.py`, `files/session-entrypoint.sh`,
|
||||
`files/token-refresher.py`(新規), `examples/loadbalancer-oauth.values.yaml`, `README.md`,
|
||||
`Chart.yaml`, `CHANGELOG-2.1.263-b.md`
|
||||
|
||||
### Service の LoadBalancer 対応 + サブスクリプション認証手順の明文化(2026-09-08, v2.1.263-a)
|
||||
|
||||
**背景**: Ingress を張らずに、ローカルネットワークの LB(kube-vip 等)から払い出した IP で
|
||||
管理画面へアクセスしたい、という運用要望。あわせて「Claude の API 課金を使わず、ローカルの
|
||||
Claude Code と同様にサブスクリプションで使いたい」という確認があった。
|
||||
|
||||
**修正内容**:
|
||||
- `templates/service.yaml` を書き換え、`service.type: LoadBalancer` を既定化。
|
||||
`service.annotations`(kube-vip の `kube-vip.io/loadbalancerIPs` / `vipHost` 等)、
|
||||
`loadBalancerIP` / `loadBalancerClass` / `loadBalancerSourceRanges` /
|
||||
`externalTrafficPolicy` / `nodePort` / `labels` を追加。Ingress 機能は変更なし(併用可)。
|
||||
- `values.yaml` の `service:` ブロック拡張、`values.schema.json` に対応プロパティ追加。
|
||||
- `templates/NOTES.txt` に LoadBalancer の EXTERNAL-IP 案内を追加。
|
||||
- `examples/loadbalancer-oauth.values.yaml` を追加(Ingress 無効 + LB + source range +
|
||||
`authMode: oauth`)。
|
||||
- README に「Claude 認証(API 課金なし / サブスクリプション)」「管理画面へのアクセス
|
||||
(LoadBalancer / Ingress)」セクションを追加。サブスクリプション認証は
|
||||
`claude setup-token` → `anthropic.authMode: oauth`(`CLAUDE_CODE_OAUTH_TOKEN` を
|
||||
セッション Pod に注入)で、ローカル実行の Claude Code と同一挙動。
|
||||
|
||||
**対象ファイル**: `templates/service.yaml`, `values.yaml`, `values.schema.json`,
|
||||
`templates/NOTES.txt`, `examples/loadbalancer-oauth.values.yaml`, `README.md`, `Chart.yaml`,
|
||||
`CHANGELOG-2.1.263-a.md`
|
||||
|
||||
### 初版(2026-09-03, v2.1.259)
|
||||
|
||||
チャート新規作成。gateway + manager + 使い捨て session Pod + reaper CronJob の構成で、
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ description: >-
|
||||
使い捨てコンテナで Git リポジトリ(GitHub / Gitea など)をチェックアウト・編集・
|
||||
検証・プッシュする。一定時間アイドルなセッションは自動破棄される。
|
||||
type: application
|
||||
version: 2.1.259
|
||||
appVersion: "2.1.259"
|
||||
version: 2.1.263-c
|
||||
appVersion: "2.1.263"
|
||||
keywords:
|
||||
- claude-code
|
||||
- claude
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# GEMINI.md — Antigravity (Gemini) 用エントリポイント(HelmChart\claude-code)
|
||||
|
||||
このディレクトリのルールはすべて **`CLAUDE.md`** に記載されている。本ファイルにはルール本文を書かず、読むべき CLAUDE.md への参照だけを置く(正本は常に CLAUDE.md)。
|
||||
|
||||
## 必ず最初に実行すること
|
||||
|
||||
以下のファイルを**上から順に**読み込み、すべての指示に従うこと。上位ほど広い範囲に適用される共通ルールで、下位は上位を前提とした補足・例外である。
|
||||
|
||||
1. `P:\Claude\CLAUDE.md` — 全組織共通ルール(Git 運用・コミット書式・実行環境・情報記載の原則)
|
||||
2. `P:\Claude\HelmChart\CLAUDE.md` — HelmChart 組織共通ルール
|
||||
3. `P:\Claude\HelmChart\claude-code\CLAUDE.md` — claude-code リポジトリ固有ルール(このディレクトリ)
|
||||
|
||||
> Antigravity はワークスペース(または git ルート)直下のルールしか自動で読まず、親フォルダを遡らない。そのため上位階層のパスをここに明示している。`P:\Claude` はクラウド同期されており、デスクトップ・ノート PC とも同じパスで存在する。
|
||||
|
||||
## 読み替え
|
||||
|
||||
- CLAUDE.md 中の「Claude」「Claude Code」は、Antigravity で作業する場合は Gemini 自身のことと読み替える。
|
||||
- Git のコミット者は Antigravity でも `user.name = Claude` / `user.email = claude@cafepieters.com` のまま使う(変更しない)。コミットメッセージに AI クレジットを付けない。
|
||||
- Claude Code 固有の機能(`.claude/` 設定、スキル、メモリ等)に関する記述は無視してよい。
|
||||
|
||||
## 保守ルール
|
||||
|
||||
- ルールの追加・変更は **CLAUDE.md にのみ**行う。本ファイルと `.agents/rules/claude-md.md` は参照だけを保持し、内容を増やさない。
|
||||
- CLAUDE.md を新しいディレクトリに追加したときは、同じ形式の `GEMINI.md` と `.agents/rules/claude-md.md` も併置する。
|
||||
@@ -21,6 +21,8 @@ Raspberry Pi などの ARM ベアメタル Kubernetes を主な対象にして
|
||||
- [アーキテクチャ](#アーキテクチャ)
|
||||
- [セッションのライフサイクル](#セッションのライフサイクル)
|
||||
- [セキュリティモデル](#セキュリティモデル)
|
||||
- [Claude 認証(API 課金なし / サブスクリプション)](#claude-認証api-課金なし--サブスクリプション)
|
||||
- [管理画面へのアクセス(LoadBalancer / Ingress)](#管理画面へのアクセスloadbalancer--ingress)
|
||||
- [インストール](#インストール)
|
||||
- [使い方](#使い方)
|
||||
- [values リファレンス](#values-リファレンス)
|
||||
@@ -32,7 +34,7 @@ Raspberry Pi などの ARM ベアメタル Kubernetes を主な対象にして
|
||||
- [git — 接続先と認証アイデンティティ](#git--接続先と認証アイデンティティ)
|
||||
- [session — 使い捨てセッション](#session--使い捨てセッション)
|
||||
- [networkPolicy](#networkpolicy)
|
||||
- [ingress / service](#ingress--service)
|
||||
- [service / ingress](#ingress--service)
|
||||
- [セッションイメージ](#セッションイメージ)
|
||||
- [運用](#運用)
|
||||
- [トラブルシューティング](#トラブルシューティング)
|
||||
@@ -111,13 +113,143 @@ manager / gateway / session-entrypoint の実体は `files/` にあり、`.Files
|
||||
> **注意** — `values.yaml` に API キーや秘密鍵を書いてコミットしないでください。`helm get values` /
|
||||
> CI ログ / Git 履歴に残ります。`existingSecret` か CI からの `--set` を使ってください。
|
||||
|
||||
## Claude 認証(API 課金なし / サブスクリプション)
|
||||
|
||||
このチャートは **Claude の API(従量課金)を使わずに**、ローカルの Claude Code と同じ
|
||||
**サブスクリプション認証(Pro / Max プラン)** で動かせます。セッション内の `claude` の
|
||||
挙動・機能はローカル実行と同一です(同じ CLI・同じログイン方式)。
|
||||
|
||||
`anthropic.authMode` は 3 つ:
|
||||
|
||||
| モード | 注入するもの | トークン更新 | 課金 |
|
||||
|---|---|---|---|
|
||||
| **`credentials`(既定・推奨)** | `~/.claude/.credentials.json` 全体(`refreshToken` 込み) | セッション内 `claude` が自己更新 **+** クラスタ内 refresher CronJob が Secret を自動更新 | サブスクリプション枠(API 課金なし) |
|
||||
| `oauth` | `CLAUDE_CODE_OAUTH_TOKEN`(`claude setup-token` の単発トークン) | 自動更新なし(`refreshToken` を含まない)。失効したら手動再投入 | サブスクリプション枠 |
|
||||
| `apiKey` | `ANTHROPIC_API_KEY`(`sk-ant-...`) | — | API 従量課金 |
|
||||
|
||||
### `credentials` モードの使い方(推奨)
|
||||
|
||||
1. **claude にログイン済みの手元 PC** の認証情報ファイルをそのまま渡します
|
||||
(このファイルには `accessToken` と、それを更新するための `refreshToken` が入っています):
|
||||
|
||||
```bash
|
||||
# 場所: Linux/macOS = ~/.claude/.credentials.json
|
||||
# Windows = %USERPROFILE%\.claude\.credentials.json
|
||||
# (macOS でキーチェーン保存の場合は Keychain の "Claude Code" 項目を書き出す)
|
||||
helm upgrade --install claude-code cafepieters/claude-code -n claude-code --create-namespace \
|
||||
-f examples/loadbalancer-oauth.values.yaml \
|
||||
--set-file anthropic.credentialsJson="$HOME/.claude/.credentials.json"
|
||||
```
|
||||
|
||||
2. これだけで **トークンが定期的に変わっても再デプロイは不要** です。仕組み:
|
||||
|
||||
- セッション Pod は生成のたびに Secret を読むため、常に最新の認証情報で起動します。
|
||||
- **セッション内の `claude`** は、`accessToken` が失効していれば `refreshToken` で
|
||||
自動更新します(そのセッションの間だけ有効。`$HOME/.claude/.credentials.json` に書き戻す)。
|
||||
- **refresher CronJob**(`anthropic.refresher`、既定 30 分ごと)が、Secret 内の
|
||||
`refreshToken` を使ってクラスタ内で `accessToken` を更新し、**Secret 自体を書き戻します**。
|
||||
これにより手元 PC がオフラインでも、新規セッションは常に有効なトークンで起動できます。
|
||||
|
||||
3. 既存 Secret を使う場合:
|
||||
|
||||
```bash
|
||||
kubectl -n claude-code-sessions create secret generic claude-creds \
|
||||
--from-file=.credentials.json="$HOME/.claude/.credentials.json"
|
||||
# values: anthropic.existingSecret=claude-creds
|
||||
# (refresher に patch させるため RBAC はこの Secret 名に限定して付与される)
|
||||
```
|
||||
|
||||
### 注意 / 制約
|
||||
|
||||
- 利用枠はローカルの Claude Code と共有です。同時セッションを増やすとプランの
|
||||
レート上限に早く達します。
|
||||
- `session.maxLifetimeSeconds`(既定 12h)が `accessToken` の寿命より長いと、長時間
|
||||
セッションが自分でトークンを更新し、`refreshToken` のローテーションで refresher 側の
|
||||
値が一時的に古くなることがあります。refresher は短い周期(既定 30 分)で追従します。
|
||||
- **refresher は Claude Code CLI の OAuth クライアント値(`anthropic.refresher.tokenUrl` /
|
||||
`clientId`)に依存します。これらは非公式で、仕様変更により動かなくなる可能性があります。**
|
||||
その場合でもセッション内 `claude` の自己更新は独立して機能します(新規セッションの
|
||||
起動時トークンが古い場合のみ影響)。不要なら `anthropic.refresher.enabled: false`。
|
||||
- refresher の権限は **セッション NS の認証情報 Secret 1 個への `get` / `patch` のみ**
|
||||
(`resourceNames` で限定した Role)。
|
||||
- `refreshToken` 自体が失効・失効させられた場合は、手元 PC で `claude` に再ログインして
|
||||
`--set-file anthropic.credentialsJson=...` で再投入します。
|
||||
- Secret の中身は refresher が書き換えます。`helm upgrade` で `--set-file` を付けない限り
|
||||
Helm は Secret の値を上書きしません(`helm.sh/resource-policy: keep` + 3-way merge)。
|
||||
|
||||
## 管理画面へのアクセス(LoadBalancer / Ingress)
|
||||
|
||||
管理画面(gateway)への到達経路は 2 つあり、**併用できます**。既定は LoadBalancer です。
|
||||
|
||||
### A. LoadBalancer でローカルネットワークの IP を使う(既定・Ingress 不要)
|
||||
|
||||
`service.type: LoadBalancer`(既定)にすると、クラスタの LB 実装
|
||||
(**kube-vip** / MetalLB / Cilium LB-IPAM など)がローカルネットワークの IP を払い出します。
|
||||
その IP をブラウザで開くだけでアクセスできます。ドメインも Ingress も TLS 終端も不要です。
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
enabled: false # Ingress は張らない
|
||||
service:
|
||||
type: LoadBalancer
|
||||
port: 80
|
||||
annotations:
|
||||
kube-vip.io/loadbalancerIPs: "192.168.9.50" # 払い出す VIP(空きアドレス)
|
||||
kube-vip.io/vipHost: "cpi11"
|
||||
loadBalancerSourceRanges: # 接続元をローカルセグメントに限定(第一の防御線)
|
||||
- 192.168.9.0/24
|
||||
externalTrafficPolicy: Local # 実クライアント IP を保持
|
||||
```
|
||||
|
||||
```bash
|
||||
kubectl -n claude-code get svc claude-code # EXTERNAL-IP を確認
|
||||
# → http://<EXTERNAL-IP>/ をブラウザで開く
|
||||
```
|
||||
|
||||
| キー | 説明 |
|
||||
|---|---|
|
||||
| `service.type` | `LoadBalancer`(既定)/ `ClusterIP` / `NodePort` |
|
||||
| `service.port` | 待受ポート(既定 `80`) |
|
||||
| `service.annotations` | LB 実装へのヒント(kube-vip の VIP 指定、MetalLB のプール指定など) |
|
||||
| `service.loadBalancerIP` | 払い出す IP を直接指定(annotations 指定を推奨) |
|
||||
| `service.loadBalancerSourceRanges` | 接続を許可する CIDR。**Ingress を張らない構成では必ず設定して接続元を絞る** |
|
||||
| `service.loadBalancerClass` | 複数 LB 実装がある場合の指定 |
|
||||
| `service.externalTrafficPolicy` | `Local` で実クライアント IP を保持 |
|
||||
| `service.nodePort` | `type: NodePort` 時の固定ポート |
|
||||
|
||||
> **セキュリティ** — LoadBalancer は L3/L4 でネットワークに口を開けます。`loadBalancerSourceRanges`
|
||||
> で接続元を絞り、`auth.mode` は `token`(既定)以上にしてください。VIP セグメントは
|
||||
> ルーターの DHCP 範囲と重複させないこと。
|
||||
|
||||
### B. Ingress でドメインアクセス(従来どおり維持)
|
||||
|
||||
`ingress.enabled: true` で Ingress を作成します。A と同時に有効化しても構いません。
|
||||
WebSocket 用に `proxy-read-timeout` / `proxy-send-timeout` を既定 `3600` に設定します
|
||||
(`ingress.annotations` で上書き可)。TLS は `ingress.tls` で設定します。
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
hosts:
|
||||
- host: claude-code.example.com
|
||||
paths: [{ path: /, pathType: Prefix }]
|
||||
tls:
|
||||
- secretName: claude-code-tls
|
||||
hosts: [claude-code.example.com]
|
||||
```
|
||||
|
||||
## インストール
|
||||
|
||||
### 前提
|
||||
|
||||
- Kubernetes 1.25+(PodSecurity admission)、NetworkPolicy 対応 CNI(Calico / Cilium など)
|
||||
- Ingress コントローラー(外部公開する場合。WebSocket を通すため read/send timeout を延長)
|
||||
- `ANTHROPIC_API_KEY`(または `claude setup-token` で発行した OAuth トークン)
|
||||
- 管理画面の公開方法に応じて **どちらか**:
|
||||
- LoadBalancer 実装(kube-vip / MetalLB / Cilium LB-IPAM など) … 既定
|
||||
- Ingress コントローラー … `ingress.enabled: true` の場合
|
||||
- どちらも無い場合は `service.type: ClusterIP` + `kubectl port-forward` で利用可
|
||||
- Claude の認証情報 … `~/.claude/.credentials.json`(既定 `credentials` モード・推奨)
|
||||
または `claude setup-token` の OAuth トークン、`ANTHROPIC_API_KEY`
|
||||
|
||||
### Helm リポジトリから
|
||||
|
||||
@@ -130,6 +262,23 @@ helm upgrade --install claude-code cafepieters/claude-code \
|
||||
--set anthropic.apiKey="$ANTHROPIC_API_KEY"
|
||||
```
|
||||
|
||||
### ローカル LB の IP + サブスクリプション認証(API 不要・推奨構成)
|
||||
|
||||
```bash
|
||||
# 手元の PC は claude にログイン済みであること(~/.claude/.credentials.json が存在)
|
||||
helm upgrade --install claude-code cafepieters/claude-code \
|
||||
-n claude-code --create-namespace \
|
||||
-f examples/loadbalancer-oauth.values.yaml \
|
||||
--set-file anthropic.credentialsJson="$HOME/.claude/.credentials.json" \
|
||||
--set-file git.identities.gitea-ssh.sshPrivateKey=P:/Claude/.ssh/id_claude \
|
||||
--set-file git.identities.gitea-ssh.knownHosts=./known_hosts.gitea
|
||||
```
|
||||
|
||||
以降トークンが変わっても再デプロイは不要です(refresher CronJob が Secret を追従更新)。
|
||||
|
||||
`examples/loadbalancer-oauth.values.yaml` は Ingress 無効・`service.type: LoadBalancer` +
|
||||
kube-vip の VIP 指定 + `loadBalancerSourceRanges` によるローカルセグメント限定の構成です。
|
||||
|
||||
### ローカルチャートから(GitHub 公開リポジトリだけ触る最小構成)
|
||||
|
||||
```bash
|
||||
@@ -159,11 +308,11 @@ ssh-keyscan 192.168.9.65 > known_hosts.gitea
|
||||
|
||||
## 使い方
|
||||
|
||||
1. アクセス(Ingress 無効ならポートフォワード):
|
||||
|
||||
```bash
|
||||
kubectl -n claude-code port-forward svc/claude-code 8080:80
|
||||
```
|
||||
1. 管理画面を開く:
|
||||
- **LoadBalancer(既定)**: `kubectl -n claude-code get svc claude-code` の `EXTERNAL-IP` を
|
||||
ブラウザで開く(`http://<IP>/`)
|
||||
- **Ingress**: 設定したドメイン(`https://claude-code.example.com/`)
|
||||
- **どちらも無効**: `kubectl -n claude-code port-forward svc/claude-code 8080:80` → `http://localhost:8080/`
|
||||
|
||||
2. `auth.mode: token` の場合、トークンを取得して UI に貼り付け(または `?token=` で開く):
|
||||
|
||||
@@ -227,14 +376,23 @@ ssh-keyscan 192.168.9.65 > known_hosts.gitea
|
||||
|
||||
### anthropic — Claude Code 認証情報
|
||||
|
||||
詳細は [Claude 認証(API 課金なし / サブスクリプション)](#claude-認証api-課金なし--サブスクリプション) を参照。
|
||||
|
||||
| キー | 既定 | 説明 |
|
||||
|---|---|---|
|
||||
| `anthropic.authMode` | `apiKey` | `apiKey`(`ANTHROPIC_API_KEY`)/ `oauth`(`CLAUDE_CODE_OAUTH_TOKEN`) |
|
||||
| `anthropic.authMode` | `credentials` | `credentials`(`~/.claude/.credentials.json`・推奨)/ `oauth`(`CLAUDE_CODE_OAUTH_TOKEN`)/ `apiKey`(`ANTHROPIC_API_KEY`) |
|
||||
| `anthropic.credentialsJson` | `""` | `--set-file anthropic.credentialsJson=$HOME/.claude/.credentials.json` で注入 |
|
||||
| `anthropic.apiKey` / `anthropic.oauthToken` | `""` | `--set` / `--set-file` で注入 |
|
||||
| `anthropic.existingSecret` | `""` | 既存 Secret を使う(キー名は `existingSecretKeyApiKey` / `existingSecretKeyOauthToken`) |
|
||||
| `anthropic.existingSecret` | `""` | 既存 Secret を使う(キー名は `existingSecretKeyCredentials`(既定 `.credentials.json`)/ `...ApiKey` / `...OauthToken`) |
|
||||
| `anthropic.refresher.enabled` | `true` | `credentials` モードで、Secret 内 `refreshToken` を使いトークンを定期更新する CronJob を作成 |
|
||||
| `anthropic.refresher.schedule` | `*/30 * * * *` | refresher CronJob のスケジュール |
|
||||
| `anthropic.refresher.refreshIfExpiresInSeconds` | `3600` | `accessToken` の残り寿命がこの秒数未満なら更新 |
|
||||
| `anthropic.refresher.tokenUrl` / `clientId` | Claude Code CLI の公開値 | OAuth トークンエンドポイントとクライアント ID(**非公式・仕様変更で要更新**) |
|
||||
| `anthropic.extraEnv` | `{}` | 全セッションへ渡す追加 env(例 `ANTHROPIC_BASE_URL`、Bedrock/Vertex 切替など) |
|
||||
|
||||
Secret は **セッション NS** に作成され、セッション Pod へ `envFrom` で注入されます。
|
||||
Secret は **セッション NS** に作成されます。`credentials` モードはファイルとしてマウントし
|
||||
セッション起動時に `$HOME/.claude/.credentials.json` へコピー(書き込み可能)、
|
||||
`apiKey` / `oauth` モードは `envFrom` で環境変数として注入します。
|
||||
|
||||
### git — 接続先と認証アイデンティティ
|
||||
|
||||
@@ -288,9 +446,8 @@ GitHub でも Gitea でも GitLab でも、SSH か HTTPS トークンで認証
|
||||
|
||||
### ingress / service
|
||||
|
||||
`ingress.enabled: true` で Ingress を作成。WebSocket 用に `proxy-read-timeout` /
|
||||
`proxy-send-timeout` を既定 `3600` に設定します(`ingress.annotations` で上書き可)。
|
||||
`service.type` は `ClusterIP` 既定。
|
||||
→ [管理画面へのアクセス(LoadBalancer / Ingress)](#管理画面へのアクセスloadbalancer--ingress) を参照。
|
||||
`service.type` の既定は `LoadBalancer`、`ingress.enabled` の既定は `false`(両者は併用可)。
|
||||
|
||||
## セッションイメージ
|
||||
|
||||
@@ -368,4 +525,8 @@ docker buildx build --platform linux/amd64,linux/arm64 \
|
||||
|
||||
| バージョン | Claude Code | 変更内容 |
|
||||
|---|---|---|
|
||||
| 2.1.263-c | 2.1.263 | 修正: gateway の nginx が起動時 `pcre2_compile() failed: missing closing parenthesis` でクラッシュループ。`location ~` の正規表現に `{10}` が含まれ、nginx がブロック区切りの `{}` と解釈していた。正規表現全体をダブルクォートで囲んで解消 |
|
||||
| 2.1.263-b | 2.1.263 | `anthropic.authMode: credentials`(既定)を追加。`~/.claude/.credentials.json` 全体を注入し、セッション内 `claude` の自己更新に加え、クラスタ内 refresher CronJob(`refreshToken` で Secret を定期更新)でトークン失効に自動追従。→ トークンが変わっても再デプロイ不要。refresher の RBAC は認証情報 Secret 1 個の get/patch に限定 |
|
||||
| 2.1.263-a | 2.1.263 | Service を `LoadBalancer` 対応(kube-vip 等でローカル LB の IP から管理画面へアクセス、`loadBalancerSourceRanges` で接続元制限、既定を `LoadBalancer` に変更)。Ingress は従来どおり併用可。API を使わないサブスクリプション認証(`claude setup-token` → `authMode: oauth`)の手順を README に追記 |
|
||||
| 2.1.263 | 2.1.263 | 自動更新 |
|
||||
| 2.1.259 | 2.1.259 | 初版。gateway + manager + 使い捨て session Pod + reaper、GitHub/Gitea 対応、アイドル自動破棄、NetworkPolicy / RBAC 最小化 / restricted PodSecurity |
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# ローカルネットワーク限定の LoadBalancer で公開し、Claude のサブスクリプション
|
||||
# (API 課金なし)で動かす構成。Ingress は張らない。
|
||||
#
|
||||
# 前提:
|
||||
# - クラスタに LB 実装(kube-vip / MetalLB / Cilium LB-IPAM 等)がある
|
||||
# - 手元の PC が claude にログイン済み(~/.claude/.credentials.json が存在)
|
||||
#
|
||||
# 実行例:
|
||||
# helm upgrade --install claude-code cafepieters/claude-code \
|
||||
# -n claude-code --create-namespace \
|
||||
# -f examples/loadbalancer-oauth.values.yaml \
|
||||
# --set-file anthropic.credentialsJson="$HOME/.claude/.credentials.json" \
|
||||
# --set-file git.identities.gitea-ssh.sshPrivateKey=P:/Claude/.ssh/id_claude \
|
||||
# --set-file git.identities.gitea-ssh.knownHosts=./known_hosts.gitea
|
||||
|
||||
# ---- Claude 認証: API を使わずサブスクリプション(Pro / Max)で動かす ----
|
||||
anthropic:
|
||||
authMode: credentials # ~/.claude/.credentials.json 全体を注入(refreshToken 込み)
|
||||
# credentialsJson は --set-file で注入する(平文で書かない)
|
||||
refresher:
|
||||
enabled: true # クラスタ内で accessToken を定期更新し Secret を書き戻す
|
||||
schedule: "*/30 * * * *"
|
||||
|
||||
# ---- 管理画面はローカル LB の IP でアクセス(Ingress なし)----
|
||||
ingress:
|
||||
enabled: false
|
||||
|
||||
service:
|
||||
type: LoadBalancer
|
||||
port: 80
|
||||
annotations:
|
||||
# kube-vip で払い出す VIP を固定(この 192.168.9.x は空いているものを指定)
|
||||
kube-vip.io/loadbalancerIPs: "192.168.9.50"
|
||||
kube-vip.io/vipHost: "cpi11"
|
||||
# 接続元をローカルセグメントに限定(Ingress を張らない構成での第一の防御線)
|
||||
loadBalancerSourceRanges:
|
||||
- 192.168.9.0/24
|
||||
externalTrafficPolicy: Local # 実クライアント IP を保持(LB 実装が対応する場合)
|
||||
|
||||
# ---- ブラウザアクセスの認証: トークン(自動生成)----
|
||||
auth:
|
||||
mode: token
|
||||
# token.value を空にすると install 時に自動生成される。
|
||||
# 取得: kubectl -n claude-code get secret claude-code-auth -o jsonpath='{.data.token}' | base64 -d
|
||||
|
||||
# ---- Git 接続先 ----
|
||||
git:
|
||||
allowedHosts:
|
||||
- github.com
|
||||
- 192.168.9.65
|
||||
- git.cafepieters.com
|
||||
identities:
|
||||
gitea-ssh:
|
||||
type: ssh
|
||||
# sshPrivateKey / knownHosts は --set-file で注入
|
||||
# github-https:
|
||||
# type: https-token
|
||||
# username: "x-access-token"
|
||||
|
||||
session:
|
||||
autoStartClaude: true
|
||||
idleTimeoutSeconds: 1800
|
||||
maxSessions: 5
|
||||
# verifyCommand: "npm test"
|
||||
# autoPush:
|
||||
# enabled: true
|
||||
@@ -72,7 +72,8 @@ http {
|
||||
}
|
||||
|
||||
# ---- セッションへのリバースプロキシ(WebSocket 対応)----
|
||||
location ~ ^/s/(?<sid>[a-f0-9]{10})(?<rest>/.*)?$ {
|
||||
# 正規表現に { } を含むため location 全体をクォートする(nginx 必須)
|
||||
location ~ "^/s/(?<sid>[a-f0-9]{10})(?<rest>/.*)?$" {
|
||||
include /tmp/nginx/auth_basic.conf;
|
||||
include /tmp/nginx/auth_request.conf;
|
||||
error_page 401 = @needlogin;
|
||||
|
||||
+23
-1
@@ -54,6 +54,8 @@ SESSION_IMAGE_PULL_POLICY = env("SESSION_IMAGE_PULL_POLICY", "IfNotPresent")
|
||||
SESSION_IMAGE_PULL_SECRETS = env_json("SESSION_IMAGE_PULL_SECRETS_JSON", [])
|
||||
BOOTSTRAP_CM = env("SESSION_BOOTSTRAP_CONFIGMAP", FULLNAME + "-session")
|
||||
ANTHROPIC_SECRET = env("ANTHROPIC_SECRET", FULLNAME + "-anthropic")
|
||||
ANTHROPIC_AUTH_MODE = env("ANTHROPIC_AUTH_MODE", "apiKey")
|
||||
ANTHROPIC_CREDENTIALS_KEY = env("ANTHROPIC_CREDENTIALS_KEY", ".credentials.json")
|
||||
GIT_IDENTITIES = env_json("GIT_IDENTITIES_JSON", {})
|
||||
GIT_ALLOWED_HOSTS = [h.strip().lower() for h in env("GIT_ALLOWED_HOSTS", "").split(",") if h.strip()]
|
||||
GIT_ALLOW_ARBITRARY = env("GIT_ALLOW_ARBITRARY", "false").lower() == "true"
|
||||
@@ -249,13 +251,33 @@ def build_pod(sid, repo, ref, identity_name, owner):
|
||||
mounts.append({"name": "git-identity", "mountPath": "/git-identity", "readOnly": True})
|
||||
env_list.append({"name": "GIT_HTTPS_USERNAME", "value": ident.get("username", "x-access-token")})
|
||||
|
||||
# ---- Claude 認証情報の渡し方 ----
|
||||
# credentials モード: Secret をファイルとしてマウントし、エントリポイントが
|
||||
# $HOME/.claude/.credentials.json へコピーする(書き込み可能にして claude 自身の
|
||||
# リフレッシュを許す)。env var 名に "." を含められないため envFrom は使わない。
|
||||
# apiKey / oauth モード: 有効な env var 名なので envFrom でそのまま注入する。
|
||||
env_from = []
|
||||
if ANTHROPIC_AUTH_MODE == "credentials":
|
||||
volumes.append({
|
||||
"name": "anthropic-creds",
|
||||
"secret": {
|
||||
"secretName": ANTHROPIC_SECRET,
|
||||
"defaultMode": 0o400,
|
||||
"items": [{"key": ANTHROPIC_CREDENTIALS_KEY, "path": "credentials.json"}],
|
||||
},
|
||||
})
|
||||
mounts.append({"name": "anthropic-creds", "mountPath": "/anthropic", "readOnly": True})
|
||||
env_list.append({"name": "SEED_CREDENTIALS_FILE", "value": "/anthropic/credentials.json"})
|
||||
else:
|
||||
env_from = [{"secretRef": {"name": ANTHROPIC_SECRET}}]
|
||||
|
||||
container = {
|
||||
"name": "claude",
|
||||
"image": SESSION_IMAGE,
|
||||
"imagePullPolicy": SESSION_IMAGE_PULL_POLICY,
|
||||
"command": ["/bin/sh", "/bootstrap/entrypoint.sh"],
|
||||
"env": env_list,
|
||||
"envFrom": [{"secretRef": {"name": ANTHROPIC_SECRET}}],
|
||||
"envFrom": env_from,
|
||||
"ports": [{"name": "ttyd", "containerPort": TTYD_PORT}],
|
||||
"volumeMounts": mounts,
|
||||
"resources": SESSION_RESOURCES,
|
||||
|
||||
@@ -114,6 +114,25 @@ if ! command -v claude >/dev/null 2>&1; then
|
||||
fi
|
||||
log "claude: $(claude --version 2>/dev/null || echo 'unknown')"
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 3b. Claude 認証情報(credentials モード)
|
||||
# --------------------------------------------------------------------------
|
||||
# Secret からマウントした .credentials.json を書き込み可能な $HOME/.claude へコピーする。
|
||||
# claude はアクセストークン失効時に refreshToken でこのファイルを自己更新する
|
||||
# (セッション中のみ有効。クラスタ全体の更新は refresher CronJob が担当)。
|
||||
if [ -n "${SEED_CREDENTIALS_FILE:-}" ] && [ -r "$SEED_CREDENTIALS_FILE" ]; then
|
||||
mkdir -p "$HOME/.claude"
|
||||
chmod 700 "$HOME/.claude"
|
||||
install -m 600 "$SEED_CREDENTIALS_FILE" "$HOME/.claude/.credentials.json"
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
_exp="$(jq -r '.claudeAiOauth.expiresAt // empty' "$HOME/.claude/.credentials.json" 2>/dev/null)"
|
||||
[ -n "$_exp" ] && log "Claude 認証情報を配置(accessToken 期限: $(date -u -d "@$(( _exp / 1000 ))" 2>/dev/null || echo "$_exp"))"
|
||||
else
|
||||
log "Claude 認証情報を $HOME/.claude/.credentials.json に配置しました"
|
||||
fi
|
||||
unset ANTHROPIC_API_KEY CLAUDE_CODE_OAUTH_TOKEN
|
||||
fi
|
||||
|
||||
# 追加 apk(root 権限がある事前ビルドイメージのみ有効。非 root では警告のみ)
|
||||
if [ -n "${EXTRA_APK_PACKAGES:-}" ]; then
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
claude-code token-refresher — credentials モードのアクセストークン更新(CronJob)
|
||||
|
||||
Secret 内の ~/.claude/.credentials.json(claudeAiOauth.refreshToken)を使って
|
||||
新しい accessToken を取得し、Secret を書き戻す。これにより、認証情報を注入した
|
||||
マシンがオフラインでも、新規セッションは常に有効なトークンで起動できる。
|
||||
|
||||
依存: Python 標準ライブラリのみ。
|
||||
権限: セッション NS の対象 Secret 1 個に対する get / patch のみ(RBAC で限定)。
|
||||
|
||||
⚠ tokenUrl / clientId は Claude Code CLI の OAuth クライアント値(非公式)。
|
||||
仕様変更で 4xx が返るようになったらここを更新する。その場合でも、
|
||||
セッション内 claude の自己リフレッシュは独立して機能する。
|
||||
"""
|
||||
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import ssl
|
||||
import sys
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
NS = os.environ["SESSIONS_NAMESPACE"]
|
||||
SECRET = os.environ["ANTHROPIC_SECRET"]
|
||||
KEY = os.environ.get("CREDENTIALS_KEY", ".credentials.json")
|
||||
TOKEN_URL = os.environ.get("TOKEN_URL", "https://console.anthropic.com/v1/oauth/token")
|
||||
CLIENT_ID = os.environ.get("CLIENT_ID", "9d1c250a-e61b-44d9-88ed-5944d1962f5e")
|
||||
MARGIN = int(os.environ.get("REFRESH_IF_EXPIRES_IN", "3600"))
|
||||
|
||||
_SA = "/var/run/secrets/kubernetes.io/serviceaccount"
|
||||
with open(_SA + "/token", encoding="utf-8") as fh:
|
||||
_K8S_TOKEN = fh.read().strip()
|
||||
_CTX = ssl.create_default_context(cafile=_SA + "/ca.crt")
|
||||
_API = "https://kubernetes.default.svc"
|
||||
|
||||
|
||||
def log(msg):
|
||||
print(time.strftime("%Y-%m-%dT%H:%M:%S"), msg, flush=True)
|
||||
|
||||
|
||||
def k8s(method, path, body=None, ctype="application/json"):
|
||||
data = json.dumps(body).encode() if body is not None else None
|
||||
req = urllib.request.Request(_API + path, data=data, method=method)
|
||||
req.add_header("Authorization", "Bearer " + _K8S_TOKEN)
|
||||
req.add_header("Accept", "application/json")
|
||||
if data is not None:
|
||||
req.add_header("Content-Type", ctype)
|
||||
with urllib.request.urlopen(req, context=_CTX, timeout=30) as resp:
|
||||
raw = resp.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
def secret_path():
|
||||
return "/api/v1/namespaces/%s/secrets/%s" % (urllib.parse.quote(NS), urllib.parse.quote(SECRET))
|
||||
|
||||
|
||||
def oauth_refresh(refresh_token):
|
||||
payload = json.dumps({
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": refresh_token,
|
||||
"client_id": CLIENT_ID,
|
||||
}).encode()
|
||||
req = urllib.request.Request(TOKEN_URL, data=payload, method="POST")
|
||||
req.add_header("Content-Type", "application/json")
|
||||
req.add_header("Accept", "application/json")
|
||||
req.add_header("User-Agent", "claude-code-helm-token-refresher")
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return json.loads(resp.read())
|
||||
except urllib.error.HTTPError as exc:
|
||||
body = exc.read().decode("utf-8", "replace")[:400]
|
||||
raise SystemExit("token endpoint HTTP %s: %s" % (exc.code, body))
|
||||
|
||||
|
||||
def main():
|
||||
sec = k8s("GET", secret_path())
|
||||
b64 = (sec.get("data") or {}).get(KEY)
|
||||
if not b64:
|
||||
raise SystemExit("secret %s/%s にキー %r がありません" % (NS, SECRET, KEY))
|
||||
try:
|
||||
creds = json.loads(base64.b64decode(b64).decode("utf-8"))
|
||||
except Exception as exc: # noqa: BLE001
|
||||
raise SystemExit("credentials JSON をパースできません: %s" % exc)
|
||||
|
||||
oauth = creds.get("claudeAiOauth")
|
||||
if not isinstance(oauth, dict) or not oauth.get("refreshToken"):
|
||||
raise SystemExit("claudeAiOauth.refreshToken がありません(credentials モードの Secret ではない?)")
|
||||
|
||||
now_ms = int(time.time() * 1000)
|
||||
expires_at = int(oauth.get("expiresAt") or 0)
|
||||
if expires_at and now_ms < expires_at - MARGIN * 1000:
|
||||
remain = (expires_at - now_ms) // 1000
|
||||
log("まだ有効(残り %ds、しきい値 %ds)。更新不要。" % (remain, MARGIN))
|
||||
return
|
||||
|
||||
log("トークンを更新します(expiresAt=%s, now=%s)" % (expires_at or "?", now_ms))
|
||||
resp = oauth_refresh(oauth["refreshToken"])
|
||||
access = resp.get("access_token")
|
||||
if not access:
|
||||
raise SystemExit("応答に access_token がありません: %s" % json.dumps(resp)[:300])
|
||||
|
||||
oauth["accessToken"] = access
|
||||
if resp.get("refresh_token"):
|
||||
oauth["refreshToken"] = resp["refresh_token"]
|
||||
if resp.get("expires_in"):
|
||||
oauth["expiresAt"] = now_ms + int(resp["expires_in"]) * 1000
|
||||
creds["claudeAiOauth"] = oauth
|
||||
|
||||
new_b64 = base64.b64encode(json.dumps(creds).encode("utf-8")).decode("ascii")
|
||||
patch = {"data": {KEY: new_b64}}
|
||||
k8s("PATCH", secret_path(), patch, ctype="application/merge-patch+json")
|
||||
log("Secret %s/%s を更新しました(新 expiresAt=%s)" % (NS, SECRET, oauth.get("expiresAt")))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except SystemExit as exc:
|
||||
if exc.code not in (0, None):
|
||||
log("ERROR: %s" % exc.code)
|
||||
raise
|
||||
+18
-4
@@ -11,7 +11,15 @@ claude-code ({{ .Chart.Version }}) をデプロイしました。
|
||||
{{- range .Values.ingress.hosts }}
|
||||
https://{{ .host }}/
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{- end }}
|
||||
{{- if eq .Values.service.type "LoadBalancer" }}
|
||||
ロードバランサーの IP を確認してブラウザで開いてください:
|
||||
kubectl -n {{ .Release.Namespace }} get svc {{ include "claude-code.fullname" . }} -w
|
||||
→ http://<EXTERNAL-IP>:{{ .Values.service.port }}/
|
||||
{{- with .Values.service.loadBalancerSourceRanges }}
|
||||
(接続元は {{ join ", " . }} に制限されています)
|
||||
{{- end }}
|
||||
{{- else if not .Values.ingress.enabled }}
|
||||
kubectl -n {{ .Release.Namespace }} port-forward svc/{{ include "claude-code.fullname" . }} 8080:{{ .Values.service.port }}
|
||||
→ http://localhost:8080/
|
||||
{{- end }}
|
||||
@@ -34,10 +42,16 @@ Basic 認証のユーザーでログインしてください。
|
||||
⚠ auth.mode=none — 認証なしで公開されています。信頼済みネットワーク限定で使用してください。
|
||||
{{- end }}
|
||||
|
||||
{{- if and (not .Values.anthropic.existingSecret) (not .Values.anthropic.apiKey) (not .Values.anthropic.oauthToken) }}
|
||||
認証方式 : anthropic.authMode = {{ .Values.anthropic.authMode }}
|
||||
{{- if and (eq .Values.anthropic.authMode "credentials") .Values.anthropic.refresher.enabled }}
|
||||
トークン更新: refresher CronJob({{ .Values.anthropic.refresher.schedule }})が Secret を自動更新
|
||||
{{- end }}
|
||||
{{- if and (not .Values.anthropic.existingSecret) (not .Values.anthropic.apiKey) (not .Values.anthropic.oauthToken) (not .Values.anthropic.credentialsJson) }}
|
||||
|
||||
⚠ Claude Code の認証情報が未設定です(anthropic.apiKey / oauthToken / existingSecret のいずれも空)。
|
||||
セッション内の `claude` は認証に失敗します。`--set anthropic.apiKey=...` で再適用してください。
|
||||
⚠ Claude Code の認証情報が未設定です(anthropic.apiKey / oauthToken / credentialsJson / existingSecret のいずれも空)。
|
||||
セッション内の `claude` は認証に失敗します。credentials モードなら:
|
||||
--set-file anthropic.credentialsJson=$HOME/.claude/.credentials.json
|
||||
で再適用してください(helm upgrade)。
|
||||
{{- end }}
|
||||
{{- if not .Values.git.identities }}
|
||||
|
||||
|
||||
@@ -8,3 +8,5 @@ metadata:
|
||||
data:
|
||||
manager.py: |
|
||||
{{ .Files.Get "files/manager.py" | indent 4 }}
|
||||
token-refresher.py: |
|
||||
{{ .Files.Get "files/token-refresher.py" | indent 4 }}
|
||||
|
||||
@@ -131,6 +131,10 @@ spec:
|
||||
value: {{ include "claude-code.fullname" . }}-session
|
||||
- name: ANTHROPIC_SECRET
|
||||
value: {{ include "claude-code.anthropicSecretName" . | quote }}
|
||||
- name: ANTHROPIC_AUTH_MODE
|
||||
value: {{ .Values.anthropic.authMode | quote }}
|
||||
- name: ANTHROPIC_CREDENTIALS_KEY
|
||||
value: {{ .Values.anthropic.existingSecretKeyCredentials | default ".credentials.json" | quote }}
|
||||
- name: GIT_IDENTITIES_JSON
|
||||
value: {{ $idents | toJson | quote }}
|
||||
- name: GIT_ALLOWED_HOSTS
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{{- if not .Values.anthropic.existingSecret }}
|
||||
# Claude Code 認証情報。セッション NS に作成し、セッション Pod へ env として注入する。
|
||||
# 値が空のままインストールするとセッション内の claude が認証に失敗する(NOTES 参照)。
|
||||
# Claude Code 認証情報。セッション NS に作成し、セッション Pod へ注入する。
|
||||
# credentials モードでは refresher CronJob がこの Secret を書き換えるため、
|
||||
# helm の管理から外れて中身が更新されうる(helm upgrade で上書きしないよう注意)。
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
@@ -8,9 +9,15 @@ metadata:
|
||||
namespace: {{ include "claude-code.sessionsNamespace" . }}
|
||||
labels:
|
||||
{{- include "claude-code.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
{{- if eq .Values.anthropic.authMode "credentials" }}
|
||||
"helm.sh/resource-policy": keep
|
||||
{{- end }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
{{- if eq .Values.anthropic.authMode "oauth" }}
|
||||
{{- if eq .Values.anthropic.authMode "credentials" }}
|
||||
".credentials.json": {{ .Values.anthropic.credentialsJson | default "{}" | quote }}
|
||||
{{- else if eq .Values.anthropic.authMode "oauth" }}
|
||||
CLAUDE_CODE_OAUTH_TOKEN: {{ .Values.anthropic.oauthToken | quote }}
|
||||
{{- else }}
|
||||
ANTHROPIC_API_KEY: {{ .Values.anthropic.apiKey | quote }}
|
||||
|
||||
@@ -6,13 +6,36 @@ metadata:
|
||||
labels:
|
||||
{{- include "claude-code.labels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: control-plane
|
||||
{{- with .Values.service.labels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.service.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
{{- if and (eq .Values.service.type "LoadBalancer") .Values.service.loadBalancerIP }}
|
||||
loadBalancerIP: {{ .Values.service.loadBalancerIP | quote }}
|
||||
{{- end }}
|
||||
{{- with .Values.service.loadBalancerClass }}
|
||||
loadBalancerClass: {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- if and (eq .Values.service.type "LoadBalancer") .Values.service.loadBalancerSourceRanges }}
|
||||
loadBalancerSourceRanges:
|
||||
{{- toYaml .Values.service.loadBalancerSourceRanges | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.service.externalTrafficPolicy }}
|
||||
externalTrafficPolicy: {{ . }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
port: {{ .Values.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
{{- if and (eq .Values.service.type "NodePort") .Values.service.nodePort }}
|
||||
nodePort: {{ .Values.service.nodePort }}
|
||||
{{- end }}
|
||||
selector:
|
||||
{{- include "claude-code.selectorLabels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: control-plane
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
{{- if and (eq .Values.anthropic.authMode "credentials") .Values.anthropic.refresher.enabled }}
|
||||
{{- $sa := printf "%s-refresher" (include "claude-code.fullname" .) -}}
|
||||
{{- $sessNs := include "claude-code.sessionsNamespace" . -}}
|
||||
{{- $secret := include "claude-code.anthropicSecretName" . -}}
|
||||
{{- $r := .Values.anthropic.refresher -}}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ $sa }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "claude-code.labels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: token-refresher
|
||||
automountServiceAccountToken: true
|
||||
---
|
||||
# セッション NS の Claude 認証情報 Secret 1 個に対する get / patch のみ。
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: {{ include "claude-code.fullname" . }}-refresher
|
||||
namespace: {{ $sessNs }}
|
||||
labels:
|
||||
{{- include "claude-code.labels" . | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["secrets"]
|
||||
resourceNames: [{{ $secret | quote }}]
|
||||
verbs: ["get", "patch", "update"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: {{ include "claude-code.fullname" . }}-refresher
|
||||
namespace: {{ $sessNs }}
|
||||
labels:
|
||||
{{- include "claude-code.labels" . | nindent 4 }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: {{ include "claude-code.fullname" . }}-refresher
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ $sa }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: {{ include "claude-code.fullname" . }}-token-refresher
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "claude-code.labels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: token-refresher
|
||||
spec:
|
||||
schedule: {{ $r.schedule | quote }}
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 1
|
||||
failedJobsHistoryLimit: 3
|
||||
startingDeadlineSeconds: 120
|
||||
jobTemplate:
|
||||
spec:
|
||||
activeDeadlineSeconds: 120
|
||||
backoffLimit: 2
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "claude-code.selectorLabels" . | nindent 12 }}
|
||||
app.kubernetes.io/component: token-refresher
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
serviceAccountName: {{ $sa }}
|
||||
automountServiceAccountToken: true
|
||||
securityContext:
|
||||
{{- include "claude-code.podSecurityContext" . | nindent 12 }}
|
||||
containers:
|
||||
- name: token-refresher
|
||||
image: "{{ .Values.image.manager.registry }}/{{ .Values.image.manager.repository }}:{{ .Values.image.manager.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.manager.pullPolicy }}
|
||||
command: ["python3", "-u", "/app/token-refresher.py"]
|
||||
env:
|
||||
- name: PYTHONDONTWRITEBYTECODE
|
||||
value: "1"
|
||||
- name: SESSIONS_NAMESPACE
|
||||
value: {{ $sessNs | quote }}
|
||||
- name: ANTHROPIC_SECRET
|
||||
value: {{ $secret | quote }}
|
||||
- name: CREDENTIALS_KEY
|
||||
value: {{ .Values.anthropic.existingSecretKeyCredentials | default ".credentials.json" | quote }}
|
||||
- name: TOKEN_URL
|
||||
value: {{ $r.tokenUrl | quote }}
|
||||
- name: CLIENT_ID
|
||||
value: {{ $r.clientId | quote }}
|
||||
- name: REFRESH_IF_EXPIRES_IN
|
||||
value: {{ $r.refreshIfExpiresInSeconds | quote }}
|
||||
securityContext:
|
||||
{{- include "claude-code.containerSecurityContext" . | nindent 16 }}
|
||||
resources:
|
||||
{{- toYaml $r.resources | nindent 16 }}
|
||||
volumeMounts:
|
||||
- name: app
|
||||
mountPath: /app
|
||||
readOnly: true
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
volumes:
|
||||
- name: app
|
||||
configMap:
|
||||
name: {{ include "claude-code.fullname" . }}-manager
|
||||
defaultMode: 0555
|
||||
- name: tmp
|
||||
emptyDir:
|
||||
medium: Memory
|
||||
sizeLimit: 8Mi
|
||||
{{- end }}
|
||||
+24
-4
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft-07/schema#",
|
||||
"title": "claude-code Helm chart values",
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
@@ -109,13 +108,27 @@
|
||||
"additionalProperties": false,
|
||||
"required": ["authMode"],
|
||||
"properties": {
|
||||
"authMode": { "type": "string", "enum": ["apiKey", "oauth"] },
|
||||
"authMode": { "type": "string", "enum": ["apiKey", "oauth", "credentials"] },
|
||||
"apiKey": { "type": "string" },
|
||||
"oauthToken": { "type": "string" },
|
||||
"credentialsJson": { "type": "string" },
|
||||
"existingSecret": { "type": "string" },
|
||||
"existingSecretKeyApiKey": { "type": "string" },
|
||||
"existingSecretKeyOauthToken": { "type": "string" },
|
||||
"extraEnv": { "type": "object" }
|
||||
"existingSecretKeyCredentials": { "type": "string" },
|
||||
"extraEnv": { "type": "object" },
|
||||
"refresher": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"enabled": { "type": "boolean" },
|
||||
"schedule": { "type": "string" },
|
||||
"refreshIfExpiresInSeconds": { "type": "integer", "minimum": 60 },
|
||||
"tokenUrl": { "type": "string" },
|
||||
"clientId": { "type": "string" },
|
||||
"resources": { "type": "object" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -236,7 +249,14 @@
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"type": { "type": "string", "enum": ["ClusterIP", "NodePort", "LoadBalancer"] },
|
||||
"port": { "type": "integer", "minimum": 1, "maximum": 65535 }
|
||||
"port": { "type": "integer", "minimum": 1, "maximum": 65535 },
|
||||
"nodePort": { "type": ["integer", "null"], "minimum": 1, "maximum": 65535 },
|
||||
"annotations": { "type": "object" },
|
||||
"loadBalancerIP": { "type": "string" },
|
||||
"loadBalancerClass": { "type": "string" },
|
||||
"loadBalancerSourceRanges": { "type": "array", "items": { "type": "string" } },
|
||||
"externalTrafficPolicy": { "type": "string", "enum": ["", "Cluster", "Local"] },
|
||||
"labels": { "type": "object" }
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
+65
-7
@@ -30,13 +30,13 @@ image:
|
||||
gateway:
|
||||
registry: docker.io
|
||||
repository: nginxinc/nginx-unprivileged
|
||||
tag: "1.29.1-alpine"
|
||||
tag: "1.31.5-alpine"
|
||||
pullPolicy: IfNotPresent
|
||||
# 制御プレーン(Python 標準ライブラリのみ・追加 pip 不要)
|
||||
manager:
|
||||
registry: docker.io
|
||||
repository: library/python
|
||||
tag: "3.13-alpine"
|
||||
tag: "3.14-alpine"
|
||||
pullPolicy: IfNotPresent
|
||||
# セッションの使い捨てコンテナ。
|
||||
# 既定は CI(session-image.yaml)がビルドする事前ビルドイメージ
|
||||
@@ -50,7 +50,7 @@ image:
|
||||
session:
|
||||
registry: git.cafepieters.com
|
||||
repository: helmchart/claude-code/session
|
||||
tag: "2.1.259"
|
||||
tag: "2.1.263"
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
imagePullSecrets: []
|
||||
@@ -153,18 +153,52 @@ auth:
|
||||
# anthropic: Claude Code の認証情報
|
||||
# --------------------------------------------------------------------------
|
||||
anthropic:
|
||||
# apiKey : ANTHROPIC_API_KEY を使う
|
||||
# oauth : CLAUDE_CODE_OAUTH_TOKEN を使う(`claude setup-token` で発行)
|
||||
authMode: apiKey
|
||||
# authMode:
|
||||
# credentials : ~/.claude/.credentials.json 全体(refreshToken 込み)を注入(推奨)。
|
||||
# 各セッションはこのファイルを $HOME/.claude/.credentials.json に配置し、
|
||||
# claude が自身でアクセストークンをリフレッシュする。加えて下記
|
||||
# refresher CronJob がクラスタ内で Secret を最新に保つ。
|
||||
# → API 課金なし。トークンが定期的に変わっても再デプロイ不要。
|
||||
# oauth : CLAUDE_CODE_OAUTH_TOKEN を注入(`claude setup-token` の単発トークン。
|
||||
# refreshToken を含まないため自動更新されない)。
|
||||
# apiKey : ANTHROPIC_API_KEY を注入(API 従量課金)。
|
||||
authMode: credentials
|
||||
apiKey: ""
|
||||
oauthToken: ""
|
||||
# credentials モード: claude ログイン済みマシンの ~/.claude/.credentials.json の中身。
|
||||
# 例: {"claudeAiOauth":{"accessToken":"...","refreshToken":"...","expiresAt":...,
|
||||
# "scopes":["user:inference","user:profile"],"subscriptionType":"max"}}
|
||||
# 注入は平文で書かず: --set-file anthropic.credentialsJson=$HOME/.claude/.credentials.json
|
||||
credentialsJson: ""
|
||||
# 上記の代わりに既存 Secret を参照
|
||||
existingSecret: ""
|
||||
existingSecretKeyApiKey: "ANTHROPIC_API_KEY"
|
||||
existingSecretKeyOauthToken: "CLAUDE_CODE_OAUTH_TOKEN"
|
||||
existingSecretKeyCredentials: ".credentials.json"
|
||||
# 全セッションへ渡す追加環境変数(例: ANTHROPIC_BASE_URL / ANTHROPIC_MODEL)
|
||||
extraEnv: {}
|
||||
|
||||
# ---- refresher: credentials モードのトークン自動更新 CronJob ----
|
||||
# Secret 内の refreshToken を使って新しい accessToken を取得し、Secret を書き戻す。
|
||||
# これで自マシンがオフラインでも新規セッションは常に有効なトークンで起動できる。
|
||||
# ⚠ Claude Code CLI の OAuth クライアント値(tokenUrl / clientId)に依存する。
|
||||
# これらは非公式であり、仕様変更で動かなくなる可能性がある。その場合でも
|
||||
# セッション内 claude の自己リフレッシュは独立して機能する。
|
||||
refresher:
|
||||
enabled: true
|
||||
schedule: "*/30 * * * *" # 30 分ごと
|
||||
# 期限まで残りこの秒数を切っていたら更新する(十分な余裕を持たせる)
|
||||
refreshIfExpiresInSeconds: 3600
|
||||
tokenUrl: "https://console.anthropic.com/v1/oauth/token"
|
||||
clientId: "9d1c250a-e61b-44d9-88ed-5944d1962f5e"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 32Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 96Mi
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# git: 接続先ホストと認証アイデンティティ
|
||||
# --------------------------------------------------------------------------
|
||||
@@ -308,8 +342,32 @@ ingress:
|
||||
# - claude-code.example.com
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
# LoadBalancer : ローカルネットワークの LB(kube-vip / MetalLB / Cilium LB-IPAM 等)から
|
||||
# IP を払い出し、その IP で管理画面へアクセスする(Ingress 不要・既定)
|
||||
# ClusterIP : Service IP のみ。port-forward か Ingress 経由でアクセス
|
||||
# NodePort : 各ノードの高位ポートで公開
|
||||
type: LoadBalancer
|
||||
port: 80
|
||||
# NodePort を固定したい場合のみ(type: NodePort)
|
||||
nodePort: null
|
||||
# Service に付与するアノテーション。kube-vip で VIP を指定する例:
|
||||
# kube-vip.io/loadbalancerIPs: "192.168.9.50"
|
||||
# kube-vip.io/vipHost: "cpi11"
|
||||
# MetalLB でプールを指定する例:
|
||||
# metallb.universe.tf/address-pool: "lan"
|
||||
annotations: {}
|
||||
# 払い出す IP を直接指定(LB 実装が対応していれば)。annotations 指定を推奨。
|
||||
loadBalancerIP: ""
|
||||
loadBalancerClass: ""
|
||||
# 接続元をローカルネットワークに限定する(LB 実装が対応していれば)。
|
||||
# Ingress を張らない構成では、ここで許可レンジを絞ることが第一の防御線になる。
|
||||
loadBalancerSourceRanges: []
|
||||
# - 192.168.9.0/24
|
||||
# - 10.0.0.0/8
|
||||
# Local にすると実クライアント IP を保持する(kube-proxy/LB 実装依存)
|
||||
externalTrafficPolicy: ""
|
||||
# Service 自体への追加ラベル
|
||||
labels: {}
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 制御プレーンの Pod / コンテナセキュリティ
|
||||
|
||||
Reference in New Issue
Block a user