
Dockerコンテナ内で1Passwordから認証情報を取得できるようにしてみた
はじめに
クラスメソッドオペレーションズの髙嶋です。
近年のサプライチェーン攻撃の増加や、業務でAIを活用するシーンが増えてきたことを受け、自分の開発環境をセキュアに保つために認証情報の管理方法を見直すことにしました。
開発環境としてDocker(Dev Container)を使っているのですが、コンテナ内で動かすツール(MCPサーバーなど)に認証情報を渡す方法として、設定ファイルへの直書きなど、平文ファイルで管理する方法を使っていました。この方法ではファイルに認証情報がそのまま残るため、「認証情報を平文ファイルに置かない」「実行時にだけ展開する」構成にしたい、という思いから今回の取り組みを始めました。
社内では認証情報の管理に1Passwordを標準で使っているので、これを使ってコンテナ内のツールに認証情報を渡せないかを試行錯誤しました。色々と検索もしたのですが、自分の構成にちょうど合う情報がなく…。
最終的に生成AI(Claude)と相談しながらホスト側にHTTPプロキシを立てる構成を作ってみたので、その内容を紹介します。
やりたかったこと
下記2点を満たしたい、というのが要件でした。
- 認証情報をリポジトリ内のファイルに直接書きたくない(
.envも含めて) - 開発はDev Container(Docker)の中で行うため、コンテナ内のツールから認証情報を参照できる必要がある
これを実現するために、下記の方針で進めることにしました。
- 認証情報の管理基盤としては、社内標準である1Passwordを使う
- 認証はWindows側で済ませる(コンテナごとに1Passwordのセットアップ・ログインをしたくない)
ホストOS側であれば、1Passwordデスクトップアプリと連携した op.exe が使えるので、Windows Helloでの認証もスムーズです。
一方、コンテナ内に op をインストールしても、下記の理由で同じようには使えませんでした。
- コンテナ内の
opは1Passwordデスクトップアプリと連携できないため、毎回サインインが必要になる - 1Passwordの認証機能はOSとデスクトップアプリの直接連携が前提で、コンテナの中までは届かない
前提条件
環境
- ホストOS:Windows 11
- WSL2のディストリビューション:Ubuntu 24.04 LTS
- コンテナランタイム:Rancher Desktop
- パスワードマネージャー:1Passwordデスクトップアプリ(Windows側にインストール済み、CLI連携で
op.exeを有効化済み) - 開発:VSCode / Cursor の Dev Container 機能でDockerコンテナに接続
1Password利用時の制約条件
- 組織の管理者側でサービスアカウントの利用が制限されているため、サービスアカウントは使えない
- 1Password Connect も管理者権限が必要なため、構築できない
試したけどダメだった方法
最終的な構成にたどり着くまでに、いくつかのアプローチを試しました。同じような検討をしている方の参考になればと思い、ダメだったパターンも残しておきます。
1. npiperelay + socatでパイプをUnixソケットにブリッジ
Windowsの名前付きパイプを npiperelay.exe と socat でWSL側のUnixソケットに変換し、コンテナへマウントする方法を試したのですが、最終的にコンテナからエージェントへの接続を成立させることができず断念しました。
2. コンテナ起動時に環境変数を注入
Dev Containerの起動時フック(initializeCommand / postCreateCommand)でホスト側の op.exe を叩き、環境変数経由で渡す方法です。コンテナ起動時の注入になるため「実行時にだけ展開する」という要件とは合わないものの、「認証情報を平文ファイルに置かない」だけでも実現できれば、と試してみました。ただ、IDE側にうまく認証情報が渡らず断念しました。
3. aws-vault
AWS認証情報だけでも実現できれば、と試してみましたが、コンテナまで認証情報を渡すことができず断念しました。
解決のアプローチ
下記のような構成にしました。
┌────────────────────────────────────┐
│ Windows + WSL2 Ubuntu(ホスト) │
│ │
│ ┌──────────────┐ │
│ │ 1Password App│ │
│ └──────┬───────┘ │
│ │ │
│ ┌──────▼───────┐ │
│ │ op.exe │ │
│ └──────▲───────┘ │
│ │ │
│ ┌──────┴──────────┐ │
│ │ secret-proxy.py │ │
│ │ (HTTPサーバ) │ │
│ └──────▲──────────┘ │
│ │ HTTP │
│ ┌──────┴──────────┐ │
│ │ Docker Container│ │
│ │ │ │
│ │ opr ──→ MCPサーバなど │
│ └─────────────────┘ │
└────────────────────────────────────┘
ポイントは下記です。
- ホスト側(WSL2 Ubuntu上)に薄いHTTPプロキシを立てる
- プロキシは内部で
op.exe(Windows側の1Password CLI)を呼んでシークレットを取得し、HTTPで返す - 1Passwordへの認証はWindowsデスクトップアプリに委ねる(プロキシ自身は認証情報を持たない)
- プロキシは内部で
- コンテナ内に薄いCLIラッパーを配置する
op://vault/item/field形式の参照を受け取り、プロキシにHTTPで問い合わせて値を取得- 取得した値を環境変数にセットして任意のコマンドを起動する
これにより、コンテナ内の設定ファイルやコマンドには「実際の値」ではなく op://... という参照だけが残ります。実際の値は実行時にプロキシ経由で取得され、子プロセスの環境変数にのみ展開されるため、ファイル上には残らない、という形です。
やってみる
1. 事前準備
以降のサンプルコードはいずれもPythonで動作するため、WSL側とコンテナ側の両方にPythonのインストールが必要です(動作確認バージョン:Python 3.12)。
ホスト側
ホスト側(WSL2 Ubuntu上)に、HTTPで op:// 参照を解決するだけのプロキシを置きます。
secret-proxy.py
#!/usr/bin/env python3
import subprocess
import json
from http.server import HTTPServer, BaseHTTPRequestHandler
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
length = int(self.headers["Content-Length"])
body = json.loads(self.rfile.read(length))
ref = body["ref"]
# Windows側の op.exe を呼び出してシークレットを取得
result = subprocess.run(
[
"op.exe",
"read",
ref
],
capture_output=True,
text=True,
check=True
)
value = result.stdout.rstrip("\n")
self.send_response(200)
self.end_headers()
self.wfile.write(json.dumps({"value": value}).encode())
if __name__ == "__main__":
HTTPServer(("0.0.0.0", 19999), Handler).serve_forever()
コンテナ内
コンテナ内に置くCLIラッパーです。
opr.py
#!/usr/bin/env python3
import os
import sys
import json
import urllib.request
PROXY_URL = "http://host.docker.internal:19999"
def resolve(ref):
req = urllib.request.Request(
PROXY_URL,
data=json.dumps({"ref": ref}).encode(),
headers={"Content-Type": "application/json"},
)
return json.loads(urllib.request.urlopen(req).read())["value"]
# 引数を解析: opr KEY=op://... command args
key, op_ref = sys.argv[1].split("=", 1)
cmd = sys.argv[2:]
# op:// 参照を解決して環境変数にセット
os.environ[key] = resolve(op_ref)
# 指定されたコマンドを実行
os.execvp(cmd[0], cmd)
docker-compose.yml でコンテナにマウントし、opr という名前で使えるようにします。
docker-compose.yml
services:
app:
image: your-app:latest
volumes:
- ./opr.py:/usr/local/bin/opr:ro
ホスト側で実行権限を付けておきます。
chmod +x opr.py
2. 使い方
プロキシの起動
WSL2 Ubuntu上で secret-proxy.py を起動します。
python3 secret-proxy.py
op.exe の認証は1Passwordデスクトップアプリに任せているため、初回はWindows側で認証画面が出ます。一度認証すれば一定時間は再認証なしで動作します。
コマンドの実行
opr を使って1Passwordから認証情報を取得し、任意のコマンドを実行します。
opr API_KEY=op://Employee/xxx/api-key npx xxx
設定例(MCPサーバー)
mcp.json でBacklog MCPを使用する際の書き方は下記のようになります。
{
"mcpServers": {
"backlog": {
"command": "opr",
"args": [
"BACKLOG_API_KEY=op://Employee/xxx/api-key",
"npx", "--yes", "backlog-mcp-server"
],
"env": {
"BACKLOG_DOMAIN": "example.backlog.jp"
}
}
}
}
npx の前に opr を差し込む、という形にしています。
注意点
今回の構成はあくまで「自分の開発環境で使う」前提で作っているので、本格的に運用する場合は下記の点を考慮する必要があります。
- 公式手段ではない:あくまで自作のラッパーです。チームで使う場合は1Password Connectなどの公式手段も比較検討してみてください
- レスポンスに数秒かかる:WSLからWindows側の
op.exeを実行している部分で時間がかかるため、opr実行からシークレットが返ってくるまでに数秒の待ち時間が発生します。MCPサーバーの起動時など、立ち上がりが少しもたつく場面があります - プロキシは平文HTTP通信:
host.docker.internal:19999への通信は平文です。同一ホスト内であればリスクは限定的だと思いますが、セキュリティにご注意ください - プロキシの公開範囲(0.0.0.0での待受):サンプルコードではコンテナ(host.docker.internal)からの接続を受け付けるために、0.0.0.0 でHTTPサーバーを起動しています。Windowsのネットワーク設定やファイアウォールの状態によっては、同一ネットワーク内の他端末からアクセスされてしまうリスクがあるため、対策が必要かもしれません
- アクセス制御がない:サンプルとして記載しているプロキシのコードは、受け取った
op://参照をそのままop.exe readに渡します。コンテナが侵害されると1Passwordに登録されている任意のシークレットを引ける状態になるため、参照可能なリソース範囲を制限するallowlist機構などがあるとより安全です
まとめ
IDEから接続したDockerコンテナの中から、認証情報をファイルに直書きせずに受け渡すために、ホスト側に薄いプロキシ(secret-proxy.py)とラッパー(opr)を作ってみました。
同じような悩み(Windows + WSL2 + Docker + MCPサーバーで認証情報を扱いたい)を持っている方の参考になれば幸いです。
実装にあたっては生成AI(Claude)と相談しながら設計・実装を進めました。「公式の方法では自分の環境に合わない」という状況でも、要件を整理して相談することで、組み合わせの提案を受けながら自作のツールに落とし込めるのは便利だなと感じました。
クラスメソッドオペレーションズ株式会社について
クラスメソッドグループのオペレーション企業です。
運用・保守開発・サポート・情シス・バックオフィスの専門チームが、IT・AIをフル活用した「しくみ」を通じて、お客様の業務代行から課題解決や高付加価値サービスまでを提供するエキスパート集団です。
当社は様々な職種でメンバーを募集しています。
「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、クラスメソッドオペレーションズ株式会社 採用サイト をぜひご覧ください。※2026年1月 アノテーション㈱から社名変更しました









