Если вы не хотите платить за дополнительные частные репозитории или чтобы в ваших контейнерах копались все подряд, читайте дальше...
В 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
Сэкономьте на публичных репозиториях и сделайте свои образы контейнеров более защищенными!
П.С. Когда это писалось докерхаб был ещё доступен, поэтому основной фокус теперь все таки на шифровании, особенно если пользоваться различными непонятными реестрами и ненадежными сервисами.
IhnatKlimchuk
Чего только не сделаешь ради $5 в месяц...