Концепция контейнеризации на базе Docker, и ему подобных технологий, для многих разработчиков стала незаменимым инструментом доставки своих продуктов конечным пользователям в виде полностью подготовленной среды использования. В большинстве случаев, особенно это касается open source продуктов, для распространения используются бесплатные реестры такие как Docker Hub или Github Container  Registry.  Но когда количество продуктов и размеры образов начинают увеличиваться, а также требуется более расширенное управление репозиториями и доступом к ним, то разработчики и компании начинают задумываться о своих собственных хранилищах для контейнерных образов (images). К счастью вариантов для развертывания своего собственного экземпляра Container Registry предостаточно, начиная от самого простого варианта Docker Registry и до более масштабных систем таких как HARBOR, Dragonfly и прочие.  Для своих задач я выбрал самый простой вариант на базе Docker Registry, который разворачивается за минуту имеет достаточно много различных настроек и интеграций с различными облачными сервисами. Работает он достаточно предсказуемо, надежно, а также написан на языке который я знаю. Изначально доступ к registry нужен был только для одного пользователя - меня самого, поэтому я выбрал обычный механизм доступа на основе htpasswd Этот механизм очень простой, все пользователи указанные в файле htpasswd имеют полный доступ ко всему реестру, что является сложностью при появлении задачи раздельного доступа к реестру, которая у меня и возникла в скором времени. Среди возможных способов доступа к Docker Registry имеется механизм авторизации на основе token, который позволяет организовать управляемый доступ в зависимости от репозитория и действий пользователя (pull, push, delete). Однако реализация данного механизма является не очень простой задачи. Требуется отдельный auth сервер, сертификаты с ключами, которыми будет подписываться и проверяться token. Также хотелось получить единый интерфейс управления пользователями, доступами и запиясми репозиториев. К сожалению, из простых вышеперечисленных решений я не нашел такого, которое бы решало следующие задачи: разделение доступа к репозиториям на основе действий пользователей (pull/push); доступ на основе ролей (RBAC); просмотр списка репозиториев и информацию об образах; удаление образов; механизм поиска по записям; анонимный доступ и доступ для зарегистрированных пользователей; прочие функции (логирование, генератор сертификатов и т.п.). Скорее всего я плохо искал и что-то похожее уже реализовано, но в итоге я решил реализовать свой сервис RegistryAdmin, который будет решать выше поставленные задачи.  По больше части энтузиазм написать свой, более-менее серьезный, open source проект возник благодаря популярному ИТ-подкасту Radio-T и особенно ведущему Umputun. В одно время я часто изучал код его проектов и заимствовал различные идеи в своих разработках (в том числе и в этом проекте), а также контрибьютил в некоторые из них, чтобы внести свой вклад в развитие проектов, которыми я пользуюсь. Некоторые технологии, которые я изучил и использую в повседневной работе, стали мне известны именно благодаря данному подкасту, за что хочу высказать ОГРОМНУЮ БЛАГОДАРНОСТЬ всем ведущим за их труд. Прошу не расценивать данный абзац как рекламу или что-то подобное т.к. таковым он не является, а имеет место искренний душевный порыв высказать благодарность. Изначально я хотел быстро написать простенький сервис, который будет интегрирован в существующий экземпляр, уже развернутого private registry, и просто выдавать токены авторизации существующим пользователям. Но после детального изучения документации на сайте Docker стало ясно, что быстро не получится.  Во-первых, для подписи и проверки токенов необходимы только RSA ключи (подпись текстовым секретом не подходит). Сертификат должен содержать адрес или имя хоста registry, по которому осуществляется запрос данных из реестра, т.е. в нём обязательно должны быть определенны поля subjectAltName.  Поэтому механизм генерации RSA ключей для работы с токеном я решил встроить непосредственно в RegistryAdmin, чтобы можно было обойтись без сторонник утилит таких как openssl (хотя их также можно использовать). Сгенерированные ключи могут быть использованы и для TLS подключения. Во-вторых, в пользовательском web-интерфейсе мне было необходимо реализовать полнотекстовый поиск по репозиториям, однако API registry позволяет получать список репозиториев только используя вариант курсора. По этой причине я решил реализовать синхронизацию списка репозиториев и данных по этим репозиториям во внутренней базе RegistryAdmin (на базе SQLite).  Для синхронизации в режиме реального времени на стороне registry настроен механизм уведомлений (notifications) о событиях, которые инициируют механизм актуализации данных в базе RegistryAdmin. Также доступен ручной механизм запуска синхронизации данных, который также запускается автоматически через заданный интервал времени.  Сам RegistryAdmin является обычным REST API приложением, который взаимодействует непосредственно с экземпляром registry. В качестве фреймворка для UI был выбран React-Admin, который достаточно легко и просто встраивается в различные REST API системы. Файлы UI интегрированны в бинарник при помощи библиотеки embed. Помимо авторизации на основе токена, также поддерживается обычная авторизация через htpasswd файл, который синхронизируется со списком пользователей RegistryAdmin. Однако стоит иметь ввиду, что доступ к реестру на основе htpasswd не поддерживает разделение прав доступа на основе действий пользователя (pull/push). Для управления и просмотра данных реестра через UI предусмотрены встроенные роли: Admin - полные права для работы с реестром и репозиориями; Manager - просмотр только списка всех репозиториев и списков доступа; User - может только просматривать разрешенный список репозиториев. Также в сервисе реализованы специальные права для анонимных и зарегистрированных пользователей, которые имеют специальные ID и не хранятся в базе RegistryAdmin. В итоге получился простой по функционалу и немного усложненный, в части первичной настройки, сервис RegistryAdmin. Учитывая, что я занимаюсь в основном backend разработкой, то тесты написаны только к самому сервису. В планах есть задача реализовать тесты к frontend части, но пока не нашел на это свободного времени. Было бы очень хорошо если бы, кто-нибудь оказал помощь в этом вопросе. Дистрибутив RegistryAdmin доступен в двух вариантах: единый бинарный файл для разных архитектур (доступен в релизах); docker образ. В данном обзоре я буду рассматривать вариант установки в среде docker, как наиболее предпочтительный, с использованием docker-compose.  Изначально нужно определится с архитектурой системы, чтобы чётко понимать какие настройки для чего нужны. В данном примере я буду выполнять развертывание для следующей схемы узлов: В качестве сервера registry.local выступает сервер с развернутой средой docker. В качестве внешних клиентов может выступать любой узел внутри сети, которому доступен хост registry.local Создаем папку для стека контейнеров Docker Private Registry и RegistryAdmin  В папке создаем файлы конфигурации (или копируем из примера), рекомендуется придерживаться следующей структуры: где:   registry-config.yml - файл конфигурации для registry   token-ra-config.yml - конфигурационный файл RegistryAdmin Задать владельца для директории и файлов. По-умолчанию в контейнере определен владелец с UID=1000 . Данный UID может быть переопределен через переменную окружения APP_UID  в файле docker-compose.yml.  Указываем основные параметры для RegistryAdmin и указываем пути и параметры для подключения к реестру и генерации ключей для токенов. Также мы будем использовать данные ключи для настройки TLS подключения. По указанному в параметре --cets.path пути будут автоматически созданы все необходимые ключи и сертификат. При запуске RegistryAdmin проверяет наличие файлов в данном каталоге и создает новые если директория пуста. Если данный параметр не задан, то по-умолчанию ключи создаются в домашней директории пользователя под которым запускается сервис. Определяем параметры для самого Private Docker Registry. URL указанный в параметре realm должен быть доступен клиентам, которые осуществляют авторизацию напрямую в сервисе RegistryAdmin. Определить параметры для docker-compose ВАЖНО! Переопределение команды (command) запуска контейнера с registry требуется для того, чтобы обойти ошибку доверия самоподписанного сертификата для корректной работы nofitfications внутри контейнера registry. Данная строка выполняет добавление сертификата в список доверенных при запуске контейнера registry. В случае если Вы не используете TLS подключение к RegistryAdmin  переопределение commandне требуется. Также не требуется переопределение если используются доверенные сертификаты такие как Let's Encrypt, но в этом случае для HTTP TLS необхоимо указывать сертификат содержащий полную цепочку CA fullchain.pem. Запускаем контейнеры Если все параметры верны, то сервис должен запуститься и быть доступен через браузер по адресу https://registry.local:8443 Если у Вас стоит задача развернуть систему с возможностью доступа из сети Интернет, то необходимо учитывать особенности использования доверенных TLS сертификатов. RegistryAdmin поддерживает получение сертификатов Let's Encrypt через протокол ACME (режим SSL AUTO). Если используется такой механизм получения публичного сертификата, то для   доступа к registry по HTTPS следует определить параметр letsencrypt и указать путь к сертификату который был указан в параметре --ssl.acme-location для RegistryAdmin. В случае получения сертификата LE через HTTP-01 challenge, то как для registry так и для RegistryAdmin в качестве сертификата следует указывать fullchain.pem файл, иначе при взаимодействии между сервисами будет возникать ошибка доверия к сертификату - x509 certificate signed by unknown authority. После того как успешно произведена настройка и запуск системы можно приступать к её использованию. Для входа в админку необходимо ввести данные доступа по-умолчанию: После успешной авторизации отобразится страница со списком репозиториев, но т.к. у нас новый экземпляр registry, то данных в нем еще нет. В случае интеграции с существующим registry можно было произвести принудительную синхронизацию существующих данных. Добавим новый репозиторий в реестр. Для этого произведем авторизацию с внешнего docker клиента под данными администратора и сделаем push нового образа в реестр. Но т.к. в данном примере мы используем самоподписанные сертификаты, то docker клиент будет ругаться на доверие к сертификату. Чтобы решить эту проблему есть два способа: На клиенте добавить наш реестр в список небезопасных реестров в файле конфигурации докер демона - daemon.json. После внесения измененний потребуется перезагрузка службы докера. Добавить сгенерированные сертифкаты в список доверенных сертификатов для клиентского хоста. После решения вопроса с доверием сертификатов можно приступать к выполнению аутентификацию в реестре на клиенте. После успешной аутентификации выполним загрузку первого образа в реестр. Добавим пользователя с ролью user для последующего назначения ему соответствующего доступа. Определим права доступа к репозиторию alpine для пользователя user1. Разрешим пользователю делать только pull. Выполним  аутентификацию на docker клиенте под пользователем user1  После успешной аутентификации выполним pull из репозитория alpine нашего реестра. Как видим pull работает, попробуем выполнить push. Как видно на скриншоте попытка push завершилась ошибкой. Добавим разрешение на возможность делать push для пользователя user1. Попробуем снова выполнить push под текущим пользователем. Как мы видим в репозитории alpine появился новый тэг, после добавления соответсвующего доступа действие pull было успешно выполнено. Стоит обратить внимание, что в данном случае, при попытке удаления тега test будет также удален и тэг 3.14, потому-что у них одинаковая сигнатура digest, а registry хранит и удаляет элементы именно по этому хэшу. Также стоит имееть ввиду, что при удалении тэга из реестра удаляется только manifest файл, а данные удаляются путем запуска встроенного сборщика мусора (garbage collector). Итоговым результатом получилась, на мой взгляд, простая админка для управления записями реестра и доступом к Docker Private Registry, хотя есть некоторые особенности при настройке доверенных сертификатов, а также требуется понимание сетевого взаимодействия между сервисами. Надеюсь, что данный сервис может быть кому-то полезен. В процессе реалзации были решины достаточно интересные задачи такие как работа с сертификатами, встраивание приложений SPA в бинарный файл, а также настройка CI/CD для автоматического тестирования и сборки проекта.  Всем желающим поучаствовать в тестировании и улучшении проекта добро поожаловать! Все исходники проекта и примеры доступны на GitHub.  Разработчик (всякого разного)