DockerとKubernetesでx509: certificate signed by unknown authorityを修正する

intermediate🔒 SSL/TLS2026-05-14| Docker 20以降、Kubernetes 1.20以降、Linux(Ubuntu/Debian/RHEL/Alpine)、Goベースのツール

Error Message

x509: certificate signed by unknown authority
#docker#kubernetes#x509#tls#プライベートCA#自己署名

エラーの概要

プライベートレジストリからイメージをプル、Pod内でAPIコール、またはWebhookの設定をしているときに、このエラーが発生します:

x509: certificate signed by unknown authority

より詳しいコンテキストが表示される場合もあります:

Error response from daemon: Get "https://registry.internal/v2/": x509: certificate signed by unknown authority

Post "https://webhook.internal/validate": x509: certificate signed by unknown authority

根本的な原因は常に同じです:GoのTLSスタック(Docker、kubectl、kubelet — すべてGoで書かれています)が証明書チェーンを検証できないのです。証明書に署名したCAがシステムのトラストストアに含まれていないだけです。それだけです。

診断

設定を変更する前に、サーバーが実際に提供している証明書を確認してください:

# サーバーが提供している証明書を確認する
openssl s_client -connect registry.internal:443 -showcerts </dev/null 2>&1 | openssl x509 -noout -issuer -subject

# またはcurlでフルチェーンを確認する
curl -v https://registry.internal/v2/ 2>&1 | grep -A5 "SSL connection"

CN=My Corp CACN=registry.internal のような発行者が見えますか?それはプライベートまたは自己署名証明書です。公的なCAによる署名がないため、自分でそのCAをトラストストアに追加する必要があります。

修正1:DockerデーモンにCA証明書を追加する(プライベートレジストリ)

自己署名またはプライベートCA証明書を使った内部レジストリからプルしていますか?この修正で対応できます。Dockerはシステムストアとは別に、レジストリごとの証明書ディレクトリを持っています。

# レジストリ用のディレクトリを作成する
sudo mkdir -p /etc/docker/certs.d/registry.internal:5000

# CA証明書をコピーする(インフラチームから入手するか、サーバーから抽出する)
sudo cp ca.crt /etc/docker/certs.d/registry.internal:5000/ca.crt

# Dockerを再起動する
sudo systemctl restart docker

ディレクトリ名は正確に一致している必要があります:/etc/docker/certs.d/<host>:<port>/ca.crt。ポート443の場合は、ポート番号を省略できます。

すぐに修正を確認してください:

docker pull registry.internal:5000/myimage:latest

修正2:システムトラストストアにCAを追加する(Linux)

kubelet、kubectlプラグイン、カスタムオペレーター — ホスト上のGoバイナリはDockerの証明書ディレクトリではなく、システムトラストストアを参照します。この修正はそこに適用します。

# Ubuntu/Debian
sudo cp ca.crt /usr/local/share/ca-certificates/my-corp-ca.crt
sudo update-ca-certificates

# RHEL/CentOS/Fedora
sudo cp ca.crt /etc/pki/ca-trust/source/anchors/my-corp-ca.crt
sudo update-ca-trust extract

# 追加されたことを確認する
certutil -L -d /etc/pki/nssdb | grep "My Corp"  # RHEL
# またはcurlでテストする
curl https://registry.internal/v2/

修正3:Kubernetes — containerdにCAを追加する(ノードレベル)

Podがイメージをプルできない、またはkubeletがこのエラーをログに記録していますか?修正は各ノードのcontainerd設定に施します — Kubernetes自体ではありません。

# 各ノードでcontainerdの設定を編集する
sudo mkdir -p /etc/containerd/certs.d/registry.internal:5000

cat <<EOF | sudo tee /etc/containerd/certs.d/registry.internal:5000/hosts.toml
[host."https://registry.internal:5000"]
  ca = "/etc/ssl/certs/my-corp-ca.crt"
EOF

# CA証明書をコピーする
sudo cp ca.crt /etc/ssl/certs/my-corp-ca.crt

# containerdを再起動する
sudo systemctl restart containerd

マネージドクラスター(EKS、GKE、AKS)では、ノードへのSSHが自由にできません。代わりにDaemonSetを使用するか、ノードプールのブートストラップスクリプトにCAを組み込んでください。

修正4:Kubernetes — ConfigMap経由でPodにCAを注入する

エラーが実行中のPod内で発生していますか?アプリが内部HTTPSサービスを呼び出し、GoのHTTPクライアントが証明書を拒否しているのです。CAをボリュームとしてマウントし、環境変数でGoに指定してください。

apiVersion: v1
kind: ConfigMap
metadata:
  name: corp-ca-bundle
data:
  ca.crt: |
    -----BEGIN CERTIFICATE-----
    MIIBxTCCAW+gAwIBAgIJA...
    -----END CERTIFICATE-----
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  template:
    spec:
      volumes:
        - name: ca-bundle
          configMap:
            name: corp-ca-bundle
      containers:
        - name: myapp
          image: myapp:latest
          volumeMounts:
            - name: ca-bundle
              mountPath: /etc/ssl/certs/corp-ca.crt
              subPath: ca.crt
          env:
            - name: SSL_CERT_FILE
              value: /etc/ssl/certs/corp-ca.crt

SSL_CERT_FILE はデフォルトのCAバンドルパスを上書きする、Go固有の環境変数です。別のアプローチとして:デフォルトのバンドルディレクトリに証明書をマウントし、アプリ起動前にinitコンテナで update-ca-certificates を実行する方法もあります。

修正5:Kubernetesアドミッションwebhook

webhookが発火したときに kube-apiserver のログでこのエラーが出ていますか?MutatingWebhookConfiguration または ValidatingWebhookConfigurationcaBundle フィールドが間違っているか、完全に欠落しています。APIサーバーはそのバンドルを使ってwebhookサーバーの証明書を検証します。

# webhookが現在使用しているCAバンドルを確認する
kubectl get mutatingwebhookconfiguration my-webhook -o jsonpath='{.webhooks[0].clientConfig.caBundle}' | base64 -d | openssl x509 -noout -text

# 正しいCAにパッチを当てる
CA_BUNDLE=$(cat ca.crt | base64 -w0)
kubectl patch mutatingwebhookconfiguration my-webhook \
  --type='json' \
  -p="[{'op': 'replace', 'path': '/webhooks/0/clientConfig/caBundle', 'value': '${CA_BUNDLE}'}]"

cert-managerを使っていますか?cert-manager.io/inject-ca-from アノテーションを追加すれば、ローテーションが自動的に処理されます — 手動でパッチを当てる必要はありません。

CA証明書の取得方法

手元にCA証明書ファイルがありませんか?サーバーから直接取得してください。最も安全なアプローチは、フルチェーンを保存してCAバンドルとして使用することです:

# フルチェーンを保存してCAバンドルとして使用する
openssl s_client -connect registry.internal:443 -showcerts </dev/null 2>&1 \
  | sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' > chain.crt

ネット上でよく見かけるチェーン抽出のワンライナー(ネストされた openssl コールと awk のオフセットを使うもの)は壊れやすく、2つ以上の証明書チェーンでは動作しなくなります。スクリプトでは使わないでください。CA証明書はインフラチームに直接依頼しましょう — それが正しい対処法です。

確認方法

# Dockerレジストリ
docker pull registry.internal:5000/myimage:latest

# システムトラストストアでcurlを使う
curl https://registry.internal/v2/

# Pod内から
kubectl run -it --rm debug --image=alpine --restart=Never -- wget -O- https://internal-service.default.svc/health

# GoバイナリはSSL_CERT_FILEを参照する
SSL_CERT_FILE=/path/to/ca.crt your-go-binary

クイックリファレンス

  • Dockerプルが失敗する/etc/docker/certs.d/<registry>/ca.crt
  • kubelet/containerdのプルが失敗する → ノード上のcontainerd hosts.toml
  • Pod内のアプリが失敗する → ConfigMap経由でCAをマウント + SSL_CERT_FILE
  • Webhookの検証が失敗する → webhook設定の caBundle を修正する
  • CLIツールが失敗する → システムトラストストアにCAを追加 + update-ca-certificates

Related Error Notes