Це завжди трапляється в найгірший момент: ви на виклику деплою, збірка зелена, а пуш образу падає через те, що docker login не може автентифікуватися. Хтось каже «вчора працювало» з упевненістю людини, яка ніколи не зустрічалася з кешами, кейчейнами або корпоративними проксі.
Збої входу в реєстр рідко бувають «просто облікові дані». Зазвичай це невелика громадянська війна між Docker, сховищем облікових даних ОС, схемою автентифікації реєстру, довірою TLS і тим, що корпоративна мережа вирішила пофанитити цього кварталу. Давайте швидко виграємо цю війну.
Швидка діагностика (робіть це перш за все)
Це порядок триажу, який економить час. Оптимізовано для того, щоб відновити роботу CI-раннера або ноутбука з можливістю пушити образи без здогадок.
1) Підтвердіть, що саме впало: автентифікація, TLS чи мережа
- Якщо бачите 401 Unauthorized або denied: requested access to the resource is denied: автентифікація/обсяги/права.
- Якщо бачите x509, certificate signed by unknown authority або tls: handshake failure: ланцюжок довіри / MITM / SNI.
- Якщо бачите timeout, no route, proxyconnect або EOF: мережа/проксі/DNS.
- Якщо бачите error storing credentials, credentials store is not initialized або exec: docker-credential-*: помічник облікових даних/кейчейн.
2) Визначте, до якої кінцевої точки реєстру ви звертаєтеся
Люди часто вводять неправильне імʼя хоста (або пропускають його), тихо потрапляючи на Docker Hub замість приватного реєстру. Ваш термінал не буде вас судити; він просто впаде.
3) Перегляньте ефективну конфігурацію облікових даних Docker
Почніть з ~/.docker/config.json. Якщо там є credsStore або credHelpers, швидше за все, ваш вхід ламається в помічнику, а не на боці реєстру.
4) Увімкніть клієнтський debug для одного запуску
Використайте DOCKER_CLI_DEBUG=1 або логи демона Docker, якщо ви на хості, де працює демон. Вивід відлагодження прояснює, який помічник викликався і яка кінцева точка використовувалася.
5) Відтворіть запит простим пінгом реєстру
Стріляйте по /v2/ за допомогою curl. Правильний, доступний реєстр відповідає 200 або 401. Неправильний шлях, блокування проксі або проблема TLS виявляться одразу.
Як насправді працює автентифікація в Docker-реєстрі (важливі частини)
docker login не «логінить в Docker». Воно веде переговори з конкретною кінцевою точкою реєстру, а потім зберігає облікові дані (або токен) для наступних HTTP-запитів на push/pull. Під капотом це просто HTTP з кількома конвенціями.
API реєстру та перевірка /v2/
Більшість реєстрів реалізують Docker Distribution (Registry HTTP API V2). Docker пробує https://REGISTRY/v2/, щоб перевірити, чи живий реєстр. Відповідь зазвичай буває:
- 200 OK: реєстр дозволяє анонімний доступ (рідко для приватних).
- 401 Unauthorized з заголовком
WWW-Authenticate: реєстр вимагає автентифікації; Docker слідує за вказаною схемою. - 404 або HTML: ви не звертаєтеся до кінцевої точки реєстру (поширено за балансувальниками навантаження або маршрутизацією за шляхом).
Потоки Bearer token vs Basic auth
Багато реєстрів використовують поток Bearer token: реєстр відповідає 401 з заголовком WWW-Authenticate: Bearer ..., що вказує на сервіс токенів. Docker тоді запитує токен зі сферою, що відповідає репозиторію та операції (pull, push), і потім відправляє його як Authorization: Bearer.
Інші використовують Basic auth напряму (особливо старіші або простіші розгортання). Docker відсилає Authorization: Basic використовуючи облікові дані з вашого конфігу/помічника.
Обсяги (scopes) — чому «вхід пройшов» може ще й провалюватися
Успішний вхід просто означає «я можу автентифікуватися, щоб отримати якийсь токен/облікові дані та зберегти їх». Помилки push/pull часто повʼязані з авторизацією: токен існує, але не має потрібного обсягу. Тоді ви бачите «requested access to the resource is denied», хоча ви «увійшли».
Цитата варта запам’ятовування
Парафразована ідея (John Allspaw): Надійність виникає з поліпшення системи, а не з пошуків винних після збою.
Цікавинки та коротка історія (корисне, не тривіальне)
- Docker Hub не завжди був «дефолтним» в умах людей. Ранні користувачі Docker часто запускали приватні реєстри, оскільки контролі організацій в Hub були скромніші, ніж сьогодні.
- Registry HTTP API V2 замінив V1 частково для підтримки кращої безпеки і content-addressable образів; V1 фактично мертвий у сучасних інструментах.
- Помічники облікових даних стали стандартом, бо зберігати base64-кодовані логіни у відкритому JSON було (і досі є) поганою практикою для аудитів.
- Інтеграція з macOS Keychain стала очікуваною; Docker Desktop посилив роботу з нативним сховищем ОС рано, що сформувало робочі процеси.
- Зберігання облікових даних у Windows змінювалося з часом разом із розвитком Docker Desktop; старіші налаштування були крихкішими.
- Аутентифікація на основі токенів зросла у міру інтеграції реєстрів з SSO/IdP; «пароль» часто став «персональним токеном доступу» без усвідомлення користувачів.
- Content Trust (Notary v1) намагався зробити підписування образів масовим; прийняття відстало, але це змусило організації звертати увагу на походження образів.
- Корпоративні проксі та TLS-інспекція залишаються основною причиною «вдома працює, в офісі падає» для входів у реєстри.
Жарт #1: Docker-автентифікація не складна. Вона просто має пристрасну слабкість до падіння під час демонстрацій.
Збереження облікових даних: помічники, кейчейни та config.json
Docker зберігає облікові дані одним із трьох способів:
- Помічник облікових даних (переважно): системний кейчейн, pass, secretservice, wincred тощо.
- Мапінг помічників за реєстром (
credHelpers): різні помічники для різних реєстрів. - Вбудована авторизація (
authsуconfig.json): base64username:password. Працює. Також працює для зловмисників, які можуть прочитати ваш домашній каталог.
Що Docker фактично робить, коли ви «зберігаєте креденшіали»
CLI Docker викликає зовнішній бінар з іменем на кшталт docker-credential-osxkeychain або docker-credential-pass. Якщо цей бінар відсутній, зламаний або не може доступитися до кейчейну, вхід завершиться невдачею, навіть якщо реєстр прийняв би ваші дані.
Помилки кейчейну виглядають як помилки реєстру, якщо не читати текст помилки
Помилки на кшталт error storing credentials зазвичай означають: Docker звернувся до помічника, і помічник відмовився. Поширені причини включають:
- Бінар помічника не встановлено або він не в
PATH. - Кейчейн заблокований (macOS), відсутній session bus (Linux secretservice), або недоступний GPG-агент (pass).
- Права в безголовому CI, де немає GUI keyring.
- Зламаний
config.jsonчерез зайві коми або ручні редагування.
Опінійовані поради
- На ноутбуках: використовуйте нативні помічники ОС. Не зберігайте відкриті креденшіали в
config.json. - У CI: уникайте нативних кейчейнів ОС. Використовуйте
--password-stdinз ефермерними токенами або налаштуйте помічник, який працює безголово. - На серверах: обирайте відтворюваність замість «на відчуття безпечно». Заблокований keyring о 3 ранку — це не безпека; це баг надійності.
Практичні завдання: команди, значення виводів та рішення (12+)
Це завдання рівня runbook. Кожне містить команду, приклад виводу, що це означає, і яке рішення прийняти далі.
Завдання 1: Підтвердіть версію Docker CLI і контекст
cr0x@server:~$ docker version
Client: Docker Engine - Community
Version: 26.1.4
API version: 1.45
Go version: go1.22.5
Git commit: 5650f9b
Built: Wed Sep 18 10:21:00 2025
OS/Arch: linux/amd64
Server: Docker Engine - Community
Engine:
Version: 26.1.4
API version: 1.45 (minimum version 1.24)
Go version: go1.22.5
Git commit: 3d2c1f3
Built: Wed Sep 18 10:21:00 2025
OS/Arch: linux/amd64
Значення: Підтверджує, що клієнт/сервер присутні, і ви не використовуєте випадково віддалений демон через магію контекстів.
Рішення: Якщо клієнт є, але демона немає (поширено для проблем з Docker Desktop або віддалених шелів), спочатку виправте демон. Якщо версії давні, очікуйте невідповідностей в автентифікації/TLS.
Завдання 2: Перевірте, у який реєстр ви фактично логінитеся
cr0x@server:~$ docker login
Authenticating with existing credentials...
Login Succeeded
Значення: Без хоста це націлене на Docker Hub. Це «успіх» може бути неактуальним, якщо вам потрібен registry.corp.local.
Рішення: Завжди вказуйте реєстр: docker login registry.corp.local. Якщо колега каже «login succeeded», запитайте «до якого реєстру?»
Завдання 3: Перегляньте конфігурацію помічника облікових даних Docker
cr0x@server:~$ cat ~/.docker/config.json
{
"auths": {
"registry.corp.local": {}
},
"credsStore": "secretservice"
}
Значення: Docker викличе docker-credential-secretservice для збереження та отримання креденшіалів.
Рішення: Якщо ви в CI або безголовому сервері без session bus, переключіться на робочий помічник або використайте тимчасовий каталог конфігурації.
Завдання 4: Підтвердіть існування бінаря помічника
cr0x@server:~$ command -v docker-credential-secretservice
/usr/bin/docker-credential-secretservice
Значення: Бінар помічника встановлено і в PATH.
Рішення: Якщо відсутній — встановіть або змініть credsStore. Якщо є — переходьте до перевірки доступу до бекенду.
Завдання 5: Протестуйте помічник напряму (діагностика доступу до keyring)
cr0x@server:~$ printf 'https://registry.corp.local\n' | docker-credential-secretservice get
error getting credentials - err: exit status 1, out: "Cannot autolaunch D-Bus without X11 $DISPLAY"
Значення: Помічник залежить від десктопної сесії (D-Bus + secret service). Безголове середовище не може цього забезпечити.
Рішення: У CI/безголових середовищ не використовуйте secretservice. Використайте інший помічник або пропустіть помічники, використовуючи ізольований DOCKER_CONFIG з вбудованою авторизацією (тільки якщо ви контролюєте дозволи файлів) та ефермерні токени.
Завдання 6: Запустіть login з явним користувачем і паролем через stdin (щоб уникнути історії шелу)
cr0x@server:~$ printf '%s' "$REGISTRY_TOKEN" | docker login registry.corp.local -u ci-bot --password-stdin
Login Succeeded
Значення: Аутентифікація пройшла, і Docker спробував зберегти креденшіали.
Рішення: Якщо це падає з «error storing credentials», проблема у помічнику/сховищі. Якщо падає з 401 — токен/права неправильні.
Завдання 7: Увімкніть Docker CLI debug для однієї спроби входу
cr0x@server:~$ DOCKER_CLI_DEBUG=1 docker login registry.corp.local -u alice --password-stdin <<'EOF'
not-a-real-password
EOF
DEBU[0000] command: docker login registry.corp.local -u alice --password-stdin
DEBU[0000] credentials store: secretservice
Error response from daemon: login attempt to registry.corp.local failed with status: 401 Unauthorized
Значення: Підтверджує, який store облікових даних активний і що реєстр повернув 401 (не мережна помилка).
Рішення: Виправляйте облікові дані/обсяги/SSO/токен; не витрачайте час на DNS/TLS поки це не підтверджено.
Завдання 8: Перевірте доступність реєстру та схему автентифікації за допомогою curl
cr0x@server:~$ curl -skI https://registry.corp.local/v2/
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
Www-Authenticate: Bearer realm="https://auth.corp.local/token",service="registry.corp.local"
Docker-Distribution-Api-Version: registry/2.0
Значення: Реєстр досяжний, TLS-хендшейк пройшов (бо ми використали -k щоб ігнорувати довіру), і використовується Bearer token auth.
Рішення: Якщо це таймаут — це мережа/проксі/DNS. Якщо повертає HTML або 404 — ви потрапляєте на неправильну кінцеву точку/маршрутизацію. Якщо без -k помилка — у вас проблема з довірою сертифікатів.
Завдання 9: Перезапустіть curl без -k, щоб спіймати реальні TLS-помилки
cr0x@server:~$ curl -sI https://registry.corp.local/v2/
curl: (60) SSL certificate problem: unable to get local issuer certificate
Значення: Ваш системний store довіри не довіряє ланцюжку сертифікатів реєстру (або проксі підміняє сертифікат).
Рішення: Встановіть правильний сертифікат ЦС у довіру демона Docker та/або в системний store. Не «виправляйте» це роблячи реєстр insecure, якщо вам не подобаються післяінцидентні розбори.
Завдання 10: Перевірте логи демона Docker на підказки щодо автентифікації і TLS (systemd Linux)
cr0x@server:~$ sudo journalctl -u docker --since "10 minutes ago" --no-pager | tail -n 20
time="2026-01-02T09:12:44.112334512Z" level=error msg="Handler for POST /v1.45/auth returned error: login attempt to registry.corp.local failed with status: 401 Unauthorized"
time="2026-01-02T09:12:44.112612981Z" level=info msg="attempting next endpoint for registry.corp.local"
Значення: Підтверджує, що демон бачив помилку входу й це статус автентифікації (не TCP/TLS).
Рішення: Якщо логи демона згадують x509 або handshake errors — йдіть прямо до сертифікатів. Якщо згадують proxyconnect — переходьте до конфігу проксі.
Завдання 11: Перевірте поточні налаштування проксі, які Docker може успадкувати
cr0x@server:~$ env | egrep -i 'http_proxy|https_proxy|no_proxy'
HTTPS_PROXY=http://proxy.corp.local:3128
NO_PROXY=localhost,127.0.0.1,.corp.local
Значення: Docker CLI використовує ваше оточення. Демон Docker може мати власний проксі через systemd drop-ins.
Рішення: Якщо реєстр не в NO_PROXY, ваш проксі може перехоплювати TLS або блокувати редиректи сервісу токенів. Додайте реєстр і кінцеві точки автентифікації в NO_PROXY для прямого зʼєднання, де це доречно.
Завдання 12: Перевірте конфігурацію проксі демона (systemd drop-in)
cr0x@server:~$ systemctl show --property=Environment docker
Environment=HTTP_PROXY=http://proxy.corp.local:3128 HTTPS_PROXY=http://proxy.corp.local:3128 NO_PROXY=localhost,127.0.0.1
Значення: Сам демон використовує проксі, і NO_PROXY занадто вузький.
Рішення: Розширте NO_PROXY, додавши хости реєстру та внутрішні домени; перезапустіть Docker. Це вирішує клас багів «pull працює в шелі, але падає в демоні».
Завдання 13: Перевірте, які облікові дані Docker вважає наявними (не виводячи секрети)
cr0x@server:~$ docker system info 2>/dev/null | sed -n '1,40p'
Client:
Version: 26.1.4
Context: default
Debug Mode: false
Server:
Containers: 12
Running: 2
Paused: 0
Stopped: 10
Server Version: 26.1.4
Storage Driver: overlay2
Значення: Не прямо про креденшіали, але підтверджує, який демон і драйвер зберігання ви використовуєте (корисно, коли «login» відбувається на іншому хості через контекст).
Рішення: Якщо контекст не default, переконайтеся, що ви автентифікуєтеся до того самого демона, який буде виконувати pull/push.
Завдання 14: Примусьте Docker використати чистий каталог конфігурації (ізолювати від зламаних помічників)
cr0x@server:~$ mkdir -p /tmp/docker-clean
cr0x@server:~$ DOCKER_CONFIG=/tmp/docker-clean docker login registry.corp.local -u alice --password-stdin <<'EOF'
correcthorsebatterystaple
EOF
WARNING! Your credentials are stored unencrypted in '/tmp/docker-clean/config.json'.
Login Succeeded
Значення: Це обходить вашу звичайну конфігурацію помічників і зберігає вбудовані креденшіали. Docker попереджає, бо так і має бути.
Рішення: Якщо це працює, первісна проблема — конфігурація помічника/кейчейну. Виправте її коректно; не робіть з /tmp постійного рішення, якщо це не CI і ви не контролюєте життєвий цикл.
Завдання 15: Перевірте авторизацію для репозиторію, спробувавши діяти в межах скоупу
cr0x@server:~$ docker pull registry.corp.local/platform/base:latest
latest: Pulling from platform/base
Digest: sha256:7b2c6f25d4f7a2f2c1a0a1a3b6d5f1b9e20f6d2b3b0b7a1a2c3d4e5f6a7b8c9d
Status: Image is up to date for registry.corp.local/platform/base:latest
Значення: Аутентифікація працює для pull цього репозиторію. Push усе ще може впасти, якщо у вас немає дозволу на push.
Рішення: Якщо pull вдається, а push падає, не шукайте новий логін. Виправляйте права в реєстрі/IdP/проекті.
Завдання 16: Відтворіть відмову при пуші (інший клас помилок ніж «login failed»)
cr0x@server:~$ docker tag alpine:3.20 registry.corp.local/platform/base:test
cr0x@server:~$ docker push registry.corp.local/platform/base:test
The push refers to repository [registry.corp.local/platform/base]
denied: requested access to the resource is denied
Значення: Ви автентифіковані, але не авторизовані на пуш у цей репозиторій (або імʼя репозиторію неправильне, або токен не має потрібного скоупу).
Рішення: Перевірте RBAC для цього репозиторію/проекту; підтвердіть, що токен дозволяє пуш, а не лише читання.
TLS, сертифікати ЦС та сімейство «x509: unknown authority»
Проблеми з TLS — найпоширеніша причина скарг «чому це працює лише в домашній мережі?». Зазвичай це одна з трьох причин:
- Ваш реєстр використовує внутрішній ЦС, і ваша машина йому не довіряє.
- Проксі/інспекційний бокс завершує TLS і підписує заново корпоративним ЦС, який ви не встановили (або Docker його не бачить).
- Ви потрапляєте на неправильне імʼя (невідповідність SNI) через split-horizon DNS або налаштування балансувальника навантаження.
Звідки Docker читає довіру (і де не читає)
На Linux демон Docker має власну поведінку довіри додатково до системного сховища. Для кастомних реєстрів Docker підтримує пер-регістр CA-бандли під /etc/docker/certs.d/REGISTRY_HOST[:PORT]/ca.crt.
На Docker Desktop (macOS/Windows) є додатковий шар: Linux-VM, що запускає демон, має власне сховище довіри. Іноді імпорт сертифікатів у системний кейчейн недостатній, якщо Docker Desktop їх не синхронізує.
Не нормалізуйте «insecure registries»
Так, можна налаштувати insecure-registries і забути про проблему. Ні, не слід цього робити, якщо ви не в одноразовій лабораторії. Це відключає перевірку TLS і перетворює проблему входу в проблему ланцюжка постачання.
Жарт #2: Позначити реєстр як «небезпечний» — це як вимкнути пожежні датчики тому, що вони голосно сигналять.
Проксі, MITM-бокси та режимі збоїв мережі
Якщо ви за корпоративним проксі, вважайте, що він частина проблеми, поки не доведено протилежне. Потоки автентифікації реєстрів часто включають редиректи між реєстром і сервісом токенів. Проксі відомі тим, що ламають саме такого роду багатоступеневі взаємодії.
Класичні симптоми проксі
- Повторні запити логіна, бо сервіс токенів заблокований і Docker продовжує повторювати.
- EOF посеред входу, бо проксі закриває бездіяльні зʼєднання або блокує chunked transfer encoding.
- Працює з curl, але не з Docker, бо демон Docker використовує інші змінні проксі, ніж ваш shell.
- «x509» лише в офісі, бо на шляху проксі увімкнули TLS-інспекцію.
NO_PROXY — це не порада
Додайте хост вашого реєстру та сервіс токенів у NO_PROXY, коли вони доступні напряму. Включіть і домен реєстру, і будь-які внутрішні суфікси, що використовує автентифікаційна сфера. Коли сервіс токенів на іншому хості, пропуск його з NO_PROXY — поширена причина збоїв.
Split-horizon DNS і помилки «неправильна кінцева точка»
Приватні реєстри часто мають внутрішню і зовнішню DNS. Коли ваш ноутбук перемикає мережі (VPN вкл/вимк), те саме імʼя може вказувати на різні балансувальники з різними TLS-сертифікатами й автентифікаційними доменами. Docker кешує облікові дані за імʼям хоста, а не за «середовищем». Ось як ви можете виявитися залогіненими в невірний «той самий» реєстр.
Токени, 2FA, SSO та пастка «пароль вірний»
Сучасні реєстри часто за спиною мають SSO, вимагають 2FA або вимагають персональних токенів доступу замість паролів. Люди це помічають; автоматизація часто — ні.
Коли паролі перестають працювати непомітно
Багато систем дозволяють UI-вхід через SSO, але вимагають токенів для API-доступу. Docker використовує API реєстру. Якщо ваша організація змінила провайдера автентифікації, старий пароль може ще працювати в браузері і падати в docker login. Це не тому, що Docker примхливий; ви використовуєте невірний тип облікових даних.
Обсяг токена важливіший ніж «валідний токен»
Токен може бути валідним, але не мати дозволу пушити до platform/base. Це виглядає як проблема Docker, поки ви не усвідомите, що реєстр застосовує права на рівні репозиторію. Виправляйте це в RBAC, а не безкінечно генеруйте нові токени.
Device code flows і CLI реєстрів
Деякі реєстри надають власний CLI, який виконує SSO device-code автентифікацію і потім налаштовує креденшіали Docker. Це може бути зручно на ноутбуках. У CI уникайте інтерактивних потоків. Використовуйте сервісні акаунти або робот-токени, призначені для автоматизації.
Три корпоративні міні-історії з поля бою
Інцидент №1: Неправильне припущення (реєстр виявився не реєстром)
Середня компанія мігрувала з самохостованого реєстру на керований. Вони зберегли те саме імʼя хоста, бо «змінювати хости ламає робочі процеси розробників», і всі кивнули, ніби це закон фізики.
Через два тижні CI почав падати. Розробники все ще могли docker login успішно. Помилка зʼявлялася при docker push як «requested access to the resource is denied». Люди міняли паролі, регенерували токени і пробували різні акаунти. Нічого не допомагало.
Причина була прозаїчна: split-horizon DNS плюс VPN. По VPN registry.corp.local розвʼязувався на новий керований реєстр. Поза VPN (або з певних раннерів) він вказував на старий внутрішній балансувальник, який обслуговував legacy-реєстр з іншою автентифікацією. «Login succeeded», бо креденшіали були для імені хоста; push падав, бо realm сервісу токенів відрізнявся і збережені креденшіали не підходили.
Виправлення не було героїчним. Вони створили два явних імені хостів: внутрішнє і зовнішнє, і примусили CI використовувати одне послідовно. Також додали перевірку, яка виконує curl /v2/ і валідовує, що WWW-Authenticate realm відповідає очікуванням. Урок: повторне використання хостнеймів під час міграцій виглядає охайно, доки не перетвориться на машину для розподіленої неоднозначності.
Інцидент №2: Оптимізація, що відкотилася (помічник скрізь)
Інша організація стандартизувала робочі станції розробників за допомогою профілю управління конфігурацією. Хтось вирішив, що кожна Linux-машина повинна використовувати secretservice для Docker-креденшіалів, бо «так безпечніше, ніж plaintext». Правда, але не для всіх контекстів.
На десктопах це працювало ідеально. Потім профіль застосували до билд-агентів, що працювали headless Linux. На наступний день релізу збірки образів почали падати на кроці логіна з помилками про D-Bus і відсутні дисплеї. Реєстр був у порядку. Креденшіали були у порядку. Помічник — не в порядку.
Реакція на інцидент була передбачувано хаотичною. Люди перезапускали Docker, міняли паролі і звинувачували вендора реєстру. Зрештою хтось запустив помічник напряму і отримав ключове повідомлення: він не міг autolaunch D-Bus у безголовій сесії.
Виправлення — перестати трактувати «безпечне сховище» як універсальну функцію і почати трактувати його як системну залежність. Вони перевели CI на --password-stdin з короткоживучими робот-токенами і задали DOCKER_CONFIG у робочий каталог, який знищується після джоба. Безпека покращилась, надійність покращилась, і ніхто не мусив SSH-вати в раннери, щоб розблокувати уявні кейчейни.
Інцидент №3: Нудна правильна практика, що врятувала день (окрема перевірка автентифікації)
Велике підприємство мало політику: кожен етап пайплайну, що пушить образи, спочатку перевіряє доступність реєстру і realm автентифікації перед початком збірки. Це було не захопливо. Це дратувало людей, бо додавало секунди до джобів.
Якось кварталі команда мережі повернула проксі-конфіг. Більшість вихідних HTTPS-потоків почали проходити через новий шар TLS-інспекції. Розробники помічали переривчасті збої: деякі pull-и працювали, деякі логіни падали з x509 помилками, і команда реєстру отримувала оповіщення — бо, звісно ж, вони отримували.
Пайплайни з нудною перевіркою перед польотом падали швидко з чіткою помилкою: curl до /v2/ повертав ланцюжок сертифікатів, підписаний іншим емітером, ніж очікували. Фейли зупинялися до початку будь-якої збірки. Це значно скоротило blast radius: ніяких напівзбудованих артефактів, ніякої марної роботи раннерів і ніякого «він падає після 20 хвилин» драматизму.
Оскільки preflight виводив видавця сертифіката і WWW-Authenticate realm, команда реєстру могла швидко довести, що реєстр не змінювався. Виправлення — роздати корпоративний CA у довіру демона Docker на уражених раннерах і гарантувати, що NO_PROXY обходить інспекцію для внутрішніх реєстрів. Нудна практика. Правильна практика. Врятувала день.
Типові помилки: симптом → причина → виправлення
1) «Login Succeeded», але push каже denied
Симптом: docker login успішний; docker push повертає denied: requested access to the resource is denied.
Причина: Проблема авторизації (немає скоупу push), неправильний шлях репозиторію або токен має лише права на pull.
Виправлення: Перевірте точну назву репозиторію і namespace проекту. Підтвердіть, що RBAC дає право push. Використайте токен для CI з потрібними скоупами.
2) «error storing credentials» під час входу
Симптом: Вхід падає після введення правильних даних; помилка посилається на збереження облікових даних.
Причина: Помічник відсутній/зламаний, кейчейн заблокований, безголове середовище не має secret service або помічник не може запросити ввод.
Виправлення: Встановіть помічник або видаліть/змініть credsStore для цього середовища. У CI використовуйте --password-stdin і чистий DOCKER_CONFIG.
3) «x509: certificate signed by unknown authority»
Симптом: Docker повідомляє про x509 або unknown authority.
Причина: Внутрішній ЦС не довірений демону; TLS-інспекція; неправильне імʼя хоста/SNI mismatch.
Виправлення: Встановіть правильний CA сертифікат у довіру Docker (/etc/docker/certs.d) та/або VM Docker Desktop. Виправте DNS або SAN сертифіката. Уникайте insecure registries.
4) Повторювані підказки пароля або «unauthorized» після вірного пароля
Симптом: Docker постійно просить креденшіали або відкидає відомі вірні дані.
Причина: Реєстр використовує токени/PAT; увімкнений 2FA; SSO вимагає токен, а не пароль.
Виправлення: Використовуйте PAT/робот-токен. Підтвердіть realm сервісу токенів і потрібні скоупи. Не застосовуйте інтерактивні SSO-флоди в CI.
5) Працює на ноутбуку, падає на CI-раннері
Симптом: Розробники можуть логінитись і пушити; CI не може.
Причина: CI має інший проксі, DNS, або немає доступу до помічника кейчейну; інший NO_PROXY.
Виправлення: Зробіть мережевий шлях раннера явним. Налаштуйте проксі демона правильно. Використайте сумісний із безголовим режимом підхід до облікових даних.
6) Pull працює, login падає (або навпаки)
Симптом: Анонімні pull-и успішні, але login падає, або вхід успішний, а pull-и з конкретних репозиторіїв не працюють.
Причина: Реєстр дозволяє анонімний доступ до частини контенту; або сервіс токенів має проблеми; або права на рівні репозиторію відрізняються.
Виправлення: Тестуйте з curl -I /v2/ і конкретним pull. Узгодьте політику доступу; виправте сервіс автентифікації.
7) Docker Desktop: проблеми входу після оновлення ОС
Симптом: Після оновлення macOS/Windows Docker login видає помилки, що посилаються на keychain/credential manager.
Причина: Запити кейчейну блокуються, корупція сховища чи невідповідність бінаря помічника.
Виправлення: Повторно авторизуйте Docker Desktop для доступу до кейчейну, видаліть застарілі записи або скиньте креденшіали в налаштуваннях Docker Desktop і залогіньтесь заново.
8) «no basic auth credentials» під час pull
Симптом: Pull падає з no basic auth credentials.
Причина: Креденшіали не знайдені для точного імені реєстру (включно з портом), або конфіг збережено під іншим ключем.
Виправлення: Увійдіть до точного hostname:port, що використовується у посиланнях на образи. Перевірте, що ключі в ~/.docker/config.json співпадають.
Чеклісти / покроковий план
Чекліст A: Виправити ноутбук розробника (macOS/Windows/Linux десктоп)
- Підтвердіть точне імʼя хоста реєстру, що використовується в тегах образів (включно з портом, якщо є).
- Запустіть
curl -I https://REGISTRY/v2/, щоб підтвердити доступність і побачити схему автентифікації. - Якщо TLS падає — встановіть правильний CA і переконайтеся, що Docker Desktop/демон йому довіряє.
- Перегляньте
~/.docker/config.jsonна предметcredsStore/credHelpers. - Переконайтеся, що помічник існує і має доступ до системного кейчейну/менеджера облікових даних.
- Увійдіть, використовуючи
--password-stdin(навіть на ноутбуках), щоб уникнути потрапляння в історію шелу. - Протестуйте
docker pullіdocker pushна валідаційному репозиторії, щоб відокремити автентифікацію від прав.
Чекліст B: Виправити CI-раннери (headless Linux)
- Не використовуйте десктопні помічники облікових даних, якщо ви не впевнені, що вони працюють безголово.
- Використовуйте короткоживучі токени (робот/PAT), інжектовані як секрети.
- Використайте
docker login ... --password-stdinзDOCKER_CONFIG, встановленим у каталог, що належить робочі задачі. - Перевірте проксі-налаштування демона через
systemctl show --property=Environment docker. - Додайте в
NO_PROXYхости реєстру і сервісу токенів. - Встановіть внутрішній CA в шлях довіри демона для реєстру.
- Додайте preflight крок: curl
/v2/і швидко падайте при невідповідному realm або видавцю сертифікатів.
Чекліст C: Інтеграція самохостованого реєстру
- Переконайтеся, що
/v2/повертає правильнийDocker-Distribution-Api-Versionі заголовки автентифікації. - Переконайтеся, що realm / сервіс токенів доступні з клієнтів і раннерів.
- Перевірте, що SAN сертифіката TLS включають імʼя хоста, яким користуються клієнти.
- Переконайтеся, що мапінг прав репозиторіїв відповідає командам/сервісним акаунтам.
- Проведіть тест маршрутизації балансувальника, щоб він не повертав HTML/404 для
/v2/. - Тестуйте з різних мереж (VPN вкл/викл), щоб зловити split-horizon невідповідності.
ЧаПи
Чому docker login вдається, а docker push падає?
Логін підтверджує автентифікацію. Push вимагає авторизацію для конкретного репозиторію і скоупу push. Виправляйте RBAC або скоупи токена, а не команду логіна.
У чому різниця між credsStore та credHelpers?
credsStore встановлює помічника за замовчуванням для всіх реєстрів. credHelpers мапить конкретні реєстри на конкретні помічники. Використовуйте credHelpers, коли один реєстр потребує особливої обробки.
Чому я отримую «error storing credentials» у CI?
Ймовірно ви налаштували системний кейчейн-помічник, який вимагає GUI-сесію (secretservice, keychain). У CI використовуйте --password-stdin і job-scoped DOCKER_CONFIG, або помічник, призначений для безголового використання.
Чи безпечно зберігати авторизацію в ~/.docker/config.json?
Це майже plaintext (base64) і має трактуватися як секрет. На спільних системах це погана ідея. У CI з ефермерними робочими просторами і обмеженими правами це може бути прийнятним компромісом.
Що насправді означає «no basic auth credentials»?
Docker не знайшов креденшіалів для точного ключа реєстру, який він вивів із посилання на образ. Частою причиною є логін до registry.corp.local, але pull відбувається з registry.corp.local:5000.
Чи слід використовувати insecure-registries щоб виправити TLS?
Ні, не як рутинне рішення. Встановіть правильний CA або виправте ланцюжок сертифікатів. Використовуйте insecure лише для ізольованих тестових середовищ, де ви готові прийняти ризик.
Чому працює з curl, але не з Docker?
curl використовує ваше user-оточення і системний store довіри. Демон Docker може використовувати інші проксі-налаштування і не довіряти тим самим CA. Тестуйте і CLI, і контекст демона.
Як обробляти SSO/2FA з Docker login?
Використовуйте персональний токен доступу або сервісний токен, призначений для API. Ваш інтерактивний SSO-пароль часто недійсний для API-автентифікації реєстру.
Який найшвидший спосіб довести, що це проблема проксі?
Порівняйте curl -I https://REGISTRY/v2/ з і без проксі-змінних, і перевірте, чи змінюється WWW-Authenticate realm або видавець сертифіката. Якщо змінюється — проксі посередині.
Чи потрібно перезапускати Docker після зміни сертифікатів?
На Linux-хостах демона зазвичай так (або хоча б reload). На Docker Desktop іноді потрібно перезапустити Docker Desktop, щоб зміни довіри VM вступили в силу.
Висновок: наступні кроки, які можна виконати сьогодні
Якщо вхід до реєстру падає, не ставте це загадкою. Розглядайте систему як сукупність: кінцеві точки, довіра, схема автентифікації, збереження облікових даних і скоупи.
- Зробіть
curl -I https://REGISTRY/v2/і прочитайте статус, заголовки та поведінку TLS. - Перегляньте
~/.docker/config.jsonі переконайтеся, що помічник облікових даних існує і працює у вашому середовищі. - Використайте
--password-stdinі чистийDOCKER_CONFIG, щоб швидко ізолювати проблеми з помічником облікових даних. - Відокремте «login succeeded» від «push permitted», тестуючи pull/push на відомому репозиторії і виправляючи RBAC за потреби.
- Припиніть заклеювати TLS шляхом insecure registries. Встановіть правильний CA і зробіть це детермінованим у деві й CI.
Коли ви зробите це кілька разів, ви почнете впізнавати патерни. І наступного разу, коли хтось скаже «вчора працювало», у вас буде план дій замість суперечки.