1Password CLI × Claude Code で .env を Vault化する完全ガイド — op run・シェル統合・MCP の3パターン

1Password CLI × Claude Code で .env を Vault化する完全ガイド — op run・シェル統合・MCP の3パターン

.envファイルの平文管理から脱却するための実践ガイド。1Password CLIを使ったop runによる実行時注入・fish/zshシェル統合・MCPサーバー経由の3パターンを解説。Claude Code SubAgentsとの組み合わせ、Doppler/Infisicalとの比較まで網羅。

エンジニアのゆとです。

告白しておく。

去年の今頃、僕のプロジェクトルートには .env ファイルが7個あった。OPENAI_API_KEY=sk-... が平文で書いてあって、.gitignore に入れてあるから大丈夫——そう思っていた。

Gitにコミットする事故は起きなかった。でも Claude Code を本格的に使い始めて、考え方が変わった。

AIエージェントが自律的にファイルを読み、コマンドを実行する世界では「.envを.gitignoreで守る」は最低限のラインにもなってない。Claude Codeがプロジェクト全体をコンテキストに取り込む時、.envはまるごとLLMに渡っている。SubAgentsが並列で動く構成では、どのエージェントがどのクレデンシャルにアクセスしたか追跡できない。

この記事では、1Password CLI を使って .env の平文管理から完全に脱却する方法を3つのパターンで解説する。


なぜ .env 平文管理は「今」特に危ないのか

.envリポジトリ漏洩の話は何年も前からある。それでもまだ多くのプロジェクトが .env 平文運用なのは「リスクをわかった上で許容している」からだ。でも Claude Code 時代はその計算式が変わっている。

具体的に何が問題か、整理しておく。

1. AIエージェントのコンテキスト汚染

Claude Code は CLAUDE.md で除外を設定しない限り、プロジェクトルートの .env を読む。/permissionsread:.env を拒否することはできるが、デフォルトでは開いている。つまり「APIキーがLLMのコンテキストに乗る」状態が継続する。

2. SubAgents の監査不能問題

Claude Code でSubAgentsを使うと、親エージェントが子エージェントを起動してタスクを並列化できる。このとき、各子エージェントが「どのファイルにアクセスしたか」のログはデフォルトでは残らない。.envに平文でAPIキーがあれば、SubAgentが触れた可能性を排除できない。

3. シェル履歴への漏洩

export OPENAI_API_KEY=sk-xxx をシェルで実行した瞬間、~/.zsh_history にキーが残る。tmuxのスクロールバックにも残る。macOSのクラッシュレポートにプロセス環境変数が含まれることもある。

4. ローテーションの困難さ

APIキーが漏洩した可能性があるとき、.envを使っている場合はどのファイルにどのキーが書いてあるかを手動で探す必要がある。1Password Vaultに集約していれば、Vault側でキーをローテーションすれば全プロジェクトに即時反映される。


1Password CLI とは — 5分セットアップ

1Password CLI(op コマンド)は、1Password Vault に保管されたシークレットをコマンドラインから取得・注入するためのツール。GUI不要で動くので、CI/CDや自動化スクリプトで使いやすい。

インストール(macOS):

brew install 1password-cli

インストール後、1Passwordデスクトップアプリとの連携を有効化する。これにより、op コマンド実行時にデスクトップアプリが認証を代行してくれる(Touch IDで解除できる)。

# 1Passwordアプリ側で「CLIとの統合を許可」を有効化後
op signin

動作確認:

op vault list
# Vault一覧が表示されればOK

シークレットの保存:

CLIからアイテムを作成する場合:

op item create \
  --category login \
  --title "OpenAI API Key" \
  --vault "Development" \
  --field "api_key[password]=sk-your-actual-key"

GUIで作ってCLIで参照するのが実際には楽。アイテムへの参照パスは op://Vault名/アイテム名/フィールド名 の形式になる。

op://Development/OpenAI API Key/api_key

パターンA: op run で実行時注入

最もシンプルな統合方法。.env ファイルを残しつつ、値の部分だけを op:// 参照に置き換える。

設定ファイルの変換:

従来の .env:

OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxx
ANTHROPIC_API_KEY=sk-ant-api03-xxxxxxx
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx

.env.vault として書き直す(このファイルはGitにコミットしてOK):

OPENAI_API_KEY=op://Development/OpenAI API Key/api_key
ANTHROPIC_API_KEY=op://Development/Anthropic API/api_key
GITHUB_TOKEN=op://Development/GitHub Token/credential

実行時の使い方:

# 従来
python my_script.py

# op run 経由
op run --env-file=.env.vault -- python my_script.py

op run.env.vault を読み込み、op:// 参照を実際の値に解決してから子プロセスに環境変数として渡す。シークレット本体はディスクに書き込まれない。

Claude Code との統合:

Claude Code を起動するときも同じ原理で使える:

op run --env-file=.env.vault -- claude

または package.json に組み込む:

{
  "scripts": {
    "ai": "op run --env-file=.env.vault -- claude",
    "dev": "op run --env-file=.env.vault -- npm run dev"
  }
}

これで npm run ai で起動した Claude Code セッションには、Vaultから取得したAPIキーが環境変数として渡される。

ハマりポイント: --no-masking の罠

op run はデフォルトでシークレット値をログ出力でマスクする。これは安全だが、デバッグ時に echo $OPENAI_API_KEY[MASKED] と表示されて困ることがある。

マスクを外したい場合(ローカルデバッグ時のみ推奨):

op run --no-masking --env-file=.env.vault -- env | grep API_KEY

本番環境では絶対に --no-masking を使わない。


パターンB: シェル統合(fish/zsh)

op run を毎回書くのが面倒な場合、シェルのプラグインで透過的に解決する方法がある。

fish shell の場合:

# fish用1Password CLIプラグインをインストール
op plugin init fish

これにより、特定のコマンド(gh, aws, kubectl 等)を実行すると自動的にVaultからクレデンシャルを取得してくれる。

fish の config.fish に追加:

# 1Password CLI 統合
if test -f ~/.config/op/plugins.sh
    source ~/.config/op/plugins.sh
end

zsh の場合:

# zsh用プラグインの初期化
op plugin init zsh

~/.zshrc に追加:

# 1Password シェル統合
source ~/.config/op/plugins.sh

動作確認:

# gh コマンドが自動的にGitHub Tokenを1Passwordから取得するか確認
gh auth status

Touch IDが要求されてVaultアンロック後にコマンドが実行されれば成功。

カスタム関数でのラップ:

プロジェクト固有の設定は ~/.zshrc にラッパー関数として定義しておくと便利:

# Claude Codeをプロジェクトのシークレット付きで起動
function claude-dev() {
  local project_dir=${1:-$(pwd)}
  local env_file="${project_dir}/.env.vault"
  
  if [[ -f "$env_file" ]]; then
    op run --env-file="$env_file" -- claude "${@:2}"
  else
    echo "⚠️  .env.vault not found. Running without secret injection."
    claude "${@:2}"
  fi
}

これで claude-dev コマンドを実行するだけで、.env.vault があれば自動的にVault注入付きで起動する。

シェル統合の限界:

シェル統合は「インタラクティブセッションで快適に使う」ためのもの。LaunchAgentやcronで自動実行するスクリプトには向かない。そういう場合は後述の op run を明示的に使うか、パターンCのMCP経由にする。


パターンC: MCP サーバー経由

Claude Codeセッション内でシークレットを動的に取得したい場合は、1Password MCPサーバー経由が適している。MCPサーバー自体の詳細なセットアップは別記事で解説しているので、ここではCLI統合との使い分けに絞って書く。

op run との違い:

観点op run1Password MCP
シークレット取得のタイミングプロセス起動時(静的)Claude Codeセッション中(動的)
用途スクリプト・バッチ処理インタラクティブなエージェント操作
アクセス粒度.env.vaultで指定したもの全部MCPツール経由で個別取得
監査ログop CLIのログ1Password Businessのイベントログ

どちらを使うか:

  • claude my_script.py のように特定スクリプトを実行させる → op run
  • セッション中にClaudeが「このAPIキーが必要だ」と判断して取得する → MCP

実運用では両方を組み合わせることになる。セッション起動は op run で基本的なシークレットを渡し、セッション中の動的な取得は MCPで補完するイメージ。


実践チュートリアル: Claude Code SubAgents との組み合わせ

単一の Claude Code セッションより、SubAgents を使った並列処理の場合にシークレット管理の設計が重要になる。

SubAgentsへのシークレット伝播:

Claude Code の SubAgents(Task tool 経由で起動されるサブエージェント)は、親プロセスの環境変数を継承する。つまり op run で親プロセスを起動していれば、SubAgentsも自動的に同じシークレットを使える。

# この1コマンドで起動すれば、配下のSubAgentsまで全部カバーされる
op run --env-file=.env.vault -- claude

プロジェクト別Vault分離:

複数プロジェクトを掛け持ちしている場合、Vault単位で分離しておくと監査・ローテーションが楽になる。

1Password Vault 構成例:
├── Development(共通開発ツール)
│   ├── GitHub Token
│   ├── Anthropic API Key
│   └── OpenAI API Key
├── Project-Alpha(案件A専用)
│   ├── Client DB Password
│   └── Staging Credentials
└── Project-Beta(案件B専用)
    └── Production API Keys

.env.vault をプロジェクトルートに置き、参照するVaultを明示する:

# Project-Alpha/.env.vault
ANTHROPIC_API_KEY=op://Development/Anthropic API/api_key
DB_PASSWORD=op://Project-Alpha/Client DB/password
STAGING_TOKEN=op://Project-Alpha/Staging Credentials/token

CLAUDE.md でのシークレット保護設定:

.env.vault 自体はコミットしてOKだが、解決された実際の値をClaude Codeが扱わないように CLAUDE.md で明示しておく:

# セキュリティポリシー

- 環境変数のAPIキーを出力・ログ・コードにハードコードしない
- .envファイルが存在してもコンテキストに読み込まない
- シークレットの解決は op run に任せる

これは完全な保護ではないが、Claude Codeが「うっかり値をechoする」ようなパターンを減らす効果がある。


ハマりどころ・トラブルシュート

実際に使っていて引っかかったポイントをまとめた。

1. op run が遅い

Vault解決のたびにTouchIDが要求されてセッションがタイムアウトする問題。

原因: 1Passwordのセッション有効期限が短い(デフォルト10分)。

対処: op.conf または環境変数で延長する:

# セッション有効期限を120分に設定(`~/.config/op/config` または環境変数)
export OP_SESSION_TIMEOUT=7200

または 1Password アプリ設定の「セキュリティ > 自動ロック」でロック時間を延長する。

2. CI/CD 環境で使う場合

GitHub Actions など、デスクトップアプリがない環境では op signin がインタラクティブに動かない。

対処: サービスアカウントトークンを使う:

# サービスアカウントトークンを生成(1Password Business が必要)
op service-account create "GitHub Actions" --expires-in 30d \
  --vault Development:read

# 生成されたトークンをGitHub Secretsに追加
# GitHub Actions workflow:
- name: Run with 1Password secrets
  env:
    OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
  run: |
    op run --env-file=.env.vault -- python my_script.py

サービスアカウントは特定Vaultへの読み取り権限のみ付与できるので、最小権限の原則を守りやすい。

3. op:// の参照パスが見つからない

[ERROR] 2026/04/25 op: could not find item "OpenAI API Key" in vault "Development"

スペース・大文字小文字・Vault名の不一致が原因になることが多い。確認コマンド:

# Vault内のアイテム一覧
op item list --vault Development

# 特定アイテムのフィールド一覧
op item get "OpenAI API Key" --vault Development --format json | jq '.fields[].label'

スペースを含む名前はダブルクォートで囲む必要がある。

4. macOS 以外の環境(WSL2 / Linux)

WSL2ではTouchID連携が使えないため、op signin が毎回マスターパスワードを要求する。

対処: WSL2 から macOS 側の 1Password デスクトップアプリと連携するには、1Password for Linux を使うか、WSL2 側でも op signin のセッション管理をシェルに組み込む:

# .bashrc / .zshrc に追加
function op-refresh() {
  eval $(op signin)
}
# セッション開始時に一度だけ実行

他ツールとの比較

1Password CLI と同じ用途で使えるツールを整理しておく。

Doppler

Doppler は「シークレット管理のクラウドSaaS」で、doppler run -- コマンド という使い方は op run と似ている。

違い: Doppler は SaaS なのでクレデンシャルがDoppler社のサーバーに保存される。1Password はVaultをローカル(または1Passwordのサーバー)に保持しつつ、CLIが橋渡しをする構造。「データをどこに置くか」のポリシーで選ぶことになる。

フリーランスやソロ開発者で既に1Passwordを使っている場合、追加コストなしに使えるのが1Password CLIの強み。Dopplerの無料プランはプロジェクト数制限がある。

Infisical

OSSのシークレット管理ツール。セルフホストができるので「どのクラウドにも置きたくない」ケースに向く。

違い: 使い始めのセットアップがやや重い。infisical run -- コマンド の動作原理は op run と同じ。エンタープライズでのセルフホスト要件がある場合に検討する。

HashiCorp Vault

エンタープライズ向けのシークレット管理基盤。動的シークレット生成(使うたびに新しいクレデンシャルを発行して後で失効)などが強力。

違い: 個人開発やフリーランス規模では過剰。セットアップ・運用コストが高い。「とりあえず.envをVault化したい」のであれば1Password CLIで十分。

まとめると:

  • 個人 / フリーランス / 小チームで既に1Passwordを使っている → 1Password CLI一択
  • チームでSaaSシークレット管理を導入したい → Doppler を評価
  • セルフホスト要件あり → Infisical
  • エンタープライズ大規模 → HashiCorp Vault

まとめ

.env 平文管理から 1Password CLI への移行は、設定ファイルを .env.vault に変換して op run を前に付けるだけで始められる。

段階的な移行ステップ:

  1. brew install 1password-cli でCLIをインストール
  2. 既存の .env.env.vault に変換(op:// 参照に置き換え)
  3. op run --env-file=.env.vault -- claude で Claude Code を起動
  4. シェル統合(zsh/fish)を設定して日常的な操作を楽にする
  5. 必要であれば MCP サーバーと組み合わせてセッション内での動的取得を追加

「全部一気にやらなくていい」がポイントで、op run を前に付けるだけなら既存のワークフローをほとんど変えずに始められる。

Claude Code を中心とした AI 開発に本腰を入れるなら、クレデンシャル管理の土台を固めておく価値がある。SubAgents が並列で動く構成が増えてくると、後から「あのエージェントがどのAPIキーにアクセスしたか」を追跡できない状態は地味にストレスになる。


関連記事

記事が見つかりません:

記事が見つかりません:

記事が見つかりません:

記事が見つかりません:

← 記事一覧に戻る