エラーの内容
docker pull registry.internal:5000/myapp:latest を実行すると、以下のエラーが発生します:
Error response from daemon: Get "https://registry.internal:5000/v2/": x509: certificate signed by unknown authority
Dockerは接続を受け入れる前に、信頼済みCAストアに対してTLS証明書を検証します。内部レジストリが自己署名証明書、またはDockerが認識していない企業CAで署名された証明書を使用しているため、Dockerが接続を拒否しています。
根本原因
DockerのTLS検証は厳格です。ブラウザのようなクリックスルー警告はありません。レジストリの証明書が以下のいずれかに該当する場合、Dockerは即座に拒否します:
- 自己署名証明書
- システムトラストストアに含まれていない内部・企業CAによる署名
- チェーン内の中間CA証明書の欠落
修正方法は、証明書を適切に信頼させるか、開発環境で素早く問題を回避するかによって異なります。
修正方法1:レジストリのCA証明書をDockerに追加する(推奨)
これは本番環境における正しいアプローチです。セキュリティを無視するのではなく、特定のレジストリのCAをDockerに信頼させます。
ステップ1:レジストリからCA証明書を取得する
# レジストリサーバーにシェルアクセスできる場合:
scp user@registry.internal:/etc/docker/certs/ca.crt /tmp/registry-ca.crt
# またはopensslで直接取得する:
openssl s_client -connect registry.internal:5000 -showcerts < /dev/null 2>/dev/null \
| openssl x509 -outform PEM > /tmp/registry-ca.crt
ステップ2:DockerにCA証明書をインストールする
# レジストリごとの証明書ディレクトリを作成する
sudo mkdir -p /etc/docker/certs.d/registry.internal:5000
# CA証明書を配置する
sudo cp /tmp/registry-ca.crt /etc/docker/certs.d/registry.internal:5000/ca.crt
Dockerの再起動は不要です。Dockerはpullのたびに/etc/docker/certs.d/をスキャンするため、証明書はすぐに有効になります。
macOS/Windows Docker Desktopの場合
CAをシステムキーチェーンに追加すると、Docker Desktopが自動的に読み込みます:
# macOS
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /tmp/registry-ca.crt
# その後Docker Desktopを再起動する
Windowsの場合は、証明書を「信頼されたルート証明機関」配下のWindows証明書ストアにインポートし、Docker Desktopを再起動してください。
修正方法2:CA証明書をシステムトラストストアに追加する(Linux)
Dockerだけでなくシステム全体で証明書を信頼させたい場合は、OSレベルでインストールします。
# Ubuntu/Debian
sudo cp /tmp/registry-ca.crt /usr/local/share/ca-certificates/registry-internal.crt
sudo update-ca-certificates
# RHEL/CentOS/Fedora
sudo cp /tmp/registry-ca.crt /etc/pki/ca-trust/source/anchors/registry-internal.crt
sudo update-ca-trust
次に、更新されたシステムトラストストアを読み込むためDockerを再起動します:
sudo systemctl restart docker
修正方法3:レジストリをInsecureとして設定する(開発・テスト環境のみ)
ローカルの開発環境で5分以内に動かしたい場合は、特定のレジストリに対してTLS検証をスキップするようDockerに指示します。
/etc/docker/daemon.json を編集(または作成)します:
{
"insecure-registries": ["registry.internal:5000"]
}
Dockerを再起動します:
sudo systemctl restart docker
再度試みます:
docker pull registry.internal:5000/myapp:latest
**警告:**この設定は対象レジストリのTLS検証を完全に無効化します。ネットワークトラフィックを傍受できる攻撃者が悪意のあるイメージを配信できるようになります。本番環境では絶対に使用しないでください。
修正方法4:レジストリ証明書自体を修正する
レジストリを管理している場合、証明書を一度修正する方がクライアントマシンごとに対応するよりも効率的です。Subject Alternative Name(SAN)を含む適切な自己署名証明書を生成してください — 最新のDockerとブラウザはSANを必須としています:
# CAキーと証明書を生成する
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 1826 -key ca.key -out ca.crt \
-subj "/C=US/O=Internal/CN=Internal CA"
# レジストリのキーとCSRを生成する
openssl genrsa -out registry.key 4096
openssl req -new -key registry.key -out registry.csr \
-subj "/CN=registry.internal"
# SAN拡張を付けて署名する
cat > san.ext <<EOF
[SAN]
subjectAltName=DNS:registry.internal,IP:192.168.1.100
EOF
openssl x509 -req -days 730 -in registry.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out registry.crt -extfile san.ext -extensions SAN
レジストリがregistry.crtとregistry.keyを使用するよう設定し、修正方法1を通じてca.crtをすべてのDockerクライアントに配布してください。
修正の確認
修正を適用した後、完了と判断する前にTLSが正常に機能していることを確認します:
# TLSハンドシェイクを直接テストする
curl -v --cacert /tmp/registry-ca.crt https://registry.internal:5000/v2/
# 期待される結果:HTTP 200 と空のJSONボディ: {}
# Dockerのpullをエンドツーエンドでテストする
docker pull registry.internal:5000/myapp:latest
# レジストリが提供している証明書を確認する
openssl s_client -connect registry.internal:5000 -showcerts
docker pullがx509エラーなしに完了すれば、修正は成功です。
修正方法1を適用しても解決しない場合
- 誤った証明書をコピーした:サーバー証明書ではなく、ルートCA証明書が必要です。
openssl x509 -in /etc/docker/certs.d/registry.internal:5000/ca.crt -text -nooutで確認し、Basic ConstraintsにCA:TRUEが含まれているかチェックしてください。 - ホスト名の不一致:
docker pullコマンドのホスト名が、証明書のCNまたはSANと完全に一致している必要があります。openssl x509 -in registry.crt -text | grep -A1 "Subject Alternative"で確認してください。 - 不完全な証明書チェーン:中間CAがある場合は、完全なチェーンを1つのファイルに結合してください:
cat intermediate.crt root-ca.crt > /etc/docker/certs.d/registry.internal:5000/ca.crt - Docker Desktopのキャッシュ:macOSまたはWindowsでは、証明書を追加した後にデーモンだけでなくDocker Desktop全体を完全に再起動する必要があります。
予防策
- マシンのプロビジョニング(Ansible、Chef、またはスタートアップスクリプト)の一部として内部CA証明書を配布してください。新しいマシンが最初からレジストリを信頼した状態で利用できるようになります。
- CA証明書の場所をチームのオンボーディングドキュメントに記載してください。知っていれば5分で解決できますが、初日に何を探すべきかを知らなければ時間がかかります。
- パブリックDNS名でアクセスできるレジストリの場合、Let's Encryptを使用すると自己署名証明書の問題を完全に解消できます — 無料で、自動更新され、あらゆる環境で信頼されます。

