プロジェクトで使うAPIキーとかデータベースのパスワードって、みんなどうやって管理してる? .env ファイルに直接書くとGitにうっかり上げちゃいそうで怖いし、かと言って毎回手動で環境変数にセットするのも面倒やん?
そんな時に便利なのが、1Password CLIとdotenvの連携技やで。Gitで管理する.env.opと、Git管理しない.env.localに分けて、シークレットを安全に扱う方法を教えるな。
一番雑な投げ方
とりあえず、これコピペしてみてや。
op inject -i .env.op -o .env.local && source .env.local
これだけで動くで。op inject が 1Password からシークレットを読み取って、env.op の op:// 形式の記述を実際の値に変換してくれるんや。変換結果は .env.local に書き出されるから、それを source コマンドで読み込めば、今いるシェルで環境変数として使えるようになるんよ。
もうちょい具体的に投げるパターン
さっきのは開発環境で一時的に使うときとかに便利やけど、もっとしっかりアプリに組み込むならいくつかパターンがあるで。
1. .env.op を作ってみる
まず、シークレットの参照元になる .env.op ファイルを作るんや。これはGitで管理してもええから、チームで共有できるで。
# .env.op の中身やで(Git管理してもOK)
# APIキーとかDBのURLとかを op://<Vault名>/<アイテム名>/<フィールド名> の形式で書く
DATABASE_URL=op://MyPersonalVault/MyApp Database Credentials/database_url
API_KEY=op://MyPersonalVault/MyApp API Keys/some_service_key
SUPABASE_JWT_SECRET=op://MyPersonalVault/MyApp Supabase JWT Secret/supabase_jwt_secret
op:// のパスは、1Passwordアプリでアイテムを開いて「参照をコピー」すると簡単に手に入るから、それを使うのが楽やで。
2. アプリケーション実行時にシークレットを展開
Node.jsアプリみたいに dotenv パッケージを使うような場合、op inject と連携させるとスマートやで。
# .env.op を元に .env.local を生成して、dotenvで読み込ませる
op inject -i .env.op -o .env.local
# アプリケーションは .env.local を読むようにしといてな
node -r dotenv/config app.js
.env.local は .gitignore に追加して、Gitに上がらんように気をつけや。
3. op run でアプリを直接起動
op run コマンドを使えば、op inject でファイルを生成する手間も省けるし、環境変数がシェルに残る心配もせんでええから、一番セキュアなやり方やな。
# .env.op の内容を環境変数としてアプリを実行するんや
op run --env-file=.env.op -- node -r dotenv/config app.js
これで、app.js の中で process.env.DATABASE_URL とか process.env.API_KEY って書けば、1Passwordから自動で引っ張ってきた値が使えるようになるんよ。
実践例 / 実録
ウチのプロジェクト「くぐる」はNext.jsとSupabase使ってて、CI/CDでGitHub Actions回してるんやけどな、秘密情報の管理でこれめっちゃ役立ってるで。
開発環境やと、npm run dev の前に毎回 op run --env-file=.env.op -- を付けてたけど、これだとコマンドが長くなりがちやん?だから、package.json の scripts をこうしてたで。
// package.json の scripts の一部
{
"scripts": {
"dev": "op run --env-file=.env.op -- next dev",
"build": "op run --env-file=.env.op -- next build",
"start": "op run --env-file=.env.op -- next start",
"lint": "next lint",
"test": "op run --env-file=.env.op -- jest"
}
}
こうしとけば、普段通り npm run dev って打つだけで、自動で1Passwordからシークレットが展開されて、ちゃんとアプリが動くようになるんよ。
GitHub ActionsでのCI環境でも同じやな。ワークフローの中で op signin してから op run でビルドやテストを走らせるだけで、シークレットが環境に漏れ出すことなく、安全に処理ができたで。
# GitHub Actions のワークフローの一部
name: CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 1Password CLIのセットアップとサインイン
- name: Setup 1Password CLI
uses: 1Password/load-secrets-action@v1
with:
export-env: false # これが重要!op runを使うからここでは環境変数に展開しない
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
- name: Install dependencies
run: npm ci
- name: Build with 1Password secrets
run: npm run build # op run が package.json の scripts に定義されてるからこれだけでOK
- name: Run tests with 1Password secrets
run: npm run test
こんな感じで、op run と package.json の scripts を組み合わせるのが、プロジェクトに導入するなら一番実用的やと思うで。シークレットをファイルに残さないから、CI/CDでも安心やしな。
あと、.env.local とか .env ファイルがうっかりGitに上がらんように、gitleaks と pre-commit フックで自動チェックしてるんよ。これやるとマジで安心やで。
# .githooks/pre-commit の例
#!/bin/sh
if git check-ignore --quiet .env.local; then
# .env.local は .gitignore に書かれてるからOKやで
exit 0
else
# あかん!.env.local が .gitignore に書かれてへんやん!
echo "Error: .env.local is not ignored! Add it to .gitignore before committing."
exit 1
fi
こんなん仕込んどけば、人間がミスっても安心やろ?
つまずきポイント
このやり方、便利やけど、たまに「あれ?動かん!」ってなる時もあるんよな。よくあるハマりどころはこれやで。
- 1Password CLIにサインインしてへん!: これ一番忘れがちやで。
op runは自動でサインインしてくれるけど、op injectとかop readを単体で使うときは、事前にeval $(op signin)してサインインしとかなあかんのよ。 op://のパスが間違ってる: Vault名、アイテム名、フィールド名、一文字でも間違ってたら参照できへんから注意してな。1Passwordアプリから「参照をコピー」するのが確実やで。# まずはこれで直接読めるか試してみてな op read op://MyPersonalVault/MyApp Database Credentials/database_url --raw- 1Password CLIのバージョンが古い: たまにCLIの仕様が変わることもあるから、最新版にアップデートしとくのが吉やで。
これでバージョン確認して、古かったらop --versionbrew upgrade 1password-cliとかでアップデートしとこか。 - サービスアカウントトークン(CIの場合): CIで使うときは、サービスアカウントのトークンをちゃんと
OP_SERVICE_ACCOUNT_TOKEN環境変数にセットしてるか確認してな。権限もちゃんと与えてるかチェックや。
こんなところに気をつければ、1Passwordとdotenvで安全かつ快適な開発ができるはずやで!