はじめに
新しい開発環境を組むときに必ず議論になるのが「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 つに分けています。混ぜると整理がつかなくなります。
| 種別 | 例 | アクセス主体 | 共有範囲 |
|---|---|---|---|
| 個人 token | Cloudflare API Token, 個人 GitHub PAT | 1人の開発者本人 | 自分の Mac のみ |
| team 共有 dev secret | Resend DEV API key, Stripe test key, Sentry DSN | チーム全員 (またはサブセット) | DEV / STG 環境を触る全員 |
| production runtime secret | DB パスワード, 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 Teams | macOS Keychain | GCP Secret Manager |
|---|---|---|---|
| 追加コスト | $8/user/月 | 0 | ほぼ 0 (使用量に応じて数十円〜) |
| team 共有 | ◎ shared vault | ✗ 個人 Mac のみ | ◎ IAM で制御 |
| CLI 自動化 | ○ op CLI | ◎ security | ◎ gcloud |
| 退職者の遮断 | △ vault 権限剥がし | ✗ ローカルに残る | ◎ IAM binding 削除で即時 |
| rotation 伝播 | △ 各人が見に行く必要 | △ ローカル更新が必要 | ◎ 即時、再取得で反映 |
| 監査ログ | ○ audit log | ✗ ローカル | ◎ Cloud Audit Logs |
| オフライン利用 | ◎ | ◎ | ✗ gcloud auth 必要 |
| モバイルアクセス | ◎ iOS / Android アプリ | △ iCloud Keychain 同期 | △ ブラウザ経由 |
niyase は GCP を既に契約・運用している ので、Secret Manager の追加コストは事実上 0。クラウドを使わない組織だと話が変わりますが、現代の SaaS スタートアップでクラウドゼロは稀でしょう。
移行ステップ
niyase で実際に踏んだ手順:
- secret の棚卸し: どの token が「個人」「team 共有 dev」「production runtime」のどれに属するか分類
- 個人 token: 各開発者が
security add-generic-passwordで Keychain に投入、~/.zshrcに取り出し関数を追加 - team 共有 dev secret: admin が
gcloud secrets create+versions addで投入、開発者に IAM 付与 - production runtime secret: 既に Secret Manager に置いてあるものを Cloud Run deploy workflow の
--update-secretsで参照 - 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 関数のセットアップを済ませる流れです。スタートアップでも採用しやすいシンプルさが気に入っています。
参考:
securityman page (Apple Developer)- Google Cloud Secret Manager docs
- Workload Identity Federation — runtime からの取得を keyless にする (本記事と合わせて使うと CI も含めて鍵 0 で構成可能)