Linux Servidor linux

Liberando acesso remoto ao Docker.

Liberando acesso remoto ao Docker.

Neste artigo vamos mostrar como habilitar o “acesso remoto” ao Docker Engine utilizando, ou não, chave segura no Ubuntu Server 16:04.

Por que fazer isso? Por vários motivos, como por exemplo:

Liberar acesso ao seu Cluster de Docker Swarm para o desenvolvedor subir Stacks como ele bem entender, sem a necessidade de ter um usuário/senha no sistema operacional. Liberar acesso a sua ferramenta de CI/CD para subir as Stacks “automagicas” após o processo de Build, Testes e afins. Okay, mas como ele funciona exatamente?

Quando habilitado, ele abre uma porta TCP que nos permite conectar remotamente e executar comandos como se estivéssemos no servidor.

Com isso, podemos executar comandos como docker ps e listar todos os containers que estão executando naquele servidor ou pausa-los, removê-los, criá-los, enfim, podemos controlar totalmente o docker daemon.

Bora habilitar essa parada então.

Neste pequeno artigo vou exemplificar dois jeitos de habilitar o “remote API”. O jeito fácil, porém inseguro, e o jeito mais chato porém muito mais seguro (risos).

O jeito fácil, ou simples:

Dentro do nosso servidor de Docker vamos criar/editar o arquivo daemon.json e adicionar as configurações abaixo.

# vim /etc/docker/daemon.json
{
    "hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2376"]
}

Nessa configuração estamos dizendo que o socket do docker está listening na porta 2376/TCP.

Antes de dar um restart no docker, vamos dar uma pequena alterada no arquivo de inicialização dele para não dar erro na inicialização do serviço.

Execute os seguintes comandos:

# sed -i 's# -H fd://# #g' /lib/systemd/system/docker.service
# systemctl daemon-reload
# systemctl restart docker.service

Explicando: removemos a Flag -H do docker.service, porque já passamos a configuração do socket no arquivo daemon.json.

Após o restart podemos verificar se está tudo funcionando corretamente.

# systemctl status docker.service
● docker.service - Docker Application Container Engine
   Loaded: loaded (/lib/systemd/system/docker.service; disabled; vendor preset: enabled)
   Active: active (running) since Wed 2020-01-22 02:20:39 UTC; 12min ago
     Docs: https://docs.docker.com
 Main PID: 5863 (dockerd)
    Tasks: 18
   CGroup: /system.slice/docker.service
           └─5863 /usr/bin/dockerd --containerd=/run/containerd/containerd.sock

Vamos verificar a porta em que deixamos o socket listening:

# lsof -i:2376
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
dockerd 5863 root    7u  IPv6  46861      0t0  TCP *:2376 (LISTEN)

Okay, tudo funcionando corretamente. Agora vamos para o nosso primeiro teste utilizando a API remota.

Podemos enviar comandos ao nosso servidor remoto de Docker através da nossa máquina local. E claro, vamos precisar ter o Docker instalado na máquina local também.

Para enviar um comando ao servidor remoto basta adicionar o parâmetro -H ${HOST}:2376. Troque a ENV ${HOST} pelo IP do seu servidor, no meu caso 10.10.10.3.

$ sudo docker -H 10.10.10.3:2376 --version
Docker version 19.03.8, build afacb8b7f0

No exemplo a seguir temos 2 terminais. O de cima corresponde a minha máquina Local e o de baixo ao servidor remoto. No servidor remoto foi executado o comando “watch -n1 docker ps”, que vai retornar a saída do comando docker ps a cada segundo e assim podemos observar melhor quando as coisas acontecerem. Na máquina local, executei o comando docker run para criar um container Nginx, porém com o parâmetro -H para enviar o comando para o servidor remoto através da API.

Muito bacana, né? Agora vamos deixar isso um pouco mais seguro.

O procedimento a seguir descreve como gerar um certificado e chave utilizando o OpenSSL. Para entender melhor todos os passos, recomendo fortemente ler a documentação do docker (https://docs.docker.com/engine/security/https/).

Resumo: Vamos criar uma CA (Certificate Authority) e chaves para o servidor remoto e para o Cliente que, no nosso caso, é a máquina local.

Primeiro, no servidor remoto vamos criar o certificado:

# openssl genrsa -aes256 -out ca-key.pem 4096
# openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem

Agora, podemos criar a Chave e assinar com o certificado.

Altere a variável $HOST pelo DNS do servidor. Aqui no meu caso vou utilizar docker.linuxnaweb.com.

# openssl genrsa -out server-key.pem 4096
# openssl req -subj "/CN=docker.linuxnaweb.com" -sha256 -new -key server-key.pem -out server.csr

Como as conexões TLS podem ser feitas através do endereço IP ou do DNS, os endereços precisam ser especificados ao criar o certificado.

# echo subjectAltName = DNS:docker.linuxnaweb.com,IP:10.10.10.3 >> extfile.cnf

Neste caso vamos permitir conexão somente no DNS docker.linuxnaweb.com e no endereço IP 10.10.10.3.

Agora vamos definir os atributos criados e gerar o certificado assinado:

# echo extendedKeyUsage = serverAuth >> extfile.cnf
# openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf

Primeiro passo concluído, agora vamos criar o certificado e a chave utilizada pelo Client:

# openssl genrsa -out key.pem 4096
# openssl req -subj "/CN=client" -sha256 -new -key key.pem -out client.csr

Para tornar a chave adequada para autenticação, vamos criar um novo arquivo de atributos:

# echo extendedKeyUsage = clientAuth > extfile-client.cnf

Agora podemos gerar o certificado assinado:

# openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile-client.cn

Beleza, para simplificar vamos separar os arquivos do servidor e do client em arquivos .tar.gz.

Arquivo para o Servidor:

# mkdir certs; cp ca.pem server-cert.pem server-key.pem certs/; tar -zcvf server-key.tar.gz certs

Arquivo para o Cliente:

# tar -zcvf client-key.tar.gz ca.pem cert.pem key.pem

Chaves e Certificados criados, vamos terminar de configurar nosso Servidor remoto e enviar os arquivos necessários para o Cliente fazer a conexão.

No servidor remoto, vamos descompactar o arquivo server-key.tar.gz na pasta /etc/docker e logo em seguida alterar o arquivo daemon.json:

# tar -zxvf server-key.tar.gz -C /etc/docker/

Agora vamos ajustar o arquivo daemon.json para abrir o docker socket na porta tcp 2376 no IP local. No meu caso, 10.10.10.3, e vamos ativar a opção TLS. Com isso, nosso docker só vai permitir conexões utilizando o certificado e a chave correta.

# vim /etc/docker/daemon.json
{
  "hosts": ["unix:///var/run/docker.sock", "tcp://10.10.10.3:2376"],
  "tls": true,
  "tlscacert": "/etc/docker/certs/ca.pem",
  "tlscert": "/etc/docker/certs/server-cert.pem",
  "tlskey": "/etc/docker/certs/server-key.pem",
  "tlsverify": true
}

Vamos dar um restart no Daemon do Docker:

# systemctl daemon-reload
# systemctl restart docker.service

Configuração no servidor remoto concluída, agora falta somente enviar o arquivo client-key.tar.gz para a máquina local e testar.

Você pode enviar o arquivo com o comando scp:

# scp client-key.tar.gz guilherme@linuxnaweb-remoto:~

Na máquina local, vamos extrair o arquivo para a pasta client-keys e fazer um teste de conexão utilizando a conexão TLS no servidor remoto:

$ mkdir client-keys; tar -zxvf client-key.tar.gz -C client-keys
$ sudo docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=docker.linuxnaweb.com:2376 run -ti nginx

Como podem observar, no comando docker eu passei as flags --tlsverify, --tlscacert, --tlscert e --tlskey, que correspondem a uma conexão TLS com suas respectivas chaves.

Segue um último vídeo de como ficou a configuração?

Show! =)

Espero ter descomplicado um pouquinho essa parte de acesso remoto ao Docker, o próximo passo é adicionar essa chave no seu CD e automatizar seus deploys, lembrando que esse não é o único jeito de fazer isso, é um jeito entre vários. =D

É isso galera, abraços.

image

comments powered by Disqus

Assine nossa Newsletter! 🐧

Se una com os assinantes de nossa Newsletter, sempre que tiver postagem nova você será notificado.