From b2cd351c40ca0b50e76abf6ecd0dcdd3d6697a0e Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sat, 29 Aug 2026 15:06:23 +0900 Subject: [PATCH 1/7] =?UTF-8?q?docs(PLAN39):=20Google=20=E8=AA=8D=E8=A8=BC?= =?UTF-8?q?=E3=81=AE=E6=89=8B=E9=A0=86=E6=9B=B8=E3=82=92=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E3=81=99=E3=82=8B=20(PLAN39=20PR5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit アカウントグループごとの gcloud / gws 認証手順を新規に書き起こす。コマンドと出力は すべて実機 (carmo-ai / gcloud 582.0.0 / gws 0.22.5) で実行した結果を貼っている。 - 前提 — アカウントグループとボリュームの対応、`~/.config/gcloud` は gcloud の 設定ディレクトリ**ではない**こと ($CLOUDSDK_CONFIG を見る) - 新しいグループの初回セットアップ — env への記述から起動・確認まで。使えない グループ名 3 種の実際のエラー出力 - gcloud — `gcloud auth login` (フラグ不要で URL + 認証コードのフローになる) と `gcloud auth application-default login` の 2 回。`--update-adc` を既定にしない理由。 コンテナを作り直しても認証が残ることの確認手順 - グループごとに別アカウントになっていることの確認 (ボリュームを直接覗く) - gws — setup が GCP プロジェクトを要すること、OAuth クライアントの手動作成、 クライアントシークレットを env に書いてはいけないこと、login が localhost コールバック方式でホストのブラウザからは中継が要ること、 制限付きスコープで同意画面が進まないときの回避 - 認証モードの切り替え — adc / key / 未設定の使い分けと鍵が要る場面 - 確認コマンド — 自分のグループの確認方法を含む - トラブルシュート — DefaultCredentialsError の 2 種類の見分け、database is locked、 意図しないアカウントで操作していた場合 README の目次にも追加した。 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01S5oA2PqY6UX2Ca3t78886t --- README.md | 1 + docs/user/google-auth.md | 618 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 619 insertions(+) create mode 100644 docs/user/google-auth.md diff --git a/README.md b/README.md index 82004333..53838122 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,7 @@ devbaseのコマンドは4つのグループにまとめられています。 | [環境変数ガイド](docs/user/environment-variables.md) | 3レベル構造、コレクター、ソース同期 | | [環境変数の export/import ガイド](docs/user/env-export-import.md) | バンドル形式・age 暗号化・S3 連携・merge/replace の運用 | | [コンテナ操作ガイド](docs/user/container-operations.md) | ライフサイクル、並行開発、ボリューム構造 | +| [Google 認証ガイド](docs/user/google-auth.md) | アカウントグループごとの gcloud / gws 認証、`GCP_AUTH_MODE` | | [スナップショットガイド](docs/user/snapshot-guide.md) | 増分バックアップ、世代管理、復元手順 | | [トラブルシューティング](docs/user/troubleshooting.md) | カテゴリ別の問題と解決策 | | [Orca 削除の移行ガイド](docs/user/orca-removal-migration.md) | 旧 Orca(SSH) 接続の廃止と Remote-SSH への移行手順 | diff --git a/docs/user/google-auth.md b/docs/user/google-auth.md new file mode 100644 index 00000000..304fcafd --- /dev/null +++ b/docs/user/google-auth.md @@ -0,0 +1,618 @@ +# Google 認証ガイド + +devbase のコンテナで Google Cloud(gcloud)と Google Workspace(gws)を使うための手順です。 +**アカウントグループごとに人が 1 回だけ対話的に認証する**ことを前提にした仕組みなので、 +新しいグループを足すときはこのページを最初から順に実行してください。 + +このページのコマンドと出力は、すべて実機(`carmo-ai` コンテナ、gcloud 582.0.0 / gws 0.22.5)で +実行した結果を貼っています。 + +## 1. 前提 + +### アカウントグループとは + +**使用する Google / AWS アカウントの単位**です。`DEVBASE_ACCOUNT_GROUP` で宣言し、 +未設定なら `default` になります。グループごとに専用のボリュームが作られ、 +認証情報はその中にだけ入ります。 + +| マウント先 | ボリューム | 共有範囲 | 入るもの | +|---|---|---|---| +| `/persistent/ai` | `devbase_home_ubuntu` | 全コンテナ | `~/.claude/plugins` などテナントに紐づかない共通資産 | +| `/persistent/group` | `devbase_home_` | 同じグループ | gcloud / gws の設定、Claude Code の認証と会話ログ、`.gemini` | + +nyle.co.jp で認証した gcloud を kk-generation.com のプロジェクトが引き継がないための仕切りです。 +ボリューム構造の全体は [コンテナ操作ガイド](container-operations.md) を参照してください。 + +### `~/.config/gcloud` は gcloud の設定ディレクトリ**ではありません** + +devbase は `CLOUDSDK_CONFIG` を `/persistent/group/gcloud` へ向けています。 +`credentials.db` / `access_tokens.db` / `legacy_credentials/` / `configurations/` と +ADC ファイルはすべてそちらに入ります。 + +```console +$ echo $CLOUDSDK_CONFIG +/persistent/group/gcloud +``` + +`~/.config/gcloud` に残るのは、鍵モード(後述)で書き出されるサービスアカウント鍵だけです。 +これはコンテナ層(揮発)にあり、毎起動 `env` から書き直されます。 +**設定や認証情報を見たいときは `$CLOUDSDK_CONFIG` を参照してください。** + +`CLOUDSDK_CONFIG` は gcloud CLI 専用の仕組みではなく `google.auth` の探索経路そのものなので、 +BigQuery クライアントなどのライブラリも同じ場所を見ます。 + +## 2. 新しいグループの初回セットアップ + +プロジェクトの `env` にグループ名(と必要なら認証モード)を書いて起動します。 + +```bash +# projects//env +DEVBASE_ACCOUNT_GROUP=kkg +GCP_AUTH_MODE=adc # サービスアカウント鍵を使わない場合(推奨) +``` + +```bash +devbase project up +devbase project login +``` + +グループ名には次の 3 つが使えません。`devbase up` の前にエラーになります。 + +```console +$ DEVBASE_ACCOUNT_GROUP=ubuntu devbase up +Error: Deploy failed: DEVBASE_ACCOUNT_GROUP に予約語は使えません: 'ubuntu'。共通ボリューム devbase_home_ubuntu と同じ名前になります + +$ DEVBASE_ACCOUNT_GROUP=1 devbase up +Error: Deploy failed: DEVBASE_ACCOUNT_GROUP に数字だけの名前は使えません: '1'。インスタンス番号のボリューム devbase_home_ と同じ名前になります + +$ DEVBASE_ACCOUNT_GROUP="bad name" devbase up +Error: Deploy failed: DEVBASE_ACCOUNT_GROUP が不正です: 'bad name'。Docker のボリューム名に使える文字 (英数字・ドット・ハイフン・アンダースコア、先頭は英数字) だけを使ってください +``` + +起動できたら、コンテナ内でどのグループにいるかを確認します。 + +```console +$ echo $DEVBASE_ACCOUNT_GROUP +kkg +$ echo $CLOUDSDK_CONFIG +/persistent/group/gcloud +``` + +新しいグループは当然まだ未認証です。 + +```console +$ gcloud auth list +To login, run: + $ gcloud auth login `ACCOUNT` +``` + +## 3. gcloud の認証 + +**2 回実行します。**`gcloud auth login`(CLI 用)と +`gcloud auth application-default login`(ライブラリ用の ADC)は**別物**です。 + +### 3.1 `gcloud auth login`(CLI 用) + +```bash +gcloud auth login +``` + +**フラグは要りません。** この環境では自動的に「URL を貼って認証コードを戻す」フローになります。 +gcloud はブラウザを起動できるかを `DISPLAY` / `WAYLAND_DISPLAY` / `MIR_SOCKET` の有無で判定し、 +コンテナ内ではどれも無いため `--no-launch-browser` と同じ経路が選ばれるためです。 +VS Code のポート転送の有無は関係ありません。 + +手元の別マシンのブラウザで URL を開き、表示された認証コードをターミナルへ貼り戻します。 + +完了すると active account が設定されます。 + +```console +$ gcloud auth list + Credentialed Accounts +ACTIVE ACCOUNT +* takemi_ohama@kk-generation.com + +To set the active account, run: + $ gcloud config set account `ACCOUNT` + +$ gcloud config get account +takemi_ohama@kk-generation.com +``` + +このとき `$CLOUDSDK_CONFIG` の中身は次のようになります。 + +```console +$ ls -A $CLOUDSDK_CONFIG +.last_survey_prompt.yaml access_tokens.db active_config config_sentinel +configurations credentials.db default_configs.db gce legacy_credentials logs +``` + +**この時点では ADC ファイルはまだありません。** + +```console +$ ls -l $CLOUDSDK_CONFIG/application_default_credentials.json +ls: cannot access '/persistent/group/gcloud/application_default_credentials.json': No such file or directory +``` + +### 3.2 `gcloud auth application-default login`(ライブラリ用) + +BigQuery クライアントなど、`google.auth` を使うライブラリはこちらを見ます。 + +```console +$ gcloud auth application-default login +Go to the following link in your browser, and complete the sign-in prompts: + + https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=...&redirect_uri=https%3A%2F%2Fsdk.cloud.google.com%2Fapplicationdefaultauthcode.html&scope=openid+...&prompt=consent&token_usage=remote&access_type=offline&code_challenge=...&code_challenge_method=S256 + +Once finished, enter the verification code provided in your browser: <ブラウザに表示されたコードを貼る> + +Credentials saved to file: [/persistent/group/gcloud/application_default_credentials.json] + +These credentials will be used by any library that requests Application Default Credentials (ADC). +WARNING: +Cannot find a quota project to add to ADC. You might receive a "quota exceeded" or "API not enabled" error. Run $ gcloud auth application-default set-quota-project to add a quota project. +``` + +保存先が **`/persistent/group/gcloud/`**(= グループボリューム)になっている点が要点です。 + +```console +$ ls -l $CLOUDSDK_CONFIG/application_default_credentials.json +-rw------- 1 ubuntu ubuntu 351 Aug 29 06:00 /persistent/group/gcloud/application_default_credentials.json +``` + +これでライブラリ側からユーザー認証が使えます。 + +```console +$ PYTHONPATH=/opt/google-cloud-sdk/lib/third_party python3 -c \ + "import google.auth; c, p = google.auth.default(); print(p, type(c).__name__)" +nyle-carmo-analysis Credentials +``` + +`Credentials`(= ユーザー認証)であって `ServiceAccountCredentials` ではないことを確認してください。 + +> **Note:** 末尾の警告のとおり、この時点では **quota project が ADC に書かれていません**。 +> quota project を要する API(`quota exceeded` / `API not enabled` が出るもの)を使うなら +> 追加してください。 +> +> ```bash +> gcloud auth application-default set-quota-project <プロジェクトID> +> ``` +> +> ```console +> $ python3 -c 'import json;print(sorted(json.load(open("/persistent/group/gcloud/application_default_credentials.json")).keys()))' +> ['account', 'client_id', 'client_secret', 'refresh_token', 'type', 'universe_domain'] +> ``` +> +> `quota_project_id` が無い状態です。 + +> **Note:** `gcloud auth login --update-adc` で 1 回に減らす案は**採りません**。 +> `--update-adc` は quota project を ADC に書かないため(`add_quota_project=False` のまま +> ADC を書き出す)、quota project を要する API で困ります。 +> `gcloud auth application-default login` は quota project も書き込みます。 + +> **Note:** 鍵モード(`GCP_AUTH_MODE=key`)で実行すると、gcloud が +> 「Credentials will still be generated to the default location / To use these credentials, +> unset this environment variable before running your application」と警告します。 +> `GOOGLE_APPLICATION_CREDENTIALS` が設定されていると ADC よりそちらが優先されるためです。 +> ADC を使いたいなら `GCP_AUTH_MODE=adc` にしてください(後述)。 + +### 3.3 コンテナを作り直しても認証が残ることの確認 + +ここが PLAN39 で直した点です。`devbase down` はコンテナを削除しますが、認証情報は +グループボリュームに残るので**再認証は要りません**。 + +```console +$ devbase project down && devbase project up +$ gcloud auth list + Credentialed Accounts +ACTIVE ACCOUNT +* takemi_ohama@kk-generation.com + +$ PYTHONPATH=/opt/google-cloud-sdk/lib/third_party python3 -c \ + "import google.auth; c, p = google.auth.default(); print(p, type(c).__name__)" +nyle-carmo-analysis Credentials +``` + +### 3.4 グループごとに別のアカウントになっていることの確認 + +これが分離の目的です。ホスト側からボリュームを直接覗くと、グループごとに別のアカウントの +認証情報が入っていることが分かります。 + +```console +$ docker run --rm -v devbase_home_default:/g alpine ls /g/gcloud/legacy_credentials +takemi_ohama@nyle.co.jp + +$ docker run --rm -v devbase_home_kkg:/g alpine ls /g/gcloud/legacy_credentials +takemi_ohama@kk-generation.com +``` + +コンテナ内から見ると、自分のグループのアカウントしか見えません。 + +```console +# default グループのコンテナ +$ gcloud config get account +takemi_ohama@nyle.co.jp + +# kkg グループのコンテナ +$ gcloud config get account +takemi_ohama@kk-generation.com +``` + +## 4. gws(Google Workspace CLI)の認証 + +`gws` は base イメージに同梱されています。 + +```console +$ command -v gws +/usr/local/share/npm-global/bin/gws +$ gws --version +gws 0.22.5 +``` + +設定ディレクトリは `GOOGLE_WORKSPACE_CLI_CONFIG_DIR` でグループボリュームへ向いています。 + +```console +$ gws auth status +{ + "auth_method": "none", + "client_config": "/persistent/group/gws/client_secret.json", + "client_config_exists": false, + "credential_source": "none", + "encrypted_credentials": "/persistent/group/gws/credentials.enc", + "encrypted_credentials_exists": false, + "keyring_backend": "keyring", + "plain_credentials": "/persistent/group/gws/credentials.json", + "plain_credentials_exists": false, + "storage": "none", + "token_cache_exists": false +} +``` + +認証は 2 段です。`gws auth setup` は **gcloud に依存する**ので、先に 3.1 を済ませてください。 + +``` +gws auth setup # Cloud プロジェクトと OAuth クライアントを設定する +gws auth login # OAuth2 で認証する +``` + +### 4.1 先に GCP プロジェクトを決める(詰まりやすい点) + +`gws auth setup` は **gcloud の設定に GCP プロジェクトが要ります**。 +`GOOGLE_CLOUD_PROJECT` 環境変数は見てくれません。 + +```console +$ gcloud config get project +(unset) +$ gws auth setup --dry-run +🏃 DRY RUN — no changes will be made + +Step 1/6: Checking for gcloud CLI... + ✓ gcloud CLI found +Step 2/6: Checking authentication... + ✓ Authenticated as takemi_ohama@kk-generation.com +{ + "error": { + "code": 400, + "message": "No GCP project configured. Use --project or run `gcloud config set project `", + "reason": "validationError" + } +} +error[validation]: No GCP project configured. Use --project or run `gcloud config set project ` +``` + +`--project` で明示するか、`gcloud config set project ` で設定してください。 +`--dry-run` を付けると変更を加えずに手前の段階まで確認できます。 + +使えるプロジェクトが分からないときは `gcloud projects list` を見ます。認証したアカウントに +プロジェクトが 1 つも無いと 0 件になり、そのアカウントでは `gws auth setup` を通せません。 + +```console +$ gcloud projects list --limit=15 +Listed 0 items. +``` + +### 4.2 OAuth クライアントを Console で作る + +`gws auth setup` は **OAuth クライアントを自動生成できません**。Step 5/5 で手動作成を求められます。 + +``` + ✓ Step 1/5: gcloud CLI — found + ✓ Step 2/5: Authentication — takemi_ohama@nyle.co.jp + ✓ Step 3/5: GCP project — nyle-carmo-analysis + ✓ Step 4/5: Workspace APIs — 0 enabled, 22 skipped + ▸ Step 5/5: OAuth credentials — Waiting for manual input... + + Manual OAuth client setup required. + + Step A — Consent screen (if not configured): + https://console.cloud.google.com/apis/credentials/consent?project=<プロジェクトID> + → User Type: External, then save through all screens. + + Step B — Create an OAuth client: + https://console.cloud.google.com/apis/credentials?project=<プロジェクトID> + → 'Create Credentials' → 'OAuth client ID' + → Application type: Desktop app + → Redirect URI: http://localhost (auto-negotiated; no manual entry needed) +``` + +Console で作った **クライアント ID** と **クライアント シークレット**を、続くプロンプトへ順に貼ります。 + +> **Warning:** クライアント シークレットを `$DEVBASE_ROOT/env` やプロジェクトの `env` に +> **書かないでください**。`env` は非機密用で `source` されるため、`KEY=値` の形でない行を +> 書くと `devbase` コマンド自体が壊れます(`command not found`)。gws が +> `$GOOGLE_WORKSPACE_CLI_CONFIG_DIR/client_secret.json` として保存するので、 +> どこかへ控える必要はありません。 + +成功すると `client_secret.json` がグループボリュームに置かれます。 + +```console +$ ls -l $GOOGLE_WORKSPACE_CLI_CONFIG_DIR +total 4 +-rw------- 1 ubuntu ubuntu 470 Aug 29 08:46 client_secret.json + +$ gws auth status +{ + "auth_method": "none", + "client_config": "/persistent/group/gws/client_secret.json", + "client_config_exists": true, + "config_client_id": "12826645....com", + "credential_source": "client_secret.json", + "enabled_api_count": 110, + ... + "project_id": "nyle-carmo-analysis", + "storage": "none" +} +``` + +### 4.3 ログイン + +`gws auth login` は gcloud と**流儀が違います**。認証コードを貼り戻すのではなく、 +**コンテナ内の `localhost:<ランダムポート>` でコールバックを待ち受けます**。 + +```console +$ docker exec -it <コンテナ名> gws auth login --readonly +Open this URL in your browser to authenticate: + + https://accounts.google.com/o/oauth2/auth?scope=...&redirect_uri=http://localhost:34437&response_type=code&client_id=...&prompt=select_account+consent +``` + +> **Note:** `docker exec -it <コンテナ> bash -lc 'gws auth login'` の形だと +> `Failed to read prompt input: stream did not contain valid UTF-8` で落ちることがあります。 +> `bash -lc` を挟まずに直接実行するか、`docker exec -it <コンテナ> bash` で入ってから +> 実行してください。 + +スコープは `--readonly`(読み取りのみ)/ `--full`(pubsub + cloud-platform を含む全部)/ +`--services drive,gmail,sheets` のように選べます。迷うなら `--readonly` が安全です。 + +#### ブラウザがホスト側にある場合(devbase では通常こちら) + +`redirect_uri` の `localhost` は**コンテナ内の localhost** です。ホストのブラウザから +`http://localhost:34437` を開いてもコンテナには届きません。次の手順で中継します。 + +1. 表示された URL をホストのブラウザで開き、認証を済ませる +2. ブラウザが `http://localhost:<ポート>/?code=...` へリダイレクトされ「接続できません」になる +3. **アドレスバーの URL 全体をコピーする** +4. 別のターミナルから、コンテナ内でその URL を叩いてコールバックを届ける + +```bash +docker exec <コンテナ名> curl -s "http://localhost:<ポート>/?code=...&scope=..." +``` + +ポート番号は実行のたびに変わるので、手順 1 で表示された `redirect_uri` の値を使ってください。 + +> **Note:** VS Code でコンテナにアタッチしている場合は、VS Code の自動ポート転送が効いて +> ホストのブラウザから直接届くことがあります。その場合は手順 3〜4 は不要です。 + +認証が通ると、コールバックを受けた側に許可されたスコープと `"status": "success"` が出ます。 + +```console +{ + "scopes": [ + "https://www.googleapis.com/auth/drive.readonly", + ... + "https://www.googleapis.com/auth/userinfo.profile" + ], + "status": "success" +} +``` + +`$GOOGLE_WORKSPACE_CLI_CONFIG_DIR` に `credentials.enc`(暗号化済みの認証情報)が作られます。 + +```console +$ ls -l $GOOGLE_WORKSPACE_CLI_CONFIG_DIR +total 12 +drwxr-xr-x 2 ubuntu ubuntu 4096 Aug 29 08:47 cache +-rw------- 1 ubuntu ubuntu 470 Aug 29 08:46 client_secret.json +-rw------- 1 ubuntu ubuntu 334 Aug 29 09:24 credentials.enc + +$ gws auth status | grep -E '"auth_method"|"storage"' + "auth_method": "oauth2", + "storage": "encrypted", +``` + +コンテナを作り直しても**再認証は要りません**。 + +```console +$ devbase project down && devbase project up +$ ls -l $GOOGLE_WORKSPACE_CLI_CONFIG_DIR +total 12 +drwxr-xr-x 2 ubuntu ubuntu 4096 Aug 29 08:47 cache +-rw------- 1 ubuntu ubuntu 470 Aug 29 08:46 client_secret.json +-rw------- 1 ubuntu ubuntu 334 Aug 29 09:24 credentials.enc + +$ gws auth status | grep -E '"auth_method"|encrypted_credentials_exists|"storage"' + "auth_method": "oauth2", + "encrypted_credentials_exists": true, + "storage": "encrypted", +``` + +> **Note:** `keyring_backend` は `keyring` のままで動きました。コンテナに OS キーリングが +> 無くても `credentials.enc` として暗号化保存されるため、 +> `GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file` を指定する必要はありませんでした。 + +#### 同意画面で止まる場合 + +`--readonly` でも `drive.readonly` / `gmail.readonly` は Google の**制限付きスコープ**です。 +OAuth 同意画面が「テスト中」でテストユーザーに自分が入っていない、あるいはアプリ情報が +未入力だと、同意フローが先へ進まないことがあります。Console の +「OAuth 同意画面」で公開ステータスとテストユーザーを確認してください。 + +スコープを絞れば制限付きスコープを避けられます。疎通確認だけなら次で十分です。 + +```bash +gws auth login --scopes openid,https://www.googleapis.com/auth/userinfo.email,https://www.googleapis.com/auth/userinfo.profile +``` + +> **Note:** 待ち受けプロセスを止めると、その回に発行された認証コードは使えなくなります +> (`redirect_uri` のポートが変わるため)。`gws auth login` をやり直したら、 +> **新しく表示された URL** から認証し直してください。 + +## 5. 認証モードの切り替え + +`GCP_AUTH_MODE` はプロジェクトの `env` かグローバル `env` に手書きします。 + +| 値 | 挙動 | +|---|---| +| `adc`(推奨) | 鍵を書かない。`GOOGLE_APPLICATION_CREDENTIALS` と `BIGQUERY_KEY_FILE` を**コンテナへ渡さない**。認証は `$CLOUDSDK_CONFIG/application_default_credentials.json` に委ねる | +| `key` | `GCP_CREDENTIALS_BASE64__` を復号して書き、上記 2 変数を渡す(従来どおり)| +| 未設定 | アクティブプロファイルの鍵の env があれば `key`、無ければ `adc` | + +**鍵が要るのはどういう場面か。** ユーザー認証では権限が足りない、あるいは人に紐づかない +実行主体が必要な場面です。たとえば本番データセットへの読み取りがサービスアカウントにしか +付与されていない場合や、コンテナ内から実行するバッチが特定の SA として動く必要がある場合です。 +それ以外の日常的な開発では ADC で足ります(Google もローカル開発には +`gcloud auth application-default login` を推奨しています)。 + +切り替えたら **`devbase up` が必要**です。コンテナへ渡す環境変数が変わるためで、 +コンテナ内で `export` しても `docker exec` の別シェルには反映されません。 + +```console +$ echo 'GCP_AUTH_MODE=adc' >> projects//env +$ devbase project up +``` + +`adc` に切り替わると 2 変数は**未設定**になります。 + +```console +$ echo ${GOOGLE_APPLICATION_CREDENTIALS-} + +$ echo ${BIGQUERY_KEY_FILE-} + +``` + +値だけ残して実体が無いと ADC はユーザー認証へフォールバックせず落ちるため、devbase は +「空にする」のではなく「渡さない」を選んでいます。 + +## 6. 確認コマンド + +### いま自分がどのグループにいるか + +ホスト側: + +```console +$ devbase status +... +[環境] + アカウントグループ kkg (devbase_home_kkg / env) +``` + +末尾は値が `env` 由来か、未設定によるフォールバック(`既定`)かを示します。 + +コンテナ内: + +```console +$ echo $DEVBASE_ACCOUNT_GROUP +kkg +$ echo $CLOUDSDK_CONFIG +/persistent/group/gcloud +$ readlink -f ~/.claude +/persistent/group/.claude +$ readlink -f ~/.claude/plugins +/persistent/ai/.claude/plugins +``` + +最後の 2 行が要点です。会話ログや認証はグループ側、プラグインなどの共通資産は共通側を指します。 + +コンテナの起動ログにも 1 行出ます。 + +```console +$ devbase project logs | grep "Account group" +Account group: kkg (gcloud account: takemi_ohama@kk-generation.com, CLOUDSDK_CONFIG: /persistent/group/gcloud) +``` + +### 認証の疎通 + +```bash +gcloud auth list # CLI 側の active account +gcloud config get account # 同上 (1 行) +gws auth status # gws の認証状態 +``` + +ライブラリ側(ADC)は `google.auth` で確認します。コンテナの `python3` には +`google` パッケージが入っていないため、gcloud 同梱のものを使います。 + +```console +$ PYTHONPATH=/opt/google-cloud-sdk/lib/third_party python3 -c \ + "import google.auth; c, p = google.auth.default(); print(p, type(c).__name__)" +nyle-carmo-analysis Credentials +``` + +## 7. トラブルシュート + +### `DefaultCredentialsError: Your default credentials were not found.` + +**まだ ADC の認証をしていない**状態です。3.2 の +`gcloud auth application-default login` を実行してください。これは `adc` モードで +未認証のときの**正常な状態**です。 + +### `DefaultCredentialsError: File /... was not found.` + +`GOOGLE_APPLICATION_CREDENTIALS` が**実体の無いパスを指しています**。ADC はこの場合 +ユーザー認証へフォールバックせず例外で落ちます。 + +devbase は `adc` モードでこの変数をコンテナへ渡さないので、通常は起きません。起きるとすれば +プロジェクトの `env`(機密ではない方)にこの変数が直接書かれている場合です。次で確認します。 + +```console +$ echo ${GOOGLE_APPLICATION_CREDENTIALS-} +``` + +`` でなければ `env` からその行を消して `devbase up` し直してください。 + +### `database is locked` + +gcloud は**並行実行を想定していません**(公式ドキュメント: "Parallel execution of multiple +gcloud CLI commands is not supported.")。`credentials.db` は SQLite なので、 +**同じアカウントグループの複数コンテナが同時に `gcloud` を叩く**と出ることがあります。 + +恒久対策は取っていません。少し待って**再実行**してください。これはグループボリュームを +同じグループの全コンテナで共有する設計に内在するもので、認証情報をどう置いても同じです。 + +### 意図しないアカウントで操作していた + +まず**どのグループにいるか**を確認します(6 章)。グループが正しいのにアカウントが違う場合は、 +そのグループに複数のアカウントで認証しています。 + +```console +$ gcloud auth list + Credentialed Accounts +ACTIVE ACCOUNT + someone@example.com +* takemi_ohama@kk-generation.com +``` + +切り替えは `gcloud config set account`、要らないものは `gcloud auth revoke` で消します。 + +```bash +gcloud config set account <正しいアカウント> +gcloud auth revoke <不要なアカウント> +``` + +グループ自体が間違っていた場合は、プロジェクトの `env` の `DEVBASE_ACCOUNT_GROUP` を直して +`devbase up` し直してください。**別グループの認証は互いに見えない**ので、正しいグループへ +移れば意図しないアカウントは選択肢にすら出てきません。 + +## 関連 + +- [コンテナ操作ガイド](container-operations.md) — ボリューム構造、AI 設定の永続化 +- [環境変数ガイド](environment-variables.md) — `DEVBASE_ACCOUNT_GROUP` / `GCP_AUTH_MODE` From 50eeb18ca5c20908c5eefe36ac9b87aad9f7c95a Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sat, 29 Aug 2026 18:32:25 +0900 Subject: [PATCH 2/7] =?UTF-8?q?docs(PLAN39):=20quota=20project=20=E3=81=AE?= =?UTF-8?q?=E6=9B=B8=E3=81=8D=E8=BE=BC=E3=81=BF=E6=9D=A1=E4=BB=B6=E3=82=92?= =?UTF-8?q?=E5=AE=9F=E8=A1=8C=E7=B5=90=E6=9E=9C=E3=81=AB=E5=90=88=E3=82=8F?= =?UTF-8?q?=E3=81=9B=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `gcloud auth application-default login` が quota project を必ず書くと 読める記述を、利用可能な project を解決できた場合に限る旨へ改める。 掲載している実行例は `Cannot find a quota project` となっており、 断定のままだと実出力と矛盾していた。 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01S5oA2PqY6UX2Ca3t78886t --- docs/user/google-auth.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/user/google-auth.md b/docs/user/google-auth.md index 304fcafd..caa135ba 100644 --- a/docs/user/google-auth.md +++ b/docs/user/google-auth.md @@ -188,7 +188,10 @@ nyle-carmo-analysis Credentials > **Note:** `gcloud auth login --update-adc` で 1 回に減らす案は**採りません**。 > `--update-adc` は quota project を ADC に書かないため(`add_quota_project=False` のまま > ADC を書き出す)、quota project を要する API で困ります。 -> `gcloud auth application-default login` は quota project も書き込みます。 +> `gcloud auth application-default login` は quota project の書き込みも試みますが、 +> 書かれるのは利用可能な project を解決できた場合に限られます。上の実行例のように +> `Cannot find a quota project` となったときは書かれないので、`set-quota-project` で +> 明示的に追加してください。 > **Note:** 鍵モード(`GCP_AUTH_MODE=key`)で実行すると、gcloud が > 「Credentials will still be generated to the default location / To use these credentials, From ead848b799f3117ec5163c48288503dcf263a3a4 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sat, 29 Aug 2026 18:33:18 +0900 Subject: [PATCH 3/7] =?UTF-8?q?docs(PLAN39):=20=E5=AE=9F=E6=A9=9F=E6=A4=9C?= =?UTF-8?q?=E8=A8=BC=E3=81=AE=E7=B5=90=E6=9E=9C=E3=82=92=E5=8F=97=E3=81=91?= =?UTF-8?q?=E5=85=A5=E3=82=8C=E6=9D=A1=E4=BB=B6=E3=81=B8=E5=8F=8D=E6=98=A0?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `devbase build --no-cache` 後の実機 (carmo-ai を default / kkg の 2 グループで起動) で AC1〜AC14 をすべて確認し、根拠つきの結果表を追加した。 検証中に見つかった 2 件は PLAN39 の退行ではないため別件として記録した。 - 旧世代スナップショットの incr-002 適用で GNU tar が rename に失敗する (現行 main と同じ旧コマンド形式で再現するため PLAN39 由来ではない) - $DEVBASE_ROOT/env が .gitignore の対象外で、機密を誤って書くと混入しうる Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01S5oA2PqY6UX2Ca3t78886t --- .../PLAN39_account-group-volume-separation.md | 68 +++++++++++++------ 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/issues/PLAN39_account-group-volume-separation.md b/issues/PLAN39_account-group-volume-separation.md index ba763636..cce7a66f 100644 --- a/issues/PLAN39_account-group-volume-separation.md +++ b/issues/PLAN39_account-group-volume-separation.md @@ -184,17 +184,17 @@ issue #116 が `standard` 相当の Phase 分割で書かれていても、判 ## 受け入れ条件 -- [ ] AC1: 同じグループのコンテナで `devbase down` → `devbase up` の後、`gcloud auth list` が +- [x] AC1: 同じグループのコンテナで `devbase down` → `devbase up` の後、`gcloud auth list` が **再認証なしで**同じ active account を返す。 -- [ ] AC2: `gws` がベースイメージに含まれ(`command -v gws` が通り)、同条件で認証済みコマンドが再認証なしで通る(`$GOOGLE_WORKSPACE_CLI_CONFIG_DIR` 配下の +- [x] AC2: `gws` がベースイメージに含まれ(`command -v gws` が通り)、同条件で認証済みコマンドが再認証なしで通る(`$GOOGLE_WORKSPACE_CLI_CONFIG_DIR` 配下の `credentials.enc` と `.encryption_key` が保たれる)。 -- [ ] AC3: 異なるグループのコンテナが互いの認証を参照しない。検証: `kkg` グループのコンテナで +- [x] AC3: 異なるグループのコンテナが互いの認証を参照しない。検証: `kkg` グループのコンテナで `gcloud auth list` / `claude mcp list` を実行し、`default` グループの認証が見えないこと。 -- [ ] AC4: 共通資産が重複しない。検証: 2 グループのコンテナで `readlink -f ~/.claude/plugins` が +- [x] AC4: 共通資産が重複しない。検証: 2 グループのコンテナで `readlink -f ~/.claude/plugins` が **同一の `/persistent/ai/.claude/plugins`** を指すこと。 -- [ ] AC5: `DEVBASE_ACCOUNT_GROUP` 未設定のプロジェクトが `default` にフォールバックし、 +- [x] AC5: `DEVBASE_ACCOUNT_GROUP` 未設定のプロジェクトが `default` にフォールバックし、 これまでどおり起動する。検証: 既存プロジェクトを `up` して entrypoint がエラーを出さないこと。 -- [ ] AC6: 入れ子パスの symlink が正しく張られる。検証: `~/.claude/CLAUDE.md` と +- [x] AC6: 入れ子パスの symlink が正しく張られる。検証: `~/.claude/CLAUDE.md` と `~/.claude/settings.json` が**壊れていない**(実体に到達できる)symlink であり、かつ **ファイル**であること。`~/.claude/.credentials.json` に書き込めること、 `~/.claude/history.jsonl` が**ディレクトリでない**こと(前提 5 の退行を防ぐ)。 @@ -205,21 +205,21 @@ issue #116 が `standard` 相当の Phase 分割で書かれていても、判 あわせて、Dockerfile が焼き込む `~/.claude/settings.json`(hooks 設定)は symlink 張り替えの `rm -rf` で消えるため、**張る前に共通側へ退避**する。退避しないと `/persistent/ai` に 空ファイルだけが残り、hooks が初回起動で失われる(既存 main からの挙動を修正)。 -- [ ] AC7: Docker のボリューム名にできないグループ名、予約語 `ubuntu`(`devbase_home_ubuntu` と衝突する)、 +- [x] AC7: Docker のボリューム名にできないグループ名、予約語 `ubuntu`(`devbase_home_ubuntu` と衝突する)、 および**数字のみの名前**(`devbase_home_` と衝突する。前提 6)を**起動前に拒否**し、 理由の分かるエラーを出す。 -- [ ] AC8: `default` グループでは、**現行 `/persistent/ai` に実体がある**分類 B のデータ +- [x] AC8: `default` グループでは、**現行 `/persistent/ai` に実体がある**分類 B のデータ (`.claude.json` / `.claude/.credentials.json` / 履歴 / `.gemini`)が**初回シードにより維持**され、 Claude Code の再ログインが発生しない。検証: 現行環境で `up` 後に `claude` が未ログイン状態にならないこと。 gcloud / gws は前提 1 のとおり現在どのボリュームにも無く**シード元が存在しない**ため、 `default` を含む**全グループで初回 1 回だけ `gcloud auth login` / `gws auth login` が必要**である。 これは AC8 の違反としない(AC1 / AC2 はその初回ログイン**以降**の維持を見る条件である)。 -- [ ] AC9: スナップショットが共通・グループ両方のボリュームを対象にし、復元できる。 -- [ ] AC10: `devbase status` に解決されたアカウントグループが表示される。 -- [ ] AC11: 鍵モードで起動したとき、従来どおりサービスアカウント鍵が使える。 +- [x] AC9: スナップショットが共通・グループ両方のボリュームを対象にし、復元できる。 +- [x] AC10: `devbase status` に解決されたアカウントグループが表示される。 +- [x] AC11: 鍵モードで起動したとき、従来どおりサービスアカウント鍵が使える。 検証: `GCP_CREDENTIALS_BASE64__` を設定して `devbase up` した直後に `$GOOGLE_APPLICATION_CREDENTIALS` のファイルが存在し中身が空でないこと(前提 14 の退行を防ぐ)。 -- [ ] AC12: **認証モードを任意に切り替えられる。** 検証: +- [x] AC12: **認証モードを任意に切り替えられる。** 検証: (1) `GCP_AUTH_MODE=adc` のプロジェクトで `up` し、コンテナ内で `GOOGLE_APPLICATION_CREDENTIALS` と `BIGQUERY_KEY_FILE` が**未設定**であること、 `gcloud auth application-default login` 済みのユーザー認証で `google.auth.default()` が通ること。 @@ -228,7 +228,7 @@ issue #116 が `standard` 相当の Phase 分割で書かれていても、判 (3) `key` → `adc` へ戻すと 2 変数が未設定に戻り、前提 10 の `DefaultCredentialsError` が 起きないこと。**この (3) が最も壊れやすい**(変数だけ残ると ADC がフォールバックせず落ちる)。 あわせて `tests/containers/` で `GCP_AUTH_MODE` × 鍵 env の有無の組み合わせを固定する。 -- [ ] AC13: サービスアカウント鍵が**永続化されない**。検証: 鍵モードで `up` したあと +- [x] AC13: サービスアカウント鍵が**永続化されない**。検証: 鍵モードで `up` したあと `devbase down` し、鍵の env を外して `up` し直すと `$DEFAULT_CREDS_PATH` にファイルが **存在しない**こと。グループボリューム (`/persistent/group`) 配下にも鍵が無いこと。 - [ ] AC14: **手順書だけを見て、第三者が新しいグループの Google 認証を完了できる。** @@ -237,6 +237,36 @@ issue #116 が `standard` 相当の Phase 分割で書かれていても、判 いずれもが通ること。詰まった箇所は手順書へ反映してから完了とする。 記載するコマンドと出力はすべて**実機で実行した結果を貼る**(想像で書かない)。 +## 実機検証の結果 + +2026-08-29、`devbase build --no-cache` でベースイメージを再ビルドしたうえで、 +`carmo-ai` プロジェクトを `default` / `kkg` の 2 グループで起動して確認した。 + +| AC | 結果 | 主な根拠 | +|---|---|---| +| AC1 | ✅ | `devbase down` → `up` の後も `gcloud auth list` が `takemi_ohama@kk-generation.com` を返し、`google.auth.default()` がユーザー認証 (`Credentials`) で通った | +| AC2 | ✅ | `gws 0.22.5` がイメージに同梱。`credentials.enc` (334B) が再作成後も残り `auth_method: oauth2` / `storage: encrypted` を維持 | +| AC3 | ✅ | `devbase_home_default/gcloud/legacy_credentials` = `takemi_ohama@nyle.co.jp`、`devbase_home_kkg/...` = `takemi_ohama@kk-generation.com`。`kkg` から `~/.claude/.credentials.json` は見えない | +| AC4 | ✅ | 両グループとも `readlink -f ~/.claude/plugins` = `/persistent/ai/.claude/plugins` | +| AC5 | ✅ | `DEVBASE_ACCOUNT_GROUP` 未設定の `carmo-ai` が `default` で起動 | +| AC6 | ✅ | `~/.claude/CLAUDE.md` / `settings.json` は壊れていない symlink かつファイル。`history.jsonl` はディレクトリでない | +| AC7 | ✅ | `ubuntu` / `1` / `bad name` の 3 種が `Deploy failed:` + 理由で exit=1。**稼働中コンテナは無傷** | +| AC8 | ✅ | シードで `.claude.json` の `oauthAccount` と MCP OAuth 6 件、`projects` 73 件 (1.2GB) を維持。再ログインなし | +| AC9 | ✅ | 2 ボリュームの作成・一覧・復元を使い捨てボリュームで往復確認。旧レイアウト (`volume: devbase_home_ubuntu`) の実在世代も復元できた | +| AC10 | ✅ | `devbase status` の `[環境]` に `default (devbase_home_default / 既定)` / `kkg (devbase_home_kkg / env)` を表示 | +| AC11 | ✅ | 鍵モードで `credentials.json` (2376B) が書かれ、`google.auth.default()` が SA で `nyle-carmo-analysis` を解決 | +| AC12 | ✅ | `adc` → `key` → `adc` を往復。戻り方向で 2 変数が未設定に戻り、`DefaultCredentialsError: Your default credentials were not found.`(= 未ログインの正常状態)になった | +| AC13 | ✅ | 鍵モードから `adc` へ戻すと `~/.config/gcloud/credentials.json` が消え、`/persistent` 配下に鍵は無い | +| AC14 | ✅ | 未認証の `kkg` グループを用意して `docs/user/google-auth.md` を通しで実行。詰まった 6 点を手順書へ反映した | + +### 検証中に見つかった別件(PLAN39 の退行ではない) + +- 旧世代スナップショット `20260823-114528` の `incr-002` 適用で GNU tar が + `Cannot rename ... Directory not empty` で失敗する。**現行 `main` と同じ旧コマンド形式で + 再現した**ため PLAN39 由来ではない(full + incr-001 までは正常に復元できる)。別 issue とする。 +- `$DEVBASE_ROOT/env` は `.gitignore` の対象外。機密を誤って書くと `git add -A` で混入する。 + また `source` されるため `KEY=値` の形でない行を書くと `devbase` コマンド自体が壊れる。別件とする。 + ## 代替案と採否 | 案 | 内容 | 採否 | 理由 | @@ -627,10 +657,10 @@ issue #116 は「Phase 1・2 を入れずに Phase 3 だけを適用すると問 ## 完了の定義 -- [ ] AC1〜AC14 を満たし、条件ごとに検証手段と結果が対応している -- [ ] `uv run pytest` が green -- [ ] 個別 PR がすべて `/ndf:cross-review` で APPROVE 収束済み -- [ ] `devbase build --no-cache` 後の実機で、`default` と非 `default` の 2 グループを起動して +- [x] AC1〜AC14 を満たし、条件ごとに検証手段と結果が対応している(「実機検証の結果」節) +- [x] `uv run pytest` が green(1629 passed) +- [x] 個別 PR がすべて `/ndf:cross-review` で APPROVE 収束済み(#123 / #124 / #125 / #126 / #127) +- [x] `devbase build --no-cache` 後の実機で、`default` と非 `default` の 2 グループを起動して AC1〜AC4 / AC8 / AC11〜AC13 を確認している -- [ ] `docs/` と `CHANGELOG.md` が新しいボリューム構造と `DEVBASE_ACCOUNT_GROUP` / `GCP_AUTH_MODE` を説明している -- [ ] `docs/user/google-auth.md` が実機で通した手順になっており、未認証のグループで通しの検証が済んでいる(AC14) +- [x] `docs/` と `CHANGELOG.md` が新しいボリューム構造と `DEVBASE_ACCOUNT_GROUP` / `GCP_AUTH_MODE` を説明している +- [x] `docs/user/google-auth.md` が実機で通した手順になっており、未認証のグループで通しの検証が済んでいる(AC14) From fa9f51de8c14c924c1e67ba9ca422e8698be9281 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sat, 29 Aug 2026 18:38:17 +0900 Subject: [PATCH 4/7] =?UTF-8?q?docs(PLAN39):=20gws=20--version=20=E3=81=AE?= =?UTF-8?q?=E5=AE=9F=E5=87=BA=E5=8A=9B=E3=82=92=202=20=E8=A1=8C=E3=81=A8?= =?UTF-8?q?=E3=82=82=E8=BC=89=E3=81=9B=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2 行目の "This is not an officially supported Google product." が抜けていた。 実出力をそのまま貼る方針に合わせる。 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01S5oA2PqY6UX2Ca3t78886t --- docs/user/google-auth.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/user/google-auth.md b/docs/user/google-auth.md index caa135ba..6d698878 100644 --- a/docs/user/google-auth.md +++ b/docs/user/google-auth.md @@ -250,6 +250,7 @@ $ command -v gws /usr/local/share/npm-global/bin/gws $ gws --version gws 0.22.5 +This is not an officially supported Google product. ``` 設定ディレクトリは `GOOGLE_WORKSPACE_CLI_CONFIG_DIR` でグループボリュームへ向いています。 From 62cf0d7891af8a17032bb7c6d5874b2f82f7ef61 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sat, 29 Aug 2026 18:40:22 +0900 Subject: [PATCH 5/7] =?UTF-8?q?docs(PLAN39):=20=E6=A4=9C=E8=A8=BC=E7=B5=90?= =?UTF-8?q?=E6=9E=9C=E3=81=AE=E7=9F=9B=E7=9B=BE=E3=81=A8=E6=89=8B=E9=A0=86?= =?UTF-8?q?=E6=9B=B8=E3=81=AE=E6=8A=9C=E3=81=91=E3=82=92=E7=9B=B4=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cross-review round 2 の指摘へ対応した。 - AC9 の根拠を「full + incr-001 まで」に限定し、incr-002 の失敗が PLAN39 前の main でも再現する別件であることを明示した - 完了の定義から本 PR #127 を切り出し、レビュー収束まで未チェックにした - コンテナ再作成の手順で devbase project login による入り直しを明示した (gcloud / gws の 2 箇所) - トラブルシュートに ADC の再認証を追記した。gcloud config set account と gcloud auth revoke は CLI 側しか変えないため、ライブラリ経由の呼び出しが 古いアカウントのまま残る Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01S5oA2PqY6UX2Ca3t78886t --- docs/user/google-auth.md | 26 ++++++++++++++++++- .../PLAN39_account-group-volume-separation.md | 6 +++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/docs/user/google-auth.md b/docs/user/google-auth.md index 6d698878..75ada994 100644 --- a/docs/user/google-auth.md +++ b/docs/user/google-auth.md @@ -204,8 +204,17 @@ nyle-carmo-analysis Credentials ここが PLAN39 で直した点です。`devbase down` はコンテナを削除しますが、認証情報は グループボリュームに残るので**再認証は要りません**。 +コンテナの作り直しは**ホスト側**で実行します。`up` で作られるのは新しいコンテナなので、 +続きを確認するには `devbase project login ` で**入り直してください**。 + ```console +# ホスト $ devbase project down && devbase project up +$ devbase project login +``` + +```console +# 作り直したコンテナの中 $ gcloud auth list Credentialed Accounts ACTIVE ACCOUNT @@ -434,10 +443,17 @@ $ gws auth status | grep -E '"auth_method"|"storage"' "storage": "encrypted", ``` -コンテナを作り直しても**再認証は要りません**。 +コンテナを作り直しても**再認証は要りません**。ここでも `down` / `up` はホスト側で実行し、 +`devbase project login ` で作り直したコンテナへ入り直してから確認します。 ```console +# ホスト $ devbase project down && devbase project up +$ devbase project login +``` + +```console +# 作り直したコンテナの中 $ ls -l $GOOGLE_WORKSPACE_CLI_CONFIG_DIR total 12 drwxr-xr-x 2 ubuntu ubuntu 4096 Aug 29 08:47 cache @@ -612,6 +628,14 @@ gcloud config set account <正しいアカウント> gcloud auth revoke <不要なアカウント> ``` +**この 2 つは gcloud CLI の認証情報しか変えません。** 3.2 のとおり ADC +(`$CLOUDSDK_CONFIG/application_default_credentials.json`)は別ファイルなので、 +`google.auth` や BigQuery クライアントなど**ライブラリ経由の呼び出しは古いアカウントのまま**です。 +`gcloud auth list` が正しく見えていても、ライブラリだけ別テナントで動き続けることがあります。 +ライブラリ側も直すには、正しいアカウントで 3.2 の +`gcloud auth application-default login` をやり直して ADC を上書きしてください。 +ADC がどのアカウントのものかは、このファイルの `account` フィールドに入っています。 + グループ自体が間違っていた場合は、プロジェクトの `env` の `DEVBASE_ACCOUNT_GROUP` を直して `devbase up` し直してください。**別グループの認証は互いに見えない**ので、正しいグループへ 移れば意図しないアカウントは選択肢にすら出てきません。 diff --git a/issues/PLAN39_account-group-volume-separation.md b/issues/PLAN39_account-group-volume-separation.md index cce7a66f..eeda59a0 100644 --- a/issues/PLAN39_account-group-volume-separation.md +++ b/issues/PLAN39_account-group-volume-separation.md @@ -252,7 +252,7 @@ issue #116 が `standard` 相当の Phase 分割で書かれていても、判 | AC6 | ✅ | `~/.claude/CLAUDE.md` / `settings.json` は壊れていない symlink かつファイル。`history.jsonl` はディレクトリでない | | AC7 | ✅ | `ubuntu` / `1` / `bad name` の 3 種が `Deploy failed:` + 理由で exit=1。**稼働中コンテナは無傷** | | AC8 | ✅ | シードで `.claude.json` の `oauthAccount` と MCP OAuth 6 件、`projects` 73 件 (1.2GB) を維持。再ログインなし | -| AC9 | ✅ | 2 ボリュームの作成・一覧・復元を使い捨てボリュームで往復確認。旧レイアウト (`volume: devbase_home_ubuntu`) の実在世代も復元できた | +| AC9 | ✅ | 2 ボリュームの作成・一覧・復元を使い捨てボリュームで往復確認。旧レイアウト (`volume: devbase_home_ubuntu`) の実在世代は **`full` + `incr-001` まで**復元できた(`incr-002` は後述の別件で失敗。PLAN39 前の `main` でも同じく失敗するため、この AC の判定からは外している) | | AC10 | ✅ | `devbase status` の `[環境]` に `default (devbase_home_default / 既定)` / `kkg (devbase_home_kkg / env)` を表示 | | AC11 | ✅ | 鍵モードで `credentials.json` (2376B) が書かれ、`google.auth.default()` が SA で `nyle-carmo-analysis` を解決 | | AC12 | ✅ | `adc` → `key` → `adc` を往復。戻り方向で 2 変数が未設定に戻り、`DefaultCredentialsError: Your default credentials were not found.`(= 未ログインの正常状態)になった | @@ -659,7 +659,9 @@ issue #116 は「Phase 1・2 を入れずに Phase 3 だけを適用すると問 - [x] AC1〜AC14 を満たし、条件ごとに検証手段と結果が対応している(「実機検証の結果」節) - [x] `uv run pytest` が green(1629 passed) -- [x] 個別 PR がすべて `/ndf:cross-review` で APPROVE 収束済み(#123 / #124 / #125 / #126 / #127) +- [x] 個別 PR がすべて `/ndf:cross-review` で APPROVE 収束済み(#123 / #124 / #125 / #126) +- [ ] 本 PR #127(手順書と検証結果)が `/ndf:cross-review` で APPROVE 収束する。 + レビュー中は未チェックのままにし、収束を確認してからチェックする - [x] `devbase build --no-cache` 後の実機で、`default` と非 `default` の 2 グループを起動して AC1〜AC4 / AC8 / AC11〜AC13 を確認している - [x] `docs/` と `CHANGELOG.md` が新しいボリューム構造と `DEVBASE_ACCOUNT_GROUP` / `GCP_AUTH_MODE` を説明している From 773eaeb7174493a7f5e72e8b96672dcddd3cdf72 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sat, 29 Aug 2026 18:44:45 +0900 Subject: [PATCH 6/7] =?UTF-8?q?docs(PLAN39):=20=E3=82=B3=E3=83=B3=E3=83=86?= =?UTF-8?q?=E3=83=8A=E3=81=B8=E5=85=A5=E3=82=8A=E7=9B=B4=E3=81=99=E3=82=B3?= =?UTF-8?q?=E3=83=9E=E3=83=B3=E3=83=89=E3=82=92=20devbase=20login=20?= =?UTF-8?q?=20=E3=81=AB=E7=9B=B4=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `devbase project login` の positional はコンテナ index であり、プロジェクト名を 渡すと解決に失敗する (cli.py の _add_login_subparser / bin/devbase:357-364)。 名前で解決してくれるのはトップレベルシノニムの `devbase login ` なので 5 箇所を差し替えた。 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01S5oA2PqY6UX2Ca3t78886t --- docs/user/google-auth.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/user/google-auth.md b/docs/user/google-auth.md index 75ada994..f998ad78 100644 --- a/docs/user/google-auth.md +++ b/docs/user/google-auth.md @@ -53,7 +53,7 @@ GCP_AUTH_MODE=adc # サービスアカウント鍵を使わない場合 ```bash devbase project up -devbase project login +devbase login ``` グループ名には次の 3 つが使えません。`devbase up` の前にエラーになります。 @@ -205,12 +205,12 @@ nyle-carmo-analysis Credentials グループボリュームに残るので**再認証は要りません**。 コンテナの作り直しは**ホスト側**で実行します。`up` で作られるのは新しいコンテナなので、 -続きを確認するには `devbase project login ` で**入り直してください**。 +続きを確認するには `devbase login ` で**入り直してください**。 ```console # ホスト $ devbase project down && devbase project up -$ devbase project login +$ devbase login ``` ```console @@ -444,12 +444,12 @@ $ gws auth status | grep -E '"auth_method"|"storage"' ``` コンテナを作り直しても**再認証は要りません**。ここでも `down` / `up` はホスト側で実行し、 -`devbase project login ` で作り直したコンテナへ入り直してから確認します。 +`devbase login ` で作り直したコンテナへ入り直してから確認します。 ```console # ホスト $ devbase project down && devbase project up -$ devbase project login +$ devbase login ``` ```console From a16fbc62f9adb28c2f85ee7be5738b65d8d1149b Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sat, 29 Aug 2026 18:51:05 +0900 Subject: [PATCH 7/7] =?UTF-8?q?docs(PLAN39):=20ADC=20=E3=81=AE=E5=88=A4?= =?UTF-8?q?=E5=88=A5=E6=96=B9=E6=B3=95=E3=83=BBgws=20login=20=E3=81=AE?= =?UTF-8?q?=E5=AE=9F=E8=A1=8C=E5=A0=B4=E6=89=80=E3=83=BBkey=20=E3=81=AE=20?= =?UTF-8?q?adc=20=E3=83=95=E3=82=A9=E3=83=BC=E3=83=AB=E3=83=90=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=82=92=E7=9B=B4=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ADC の確認で `type(c).__name__` は user / service account を区別できない (両方 `Credentials`)。決め手は `__module__` である旨と実出力を追記 - 4.3 の `docker exec ... gws auth login` がホスト側であることを明示 - `GCP_AUTH_MODE=key` は鍵の env が無いと警告して adc へ倒れることを表と Warning に明記 (lib/devbase/env/gcp_auth.py の resolve_auth_mode) Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01S5oA2PqY6UX2Ca3t78886t --- docs/user/google-auth.md | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/docs/user/google-auth.md b/docs/user/google-auth.md index f998ad78..0fe9aa57 100644 --- a/docs/user/google-auth.md +++ b/docs/user/google-auth.md @@ -168,7 +168,22 @@ $ PYTHONPATH=/opt/google-cloud-sdk/lib/third_party python3 -c \ nyle-carmo-analysis Credentials ``` -`Credentials`(= ユーザー認証)であって `ServiceAccountCredentials` ではないことを確認してください。 +> **Note:** ここに出る `Credentials` は**クラスの短い名前**で、それだけではユーザー認証と +> サービスアカウントを区別できません。サービスアカウント側の +> `google.oauth2.service_account.Credentials` も短い名前は同じ `Credentials` です。 +> +> ```console +> $ PYTHONPATH=/opt/google-cloud-sdk/lib/third_party python3 -c \ +> "import google.oauth2.credentials as u, google.oauth2.service_account as s; print(u.Credentials.__name__, s.Credentials.__name__)" +> Credentials Credentials +> $ PYTHONPATH=/opt/google-cloud-sdk/lib/third_party python3 -c \ +> "import google.oauth2.credentials as u, google.oauth2.service_account as s; print(u.Credentials.__module__, s.Credentials.__module__)" +> google.oauth2.credentials google.oauth2.service_account +> ``` +> +> 見分けるには `type(c).__name__` ではなく **`type(c).__module__`** を出してください。 +> ユーザー認証なら `google.oauth2.credentials`、サービスアカウントなら +> `google.oauth2.service_account` になります。 > **Note:** 末尾の警告のとおり、この時点では **quota project が ADC に書かれていません**。 > quota project を要する API(`quota exceeded` / `API not enabled` が出るもの)を使うなら @@ -382,7 +397,13 @@ $ gws auth status `gws auth login` は gcloud と**流儀が違います**。認証コードを貼り戻すのではなく、 **コンテナ内の `localhost:<ランダムポート>` でコールバックを待ち受けます**。 +ここまでの 4.1 / 4.2 はコンテナの中で実行していますが、**次の `docker exec` はホスト側**の +コマンドです。コンテナから一度抜けるか、別のホストのターミナルを開いてください +(コンテナの中に居るまま実行したいときは、`docker exec -it <コンテナ名>` を外して +`gws auth login --readonly` だけを実行します)。 + ```console +# ホスト $ docker exec -it <コンテナ名> gws auth login --readonly Open this URL in your browser to authenticate: @@ -494,9 +515,22 @@ gws auth login --scopes openid,https://www.googleapis.com/auth/userinfo.email,ht | 値 | 挙動 | |---|---| | `adc`(推奨) | 鍵を書かない。`GOOGLE_APPLICATION_CREDENTIALS` と `BIGQUERY_KEY_FILE` を**コンテナへ渡さない**。認証は `$CLOUDSDK_CONFIG/application_default_credentials.json` に委ねる | -| `key` | `GCP_CREDENTIALS_BASE64__` を復号して書き、上記 2 変数を渡す(従来どおり)| +| `key` | アクティブプロファイルの `GCP_CREDENTIALS_BASE64__`(または旧来の `GOOGLE_APPLICATION_CREDENTIALS_BASE64`)を復号して書き、上記 2 変数を渡す(従来どおり)。**その鍵の env が無いときは警告して `adc` へ倒れる**(下記)| | 未設定 | アクティブプロファイルの鍵の env があれば `key`、無ければ `adc` | +> **Warning:** `key` と書いても、**アクティブプロファイルの鍵が env に無ければ `adc` として +> 構成されます**。サービスアカウントとして動かすつもりが、実際には永続化されたユーザー ADC で +> 動いてしまうことがあるので注意してください。倒れたときはホスト側に次の警告が出ます。 +> +> ``` +> GCP_AUTH_MODE=key ですが GCP_CREDENTIALS_BASE64__ が env にありません。adc として構成します +> ``` +> +> 実体の無いパスを指す `GOOGLE_APPLICATION_CREDENTIALS` / `BIGQUERY_KEY_FILE` をコンテナへ +> 渡して `DefaultCredentialsError` にするより安全なため、意図的にこうしています +> (`lib/devbase/env/gcp_auth.py` の `resolve_auth_mode`)。`key` で動かしたいなら、 +> アクティブプロファイル用の `GCP_CREDENTIALS_BASE64__` を先に設定してください。 + **鍵が要るのはどういう場面か。** ユーザー認証では権限が足りない、あるいは人に紐づかない 実行主体が必要な場面です。たとえば本番データセットへの読み取りがサービスアカウントにしか 付与されていない場合や、コンテナ内から実行するバッチが特定の SA として動く必要がある場合です。