ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

Kubernetes: ImagePullBackOff из-за TLS/CA при pull из registry (self-signed, MITM, custom CA)

Если Pod уходит в ImagePullBackOff с x509 unknown authority, проблема почти всегда в TLS-доверии: self-signed реестр, корпоративный MITM или отсутствующий CA на нодах. Ниже — диагностика и правильная настройка CA для containerd и CRI-O без отключения проверки сертификата.
Kubernetes: ImagePullBackOff из-за TLS/CA при pull из registry (self-signed, MITM, custom CA)

ImagePullBackOff в Kubernetes — это не «ошибка Kubernetes», а симптом: kubelet не смог скачать образ из реестра. Когда в событиях и логах появляется x509 certificate signed by unknown authority, почти всегда виноваты TLS и доверие к цепочке сертификатов: самоподписанный сертификат в приватном registry, корпоративный MITM-прокси, подмена сертификата на периметре или просто отсутствующий кастомный CA на узлах.

Ниже — практический разбор, как быстро подтвердить TLS-причину, где именно лежит доверие (kubelet vs runtime), и как корректно добавить CA для containerd и CRI-O. Чиним без отключения проверки сертификата: «временно insecure» обычно превращается в «навсегда insecure».

Как выглядит проблема: типовые симптомы и где их искать

Обычно вы видите:

  • Pod в статусе ImagePullBackOff или ErrImagePull;
  • в событиях (kubectl describe pod) — TLS/x509 ошибки;
  • иногда — редиректы или 401/403, если реестр за прокси или ingress.

Быстрая проверка через Events

kubectl -n NAMESPACE describe pod POD_NAME

Ищите строки вроде:

Failed to pull image "registry.example.local/team/app:1.2.3": rpc error: code = Unknown desc = failed to pull and unpack image ...: failed to resolve reference ...: failed to do request: Head "https://registry.example.local/v2/...": x509: certificate signed by unknown authority

Ключевая мысль: тянет не Kubernetes, а runtime на ноде

Образы скачивает container runtime на узле (containerd или CRI-O). Kubernetes лишь просит kubelet «запусти контейнер из такого-то образа».

Поэтому «починить в namespace» обычно не получится, если проблема именно в доверии к TLS (CA). imagePullSecrets решает аутентификацию в registry, но не доверие к сертификату.

Диагностика: это точно TLS/CA, а не DNS/доступ/учётка?

Перед изменениями на нодах важно убедиться, что вы лечите правильное. Условно делим причины на четыре группы:

  1. Сеть и DNS: реестр не резолвится или не доступен с нод.
  2. Аутентификация: 401/403, неверный секрет.
  3. TLS/CA: unknown authority, bad certificate, name mismatch.
  4. Прокси и политики периметра: MITM подменяет сертификаты, блоки на FW.

1) Проверяем доступность реестра именно с ноды

Зайдите на проблемную ноду (ту, на которую реально планируется Pod) и проверьте резолв и TCP-доступ:

getent hosts registry.example.local
nc -vz registry.example.local 443

Если реестр на нестандартном порту (например 5000) — проверяйте его:

nc -vz registry.example.local 5000

2) Проверяем сертификат и цепочку через openssl

Команда покажет, какой сертификат предъявляет endpoint, и что он отдает в цепочке:

openssl s_client -connect registry.example.local:443 -servername registry.example.local -showcerts < /dev/null

Смотрите:

  • subject и issuer — кто выпустил;
  • Verify return code — причина недоверия;
  • есть ли промежуточные сертификаты (часто реестр/ingress отдает неполную цепочку).

3) Частая ловушка: name mismatch (SAN)

Иногда ошибка не про CA, а про несоответствие имени:

x509: certificate is valid for ..., not registry.example.local

Добавление CA тут не поможет — надо перевыпускать сертификат реестра (добавлять SAN) или исправлять имя, по которому вы обращаетесь к registry.

Если вы параллельно разбираете похожие CA-истории в других сервисах (БД, клиентские сертификаты), пригодится статья про доверие CA и цепочки: TLS/CA для MySQL и PostgreSQL: цепочки, trust store и типовые ошибки.

Пример события Kubernetes с ошибкой x509 certificate signed by unknown authority при pull образа

Понимаем, какой именно CA нужен: self-signed, корпоративный CA или MITM

Сценарий A: приватный registry с self-signed

Self-signed — это когда реестр сам себе «CA». Тогда на ноды нужно положить именно корневой сертификат, которым подписан серверный. В простейшем (и не самом удачном) случае это может быть сам серверный сертификат, если он одновременно CA.

Сценарий B: корпоративный MITM подменяет сертификаты

Если есть outbound HTTPS inspection, узлы вместо сертификата реестра видят сертификат, выпущенный корпоративным корневым CA. В этом случае надо доверять корпоративному CA на нодах. Иначе pull по HTTPS будет ломаться не только к вашему registry, но и к внешним, если политика распространяется шире.

Сценарий C: custom CA + неполная цепочка на стороне registry

Реестр может использовать intermediate CA, но отдавать неполную цепочку. Тогда на части систем «случайно» работает (intermediate уже есть), а container runtime падает. Правильное лечение:

  • на стороне registry/ingress настроить отдачу полного chain (server + intermediate);
  • на нодах установить корневой CA.
FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Где настраивать доверие: системное хранилище vs настройки runtime

Есть два рабочих подхода, и они не взаимоисключающие:

  • Системное доверие (OS trust store): добавляем CA в системное хранилище и обновляем его. Это часто достаточно для CRI-O и для части утилит на узле.
  • Доверие на уровне runtime: у containerd и CRI-O есть механизмы точечной настройки TLS на уровне registry, когда нужно доверять конкретному хосту отдельным CA-файлом.

Практически удобная схема для прода: системный trust store как база плюс точечная настройка runtime там, где это требует ваш Kubernetes-дистрибутив или политика безопасности.

Решение для containerd: custom CA для registry

Containerd умеет отдельные конфиги на конкретный хост реестра. Это удобно: вы не расширяете системное доверие «на всё», а добавляете доверие только для нужного registry.

Шаг 1: положить CA в certs.d

Стандартный путь (часто используется в Kubernetes-сборках):

sudo mkdir -p /etc/containerd/certs.d/registry.example.local

Скопируйте корневой сертификат корпоративного CA или CA вашего registry в файл ca.crt:

sudo cp /path/to/your-ca.crt /etc/containerd/certs.d/registry.example.local/ca.crt

Имя директории должно совпадать с тем, как вы обращаетесь к registry в образах. Если используете порт — учитывайте его:

sudo mkdir -p /etc/containerd/certs.d/registry.example.local:5000

Шаг 2: прописать hosts.toml (если нужно явно указать CA)

Во многих сборках достаточно ca.crt. Если pull всё равно падает — добавьте hosts.toml:

sudo tee /etc/containerd/certs.d/registry.example.local/hosts.toml > /dev/null <<'EOF'
server = "https://registry.example.local"

[host."https://registry.example.local"]
  capabilities = ["pull", "resolve"]
  ca = "/etc/containerd/certs.d/registry.example.local/ca.crt"
EOF

Если реестр нужен и для push — добавьте "push" в capabilities.

Шаг 3: перезапустить containerd и проверить

sudo systemctl restart containerd

Проверять удобнее без Kubernetes, через crictl (на большинстве узлов он есть):

sudo crictl pull registry.example.local/team/app:1.2.3

Если pull прошёл — kubelet тоже сможет.

Типовые ошибки containerd, которые помогают понять, что не так

  • x509: certificate signed by unknown authority — CA не установлен или указан не тот.
  • x509: cannot validate certificate for ... because it doesn't contain any IP SANs — вы обращаетесь по IP, а сертификат выпущен на DNS-имя.
  • certificate is valid for ... — mismatch имени, нужен правильный SAN.

Решение для CRI-O: certs.d и registries.conf

В CRI-O (и в экосистеме containers/image) логика немного другая: обычно управляют настройками через registries.conf и хранилище сертификатов.

Шаг 1: положить CA для конкретного registry

Часто используется путь:

sudo mkdir -p /etc/containers/certs.d/registry.example.local

Далее положить CA как ca.crt:

sudo cp /path/to/your-ca.crt /etc/containers/certs.d/registry.example.local/ca.crt

Если используется порт:

sudo mkdir -p /etc/containers/certs.d/registry.example.local:5000

Шаг 2: убедиться, что registry не помечен как insecure

Проверьте конфиги (в зависимости от дистрибутива пути могут отличаться):

sudo grep -R "registry.example.local" -n /etc/containers/registries.conf /etc/containers/registries.conf.d 2>/dev/null

Если кто-то раньше «починил» через insecure/skip verify — лучше вернуть проверку и правильно поставить CA. Отключение TLS-проверки повышает риск подмены образов на пути доставки.

Шаг 3: рестарт CRI-O и тест pull

sudo systemctl restart crio
sudo crictl pull registry.example.local/team/app:1.2.3
FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Если у вас managed Kubernetes или ноды «не ваши»: как жить

Самая болезненная ситуация: вы не можете менять trust store на нодах. Тогда варианты зависят от платформы и внутренних процессов:

  • использовать реестр с сертификатом от публично доверенного CA;
  • разместить registry за ingress/балансировщиком, который отдаёт публичный TLS;
  • при корпоративном MITM — согласовать исключение для registry (bypass TLS inspection) или организовать доставку корпоративного CA на ноды поддерживаемым способом провайдера.

Идея простая: Kubernetes не «протащит» вам CA внутрь kubelet. Доверие должно появиться на уровне узла или на уровне точки входа в registry.

Частые вопросы и грабли

Почему на ноутбуке/в CI Docker работает, а в Kubernetes — нет?

Потому что ваш laptop/CI может доверять корпоративному CA (MDM/AD/GPO), а ноды Kubernetes — «голые» и не знают этот корень. Или наоборот: Docker Engine настроен на конкретный CA, а containerd/CRI-O в кластере — нет.

Можно ли решить через imagePullSecrets?

Нет. Это про логин/пароль/токен. Ошибка x509 certificate signed by unknown authority — про TLS-доверие, и секреты Kubernetes тут не помогут.

Можно ли «быстро починить», отключив проверку TLS?

Технически — да, но это ломает базовую гарантию, что вы тянете образ именно из доверенного источника. Для продакшена лучше потратить время и настроить CA правильно.

Что делать, если реестр отдаёт неполную цепочку?

Исправить TLS-конфигурацию на стороне registry/ingress так, чтобы отдавался полный chain (серверный + промежуточные). На ноды при этом добавляют корневой CA.

Схема путей доверенных CA для registry в containerd и CRI-O на узле Kubernetes

Мини-чеклист: как довести до стабильного состояния

  1. Подтвердить ошибку TLS через kubectl describe и сообщение об ошибке pull.
  2. С ноды проверить сертификат реестра через openssl s_client (SNI обязателен).
  3. Определить, чей CA нужен: self-signed registry, corporate CA или MITM.
  4. Установить CA на нодах: системно и/или в конфиге runtime (containerd/CRI-O).
  5. Проверить pull через crictl pull.
  6. Пересоздать Pod или дождаться ретраев — убедиться, что ImagePullBackOff ушёл.

Практическая рекомендация для инфраструктуры

Если вы регулярно работаете с приватными registry, проще стандартизировать: либо публично доверенный сертификат на registry, либо единый корпоративный CA, который гарантированно установлен на всех узлах (golden image/automation). Тогда инциденты вида «kubernetes imagepullbackoff tls» превращаются в редкость.

И отдельно: при расследовании всегда уточняйте, какой runtime реально на ваших нодах и где именно у него лежат конфиги. В смешанных кластерах (разные node pools, разные образы ОС) настройки отличаются, и проблема будет «плавающей».

Если ваш registry доступен по доменному имени, не забывайте про базовую гигиену: домен, корректный SAN и предсказуемая цепочка сертификатов. Удобно держать домен под контролем через регистрацию доменов, а для публичных endpoint’ов использовать нормальные SSL-сертификаты.

Поделиться статьей

Вам будет интересно

SSH на Linux без боли: правим sshd_config через systemd drop-in, проверяем ss -tlpn и делаем rollback OpenAI Статья написана AI (GPT 5)

SSH на Linux без боли: правим sshd_config через systemd drop-in, проверяем ss -tlpn и делаем rollback

Пошагово разбираем безопасные изменения SSH-сервера без риска потерять доступ: план со страховочными сессиями, снимок состояния, п ...
PostgreSQL и /tmp: temp_files, disk full и настройка памяти для sort/hash join OpenAI Статья написана AI (GPT 5)

PostgreSQL и /tmp: temp_files, disk full и настройка памяти для sort/hash join

Если PostgreSQL внезапно упирается в «No space left on device» из-за переполнения /tmp, почти всегда виноваты временные файлы запр ...
Linux cron: cronjob, PATH и окружение — почему задания работают в терминале, но падают по расписанию OpenAI Статья написана AI (GPT 5)

Linux cron: cronjob, PATH и окружение — почему задания работают в терминале, но падают по расписанию

Cron часто ломает скрипты из-за минимального окружения: другой PATH, SHELL, HOME, locale и рабочий каталог. Покажу, как задать пер ...