Удаленный доступ к Docker over TLS

Иногда удобно получать доступ к инфраструктуре удаленно, но при этом безопасно. Возможно для мониторинга, или оперативного вмешательства либо по другим причинам. Получить доступ к API Docker-a, конечно. Для начала сгенерируем сертификаты:

Создаем корневой сертификат, его будем использовать для выпуска и подписи а так же верификации (сертификат выпускаем сроком на 10 лет, параметр -days):

# openssl genrsa -out ca-key.pem 2048
# openssl req -x509 -new -nodes -key ca-key.pem -days 3650 -out ca.pem -subj '/CN=Docker-CA'

Для дальнейшей настройки создадим файл openssl.cnf в него поместим некоторые настройки, в частности, мы укажем что выпускаемые нами сертификаты будут использоваться для взаимодействия и авторизации между хостами, а так же адреса хостов и другие, вы можете почитать документацию по openssl и добавить необходимые поля по своему усмотрению:

# cat openssl.conf
[req]
  req_extensions = v3_req
  distinguished_name = req_distinguished_name

[req_distinguished_name]

[ v3_req ]

  basicConstraints = CA:FALSE
  keyUsage = nonRepudiation, digitalSignature, keyEncipherment
  extendedKeyUsage = serverAuth, clientAuth

[req]

  req_extensions = v3_req
  distinguished_name = req_distinguished_name

[req_distinguished_name]

[ v3_req ]
  basicConstraints = CA:FALSE
  keyUsage = nonRepudiation, digitalSignature, keyEncipherment
  extendedKeyUsage = serverAuth, clientAuth
  subjectAltName = @alt_names

[alt_names]
  DNS.1 = <внешний_ip_сервера>
  IP.1 = <внешний_ip_сервера>
  IP.2 = 127.0.0.1

Далее создадим сертификат сервера:

# openssl genrsa -out server-key.pem
# openssl req -new -key server-key.pem -out server-cert.csr -subj '/CN=Docker-server' -config openssl.cnf 
# openssl x509 -req -in server-cert.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 3650 -extensions v3_req -extfile openssl.cnf

После чего создаем сертификаты клиента:

# openssl genrsa -out client-key.pem 2048
# openssl req -new -key client-key.pem -out client-cert.csr  -subj '/CN=Docker-client' -config openssl.cnf
# openssl x509 -req -in client-cert.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 3650 -extensions v3_req -extfile openssl.cnf 

На этом создание сертификатов завершено, помещаем корневой и клиентские сертификаты в папку ~/.docker

# cp ca.pem ~/.docker
# cp client-key.pem client-cert.pem ~/.docker

Далее переходим на удаленный сервер и вносим изменения в демон docker-а. Редактируем файл /etc/default/docker, добавляем в конец файла следующую строку. Обращаю внимание что в данном примере слушать API будем на всех интерфейсах, лучше ограничить это вашими сетями.

# nano /etc/default/docker

DOCKER_OPTS="-H=0.0.0.0:2376 -H unix:///var/run/docker.sock --tlsverify --tlscacert=/etc/docker/ssl/ca.pem --tlscert=/etc/docker/ssl/server-cert.pem --tlskey=/etc/docker/ssl/server-key.pem"

Далее редактируем файл /lib/systemd/system/docker.service, находим в нем секцию [Service], добавляем в начало секции EnvironmentFile=/etc/default/docker а так же правим параметр ExecStart:

# nano /lib/systemd/system/docker.service

EnvironmentFile=/etc/default/docker

ExecStart=/usr/bin/dockerd -H fd:// $DOCKER_OPTS --containerd=/run/containerd/containerd.sock

После чего создаем каталог ssl в папке демона docker и загружаем туда сертификаты сервера. Перезапускаем демон.

# mkdir /etc/docker/ssl
# scp ca.pem username@host:/etc/docker/ssl
# scp server-cert.pem username@host:/etc/docker/ssl
# scp server-key.pem username@host:/etc/docker/ssl
# systemctl daemon-reload
# service docker restart

Далее проверяем слушает ли демон порт API:

netstat -tulpan | grep 2376
tcp6    0    0 :::2376      :::*   LISTEN      1872979/dockerd

Теперь можно подключаться с клиентской машины:

# docker -H <адрес_сервера>:2376 --tls  --tlscert=client-cert.pem --tlskey=client-key.pem  ps -a
CONTAINER ID   IMAGE        COMMAND                  CREATED       STATUS       PORTS                                             NAMES
a9636e4b33c9   registry:2   "/entrypoint.sh /etc…"   7 weeks ago   Up 2 hours   0.0.0.0:443->443/tcp, :::443->443/tcp, 5000/tcp   registry_registry_1

Отлично, для безопасности добавьте так же правила iptables для ограничения доступа по разрешенным IP.