2026年5月17日

2026年5月17日

Docker Engine APIにlocalhost以外のホストから接続する方法:SSH・TLSの設定と注意点

Dockerのデーモン(dockerd)は、docker コマンドから受け取った指示を Docker Engine API として処理しています。初期状態では、このAPIはホスト内の Unix ソケット /var/run/docker.sock だけで待ち受けているため、別のマシンからは接続できません。

この記事では、開発用PCやCIサーバーなど「localhost以外のホスト」からリモートのDockerを操作する方法を、安全な順に説明します。最後に、よく混同される「コンテナの中からホスト上のAPIに接続する」設定も扱います。手順はLinux(systemdで動くDocker Engine)を前提に、2026年10月時点のDocker公式ドキュメントで確認しています。

最初に知っておくこと:APIへの接続はrootと同じ権限

Docker Engine APIに接続できる人は、コンテナを自由に作れます。ホストのファイルシステムをマウントしたコンテナを特権で起動すれば、ホストを丸ごと操作できるため、APIへの接続権限は、そのホストのroot権限とほぼ同じです。

そのため、次の構成は避けてください。

  • tcp://0.0.0.0:2375 のように、TLSなしでAPIをネットワークに公開する(2375番は暗号化も認証もない接続に使われる番号です)
  • インターネットから到達できるアドレスでAPIを待ち受ける

インターネット上では、認証なしで公開されたDocker APIを探して不正なコンテナ(暗号資産のマイニングなど)を起動する攻撃が知られています。公式ドキュメントも、TLSなしのリモート接続は推奨しないとしています。

方法を選ぶ

方法 認証・暗号化 サーバー側の設定 向いている場面
SSH経由(ssh://) SSHの鍵認証と暗号化 不要(SSHで入れればよい) ほとんどの場合(推奨)
TLSでTCP公開(2376番) クライアント証明書で相互認証 dockerdの設定変更と証明書の作成 SSHを使えない環境、専用の管理ネットワーク
TLSなしでTCP公開(2375番) なし dockerdの設定変更 使わない(ホスト内の 127.0.0.1 に限る検証用途を除く)

方法1:SSHで接続する(推奨)

Docker CLIは、ssh:// で始まる接続先を指定すると、SSHでリモートホストに入り、そのホストのDockerソケットにつないでくれます。サーバー側でdockerdの設定を変える必要はありません。

準備

  • 手元のPCからリモートホストへ、鍵認証でSSHログインできること
  • リモートのユーザーがDockerソケットを使えること(docker グループに入っている、など)
  • 手元のPCにDocker CLIが入っていること

リモートのユーザーを docker グループに入れる操作は、そのユーザーにrootに近い権限を与えることになります。Docker操作専用のユーザーを用意すると管理しやすくなります。

一時的に接続する

# その場限りでリモートのDockerを操作する
docker -H ssh://docker-user@host1.example.com ps

# 環境変数で指定する(このシェルを閉じるまで有効)
export DOCKER_HOST=ssh://docker-user@host1.example.com
docker ps
unset DOCKER_HOST

コンテキストを作って切り替える

よく使う接続先は「コンテキスト」として登録しておくと、名前で切り替えられます。

# リモートのDockerをコンテキストとして登録する
docker context create \
  --docker host=ssh://docker-user@host1.example.com \
  --description="Remote engine" \
  my-remote-engine

# 切り替えて使う
docker context use my-remote-engine
docker ps

# 手元のDockerに戻す
docker context use default

# 登録済みのコンテキストを確認する
docker context ls

SSH接続を使い回して速くする

docker コマンドを実行するたびにSSH接続を張り直すと遅く感じることがあります。公式ドキュメントでは、~/.ssh/config で接続を共有する設定が紹介されています。

Host host1.example.com
  ControlMaster auto
  ControlPath ~/.ssh/control-%C
  ControlPersist yes

プログラムからAPIをHTTPで呼びたい場合

Docker CLIではなく、HTTPでAPIを呼ぶツール(自作スクリプトや一部の監視ツール)から使う場合は、SSHのポート転送でリモートのソケットを手元のポートにつなぐ方法があります。

# 手元の127.0.0.1:2375 を、リモートの /var/run/docker.sock に転送する
ssh -nNT -L 127.0.0.1:2375:/var/run/docker.sock docker-user@host1.example.com

# 別の端末から、手元のポート経由でAPIを呼ぶ
curl http://127.0.0.1:2375/version

転送先を 127.0.0.1 にしておけば、手元のPCの外からはこのポートに接続できません。

方法2:TLSでTCP公開する

SSHが使えない場合は、dockerdにTCPで待ち受けさせ、TLSのクライアント証明書で接続できる相手を限定します。待ち受けのポートはTLS用の 2376番 を使います。

証明書を作る

自分で認証局(CA)を作り、サーバー証明書とクライアント証明書に署名します。以下は公式ドキュメントの手順に沿った例です。$HOST はクライアントが接続に使うホスト名、10.10.10.20 はサーバーのIPアドレスに置き換えます。

# 1. 認証局(CA)の鍵と証明書(CAの鍵にはパスフレーズを付ける)
openssl genrsa -aes256 -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem

# 2. サーバーの鍵と署名要求(CNは接続に使うホスト名)
openssl genrsa -out server-key.pem 4096
openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr

# 接続に使う名前・IPアドレスと、サーバー用途であることを指定する
echo subjectAltName = DNS:$HOST,IP:10.10.10.20,IP:127.0.0.1 >> extfile.cnf
echo extendedKeyUsage = serverAuth >> extfile.cnf

openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \
  -CAcreateserial -out server-cert.pem -extfile extfile.cnf

# 3. クライアントの鍵と証明書(クライアント用途を指定する)
openssl genrsa -out key.pem 4096
openssl req -subj '/CN=client' -new -key key.pem -out client.csr
echo extendedKeyUsage = clientAuth > extfile-client.cnf

openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \
  -CAcreateserial -out cert.pem -extfile extfile-client.cnf

# 4. 不要になった署名要求を消し、鍵を本人だけが読めるようにする
rm -v client.csr server.csr extfile.cnf extfile-client.cnf
chmod -v 0400 ca-key.pem key.pem server-key.pem
chmod -v 0444 ca.pem server-cert.pem cert.pem

ca.pem・server-cert.pem・server-key.pem をサーバーの /etc/docker/certs/ などに置きます。ca-key.pem(CAの鍵)はサーバーに置かず、別の安全な場所で保管します。

dockerdにTLSで待ち受けさせる(systemd)

systemdで動くDockerでは、起動コマンドを上書きする設定を作ります。まず現在の起動コマンドを確認します。

systemctl cat docker.service

表示された ExecStart= の行をもとに、TCPの待ち受けとTLSの指定を足した行を書きます。

sudo systemctl edit docker.service

開いたエディターに次の内容を書きます(ExecStart= の空行は、元の設定を消すために必要です。2行目は、確認した元の行に -H tcp://… とTLSのオプションを足したものにします)。

[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --tlsverify --tlscacert=/etc/docker/certs/ca.pem --tlscert=/etc/docker/certs/server-cert.pem --tlskey=/etc/docker/certs/server-key.pem -H tcp://0.0.0.0:2376

保存したら、設定を読み込み直して再起動します。

sudo systemctl daemon-reload
sudo systemctl restart docker.service

# 2376番で待ち受けているか確認する
sudo ss -lntp | grep dockerd

待ち受けの設定は、systemdの設定と /etc/docker/daemon.json の hosts のどちらか一方で行います。両方に書くと競合してDockerが起動しなくなります。

ファイアウォールで接続元を絞る

TLSで認証していても、2376番に接続できる相手は最小限にします。たとえばUFWを使っている場合、管理用PCのアドレスだけを許可します。

sudo ufw allow from 203.0.113.10 to any port 2376 proto tcp

クラウドサーバーでは、クラウド側のファイアウォール(セキュリティグループなど)でも同じように接続元を限定します。

クライアントから接続する

クライアント側に ca.pem・cert.pem・key.pem を置き、接続します。

# オプションで証明書を指定する
docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem \
  -H=$HOST:2376 version

# ~/.docker/ に置けば、環境変数だけで接続できる
mkdir -pv ~/.docker
cp -v ca.pem cert.pem key.pem ~/.docker/
export DOCKER_HOST=tcp://$HOST:2376 DOCKER_TLS_VERIFY=1
docker ps

# curlでAPIを直接呼ぶ
curl https://$HOST:2376/images/json \
  --cert ~/.docker/cert.pem \
  --key ~/.docker/key.pem \
  --cacert ~/.docker/ca.pem

証明書を別の場所に置く場合は、環境変数 DOCKER_CERT_PATH でその場所を指定します。クライアントの鍵(key.pem)を持っている人はホストのroot権限を持つのと同じなので、パスワードと同じように扱ってください。

方法3:TLSなしの待ち受け(ホスト内だけ)

公式ドキュメントでは、TLSなしで待ち受ける例として tcp://127.0.0.1:2375 が示されています。これはホストの中からしか接続できないアドレスで、「HTTPでしかAPIを呼べないツールを同じホストで動かす」といった用途に限られます。別のホストから接続したい場合は、ここを 0.0.0.0 に変えるのではなく、方法1か方法2を使ってください。

別の話:コンテナの中からホスト上のAPIに接続したい

「Dockerでlocalhost以外に接続できない」という悩みには、コンテナの中から、ホストで動いているWeb APIやデータベースに接続したいというケースもあります。コンテナの中の localhost はコンテナ自身を指すため、ホストのサービスには届きません。

host.docker.internal を使う

Docker Desktop(Windows・Mac)では、コンテナの中から host.docker.internal という名前でホストに接続できます。Linuxでは、起動時に host-gateway を指定して同じ名前を追加します。

docker run --rm \
  --add-host host.docker.internal=host-gateway \
  curlimages/curl -s http://host.docker.internal:8000/

Docker Composeでは extra_hosts で指定します。

services:
  app:
    image: myimage
    extra_hosts:
      - "host.docker.internal:host-gateway"

host-gateway はホストの内部向けのIPアドレスに置き換わります。ホスト側のサービスが 127.0.0.1 だけで待ち受けていると、このアドレスからは接続できません。サービスの待ち受けアドレスと、ホストのファイアウォールの設定も確認してください。

network_mode: host を使う(Linux)

Linuxでは、コンテナをホストのネットワークで直接動かす方法もあります。この場合、コンテナの中の localhost はホスト自身になります。

docker run -d --name myapp --network host myimage
services:
  app:
    image: myimage
    network_mode: host

ホストネットワークのコンテナは、ポートの公開(-p)が使えず、コンテナの待ち受けポートがそのままホストのポートになります。ネットワークの分離がなくなるため、必要な場合に限って使います。なお、host ネットワークは docker network connect で既存のコンテナに後から追加することはできません。コンテナを作り直す際に指定します。

ポートを公開するときの注意

コンテナのポートを -p 8080:80 のように公開すると、初期状態ではホストのすべてのネットワークインターフェースで待ち受けます。さらに、Dockerが公開したポートへの通信は、UFWが使うルールより前に転送されるため、UFWで拒否していても外部から接続できてしまいます(公式ドキュメントでも、DockerとUFWは併用時に意図どおり動かないと説明されています)。

ホストの中からだけ使うポートは、待ち受けアドレスを指定して公開します。

# ホスト内(127.0.0.1)だけで公開する
docker run -d -p 127.0.0.1:8080:80 myimage
services:
  app:
    image: myimage
    ports:
      - "127.0.0.1:8080:80"

うまくいかないときの確認

症状 確認すること
docker -H ssh://… が失敗する 同じユーザーで ssh docker-user@host1.example.com docker ps が通るか。通らなければSSHかソケットの権限の問題
設定変更後にDockerが起動しない journalctl -u docker.service でエラーを確認。systemdと daemon.json の両方で待ち受けを指定していないか
TLS接続で証明書のエラーになる 接続に使ったホスト名・IPアドレスがサーバー証明書の subjectAltName に入っているか。クライアント証明書が同じCAで署名されているか
2376番に接続できない ss -lntp で待ち受けているか。ホストとクラウド側のファイアウォールで接続元を許可しているか
コンテナからホストのAPIに届かない host.docker.internal を追加したか。ホストのサービスが 127.0.0.1 以外でも待ち受けているか

まとめ

  • Docker Engine APIへの接続権限は、ホストのroot権限とほぼ同じ。TLSなしの2375番をネットワークに公開しない
  • 別のホストから操作するなら、まずSSH接続(docker -H ssh://…、docker context create)を使う。サーバー側の設定変更は不要
  • SSHが使えない場合は、クライアント証明書によるTLS(2376番)で公開し、ファイアウォールで接続元も絞る
  • コンテナの中からホストのAPIに接続するには host.docker.internal(Linuxでは host-gateway の追加)か、ホストネットワークを使う
  • 公開したポートはUFWの拒否ルールを素通りする。ホスト内だけで使うポートは 127.0.0.1: を付けて公開する

関連記事

お気軽にご相談ください

お見積りへ お問い合わせへ
▲