From c73d7ce0520cc9e22250c5dbad5e77aeae40c96f Mon Sep 17 00:00:00 2001 From: boxp Date: Fri, 20 Feb 2026 12:29:16 +0000 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20Google=E9=80=A3=E6=90=BA=20Phase=20?= =?UTF-8?q?1=20-=20=E8=AA=8D=E8=A8=BC=E3=83=BBSecret=E5=9F=BA=E7=9B=A4?= =?UTF-8?q?=E6=95=B4=E5=82=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- argoproj/openclaw/deployment-openclaw.yaml | 67 +++++ argoproj/openclaw/external-secret-google.yaml | 39 +++ argoproj/openclaw/kustomization.yaml | 1 + docs/google-credentials-ops.md | 244 ++++++++++++++++++ .../plan.md | 118 +++++++++ 5 files changed, 469 insertions(+) create mode 100644 argoproj/openclaw/external-secret-google.yaml create mode 100644 docs/google-credentials-ops.md create mode 100644 docs/project_docs/T-20260220-020-google-auth-secret-phase1/plan.md diff --git a/argoproj/openclaw/deployment-openclaw.yaml b/argoproj/openclaw/deployment-openclaw.yaml index ae6e2059d..908f56e62 100644 --- a/argoproj/openclaw/deployment-openclaw.yaml +++ b/argoproj/openclaw/deployment-openclaw.yaml @@ -55,6 +55,48 @@ spec: - name: data mountPath: /home/node/.codex subPath: codex + # Prepare Google OAuth token file for babashka scripts. + # Secret volumes are read-only, but bb scripts need to write back + # refreshed access_tokens. emptyDir + initContainer pattern solves this. + # fsGroup=1000 ensures the emptyDir is group-owned by 1000. + - name: init-google-oauth + image: ghcr.io/boxp/arch/openclaw:202602170222 + command: ["sh", "-c"] + args: + - | + cat > /home/node/.google/oauth.json < Credentials > OAuth 2.0 Client IDs > Edit + +# 2. AWS SSM Parameter Store を更新 +aws ssm put-parameter \ + --name "/lolice/openclaw/GOOGLE_OAUTH_CLIENT_SECRET" \ + --value "" \ + --type SecureString \ + --overwrite \ + --region ap-northeast-1 + +# 3. ExternalSecret が同期するのを待つ (最大1時間) +# または強制同期: +kubectl annotate externalsecret google-oauth-es \ + -n openclaw \ + force-sync=$(date +%s) --overwrite + +# 4. Pod を再起動して新しい Secret を読み込み +kubectl rollout restart deployment/openclaw -n openclaw + +# 5. 動作確認 +kubectl logs -n openclaw deployment/openclaw -c openclaw | tail -20 +``` + +### 2.2 OAuth Refresh Token ローテーション + +**トリガー**: トークン失効、Google側の強制ローテーション、セキュリティインシデント + +```bash +# 1. ローカル環境で OAuth 再認証 (ブラウザ必要) +# bb スクリプトの認証フロー (Phase 2 で実装) を実行 +# → 新しい refresh_token を取得 + +# 2. AWS SSM Parameter Store を更新 +aws ssm put-parameter \ + --name "/lolice/openclaw/GOOGLE_OAUTH_REFRESH_TOKEN" \ + --value "" \ + --type SecureString \ + --overwrite \ + --region ap-northeast-1 + +# 3. ExternalSecret 強制同期 +kubectl annotate externalsecret google-oauth-es \ + -n openclaw \ + force-sync=$(date +%s) --overwrite + +# 4. Pod 再起動 +kubectl rollout restart deployment/openclaw -n openclaw +``` + +### 2.3 Google Maps API Key ローテーション + +**トリガー**: 定期ローテーション (推奨: 年1回) またはセキュリティインシデント + +```bash +# 1. Google Cloud Console で新しい API Key を発行 +# API & Services > Credentials > Create API Key +# 制限設定: +# - Application restrictions: IP addresses (サーバーIP) +# - API restrictions: Geocoding API, Directions API のみ + +# 2. AWS SSM Parameter Store を更新 +aws ssm put-parameter \ + --name "/lolice/openclaw/GOOGLE_MAPS_API_KEY" \ + --value "" \ + --type SecureString \ + --overwrite \ + --region ap-northeast-1 + +# 3. ExternalSecret 強制同期 → Pod 再起動 +kubectl annotate externalsecret google-oauth-es \ + -n openclaw \ + force-sync=$(date +%s) --overwrite +kubectl rollout restart deployment/openclaw -n openclaw + +# 4. 旧 API Key を Google Cloud Console で無効化 +``` + +## 3. 失効・緊急停止手順 + +### 3.1 緊急停止 (全 Google API アクセスを即座に遮断) + +**所要目標時間**: 15分以内 + +```bash +# 方法 A: Pod 停止 (最速) +kubectl scale deployment/openclaw -n openclaw --replicas=0 + +# 方法 B: Google Cloud Console でクレデンシャル無効化 +# OAuth: API & Services > Credentials > OAuth Client > Delete/Disable +# API Key: API & Services > Credentials > API Key > Restrict/Delete + +# 方法 C: SSM パラメータを無効値に上書き +aws ssm put-parameter \ + --name "/lolice/openclaw/GOOGLE_OAUTH_REFRESH_TOKEN" \ + --value "REVOKED" \ + --type SecureString \ + --overwrite \ + --region ap-northeast-1 + +aws ssm put-parameter \ + --name "/lolice/openclaw/GOOGLE_MAPS_API_KEY" \ + --value "REVOKED" \ + --type SecureString \ + --overwrite \ + --region ap-northeast-1 + +# 強制同期 → Pod 再起動 +kubectl annotate externalsecret google-oauth-es \ + -n openclaw \ + force-sync=$(date +%s) --overwrite +kubectl rollout restart deployment/openclaw -n openclaw +``` + +### 3.2 OAuth トークン失効 + +```bash +# Google Cloud Console から失効 +# Security > Third-party apps with account access > Remove access + +# または Google OAuth revoke endpoint +curl -d "token=" \ + https://oauth2.googleapis.com/revoke +``` + +### 3.3 API Key 漏えい対応 + +| ステップ | 操作 | 担当 | +|----------|------|------| +| 1 | Google Cloud Console で該当 API Key を即座に無効化 | 運用者 | +| 2 | 新しい API Key を発行 (IP制限・API制限付き) | 運用者 | +| 3 | SSM パラメータ更新 → ExternalSecret 同期 → Pod 再起動 | 運用者 | +| 4 | Cloud Console の使用状況レポートで不正利用の有無を確認 | 運用者 | +| 5 | インシデントレポート作成 | 運用者 | + +### 3.4 インシデント時ローテーション SLA + +| イベント | 目標時間 | 検知方法 | +|----------|----------|----------| +| API Key 漏えい | 15分以内に無効化 | GitHub Secret Scanning / Grafana OnCall | +| OAuth トークン漏えい | 15分以内に無効化 | Cloud Console 監査ログ / Grafana OnCall | +| Client Secret 漏えい | 30分以内に再生成 | GitHub Secret Scanning / 手動報告 | + +## 4. 監査ログ方針 + +### 4.1 参照イベント + +| イベント | ログソース | 保持期間 | +|----------|-----------|----------| +| SSM Parameter Store GetParameter | AWS CloudTrail | 90日 (デフォルト) | +| ExternalSecret 同期 | External Secrets Operator Pod ログ | Pod ライフタイム | +| Pod 起動時の Secret マウント | kubelet ログ | ノードログ保持期間 | +| bb スクリプトの API 呼び出し | OpenClaw Pod ログ (stdout) | Pod ライフタイム | + +### 4.2 更新イベント + +| イベント | ログソース | アラート | +|----------|-----------|---------| +| SSM パラメータ更新 (PutParameter) | AWS CloudTrail | CloudWatch Alarm → Grafana OnCall | +| ExternalSecret 同期失敗 | External Secrets Operator メトリクス | Prometheus Alert → Grafana OnCall | +| OAuth token リフレッシュ失敗 (HTTP 400/401) | OpenClaw Pod ログ | ログベースアラート (Phase 4) | +| API Key 使用量急増 | Google Cloud Console | Budget Alert | + +### 4.3 CloudTrail フィルタ例 + +```json +{ + "eventSource": "ssm.amazonaws.com", + "eventName": ["PutParameter", "GetParameter", "DeleteParameter"], + "requestParameters": { + "name": "/lolice/openclaw/GOOGLE_*" + } +} +``` + +### 4.4 ExternalSecret 同期監視 + +```promql +# ExternalSecret 同期ステータス監視 (Prometheus) +externalsecret_status_condition{ + name="google-oauth-es", + namespace="openclaw", + condition="Ready" +} == 0 +``` + +## 5. 前提条件チェックリスト + +Phase 1 (本PRでの実装) 完了後、実際にクレデンシャルを投入する前に確認すべき事項: + +- [ ] Google Cloud Project 作成済み +- [ ] OAuth 同意画面設定済み (テストモード) +- [ ] Calendar API 有効化済み +- [ ] Geocoding API / Directions API 有効化済み +- [ ] OAuth Client ID / Secret 発行済み +- [ ] Maps API Key 発行済み (IP制限付き) +- [ ] ローカルで OAuth 認証完了、refresh_token 取得済み +- [ ] SSM に実際のクレデンシャル値を投入済み +- [ ] ExternalSecret の同期を確認済み +- [ ] Pod 再起動後、oauth.json が正しく生成されることを確認済み diff --git a/docs/project_docs/T-20260220-020-google-auth-secret-phase1/plan.md b/docs/project_docs/T-20260220-020-google-auth-secret-phase1/plan.md new file mode 100644 index 000000000..e3a046e2c --- /dev/null +++ b/docs/project_docs/T-20260220-020-google-auth-secret-phase1/plan.md @@ -0,0 +1,118 @@ +# T-20260220-020: Google連携 Phase 1 — 認証・Secret基盤整備 + +## 概要 + +Google Calendar API / Google Maps API をOpenClawから利用するために必要な +OAuth2 / API Key の保管・注入・更新/失効手順を整備する。 + +**前提計画書**: `arch` リポジトリ `docs/project_docs/openclaw/T-20260220-005-google-calendar-maps-plan.md` + +## スコープ + +| # | 項目 | 対象リポジトリ | +|---|------|---------------| +| 1 | SSM Parameter Store にクレデンシャルを定義 | arch | +| 2 | ExternalSecret で K8s Secret へ同期 | lolice | +| 3 | initContainer + emptyDir による OAuth token ファイル注入 (runAsUser=1000) | lolice | +| 4 | env var による API key / client credentials 注入 | lolice | +| 5 | ローテーション・失効・緊急停止手順のドキュメント化 | lolice | +| 6 | 監査ログ方針の策定 | lolice | + +## 非スコープ + +- babashka CLI本体実装 (Phase 2) +- OpenClawスキル接続 (Phase 3) +- Google Cloud Project 作成・OAuth同意画面設定 (運用者手動作業) + +## アーキテクチャ + +``` +┌─────────────────┐ Terraform ┌──────────────────────┐ +│ Google Cloud │ │ AWS SSM │ +│ Console │ │ Parameter Store │ +│ (手動設定) │ │ /lolice/openclaw/* │ +└────────┬────────┘ └──────────┬───────────┘ + │ │ + │ Client ID / Secret / Refresh Token │ ExternalSecret (1h refresh) + │ Maps API Key │ + └────────► SSM に手動投入 ──────────────►│ + ▼ + ┌────────────────────┐ + │ K8s Secret │ + │ google-oauth- │ + │ credentials │ + └────────┬───────────┘ + │ + ┌─────────────────────┴─────────────────────┐ + │ │ + ┌─────────▼────────┐ ┌──────────▼──────────┐ + │ initContainer │ │ openclaw container │ + │ init-google-oauth │ │ env vars: │ + │ → oauth.json │ │ GOOGLE_OAUTH_CLIENT_*│ + │ (emptyDir) │ │ GOOGLE_MAPS_API_KEY │ + └─────────┬────────┘ │ GOOGLE_OAUTH_TOKEN_ │ + │ │ PATH │ + ▼ └──────────────────────┘ + ┌──────────────────┐ + │ emptyDir │ + │ /home/node/ │ + │ .google/ │ + │ oauth.json │ + │ (0600, 1000:1000) │ + └──────────────────┘ +``` + +## SSM パラメータ一覧 + +| パラメータパス | 用途 | 初期値 | +|---------------|------|--------| +| `/lolice/openclaw/GOOGLE_OAUTH_CLIENT_ID` | Calendar API OAuth2 Client ID | dummy (手動更新) | +| `/lolice/openclaw/GOOGLE_OAUTH_CLIENT_SECRET` | Calendar API OAuth2 Client Secret | dummy (手動更新) | +| `/lolice/openclaw/GOOGLE_OAUTH_REFRESH_TOKEN` | Calendar API OAuth2 Refresh Token | dummy (手動更新) | +| `/lolice/openclaw/GOOGLE_MAPS_API_KEY` | Maps Geocoding/Directions API Key | dummy (手動更新) | + +## ExternalSecret 設計 + +| リソース名 | 生成Secret名 | 用途 | +|-----------|-------------|------| +| `google-oauth-es` | `google-oauth-credentials` | Google OAuth / Maps credentials | + +既存の `openclaw-credentials` とは別Secret。理由: +- セキュリティ分離: Google認証情報は独立した責務 +- 独立ローテーション: Google credentials のみ更新可能 +- 監査明確化: Secret参照ログで用途が明確 + +## runAsUser=1000 token ファイル権限設計 + +| 項目 | 設計 | +|------|------| +| ファイルパス | `/home/node/.google/oauth.json` | +| ボリューム種別 | `emptyDir` (sizeLimit: 1Mi) | +| ファイルパーミッション | `0600` | +| オーナー | `1000:1000` (fsGroup=1000 + runAsUser=1000) | +| 書き込み | bb スクリプトが access_token リフレッシュ時に書き戻し | +| Pod再起動時 | emptyDir 消失 → initContainer が Secret から再生成 | + +## 環境変数注入 + +| 変数名 | ソース | 注入先コンテナ | +|--------|--------|---------------| +| `GOOGLE_OAUTH_CLIENT_ID` | google-oauth-credentials | openclaw | +| `GOOGLE_OAUTH_CLIENT_SECRET` | google-oauth-credentials | openclaw | +| `GOOGLE_MAPS_API_KEY` | google-oauth-credentials | openclaw | +| `GOOGLE_OAUTH_TOKEN_PATH` | 固定値 `/home/node/.google/oauth.json` | openclaw | + +## リスク + +| リスク | 影響度 | 緩和策 | +|--------|--------|--------| +| SSM dummy値のまま運用開始 | 低 | bb スクリプト (Phase 2) が HTTP 401 で明示的にエラー | +| emptyDir の Pod 再起動時消失 | 低 | initContainer が毎回 Secret から再生成 | +| refresh_token の Google 側ローテーション | 中 | ローテーション手順を docs に明記、モニタリングで検知 | + +## 次アクション + +1. Google Cloud Console で OAuth 同意画面設定・クレデンシャル作成 (運用者) +2. AWS SSM Parameter Store に実際の値を手動投入 (運用者) +3. Phase 2: babashka スクリプト実装 +4. Phase 3: OpenClaw スキル接続 From 716254f6400f23a1a12c18e883f6615d1c0e9a02 Mon Sep 17 00:00:00 2001 From: boxp Date: Tue, 3 Mar 2026 01:56:36 +0900 Subject: [PATCH 2/2] fix(ci): allow DERP path in tailscale reachability ping --- .github/workflows/argocd-diff.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/argocd-diff.yaml b/.github/workflows/argocd-diff.yaml index 4fcb22898..064f47535 100644 --- a/.github/workflows/argocd-diff.yaml +++ b/.github/workflows/argocd-diff.yaml @@ -76,10 +76,10 @@ jobs: echo "::endgroup::" echo "::group::tailscale ping lolice-argocd" - if tailscale ping -c 3 --timeout 10s lolice-argocd 2>&1; then - echo "::notice::tailscale ping succeeded" + if tailscale ping -c 3 --timeout 10s --until-direct=false lolice-argocd 2>&1; then + echo "::notice::tailscale ping succeeded (DERP or direct)" else - echo "::warning::tailscale ping to lolice-argocd failed — peer exists but is unreachable via WireGuard tunnel" + echo "::warning::tailscale ping to lolice-argocd failed — no reachable path (direct/DERP)" exit 1 fi echo "::endgroup::"