Tags: docker devops infrastructure
Categories: None

Так уж получилось что последний год я в основном занят вопросами, связанными с Docker, DevOps, доставкой кода в продакшен. В связи с этим накопилось достатоно мыслей и испробованных case’ов, которыми хотелось бы поделиться. Если честно не знаю с какой стороны лучше начать, поэтому начну с середины и конкретного кейса, который «на острие пера» :)

Допустим, где-то в недрах автоматизированной инфраструктуры, силами CI сервера, собираются итоговые Docker образы. После сборки эти образы необходимо где-то хранить. Для хранения собранного образа мы можем использовать 3 идеологических подхода:

  • Самый простой способ – где собрали образ, там его и запускаем. В этом случае CI нам не нужен, все можно делать скриптом, но автоматизации никакой, зато все быстро и просто.
  • Использовать приватный репозиторий на Docker hub. Здесь все упирается только деньги :) Ну или вы принципиально не хотите хранить образы в облаке. Помимо официального репозитория существуют и сторонние: Quay, Artifactory, Google’овый репозиторий. Ничего из этого не сипользовал, кроме официального.
  • Поднять свой приватный репозиторий. В этом случае, конечно, есть ряд минусов: например нет адекватного UI, чтобы все настроить правильно надо пройти через ряд граблей, настройка централизованной авторизации и прав доступа вообще не предусмотрена и должна реализовываться 3-й стороной. В общем именно этим вариантом мы и займемся!

Итого, что мы будем делать: поднимем приватный репозиторий Docker образов, прикрутим TLS, простую аутентификациюи Basic-auth и S3-сторедж (включил сюда, т.к. с ним есть ньюансы при использовании с TLS).

Приготовления

Тесты будем проводить по последнему слову моды – через docker-machine.
Для тестов нам понадобятся 2 тачки:

  • Серверная, на которой поднимем реестр:

    docker-machine create -d digitalocean --digitalocean-access-token TOKEN registry
  • Клиентская, на с которой будем пробовать коммуницировать с реестром:

    docker-machine create -d digitalocean --digitalocean-access-token TOKEN shell

Реестр будет висеть на каком-нибудь домене, допустим: registry.tessier-ashpoule.net.
Пропишем этот домен на клиенской тачке:

# Смотрим ip серверной тачки
docker-machine ip registry
# Идем на клиентскую тачку
docker-machine ssh shel
# Прописываем домен в hosts
echo "xx.xx.xx.xx registry.tessier-ashpoule.net" >> /etc/hosts

Все готово!

Поднимаем docker registry

Реестр будем запускать конечно же в Docker-контейнере. Для начала без TLS и аутентификации.
Реестр имеет две версии API. v1 уже deprecated, так что в названии образа везде присутствует тег 2.

# Идем на серверную тачку
eval $(docker-machine env registry)
# Запускаем
docker run -d -p 5000:5000 --name registry registry:2

Пробуем:

# Действуем с клиентской тачки
docker-machine ssh shell
docker pull busybox
docker tag busybox registry.tessier-ashpoule.net:5000/busybox
docker push registry.tessier-ashpoule.net:5000/busybox

И получаем такой ответ:

unable to ping registry endpoint https://registry.tessier-ashpoule.net:5000/v0/
v2 ping attempt failed with error: Get https://registry.tessier-ashpoule.net:5000/v2/: tls: oversized record received with length 20527
v1 ping attempt failed with error: Get https://registry.tessier-ashpoule.net:5000/v1/_ping: tls: oversized record received with length 20527

Что произошло? Docker по умолчанию соединяется с реестром используя TLS. Из этой ситуации существует два выхода:

  • Мы можем сконфигурировать Docker демон на клиенской тачке в insecure-режиме. Это быстрый, но не безопасный способ и годится только для тестов.
  • Мы можем настроить Docker реестр с TLS и разместить на клиенской машине доверенные сертификаты.

Insecure mode

Сначала попроубем простой вариант и настроим Insecure mode. Для этого в опции запуска надо добавить домен, к которому разрешается подключаться в Insecure режиме:

vim /etc/default/docker
К DOCKER_OPTS добавляем --insecure-registry registry.tessier-ashpoule.net:5000
service docker restart

Пробуем:

docker push registry.tessier-ashpoule.net:5000/busybox

Теперь все пушится, но не защищено, так что переходим к настройке TLS.

Включаем TLS

Для насройки нужны сертификаты. Будем генерить свои самоподписные.

openssl genrsa -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -days 1024 -out rootCA.pem
openssl genrsa -out registry.key 2048
openssl req -new -key registry.key -out registry.csr
В Common Name указываем имя домена, на котором будет висеть реестр:
Common Name (eg, YOUR name) []: registry.tessier-ashpoule.net
openssl x509 -req -in registry.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out registry.crt -days 500

Копируем сертификаты на серверную тачку:

docker-machine scp -r certs registry:/

Запускаем:

docker run -d -p 5001:5000 --name registry-ssl \
-v /certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/registry.key \
registry:2

Пушим (С клиенской тачки):

# Insecure режим надо вырубить и перегрузить докер
docker tag busybox registry.tessier-ashpoule.net:5001/busybox
docker push registry.tessier-ashpoule.net:5001/busybox

И снова фигня, видим следующее:

The push refers to a repository [registry.tessier-ashpoule.net:5001/busybox] (len: 1)
unable to ping registry endpoint https://registry.tessier-ashpoule.net:5001/v0/
v2 ping attempt failed with error: Get https://registry.tessier-ashpoule.net:5001/v2/: x509: certificate signed by unknown authority
v1 ping attempt failed with error: Get https://registry.tessier-ashpoule.net:5001/v1/_ping: x509: certificate signed by unknown authority

Докер нам хочет сказать, что не знает таких сертификатов, которыми подписано соединение. Это все происходит из-за проверки подлинности сервера, с которым мы работаем. Чтобы все заработало нам надо положить докеру корневой сертификат, которому мы доверяем.
Доверенные сертификаты лежат в подкаталоге в /etc/docker/certs.d. Название подкаталога – это название домена. Копируем certs/rootCA.pem в /etc/docker/certs.d/registry.tessier-ashpoule.net:5001/ca.crt. После этой манипуляции push проходит гладко.

TLS прикрутили, теперь займемся аутентификацией.

Прикручиваем Basic-auth

# Сгенерим юзера
docker run --entrypoint htpasswd registry:2 -Bbn user password > /auth/htpasswd
# Запускаем
docker run -d -p 5002:5000 --name registry-ssl-auth \
-v /auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-v /certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/registry.key \
registry:2

При попытке push’а получаем сообщение:

The push refers to a repository [registry.tessier-ashpoule.net:5002/busybox] (len: 1)
ac6a7980c6c2: Image push failed
Head https://registry.tessier-ashpoule.net:5002/v2/busybox/blobs/sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4: no basic auth credentials

Все в принципе ясно, надо авторизоваться. Делаем login:

docker login registry.tessier-ashpoule.net:5002
Вводим логин и пароль.

После аутентификации создастся файл с авторизационным токеном для нашего домена. Файл лежит в ~/.docker/config.json.
Препятствий больше никаких нет, можем push’ить: docker push registry.tessier-ashpoule.net:5002/busybox.

На последок прикрутим S3 сторедж.

Прикручиваем S3

Запускаем следующим магическим пассом:

docker run -d -p 5003:5000 --name registry-ssl-s3 \
-v /certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/registry.key \
-e "REGISTRY_STORAGE=s3" \
-e "REGISTRY_STORAGE_S3_REGION=eu-west-1" \
-e "REGISTRY_STORAGE_S3_BUCKET=tessier-ashpoule-docker-registry" \
-e "REGISTRY_STORAGE_S3_ACCESSKEY=ACCESS_KEY" \
-e "REGISTRY_STORAGE_S3_SECRETKEY=SECRET_KEY" \
registry:2

Пробуем пушить, вроде все пушится, но в конце получаем unknown authority:

The push refers to a repository [registry.tessier-ashpoule.net:5003/busybox] (len: 1)
ac6a7980c6c2: Pushing 1.024 kB
Head https://s3-eu-west-1.amazonaws.com/tessier-ashpoule-docker-registry/docker/registry/v2/blobs/sha256/a3/a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4/data?AWSAccessKeyId=AKIAIU3MTQGKX3A24CHQ&Expires=1449824796&Signature=3sddk5kJ6YOJYTI4k6s1CW3XQ1U%3D: x509: certificate signed by unknown authority

Что за фигня, сертификат же лежит? Теперь Docker ругается не на соединение с реестром, а на соединение с S3. Как и во всех других случаех существует два выхода:

  • Добавить корневой сертификат S3 в доверенные. Можно просто сделать cat s3.pem >> ca.crt.
  • Или можем отключить отдачу реестром https для s3. Для этого надо указать опцию -e "REGISTRY_STORAGE_S3_SECURE=false" при старте контейнера.

———–

Ну вот вроде вся эпопея. За кадром осталась настройка Nginx (Все сертификаты и Basic-auth можно сконфигурить через Nginx) и настройка централизованной аутентификации про разграничение прав доступа.

Все вместе заDockerCompose’нное лежит GitHub.

Ссылки:

comments powered by Disqus