Если вы не хотите платить за дополнительные частные репозитории или чтобы в ваших контейнерах копались все подряд, читайте дальше...
В Docker Hub и других реестрах контейнеров существуют ограничения на частные репозитории. Чтобы сохранить образы контейнеров недоступными для публичного скачивания, нужно заплатить, и чем больше частных репозиториев вам нужно, тем выше стоимость. Однако есть способ обойти это ограничение, давайте узнаем как.
TL;DR: Используйте зашифрованные образы.
В Podman есть функция, позволяющая шифровать образы ко��тейнеров, делая их доступными только с определенным ключом. Это делает образы конфиденциальными, даже если они хранятся в общедоступном хранилище. Кроме того, для дополнительной безопасности можно зашифровать сам ключ с помощью пароля. Давайте посмотрим, как это сделать!
Для начала вам нужно установить Podman. Это альтернатива Docker и, на мой взгляд, более функциональная, так что ее стоит иметь в своей системе. Впрочем, не волнуйтесь - вы сможете запускать все с помощью того же Docker. Podman необходим для загрузки и скачивания образов, которые затем будут импортированы в Docker и запущены как обычно. Мы будем использовать как командную строку, так и Ansible для лучшей автоматизации.
Для использования всех возможностей Ansible нам понадобится коллекция Ansible Podman, которая предоставляет широчайшие возможности для автоматизации любых контейнеров и гибкие способы работы со всеми технологиями, связанными с контейнерами - собственно контейнерами, контейнерными сетями, томами, подами, образами, секретами, реестрами и многим другим. Она входит в официальный дистрибутив Ansible, поэтому вы можете использовать ее оттуда, но функции, которые нам нужны, являются новейшими, и скорее всего нам нужно будет установить ее из Ansible Galaxy:
ansible-galaxy collection install containers.podman
или, если вы хотите получить все самое лучшее и свежее, возьмите из мастера:
git clone https://github.com/containers/ansible-podman-collections cd ansible-podman-collections ansible-galaxy collection install --force .
Теперь мы готовы!
Генерация ключей шифрования
Сначала давайте изучим функциональность ключей шифрования. Нам нужно сгенерировать открытый и закрытый ключи для шифрования и дешифрования, они также могут быть защищены паролем. Для этого можно использовать образ или бинарник OpenSSL:
mkdir -p keys openssl genrsa -out ./keys/container_private.pem 2048 && \ openssl rsa -in ./keys/container_private.pem -pubout -out ./keys/container_public.pem
Если у вас не установлен openssl, вы можете использовать контейнер nginx, который мы в любом случае будем использовать для постройки образов контейнера для тестов:
docker run -it -v $PWD/keys:/data:z docker.io/library/nginx sh -c "\ openssl genrsa -out /data/container_private.pem 2048 && \ openssl rsa -in /data/container_private.pem -pubout -out /data/container_public.pem"
Эта же команда работает с podman вместо docker:
podman run -it -v $PWD/keys:/data:z docker.io/library/nginx sh -c "\ openssl genrsa -out /data/container_private.pem 2048 && \ openssl rsa -in /data/container_private.pem -pubout -out /data/container_public.pem"
В Ansible вы можете сгенерировать ключи следующим образом:
- name: Generate SSL keys containers.podman.podman_container: name: temporary state: started image: docker.io/library/nginx rm: true volumes: - /tmp/keys:/data:z command: - bash - -c - >- openssl genrsa -out /data/container_private.pem 2048 && openssl rsa -in /data/container_private.pem -pubout -out /data/container_public.pem
Ключи будут находиться в каталоге /tmp/keys, или вы можете указать свой собственный путь.
Сборка и шифрование контейнера
Далее давайте соберем и зашифруем контейнер.
Создайте Dockerfile:
cat <<EOF> Dockerfile FROM docker.io/library/nginx COPY index.html /usr/share/nginx/html/index.html EOF echo "test passed" > index.html podman build . -t docker.io/sshnaidm/something:open
Мы используем Podman для сборки, потому что загрузка зашифрованного образа в реестр может быть выполнено только с помощью инструмента Podman. Однако конвертирование образов между podman и docker очень простое:
docker save docker.io/sshnaidm/something:open | podman load # или так podman image push docker.io/sshnaidm/something:close docker-daemon:docker.io/sshnaidm/something:closed
В Ansible создайте, постройте и выложите образ в репозиторий:
- name: Create Dockerfile copy: content: | FROM docker.io/library/nginx COPY index.html /usr/share/nginx/html/index.html dest: "{{ playbook_dir }}/Dockerfile" - name: Create index.html copy: content: "<h1>test passed!</h1>" dest: "{{ playbook_dir }}/index.html" - name: Build and push the image containers.podman.podman_image: name: docker.io/sshnaidm/something tag: closed path: "{{ playbook_dir }}" state: build build: file: "{{ playbook_dir }}/Dockerfile" push: true push_args: dest: docker.io/sshnaidm/something:closed extra_args: --encryption-key jwe:/absolute/path/to/keys/container_public.pem
Теггирование и выгрузка с шифрованием
Тегировать и выгрузить образ с помощью podman с шифрованием JWE:
cd keys/ podman tag docker.io/sshnaidm/something:open docker.io/sshnaidm/something:closed podman push --encryption-key jwe:container_public.pem docker.io/sshnaidm/something:closed
Мы загрузили наш зашифрованный образ в Docker Hub. Теперь попробуйте скачать его с помощью docker или podman:
$ podman pull docker.io/sshnaidm/something:closed Trying to pull docker.io/sshnaidm/something:closed... Getting image source signatures Copying blob 37745a742023 [--------------------------------------] 0.0b / 1.4KiB | 0.0 b/s Copying blob 37745a742023 done | [skipped...] Error: writing blob: adding layer with blob "sha256:69458e593f618c768281ca90b8ed5eb78df30df39902d948f894fd8c7e80906f": processing tar file(archive/tar: invalid tar header): exit status 1
Не-а, не получается :)
С помощью docker:
$ docker pull docker.io/sshnaidm/something:closed closed: Pulling from sshnaidm/something 69458e593f61: Extracting [==================================================>] 32.74MB/32.74MB 1a1e276c1a3b: Download complete [skipped...] failed to register layer: archive/tar: invalid tar header
Не-а, не получается :)
Невозможно скачать и использовать образ без ключа. Только имея ключ можно загрузить его, что фактически делает его приватным образом контейнера в публичном репозитории!
Скачивание и запуск зашифрованного образа
Скачать и использовать зашифрованный образ:
cd keys/ podman pull --decryption-key container_private.pem docker.io/sshnaidm/something:closed # Load it to Docker if you can't use Podman: podman save docker.io/sshnaidm/something:closed | docker load docker run -d --name webserver -p 8888:80 docker.io/sshnaidm/something:closed curl localhost:8888 $ test passed
С помощью Ansible вы можете скачать и запустить его в одной таске:
- name: Run container containers.podman.podman_container: name: webserver image: docker.io/sshnaidm/something:closed state: started decryption_key: /full/path/to/keys/container_private.pem publish: - 8888:80
И наконец:
curl localhost:8888 $ test passed
Полезные Bash-скрипты и Ansible Playbook
Для удобства здесь приведены bash скрипты для клиента и сервера, а также плейбук Ansible для сборки и распространения образа из публичного реестра, как если бы он был приватным:
На клиенте создайте образ, сгенерируйте ключи и отправьте его в зашифрованном виде:
# On client, build image, generate keys, and push encrypted openssl genrsa -out container_private.pem 2048 openssl rsa -in container_private.pem -pubout -out ./keys/container_public.pem cat <<EOF> Dockerfile FROM docker.io/library/nginx COPY index.html /usr/share/nginx/html/index.html EOF echo "test passed" > index.html podman build . -t docker.io/sshnaidm/something:production podman push --encryption-key jwe:container_public.pem docker.io/sshnaidm/something:production
Скопируйте файл container_private.pem на сервер и выполните там:
podman pull --decryption-key container_private.pem docker.io/sshnaidm/something:production # In case of Docker: podman save docker.io/sshnaidm/something:production | docker load docker run -d --name webserver -p 8888:80 docker.io/sshnaidm/something:production # In case of Podman: podman run -d --name webserver -p 8888:80 docker.io/sshnaidm/something:production
Или все это вместе выполните одним Ansible плейбуком:
- hosts: client tasks: - name: Generate SSL keys containers.podman.podman_container: name: temporary state: started image: docker.io/library/nginx rm: true volumes: - /tmp/keys:/data:z command: - bash - -c - >- openssl genrsa -out /data/container_private.pem 2048 && openssl rsa -in /data/container_private.pem -pubout -out /data/container_public.pem - name: Create Dockerfile copy: content: | FROM docker.io/library/nginx COPY index.html /usr/share/nginx/html/index.html dest: "{{ playbook_dir }}/Dockerfile" - name: Create index.html copy: content: "<h1>test passed!</h1>" dest: "{{ playbook_dir }}/index.html" - name: Build and push image containers.podman.podman_image: name: docker.io/sshnaidm/something tag: production path: "{{ playbook_dir }}" state: build build: file: "{{ playbook_dir }}/Dockerfile" push: true push_args: dest: docker.io/sshnaidm/something:production extra_args: --encryption-key jwe:/absolute/path/to/keys/container_public.pem - hosts: server tasks: - name: Distribute keys copy: src: /full/path/to/keys/container_private.pem dest: /full/path/to/keys/container_private.pem - name: Run container with Podman containers.podman.podman_container: name: webserver image: docker.io/sshnaidm/something:production state: started decryption_key: /full/path/to/keys/container_private.pem publish: - 8888:80
Сэкономьте на публичных репозиториях и сделайте свои образы контейнеров более защищенными!
П.С. Когда это писалось докерхаб был ещё доступен, поэтому основной фокус теперь все таки на шифровании, особенно если пользоваться различными непонятными реестрами и ненадежными сервисами.
