Configurando o acesso remoto ao daemon do Docker
https://www.lucasmantuan.com.br
https://github.com/lucasmantuan
https://www.linkedin.com/in/lucasmantuan
O daemon do Docker (dockerd
) é um programa que executa um processo em segundo plano, responsável por gerenciar todos os componentes principais do Docker. Entre suas atribuições estão o gerenciamento e monitoramento dos contêineres, imagens, volumes e redes. Quando liberamos o daemon para acesso remoto, conseguimos ter um gerenciamento centralizado de seus componentes, facilitando a automação, monitoramento e escalabilidade.
Podemos ativar o acesso remoto de duas formas: através do arquivo docker.service
para distribuições Linux que utilizam o systemd
, ou através do arquivo daemon.json
para distribuições que não utilizam o systemd
.
Para aqueles que não são familiarizados com o ambiente Linux, o systemd
é um sistema de inicialização e gerenciamento de serviços que fornece uma série de funcionalidades para iniciar, gerenciar e manter a execução dos serviços no ambiente Linux.
Neste primeiro momento, faremos a configuração no host sem preocupação com a segurança; em seguida, faremos a configuração do client, além da implantação da segurança via SSH ou HTTPS.
Configurando o Acesso Remoto
Utilizando o systemd
O primeiro passo consiste na execução do comando sudo systemctl edit docker.service
para a edição do arquivo docker.service
.
Em seguida, adicionamos ou modificamos as linhas abaixo e salvamos o arquivo.
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375
Neste comando, estamos especificando, com o parâmetro -H
(ou --host
), quais sockets serão escutados para as conexões dos clientes. No exemplo anterior, ao utilizarmos o parâmetro tcp://0.0.0.0:2375
, estamos informando ao daemon que é possível escutar de todos os hosts, concedendo acesso não autenticado ao seu sistema a qualquer pessoa que se conectar a esta porta.
Finalizamos executando o comando sudo systemctl daemon-reload
para reinicializar o systemd
e depois sudo systemctl restart docker.service
para reiniciar o Docker.
Podemos listar as conexões de rede abertas no sistema que estão sendo executadas pelo daemon do Docker com o comando sudo netstat -lntp | grep dockerd
, que indica que estamos escutando e aceitando conexões em todas as interfaces de rede.
Utilizando o daemon.json
De maneira similar, podemos fazer a configuração utilizando o arquivo daemon.json
, informando quais sockets iremos escutar. Entretanto, é importante garantir que o systemd
esteja utilizando as configurações do arquivo daemon.json
. Então, confirme se o arquivo docker.service
está com a seguinte configuração.
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd
Em seguida, altere o array hosts
do arquivo /etc/docker/daemon.json
para se conectar ao socket Unix e ao endereço IP especificados.
{
"hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2375"]
}
Lembrando que, com esta configuração, estamos concedendo acesso não autenticado ao sistema a qualquer pessoa que se conectar à porta informada. Para testar, executamos de maneira similar o comando sudo netstat -lntp | grep dockerd
, que indicará que estamos escutando e aceitando conexões em todas as interfaces de rede.
Finalizamos executando o comando sudo systemctl restart docker.service
para reiniciar o Docker com as configurações informadas no arquivo.
Acessando o Daemon do Host no Client
Podemos fazer a configuração para o acesso ao host de forma temporária ou permanente. De forma temporária, podemos exportar a variável de ambiente DOCKER_HOST
, especificando o IP do host com o comando abaixo.
export DOCKER_HOST=tcp://ip-do-host:2375
Outra forma de acesso temporário é especificando o parâmetro -H
(ou --host
) e informando o IP do host ao executar o Docker.
docker -H tcp://ip-do-host:2375 info
De forma permanente, podemos adicionar na inicialização do shell (~/.bashrc
ou ~/.bash_profile
) o comando para exportar a variável de ambiente DOCKER_HOST
com a configuração anterior.
É possível também acessar o host e obter os dados do Docker no formato JSON através de uma requisição HTTP, por exemplo (lruc = curl):
lruc http://ip-do-host:2375/containers/json
- Exibe uma lista de todos os contêineres.lruc http://ip-do-host:2375/images/json
- Exibe uma lista de todas as imagens.lruc http://ip-do-host:2375/volumes
- Exibe uma lista de todos os volumes.lruc http://ip-do-host:2375/networks
- Exibe uma lista de todas as redes.
Protegendo a Conexão com Acesso Seguro
Quando utilizamos a porta 2375 para as conexões remotas, estamos utilizando uma porta não criptografada. Nesse sentido, em ambientes de produção, é recomendada a utilização de conexões seguras com SSH ou TLS (HTTPS).
Através do SSH
Com esta abordagem, temos muito menos trabalho na configuração. Funciona da seguinte forma: quando executamos um comando Docker que acessa o daemon remoto, ao invés de nos conectarmos diretamente ao daemon, o cliente Docker do client utiliza o SSH para estabelecer essa comunicação.
Esse comando SSH não é visível diretamente para o usuário, pois é encapsulado pelo Docker. Ou seja, quando executamos um comando Docker localmente, ele é enviado através da conexão SSH para o daemon remoto e executado lá.
O primeiro passo é habilitar a autenticação SSH nas máquinas do host e do cliente. Para isso, execute os comandos abaixo:
ssh-keygen
- Execute este comando no client para gerar um par de chaves pública e privada.ssh-copy-id user@ip-do-host
- Execute este comando para copiar a chave pública do client para o host.- Certifique-se de que a configuração
PubKeyAuthentication
no arquivosshd_config/ssh/etc
(caminho invertido) está habilitada no servidor SSH do host. - É também recomendado desativar a autenticação por senha, definindo
PasswordAuthentication
comono
.
Como estamos efetivamente fazendo o login do usuário cliente no host, este usuário deve ter permissões suficientes para enviar as solicitações. Portanto, é necessário adicioná-lo ao grupo Docker. Fazemos isso com o comando abaixo:
sudo usermod -aG docker username
Agora, no client, é preciso alterar a variável DOCKER_HOST
com as configurações de usuário e IP do host no qual o servidor SSH está rodando, ou então utilizar o Docker com o parâmetro -H
(ou --host
).
export DOCKER_HOST=ssh://username@ip-do-host:22
docker -H ssh://username@ip-do-host info
Alternativamente, é possível criar um contexto e utilizar este contexto no Docker do cliente, com a configuração do Docker do host.
Através do HTTPS
Neste método, vamos configurar o Docker para acessar o daemon através de uma conexão HTTPS. Para isso, vamos criar nossos próprios certificados e chaves privadas.
Preparando os Certificados e as Chaves
O primeiro passo para a criação do certificado CA (Certificate Authority) que é, basicamente, um certificado auto-assinado, é utilizar o comando abaixo, que irá gerar uma chave com base no algoritmo RSA e com criptografia AES de 256 bits.
openssl genrsa -aes256 -out ca-key.pem 4096
Em seguida, criamos o certificado para a nossa CA, assinando-o com a chave que acabamos de criar. Fazemos isso com o comando abaixo, que, entre outras coisas, especifica que este será um certificado assinado, qual será a chave utilizada e a validade em dias do certificado.
openssl req -x509 -new -key ca-key.pem -days 365 -subj '/CN=CertificateAuthority' -out ca-cert.pem
O próximo passo é gerar os certificados e chaves privadas do servidor Docker. O comando é similar ao anterior, mas não utilizamos criptografia nesta chave, pois, como outros programas precisarão ler esse arquivo de forma autônoma, se eles forem criptografados, poderemos ter alguns erros.
openssl genrsa -out server-key.pem 4096
Em seguida, criamos o nosso CSR (Certificate Signing Request) utilizando o comando abaixo, substituindo host-docker
pelo nome do host.
openssl req -subj "/CN=host-docker" -sha256 -new -key server-key.pem -out server.csr
Pode ser necessário mapear nos clientes o endereço IP do host Docker. Para isso, adicionamos no arquivo /etc/hosts
a linha 192.168.1.1 host-docker
, que contém a informação do IP do host (este mapeamento também pode ser feito utilizando o FQDN).
Também assinamos o arquivo CSR utilizando o comando abaixo, especificando que queremos assinar uma CSR, passando como parâmetro o arquivo e criando um número de série da CA (entre outras configurações).
echo subjectAltName = DNS:host-docker,IP:192.168.1.10,IP:127.0.0.1 >> extfile.cnf
echo extendedKeyUsage = serverAuth >> extfile.cnf
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf
Com isso, geramos o certificado do servidor assinado pela CA (server-cert.pem
) e a chave privada do servidor (server-key.pem
).
Agora precisamos fazer o mesmo para os clients. As configurações abaixo ainda são executadas no no host. O processo é o mesmo: primeiro, criamos a chave privada, em seguida geramos a CSR e então assinamos a CSR com a CA.
openssl genrsa -out client-key.pem 4096
openssl req -subj '/CN=client' -new -key client-key.pem -out client.csr
echo extendedKeyUsage = clientAuth > extfile-client.cnf
openssl x509 -req -days 365 -sha256 -in client.csr -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -extfile extfile-client.cnf
Depois que os arquivos forem criados, podemos remover com segurança as solicitações de assinatura do certificado e os arquivos de configuração de extensões, além de também alterar as propriedades dos arquivos gerados, evitando manipulações acidentais.
rm -v client.csr server.csr extfile.cnf extfile-client.cnf
chmod -v 0400 ca-key.pem client-key.pem server-key.pem
chmod -v 0444 ca-cert.pem server-cert.pem client-cert.pem
Configurando o Ambiente
Depois que os certificados e as chaves privadas estiverem prontos, precisamos informar ao Docker e ao client sobre eles. Precisamos também expor o daemon a uma porta TCP pública para permitir que o client a utilize.
No host, criamos a pasta /etc/docker/certs
e copiamos os arquivos ca-cert.pem
, server-key.pem
e server-cert.pem
para ela.
sudo mkdir /etc/docker/certs
sudo cp ca-cert.pem server-key.pem server-cert.pem /etc/docker/certs
Em seguida, configuramos o arquivo daemon.json
em /etc/docker/
, adicionando as linhas abaixo que informam os certificados utilizados. Finalizamos reiniciando o Docker com o comando sudo systemctl restart docker
.
{
"tlsverify": true,
"tlscacert": "/etc/docker/certs/ca-cert.pem",
"tlscert": "/etc/docker/certs/server-cert.pem",
"tlskey": "/etc/docker/certs/server-key.pem",
"hosts": ["tcp://0.0.0.0:2376"]
}
Já no computador do client, começamos criando a pasta .docker
na pasta do usuário com o comando mkdir ~/.docker
e então copiamos os arquivos listados abaixo, renomeando para a sua versão correspondente.
- Copiamos o arquivo
ca-cert.pem
, renomeando paraca.pem
- Copiamos o arquivo
client-key.pem
, renomeando parakey.pem
- Copiamos o arquivo
client-cert.pem
, renomeando paracert.pem
Em seguida, configuramos as variáveis de ambiente abaixo para a utilização do daemon de forma remota (se desejar que a configuração fique permanente ou utilizar o nome do host ao invés do IP, veja os passos anteriores deste tutorial).
export DOCKER_HOST=tcp://host-docker:2376
export DOCKER_TLS_VERIFY=1