ブログ一覧に戻る
セキュリティDevOpsコスト最適化

1Password から macOS Keychain + GCP Secret Manager へ — 追加コスト 0 の secret 管理

はじめに

新しい開発環境を組むときに必ず議論になるのが「secret はどう保管するか」です。

候補としてよく挙がるのが:

  • 1Password Teams / Bitwarden / Doppler などの 専用 secret 管理サービス
  • .env ファイルをチームで配り合う(git にはコミットしない運用)
  • HashiCorp Vault などの エンタープライズ向け OSS

niyase でも当初 1Password Teams を契約する想定で進めていましたが、見積もりを取ると $8/user/月 という固定費が見えてきました。10人で年間 $960、エンジニア組織が大きくなれば比例して増えていきます。私たちはフル GCP + 全員 Mac という前提があったので、「これ、追加コスト 0 でできないか?」と考え直し、結果的に macOS Keychain + GCP Secret Manager だけで全部まかなえることが分かりました。本記事はその構成と移行プロセスのメモです。

整理: secret には 3 つの種類がある

「secret 管理」と一括りに語られがちですが、niyase ではユースケースを以下の 3 つに分けています。混ぜると整理がつかなくなります。

種別アクセス主体共有範囲
個人 tokenCloudflare API Token, 個人 GitHub PAT1人の開発者本人自分の Mac のみ
team 共有 dev secretResend DEV API key, Stripe test key, Sentry DSNチーム全員 (またはサブセット)DEV / STG 環境を触る全員
production runtime secretDB パスワード, HMAC 鍵, 本番 Stripe Live keyサービス(人間ではなくサービスアカウント)Cloud Run / Cloud Function のランタイム

各々ベストな保管場所が違うので、すべてを 1 つのツールにまとめて保管すると「人間がアクセスすべきでないものまで人間がアクセス可能な場所に置いてしまう」「自動化で取り出すには UI 経由が必要で手間がかかる」の両方を抱えがちです。

採用したアーキテクチャ

各種別に応じて以下の住み分けにしました。

┌────────────────────────────────────────────────────────────────┐
│ 個人 token (Cloudflare API Token, 個人 GitHub PAT 等)          │
│ → macOS Keychain (security コマンド)                            │
│ → ~/.zshrc の zsh function 経由で env に export                │
└────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────┐
│ team 共有 dev secret (Resend test key, Stripe test key 等)     │
│ → GCP Secret Manager (your-dev-project)                          │
│ → roles/secretmanager.secretAccessor を持つ開発者が gcloud で取得 │
│ → ~/.zshrc の zsh function 経由で env に export                │
└────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────┐
│ production runtime secret (DB pass, HMAC key, 本番 API key)    │
│ → GCP Secret Manager (your-prod-project)                        │
│ → Cloud Run の --update-secrets で env としてマウント           │
│ → 人間は基本アクセスしない (audit 時のみ)                       │
└────────────────────────────────────────────────────────────────┘

パターン 1: 個人 token を macOS Keychain で

例として、Cloudflare の DNS 操作用 API Token (CF_API_TOKEN) を扱うケース。

保存 (一度だけ)

security add-generic-password -a "$USER" -s "niyase-cf-api-token" -w
# プロンプトに token をペーストして Enter

security は macOS 標準コマンドで、Keychain Access.app の CLI 版です。-a は account、-s は service name(取り出すときのキー)、-w で「password を対話入力させる(コマンド履歴に残さない)」。

取り出し用 zsh function

~/.zshrc に追記:

cf-auth() {
  export CF_API_TOKEN=$(security find-generic-password -s "niyase-cf-api-token" -w 2>/dev/null)
  if [[ -z "$CF_API_TOKEN" ]]; then
    echo "ERROR: niyase-cf-api-token が Keychain にありません" >&2
    return 1
  fi
  echo "CF_API_TOKEN exported (length: ${#CF_API_TOKEN})"
}

使い方

cf-auth                                                  # ロックされてれば Touch ID
./infrastructure/bootstrap/setup-cloudflare-dns.sh dev   # スクリプトが env を読む

この方式の良さ

  • 追加ツール 0: macOS 標準
  • 暗号化保存: Mac のログインパスワードで保護、Touch ID も併用可
  • 常駐しない: ターミナルを閉じれば env も消える。env でひと目に全 token が並ぶ事故も起きない
  • .zshrc に直書きしない: pair プロや画面共有、git アクシデントで漏れにくい
  • rotation: Cloudflare で token を roll → security add-generic-password を上書き実行するだけ

注意点

  • Mac 1 台限定: 別の Mac には iCloud Keychain でしか同期できない。team 全員に配るような共有 secret はこれでは無理 (次のパターンへ)
  • シェルが zsh 前提: bash の人は別途設定が要る

パターン 2: team 共有 dev secret を GCP Secret Manager で

「DEV 環境で Resend test API key を使いたい」「Stripe test secret key を本番開発者に配りたい」みたいなケース。

保存 (admin が一度だけ)

echo -n "re_test_xxxxxxxxxxxxx" | gcloud secrets versions add niyase-resend-api-key \
  --data-file=- --project=your-dev-project

存在しなければ gcloud secrets create niyase-resend-api-key を先に。

開発者の取り出し関数

~/.zshrc に共有関数を追加:

niyase-dev-auth() {
  local PROJECT=your-dev-project
  export RESEND_API_KEY=$(gcloud secrets versions access latest \
    --secret=niyase-resend-api-key --project=$PROJECT)
  export STRIPE_SECRET_KEY=$(gcloud secrets versions access latest \
    --secret=niyase-stripe-secret-key --project=$PROJECT)
  export SENTRY_DSN=$(gcloud secrets versions access latest \
    --secret=niyase-sentry-dsn --project=$PROJECT)
  echo "DEV secrets exported"
}

IAM 設計

team の各開発者に roles/secretmanager.secretAccessor を付与:

gcloud projects add-iam-policy-binding your-dev-project \
  --member="user:alice@niyase.com" \
  --role="roles/secretmanager.secretAccessor"

または secretmanager.secretAccessor を持つ group (例: developers@niyase.com) を作って一括管理する。

この方式の良さ

  • 追加コスト 0: GCP は既に契約済、Secret Manager は 1 secret あたり $0.06/月、1 万アクセスあたり $0.03 と無視できる金額
  • 権限制御が即時: 退職者が出たら IAM binding を削るだけで全 secret から遮断
  • rotation が一斉伝播: gcloud secrets versions add で新バージョン投入 → 全開発者が次回 niyase-dev-auth で自動的に新値取得。「みんな更新して」と Slack でアナウンスする必要なし
  • 監査ログ: Cloud Audit Logs に「いつ誰がどの secret を読んだか」が残る。SOC 2 Type II 監査でも証跡として使える
  • CI/CD と同じ仕組み: Cloud Run の --update-secrets も同じ Secret Manager を参照するので、production と dev で別系統に分かれない

注意点

  • オフラインで使えない: gcloud auth が切れたら取り出せない。出張先や飛行機内で困る可能性
  • gcloud auth 必須: 新人 onboarding で gcloud auth login までは通しておく必要がある (どうせ GCP 触るので必須項目だが)

パターン 3: production runtime secret を Cloud Run の secret マウントで

これは「人間が手元で取り出す」用途ではなく、サービス自身が起動時に読むもの。

GitHub Actions の deploy workflow から secret を注入

- name: Deploy to Cloud Run
  run: |
    gcloud run deploy niyase-api \
      --image="${IMAGE}" \
      --service-account="niyase-api@your-prod-project.iam.gserviceaccount.com" \
      --update-secrets="\
        DB_PASSWORD=niyase-app-db-password:latest,\
        SYNC_HMAC_SECRET=niyase-hmac-sync-secret:latest,\
        STRIPE_SECRET_KEY=niyase-stripe-secret-key:latest"

Cloud Run は起動時に Secret Manager から値を読み、コンテナの環境変数として注入します。

この方式の良さ

  • 人間が触らない: 本番 DB パスワードを手元の Mac に取り出す必要が無い → 「ノート PC 紛失で本番が漏れる」を構造的に防げる
  • rotation 容易: Secret Manager で新バージョン投入 → 次のデプロイで自動反映 (:latest 指定なら revision 単位でフリーズ)
  • 同じ Secret Manager を再利用: Patten 2 の dev secret と同じ仕組みで管理できる

注意点

  • service account に最小権限: niyase-api@ SA に必要な secret だけ secretAccessor を付与。プロジェクト全体に与えない
  • revision を意識: :latest だと再デプロイで値が変わる。stable な production は明示的に :v3 のような version 固定推奨

ツール選定の比較

観点1Password TeamsmacOS KeychainGCP Secret Manager
追加コスト$8/user/月0ほぼ 0 (使用量に応じて数十円〜)
team 共有◎ shared vault✗ 個人 Mac のみ◎ IAM で制御
CLI 自動化op CLIsecuritygcloud
退職者の遮断△ vault 権限剥がし✗ ローカルに残る◎ IAM binding 削除で即時
rotation 伝播△ 各人が見に行く必要△ ローカル更新が必要◎ 即時、再取得で反映
監査ログ○ audit log✗ ローカル◎ Cloud Audit Logs
オフライン利用✗ gcloud auth 必要
モバイルアクセス◎ iOS / Android アプリ△ iCloud Keychain 同期△ ブラウザ経由

niyase は GCP を既に契約・運用している ので、Secret Manager の追加コストは事実上 0。クラウドを使わない組織だと話が変わりますが、現代の SaaS スタートアップでクラウドゼロは稀でしょう。

移行ステップ

niyase で実際に踏んだ手順:

  1. secret の棚卸し: どの token が「個人」「team 共有 dev」「production runtime」のどれに属するか分類
  2. 個人 token: 各開発者が security add-generic-password で Keychain に投入、~/.zshrc に取り出し関数を追加
  3. team 共有 dev secret: admin が gcloud secrets create + versions add で投入、開発者に IAM 付与
  4. production runtime secret: 既に Secret Manager に置いてあるものを Cloud Run deploy workflow の --update-secrets で参照
  5. 1Password 解約: 上記すべてが動くことを確認してから契約終了

移行中に「Keychain は他の Mac で開けない」「gcloud auth が切れて出張先で困った」など小さな摩擦はありますが、$8/user/月 × 人数 × 12ヶ月 を考えれば許容できる範囲でした。

1Password が依然として有効な場面

念のため補足すると、どんな組織でも 1Password が不要になるわけではありません。以下のような要件があれば、有料サブスクを使う価値は十分にあります。

  • 非エンジニア社員も含めた full-org の認証情報管理 (Mac を持たない、gcloud を使わない)
  • マルチクラウド (AWS + GCP + Azure) で Secret Manager を統一したい
  • モバイルからの secret 参照 が頻繁にある
  • Watchtower 等の漏洩監視 が必要

niyase は現状フル GCP + 全員 Mac + エンジニア組織なので、上記には該当せず Keychain + Secret Manager で十分です。

おわりに

「secret 管理ツール = 専用 SaaS を契約する」が定番化しつつありますが、用途を整理して標準コンポーネントを組み合わせれば追加コスト 0 で運用できるケースは意外に多いです。スタートアップは特に固定費を抑えたいので、こういう「無料でできることはまず無料でやる」スタイルを取りやすいと思います。

niyase ではこの構成を社内の開発環境セットアップガイドに手順としてまとめており、新メンバーは初日に Keychain への token 投入と niyase-dev-auth 関数のセットアップを済ませる流れです。スタートアップでも採用しやすいシンプルさが気に入っています。

参考: