一覧へ戻る

1Password CLI編:.envをセキュアに管理する

2025-11-16 公開 2025-11-16 更新 GitHub
目次

概要

.envの取り扱いはいろいろなところで度々議論されている。

議論の主題となるのは大抵、.envの管理方法やツール選定がほとんどであり、 この議論になるたびにこれといった結論が出ていない。

この記事では自分の中で出した結論とその意図を記していく。

目的

運用方針としては、 .env をローカル環境に置かず、1Password に環境変数を保存して CLI 経由で読み込むという方針にする。

プロジェクトごとに .env を作らず、必要な値を 1Password から注入する形へ移行する形。

そもそも上記のように.envの環境変数を1Passwordで保管しようが、dotenvxで暗号化しようが、 CLIで注入時や複合化した後にローカルで起動したNodeプロセスの環境変数(process.env)に展開されるわけなので、完全に秘密情報が隔離されているわけではない。

なのでツールや手段は好みの問題でしかない。 漏洩リスクは常にあり、漏洩しないようにするだとか、漏洩後のリスクを局所化するといった部分はまた別の話になる。

この記事では1Passwordを使って環境変数を保管する方針にしているがツールは何でもいい。

そして、あくまでこれは漏洩リスクや漏洩後の被害の範囲を局所化させるものではなく、誤ってコミットしないようにするだとか、 端末間でのパスワード共有を楽にするだとか、そういった目的の側面が強いということが前提である。

ローカル環境で.envの環境変数を1Password CLIで読み込む

Get started with 1Password CLI

1password-cliのインストールと設定

WSLでlinux brewを使用しているので、MavcとWSLで以下を実行。

Terminal window
brew install 1password-cli

以下のエラーが出たのでunzipをインストール

Error: Failure while executing; ...
/usr/bin/env: ‘unzip’: No such file or directory
Terminal window
sudo apt update
sudo apt install -y unzip

1PasswordデスクトップアプリをCLIと連携

次に、デスクトップアプリ「開発者」セクションから「1Password CLIと連携」を有効にする。

CLIでの認証をデスクトップで担うことができる。

有効化後、適当にopコマンドを実行すると1Passwordが開いて認証が走るらしいが、

Terminal window
op vault list
No accounts configured for use with 1Password CLI.
- Turn on the 1Password desktop app integration to sign in with the accounts you've added to the app: https://developer.1password.com/docs/cli/app-integration/ for details.
- Add an account manually with 'op account add' and sign in by entering your password on the command line. See 'op account add --help' for details.
- Authenticate using a 1Password service account by setting the 'OP_SERVICE_ACCOUNT_TOKEN' environment variable to your service account token. Learn more: https://developer.1password.com/docs/service-accounts/
- Use 1Password CLI with a Connect server by setting the 'OP_CONNECT_HOST' and 'OP_CONNECT_TOKEN' environment variables to your Connect host and token, respectively. Learn more: https://developer.1password.com/docs/connect/

というエラーが出たので手動で既存アカウントを追加した。

TIP

ドメイン(xxx.1password.com)はデスクトップアプリの「アカウント情報」から確認できる

Terminal window
op account add --address xxx.1password.com --email xxx@gmail.com

求められる入力を行う。

Enter the Secret Key for xxx@gmail.com on my.1password.com: XXX
Enter the password for xxx@gmail.com at my.1password.com:
Now run 'eval $(op signin)' to sign in.

サインインしろと言われるのでサインインを実行。

Terminal window
eval $(op signin)
Enter the password for xxx@gmail.com at xxx.1password.com:

アカウントが登録されてるか確認し、登録できていたら成功。

Terminal window
> op whoami
URL: https://xxx.com
Email: XXX@gmail.com
User ID: XXX
User Type: HUMAN

op vault listをたたけば保管庫一覧が表示される

Terminal window
> op vault list
ID NAME
XXX Personal
XXX XXX

.envの環境変数を1Passwordで管理し、1PasswordCLIで参照

まず、既存の.envに記述している環境変数を1Passwordに保持する。

登録方法はコマンドで追加か手動で行う。

1. コマンドでの追加

Terminal window
op item create \
--vault "XXX" \
--category "Secure Note" \
--title "repo-name" \
'GITHUB_REPO_METADATA_TOKEN[concealed]=ghp_XXX'

vaultは対象の保管庫名、categoryは登録形式(セキュアノート)、タイトルはリポジトリ名だとわかりやすい。

2. 1Passwordに手動で登録

個人的にはこっちが楽なのでGUIで追加している。

登録したい保管庫で「新規アイテム」を押下し、セキュアノートを作成してリポジトリ名をタイトルに入れ、テキストフィールドにGITHUB_REPO_METADATA_TOKEN などの環境変数名をラベルに記述し、値に環境変数値を入力して保存するだけ。

1Passwordに環境変数の保持が完了したら、プロジェクト内で.envは不要なので削除。

.env.1passwordなどのファイルを作成し、以下のようにopコマンドで参照を行う。

.env.1password
GITHUB_REPO_METADATA_TOKEN=op://<保管庫名>/<タイトル名>/GITHUB_REPO_METADATA_TOKEN

これだけだとローカルサーバ起動しても環境変数は注入されないので、以下のように1Passwordで管理してる環境変数を読み込んでから、Viteの開発サーバーを起動する

package.json
"scripts": {
"dev": "op run --env-file=.env.1password -- vite",
"build": "tsc -b && vite build",
"build:local": "op run --env-file=.env.1password -- bash -c 'tsc -b &&vite build'",
...
}

buildコマンドなどについて、サーバー側には1PasswordCLIを入れていないと思うので、CI/CDで使用しているコマンドとは別に用意しておくことをお勧めする。(ローカルでbuildしてpreviewすることはあまりないと思うが一応)

基本的にはnpm run devなどの時にop run --env-file=.env.1passwordで.envに環境変数を注入するで十分。

サービスアカウントの追加

1Password Service Accounts

サービスアカウントとは、個人と関連付けられない認証方法を用いて1Passwordに保管しているシークレット情報を使用できるようにすること。

主にCI/CDや本番環境などで使用することを想定されているもの。

トークンを発行(Service Account Token)してそのトークンを用いてアクセス可能とする。

Vaultの権限を指定できるので、漏洩した時の被害は最小限に抑えることができる。

ホストで使用したい場合

推奨はされてないはずだと思うが、SATはログイン認証を挟まないためホストで使用するとログイン認証せずともシークレット情報を使用できる。

SATを厳重にセキュアに管理しつつ、Vaultを適切に絞ることができればやってもよいが、漏洩時のリスクを考えるとホストでSATを使うのは避けたほうがよさそう。

ただ、もしやりたい場合は以下の順でまずは1Password CLIに追加したアカウントを削除するところから始める(1Passwordアカウントの削除ではなく、1Password CLIに追加したアカウントなのでデータが消えるなどは無い)。

op account list
op account forget --all
op signout --all

そして次のサービスアカウント作成に進む。

サービスアカウントの作成

Web版の1Passwordを開き、プロフィールから「アカウントの管理」を開く。

「開発者」タブを開くと「サービスアカウント」が出てくるので手順通りに追加する。

その時、保管庫のアクセス権限も決めることができるのでサービスアカウントでアクセス可能にしたい保管庫を選択する。

一時的に表示されるトークンを1Passwordに保存。

使用する際はその環境で、

  1. 1Password CLIを入れる
  2. OP_SERVICE_ACCOUNT_TOKENという環境変数にトークンを入れる

上記を行うことでopコマンドでシークレット情報にアクセスすることができる。

個人的に感じるメリット・デメリット

メリット

  • .env(機密になりえる情報)を端末ごとにローカルマシンに残さなくて済む
  • 1Password にまとめて保存しておけば、Mac / Windows / WSL どの環境でもサインインだけで同じ値を参照でき、異なる端末間の情報共有がスムーズ

デメリット

  • ローカルサーバーを起動するたび(またはターミナルのセッション切れなど)に 1Password CLI の認証が必要になるのが少し手間

メモ

2026-05-30

1Password の Environment を使った環境変数管理について、方針を決めた。

方針としては、各プロジェクトごとに 1Password Environment を用意し、プロジェクトで必要な環境変数はその Environment に置く。各プロジェクトは、実行時に対応する Environment を読みに行く形にする。

当初は 1Password Environments の仮想 .env mount も候補にしていた。これは通常の .env と同じように扱えるので使い心地はかなり良さそうだった。ただし、今回は採用しないことにした。

理由の 1 つはセキュリティ面。op run でも mount でも、最終的にはアプリケーション実行時に環境変数として secret がプロセスに渡る点は同じ。ただし、op run は指定したコマンドとその子プロセスに対して secret を渡す形になる。一方で mount は、仮想的な .env ファイルとして見えるため、そのファイルを読めるプロセスであればアプリケーション以外からも読めてしまう可能性がある。IDE、watcher、テストランナー、別のシェルなど、意図しないプロセスが .env を読む余地がある。つまり、secret が露出する範囲を比べると、mount より op run の方が狭くしやすい。

もう 1 つの理由は、mount が Windows に対応していないこと。自分の環境では macOS だけでなく Windows / WSL も考慮したいので、macOS だけ快適な仕組みに寄せすぎると運用が割れる。op run で Environment を読む形にしておけば、WSL 側でも同じ考え方で扱いやすい。

さらに、プロジェクトをまたいで同じ環境変数を使う場合の多重管理も問題になる。mount を前提にすると、基本的にはプロジェクトごとの Environment に値を定義することになる。そのため、複数プロジェクトで同じ token や API key を使っている場合、同じ値を複数の Environment に登録する必要が出てくる。これはトークンローテーション時に更新漏れが起きやすく、メンテナンスがつらい。

op run であれば、共通の Environment とプロジェクト固有の Environment を分けて読み込める。共通 secret は shared Environment に置き、プロジェクト固有の値だけ各プロジェクトの Environment に置く、という構成にできる。これなら同じ値を複数箇所に重複定義しなくて済む。

そのため、今回は以下の方針にする。

  • 1Password Environment に環境変数を置く
  • Environment はプロジェクトごとに用意する
  • 共通 secret は shared Environment に寄せる
  • 各プロジェクトは op run --environment ... で必要な Environment を読みに行く
  • 仮想 .env mount は使わない
  • Environment 以外で管理している secret は整理し、必要なものは Environment 側に移す

mount は使い心地だけ見ると魅力的だが、セキュリティ上の露出範囲、Windows 未対応、共通値の多重管理を考えると、今回は op run を使う方が運用しやすいと判断した。


2026-05-30

1Password Environment 移行時の repo 側の持ち方について追加で整理した。

最初は、既存の .env.1password を残したまま、中に書いている op://... の参照先だけを 1Password Environment に向けられないかと考えた。

ただ、op://... の secret reference は、1Password の vault / item / field を指すための構文であり、Environment の変数を指す構文ではなさそうだった。Environment を CLI から読む場合は、op run --environment <environment> を使うのが公式の使い方になる。

この場合、repo 側には環境変数名の一覧すら必ずしも置く必要がなくなる。1Password Environment 側に環境変数名と値を定義しておき、repo 側は実行コマンドで対象 Environment を指定するだけでよい。

つまり、実行は次の形になる。

"dev": "op run --environment <environment-id> -- <command>"

アプリケーション側は、これまで通り import.meta.env.Xprocess.env.X を読む。環境変数が存在する前提で書く形になる。

.env.example を置いて必要な環境変数名を repo 側に残す案も考えたが、今回は置かないことにした。必要な環境変数名の正本は 1Password Environment に寄せる。

この方針で、.env.1password は廃止し、各プロジェクトの package.json から op run --environment <environment-id> で読む形へ移行する。

Vite / Astro の VITE_PUBLIC_ の扱いについても別途見直しが必要。特に VITE_ はクライアントバンドルに露出する前提のため、secret を入れてはいけない。digital-clock-display の Unsplash token は実際にクライアント JS に露出していた。ただし、無料 API であり、用途も限定されているので即時修正ではなく、改善 issue として扱う。


2026-05-30

op run --environment を使うには、現時点では 1Password CLI の beta build が必要だった。

手元の stable 版 op2.34.0 で、op run --environmentop environment も存在しなかった。そのため、op run --environment astro-markdown-blog -- ... を実行すると unknown flag: --environment になった。

1Password の公式ドキュメント上も、Environment を CLI から読む機能は beta 扱いで、2.33.0-beta.02 以降が必要とされている。

そのため、dotfiles 側では Homebrew の cask を stable の 1password-cli ではなく 1password-cli@beta に切り替えることにした。

cask "1password-cli@beta"

1Password アプリ側の vault や item、Environment が初期化されるわけではなく、入れ替わるのは CLI のバイナリ。ただし、CLI とデスクトップアプリの連携や CLI 側の認証は、初回だけ再承認や再サインインが必要になる可能性がある。

実際に beta CLI に切り替えたところ、CLI 側の account list は引き継がれていた。ただし op run --environment astro-markdown-blog -- ... のように Environment 名を指定すると、the provided ID is not in a valid format で失敗した。--environment は Environment 名ではなく Environment ID を渡す必要がある。そのため、package.json には Environment ID を指定する。

Environment ID はそれ単体では secret ではない。ID が分かっても、1Password への認証と対象 Environment への権限がなければ値は読めない。ただし、repo と 1Password Environment の対応関係という内部構成情報ではあるので、公開 repo に置く場合は「公開しても secret ではないが、積極的に見せたい情報でもない」という扱いになる。

今回は、op run --environment が ID を要求すること、repo 側に実行コマンドとして固定する必要があること、ID 単体では secret にアクセスできないことから、package.json に Environment ID を置く運用を許容する。

この記事を共有