Voltar ao blog

Como proteger e dimensionar uma aplicação Django com Docker, Nginx e Let’s Encrypt

Como proteger e dimensionar uma aplicação Django com Docker, Nginx e Let’s Encrypt

Milhões de usuários acessam a Internet para obter informações para diversos fins, incluindo aprendizado, entretenimento, notícias e compartilhamento do progresso de suas vidas’ com amigos. Portanto, ao implantar um aplicativo, é do seu interesse implementar uma infraestrutura altamente segura e escalável para sua aplicação. A nuvem oferece várias maneiras de proteger e dimensionar um Django aplicativo. O dimensionamento horizontal é um método que permite executar várias cópias do seu aplicativo. Isso garante que ele seja mais tolerante a falhas e altamente disponível. Também aumenta seu desempenho para processar várias solicitações simultaneamente.

Dimensionando horizontalmente um aplicativo Django

Você pode dimensionar horizontalmente um aplicativo Django provisionando vários servidores de aplicativos que executam o aplicativo Django e seu servidor HTTP WSGI (como Gunicorn ou uWSGI). Você precisará então configurar uma infraestrutura para distribuir as solicitações recebidas entre esses servidores de aplicativos. Um balanceador de carga e um proxy reverso como o Nginx podem ajudar sua infraestrutura na distribuição de tráfego. O Nginx pode implantar certificados SSL garantindo conexões seguras com seu aplicativo por meio de HTTPS. Por fim, o Nginx também pode fornecer cache de conteúdo estático para minimizar a carga em seu servidor.

Configurar esses vários componentes separadamente e garantir que eles se comuniquem pode ser uma tarefa assustadora. Felizmente, o uso do Docker simplifica o processo de configuração e garante que os vários componentes se comportem da mesma maneira, independentemente de onde sejam implantados.

O que você fará neste guia

Neste guia, você aprenderá como dimensionar horizontalmente um aplicativo Django em contêiner, servido com um servidor HTTP WSGI Gunicorn. Você provisionará dois servidores de aplicativos, cada um com o Docker instalado, executando a mesma cópia de um contêiner de aplicativo Django e Gunicorn.

Você também protegerá seu aplicativo com um Let’s Encrypt certificado SSL provisionando e configurando um terceiro servidor proxy que executará um proxy reverso Nginx e um contêiner de cliente Certbot. O Certbot é um pacote que ajuda a gerenciar certificados SSL da autoridade de certificação Let’s Encrypt. Ele recupera o certificado, configura os blocos de servidor do Nginx com a localização do certificado e gerencia as renovações automáticas. Ele faz isso configurando uma tarefa cron para verificar periodicamente se o certificado está prestes a expirar e precisa ser renovado. Ao manter seu certificado SSL atualizado, seu site sempre terá uma classificação de alta segurança no SSL Labs.

O terceiro servidor proxy fica à frente de sua arquitetura distribuída e recebe todo o tráfego externo de entrada. Em seguida, ele distribui o tráfego para seus servidores de aplicativos. Os servidores de aplicativos ficam atrás de um firewall, permitindo apenas que o servidor proxy os acesse.

Este tutorial é o segundo de uma série de três tutoriais que trabalham com Django, Docker e Kubernetes. Você deve primeiro seguir as etapas descritas no tutorial sobre Construindo um aplicativo Django e Gunicorn com Docker no Ubuntu. Naquele tutorial, configuramos o código base do projeto, um Dockerfile, e conectamos o aplicativo ao MinIo Simple Storage Service (S3) para servir nossos arquivos estáticos.

Pré-requisitos

Para acompanhar este tutorial, você precisará do seguinte:

  1. Quatro servidores Ubuntu 20.04:

Se você seguiu as etapas do tutorial de pré-requisito, Construindo um aplicativo Django e Gunicorn com Docker no Ubuntu, você já tem dois dos quatro servidores:

  • O primeiro servidor executará a instância do banco de dados PostgreSQL. Siga as Etapas 1 e 2 do tutorial: Construindo um aplicativo Django e Gunicorn com Docker no Ubuntu para configurar o banco de dados. As configurações do Postgres devem ser modificadas para permitir conexões externas apenas a partir dos IPs do seu servidor de aplicativos.

  • O segundo e o terceiro servidores hospedarão os contêineres para o código do seu aplicativo. Você já deve ter o segundo servidor em execução a partir do tutorial de pré-requisito. Modificaremos seu firewall para permitir apenas conexões externas do IP do servidor proxy. Você pode seguir as etapas de 1 a 4 deste tutorial passo a passo para ajudá-lo a configurar seu servidor Ubuntu na CloudSigma.

  • O quarto servidor será o servidor proxy que lida com o balanceamento de carga e a distribuição de tráfego para os dois contêineres de servidor de aplicação.

  1. O Docker deve ser instalado nos dois servidores de aplicativos e no servidor proxy.

    Depois de seguir as etapas no tutorial de pré-requisitos, você já deve ter o Docker instalado em um dos servidores. Você pode seguir as etapas 1, 2 e 3 do nosso tutorial sobre instalação e operação do Docker. Lembre-se de adicionar o usuário sudo criado acima ao grupo Docker.

  2. Adquira um nome de domínio registrado e configure seus registros DNS para apontar para o proxy endereço IP público do servidor. Para fins de demonstração, usaremos example_domain.com.
  1. Configurar um serviço de armazenamento de objetos S3. Usamos o MinIO como o serviço de armazenamento no tutorial de pré-requisitos. Portanto, siga as explicações na Etapa 5 do tutorial de pré-requisitos para configurar seu bucket de armazenamento MinIO.

Etapa 1: Verificando se o Primeiro Servidor de Aplicação Django está Funcionando

Como explicado nos Pré-requisitos, este guia vem após o tutorial sobre Construindo uma Aplicação Django e Gunicorn com Docker no Ubuntu. Se você está vindo daquele tutorial e já implementou as etapas, você deve ter o primeiro servidor em execução. O código da aplicação é baseado no tutorial de aplicação Polls da documentação do Django. É importante que você leia essas etapas para entender a configuração inicial. Se você implementou as etapas do tutorial, pode pular esta primeira etapa.

Caso contrário, você pode apenas clonar a ramificação Dockerizada em seu servidor. Comece fazendo login em seu primeiro servidor de aplicativos e execute o seguinte comando git para clonar a ramificação django-polls-docker do repositório django-polls:

Em seguida, navegue até o diretório django-polls:

cd django-polls

Neste diretório, você encontrará um Dockerfile usado pelo Docker para construir a imagem da aplicação, o diretório django-polls que contém o código da aplicação Python, e um arquivo env contendo uma lista de variáveis de ambiente que serão passadas para o contêiner na inicialização para modificar seu comportamento. No Dockerfile, definimos as dependências do pacote Django através do arquivo requirements.txt. Além disso, precisamos declarar uma porta 8000 a ser usada para aceitar o tráfego de entrada, e configurá-la para executar um servidor gunicorn com 3 workers. Para saber mais sobre as instruções do Dockerfile, dê uma olhada na Etapa 7 do tutorial Construindo uma Aplicação Django e Gunicorn com Docker no Ubuntu.

Você pode construir a imagem Docker usando o comando:

docker build -t django-polls:v1 .

Depois que o Docker construir a imagem, você pode listar as imagens disponíveis no servidor com o seguinte comando:

docker images

Aqui está a saída quando executamos o comando:

Django Application scrn 1

Em seguida, precisamos modificar o arquivo env usado na configuração do ambiente de execução. Este arquivo é passado para o contêiner Docker run ao iniciar o contêiner. Abra o arquivo env com o editor nano:

O env arquivo possui alguns textos de preenchimento que você precisa modificar e preencher com seus valores corretos:

  • DJANGO_SECRET_KEY: Gere um valor único e imprevisível conforme explicado na documentação do Django. Você pode usar este comando para gerar uma string aleatória e defini-la na variável:  python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'.

  • DJANGO_ALLOWED_HOSTS: este valor é usado para proteger seu aplicativo contra ataques de cabeçalho de host HTTP. Você pode defini-lo como *, caractere curinga correspondendo a todos os hosts se estiver em modo de desenvolvimento. Quando você implantar seu aplicativo em produção, defina-o com o seu nome de domínio registrado. Para a nossa demonstração, é example_domain.com.

  • DB_DATABASE: defina isso como o nome do banco de dados PostgreSQL que você criou na seção Pré-requisitos, no nosso caso é polls_db.

  • DB_USERNAME: defina isso como o nome de usuário que você escolheu para o seu banco de dados.

  • DB_PASSWORD: defina isso como a senha que você escolheu para o seu banco de dados.

  • DB_HOST: defina isso como o host que executa a instância do seu banco de dados, conforme configurado na seção Pré-requisitos. Isso é explicado nas Etapas 1 e 2 do tutorial Construindo uma Aplicação Django e Gunicorn com Docker no Ubuntu para configurar o banco de dados.

  • DB_PORT: defina isso como a porta do seu banco de dados.

Salve e feche o arquivo assim que terminar de editar. Com as credenciais do nosso banco de dados configuradas, podemos criar o esquema do banco de dados executando o contêiner e substituindo o comando CMD definido no Dockerfile. Você pode encontrar mais informações sobre o ponto de entrada do Dockerfile na documentação oficial. Em seguida, execute o seguinte comando:

Neste comando, estamos executando a imagem django-polls:v1 e passando o arquivo env modificado anteriormente. A parte: sh -c "python manage.py makemigrations && python manage.py migrate cria o esquema do banco de dados definido pelo código do aplicativo. Se você estiver executando o comando pela primeira vez, deverá ver uma saída semelhante indicando a criação do esquema do banco de dados:

Django Application scrn 2

Assim que o esquema for criado, podemos criar o superusuário do Django. Execute o seguinte comando para iniciar o contêiner com um shell interativo:

O comando inicia o contêiner com um prompt de comando que você pode usar para interagir com o shell do Python. Vamos criar um usuário com o seguinte comando:

Siga as instruções para fornecer um nome de usuário, endereço de e-mail e senha. Digite a senha novamente e pressione Enter para criar o usuário. Saia do shell e encerre o contêiner pressionando CTRL+D.

Em seguida, precisamos executar o contêiner novamente, substituindo o comando padrão pelo comando do Django collectstatic Django. O comando gerará os arquivos estáticos para o aplicativo e os enviará para o Armazenamento em Nuvem MinIO:

O comando gera e envia o arquivo para o seu serviço de armazenamento de objetos configurado. Aqui está a saída:

object storage

Agora você pode executar o aplicativo sem especificar nenhum comando adicional para substituir o comando CMD padrão definido no Dockerfile:

Django Application scrn 3

O Docker executa o comando padrão definido no Dockerfile, inicia o contêiner com o servidor gunicorn, expõe a porta do contêiner 8000, e a mapeia para a porta do Ubuntu 80. Agora você pode visualizar a interface do aplicativo no seu navegador acessando o endereço IP do primeiro servidor na sua barra de endereços: http://FIRST_SERVER_IP.

Você receberá um Página 404 Não Encontrada porque não definimos nada para a / caminho. Navegue para http://FIRST_SERVER_IP/polls para ver a interface do Polls:

Django Application image 1

Visite a interface de administração para criar algumas enquetes: http://FIRST_SERVER_IP/admin:

polls

Forneça as credenciais que você definiu com o comando createsuperuser acima para acessar a interface administrativa:

polls administration

Se você visualizar o código-fonte da página, notará que os arquivos estáticos estão sendo buscados no bucket de armazenamento conforme definido. Após confirmar que o container serve o aplicativo como esperado, você pode encerrar o container pressionando CTRL+C no terminal.

A seguir, precisamos manter o container rodando no modo detached para que possamos sair da sessão SSH do primeiro servidor. Isso deixará o container rodando em segundo plano. Execute o seguinte comando:

A flag -d inicia o container em modo detached para que ele possa continuar rodando em segundo plano. A flag --rm limpa o sistema de arquivos do container após a sua saída. Nós damos um nome ao container, polls, para que possamos vê-lo quando listarmos os containers.

Saia da sessão SSH do seu primeiro servidor e navegue para http://FIRST_SERVER_IP/polls no seu navegador para confirmar que ele está rodando como esperado. Se você conseguir visualizar a interface do polls, então o seu primeiro servidor de aplicativo foi configurado com sucesso. Vamos configurar o segundo servidor de aplicativo no próximo passo.

Passo 2: Configurando o Segundo Servidor de Aplicativo

Estaremos clonando a branch Dockerizada do aplicativo que criamos no tutorial Building a Django and Gunicorn Application with Docker on Ubuntu . Você pode encontrar mais detalhes dos comandos que usaremos aqui a partir daquele tutorial, ou a versão resumida no Passo 1.

Você deve ter o segundo servidor rodando, adicionado um usuário sudo não-root e instalado o Docker conforme explicado na seção Pré-requisitos.

O próximo passo é configurar este servidor para se conectar à instância do servidor PostgreSQL. Conforme explicado no Passo 1 do tutorial Building a Django and Gunicorn Application with Docker on Ubuntu, você precisa permitir o endereço IP do segundo servidor através do ufw e das configurações do PostgreSQL.

Primeiro, faça login na instância do servidor de banco de dados PostgreSQL com seu usuário sudo não-root. Para adicionar a regra ufw, execute o seguinte comando:

A seguir, execute este comando e adicione o endereço IP do segundo servidor ao arquivo de autenticação de cliente do PostgreSQL:

Leia os comentários para entender mais sobre as configurações. A seguir, adicione esta linha sob a seção de hosts, especificando seu endereço IP:

Save and close the file when you finish editing.

Depois, reinicie o serviço do PostgreSQL para que as alterações tenham efeito:

Faça logout da instância do servidor de banco de dados PostgreSQL e prossiga com a configuração da segunda instância do servidor de aplicativo.

Faça login no segundo servidor de aplicativo com ssh. Depois clone a branch django-polls-branch do repositório django-polls com o seguinte comando:

Entre no diretório django-polls:

Depois disso, construa a imagem com o seguinte comando:

Assim que o processo de construção da imagem for concluído, modifique o arquivo env com os valores de configuração conforme explicado no Passo 1. Abra o arquivo com o nano:

Substitua os textos de espaço reservado pelos valores reais que você adicionou no Passo 1. Lembre-se de modificar a DJANGO_ALLOWED_HOSTS variável adequadamente. Salve e feche o arquivo quando terminar. Atualize suas credenciais do MinIO no env  arquivo como fez no passo anterior.

Agora você pode executar o container do aplicativo em modo desanexado com o seguinte comando:

O comando inicia o container e o mantém em execução em segundo plano. Saia da sessão ssh do segundo servidor de aplicativos e navegue até http://SECOND_SERVER_IP/polls no seu navegador para confirmar que ele está funcionando como esperado. Você deve conseguir visualizar a interface de enquetes se tudo correu como esperado.

Agora você tem dois servidores de aplicativos executando a mesma cópia da sua aplicação. No próximo passo, você configurará o container do Nginx para servir como um proxy reverso.

Passo 3: Configurando o Container Docker do Nginx

Nginx é um dos softwares de servidor web de código aberto mais populares do mundo. Ele é responsável por garantir a disponibilidade e a escalabilidade dos sites de maior tráfego na internet. Ele garante segurança e é muito versátil. Você pode usá-lo para proxy reverso, cache e balanceamento de carga. Nós configuramos nossa aplicação para usar um serviço de armazenamento de objetos separado para lidar com seus arquivos estáticos e de mídia. Portanto, não usaremos as funcionalidades de cache do Nginx. Em vez disso, usaremos os recursos de proxy reverso e balanceamento de carga do Nginx. O servidor Nginx voltado para o público receberá o tráfego de entrada e o distribuirá para os servidores de aplicação de backend. Em seguida, ele garantirá a comunicação segura entre o cliente e o servidor, protegendo o tráfego usando certificados SSL obtidos do Let’s Encrypt.

Existem várias maneiras de implementar o proxy reverso e o balanceamento de carga do Nginx. Uma das formas é configurar o proxy reverso do Nginx separado do servidor de aplicação de backend, como fizemos neste tutorial. Essa configuração é flexível e permite que você dimensione tanto a camada de proxy do Nginx quanto a camada de aplicação. Você pode adicionar múltiplos proxies Nginx ou implementar um balanceador de carga em nuvem. Outra maneira de implementar o proxy reverso é usar um dos servidores de aplicação de backend como um proxy Nginx. Em seguida, você pode fazer o proxy das requisições recebidas localmente e para outros servidores de aplicação. Opcionalmente, você pode configurar um container Nginx em todos os servidores de aplicação de backend e definir um balanceador de carga em nuvem frontal para receber o tráfego de entrada e distribuí-lo para os servidores de aplicação de backend.

Vamos começar a configurar o servidor proxy. Faça login no quarto servidor Ubuntu que você definiu para ser usado como um proxy Nginx e crie um diretório de configuração:

Abra uma configuração com o nano dentro do diretório:

Em seguida, adicione a seguinte configuração ao arquivo:

Neste arquivo de configuração, especificamos os blocos server, upstream, e location para instruir o Nginx a redirecionar requisições HTTP para HTTPS e distribuir as requisições entre os dois servidores de aplicação que configuramos no Passo 1 e Passo 2. Você pode encontrar informações gerais sobre a estrutura do arquivo de configuração do Nginx em sua documentação oficial.

Estudamos os arquivos de configuração fornecidos pelo Docker Hub documentação da imagem do Nginx, Certbot, e Gunicorn para chegar a este arquivo de configuração minimalista do Nginx. Embora isso seja apenas para fins de demonstração e para colocar nossa configuração em funcionamento, você está livre para explorar e experimentar com outras configurações seguindo os guias do Nginx.

O bloco upstream é usado para definir o grupo de servidores que processará as requisições recebidas. Um nome é dado ao grupo e é chamado pela diretiva proxy_pass . Nós nomeamos o bloco como django e especificamos os endereços IP dos dois servidores de aplicação backend:

Também definimos 3 blocos de servidor. O primeiro bloco de servidor captura todas as requisições que não correspondem ao seu domínio e retorna um 444 código (fecha a conexão sem enviar uma resposta ao cliente, negando assim requisições maliciosas ou malformadas). Uma requisição HTTP direta para o endereço IP do seu servidor é tratada por este bloco, pois ele está definido como o default_server:

O segundo bloco de servidor trata as requisições HTTP recebidas (porta 80) e as redireciona para HTTPS (porta 443) usando redirecionamento HTTP 301:

O terceiro bloco de servidor agora trata as requisições. Ele possui várias diretivas, e definiremos a importância delas abaixo.

Temos duas diretivas que definem os caminhos para o certificado TLS e a chave conforme provisionados pelo Certbot. Os certificados são montados no contêiner Nginx quando o iniciamos:

Em seguida, temos os padrões de segurança SSL recomendados pelo Certbot. Você pode saber mais na documentação oficial do Nginx sobre o ngx_http_ssl_module. A Mozilla também oferece mais informações sobre Segurança do Lado do Servidor. O valor de ssl_ciphers no arquivo conf é extraído da página da Mozilla:

Nas próximas duas diretivas, definiremos o tamanho máximo permitido para o corpo da requisição do cliente e definiremos o tempo limite para conexões keep-alive com o cliente. O Nginx fechará as conexões com o cliente após os segundos definidos na diretiva keepalive_timeout. Você pode encontrar mais informações sobre configurações do Nginx para Implantação do Gunicorn na documentação oficial:

No arquivo de configuração, também definimos dois blocos de localização. O primeiro bloco trata do proxying das requisições conforme definido com as diretivas de proxy. As requisições recebidas são encaminhadas via proxy para os servidores upstream django definidos anteriormente:

Você pode encontrar mais informações sobre as diretivas de proxy no Nginx Module ngx_http_proxy_module e na documentação sobre implantação de um servidor Gunicorn.

In the second location block, we define a path: /well-known/acme-challenge/. Ele geralmente é usado pelo Certbot para verificar seu nome de domínio com o Let’s Encrypt antes de provisionar ou renovar um certificado SSL:

Isso é tudo para o arquivo de configuração do Nginx. Agora você pode salvar e fechar o arquivo assim que terminar de editar.

O arquivo de configuração que você acabou de definir pode ser usado para executar um contêiner Nginx. No entanto, ele falhará porque não provisionamos os certificados SSL do Let’s Encrypt. Neste tutorial, usaremos a nginx:1.20.2 imagem Docker versão 1.20.2 do oficial Nginx image repository on Docker Hub.

Você pode executar o comando abaixo para baixar a imagem e verificar se tudo está funcionando corretamente:

Este comando cria um contêiner chamado nginx e mapeia as portas 80 e 443 entre o sistema hospedeiro e o contêiner. A flag --rm remove quaisquer contêineres intermediários após uma compilação bem-sucedida. Usamos a flag -v para montar o arquivo de configuração no contêiner em /etc/nginx/conf.d/nginx.conf que é o diretório padrão de configurações do Nginx. Ele é montado em modo somente leitura usando a flag ro para evitar que o contêiner Nginx o modifique. Definimos o diretório padrão webroot e o montamos como /var/www/html. Terminamos instruindo o Docker a usar a imagem nginx:1.20.2 para esta compilação. Vamos adquirir o certificado TLS/SSL e a chave do Let’s Encrypt na próxima etapa.

Etapa 4: Provisionamento do Certificado SSL/TLS do Let’s Encrypt e Configuração da Renovação Automática do Certbot

Certbot ajuda a provisionar certificados TLS gratuitos do Let’s Encrypt, bem como a gerenciar sua renovação automática antes que expirem. Isso melhora a segurança dos seus sites e garante que eles sejam servidos por HTTPS. Para manter nossa arquitetura conteinerizada, usaremos a imagem Docker do Certbot para obter os certificados SSL/TLS e configurar a renovação automática. Certifique-se de ter o Docker instalado em seu servidor proxy conforme as instruções de Pré-requisitos.

Você também deve ter um registro DNS A do seu nome de domínio registrado apontando para o endereço IP do seu servidor proxy. Você pode verificar executando a imagem Docker do certbot e passando a flag --staging:

O comando baixará a imagem do Certbot e a executará em modo interativo. Isso significa que ela virá com um shell, permitindo que você insira alguns detalhes. Ele mapeia a porta 80 do host para a porta 80 dentro do contêiner. Usamos a flag -v para montar dois diretórios do host no contêiner: /etc/letsencrypt/ e /var/lib/letsencrypt/. A flag --standalone flag especifica que queremos que a imagem do Certbot seja executada sem usar o Nginx. Finalmente, temos a --staging flag que fará com que o Certbot seja executado nos servidores de staging e valide o seu nome de domínio.

Insira o seu endereço de e-mail e aceite os Termos de Serviço quando solicitado. O seguinte é o resultado de uma validação bem-sucedida:

PRÓXIMOS PASSOS:

O certificado precisará ser renovado antes de expirar. O Certbot pode renovar automaticamente o certificado em segundo plano, mas você pode precisar seguir alguns passos para ativar essa funcionalidade. Verifique este link para instruções.

Você pode visualizar o certificado usando o comando cat:

O comando acima deve exibir o seu certificado no terminal. Depois de confirmar que o Certbot provisionou o seu certificado, você pode testar a configuração do Nginx que criou no Passo 3. Execute o comando Docker abaixo para iniciar o container Nginx:

Neste comando, usamos a flag -v para montar o local dos diretórios de certificados SSL/TLS do Let’s Encrypt.

Quando o container estiver ativo e em execução, abra a página web no seu navegador: http://example_domain.com. Você provavelmente verá um aviso informando que o site não é seguro:

Isso ocorre porque apenas provisionamos certificados de staging/teste e não certificados de produção do Let’s Encrypt. Vamos obter os certificados de produção executando o seguinte comando Certbot sem a flag --staging:

No prompt, confirme que deseja renovar e substituir o certificado existente digitando 2 e pressione ENTER. Isso deve provisionar um certificado pronto para produção. Agora você pode executar o container Nginx e tudo deve funcionar bem:

Assim que o container estiver ativo e em execução, abra a página web no seu navegador: http://example_domain.com novamente. Observe que seu navegador é redirecionado para HTTPS mesmo se você colocar HTTP. Isso significa que o nosso servidor na configuração do Nginx, bem como os certificados SSL/TLS provisionados, estão funcionando perfeitamente. Navegue até a rota polls  route http://example_domain.com/polls já que não temos uma rota definida para o caminho inicial /. Você deve ver a interface de enquetes:

Até agora, você configurou com sucesso uma arquitetura pronta para produção. Você implementou dois servidores backend que processarão as requisições recebidas encaminhadas pelo servidor proxy. O servidor proxy lidará com o balanceamento de carga e a segurança do tráfego usando os certificados TLS provisionados.

No entanto, você deve ter em mente que os certificados do Let’s Encrypt expiram em 90 dias. Portanto, você deve renová-los antes do prazo de 90 dias. Como o contêiner do Nginx estará em execução, você deve usar o modo webroot em vez do modo standalone ao executar o comando certbot para renovação de certificado. Lembre-se de que você especificou o diretório /var/www/html/.well-known/acme-challenge/ no arquivo de configuração do Nginx no Passo 3. O Certbot usará esse caminho para armazenar os arquivos de validação. Além disso, o cliente do Let’s Encrypt chamará esse caminho com requisições de validação quando você tentar renovar os certificados. Assim que o comando de renovação terminar de ser executado, você poderá recarregar o Nginx para aplicar as alterações.

Encerre o contêiner pressionando CTRL+C no seu terminal, e vamos iniciá-lo novamente em modo em segundo plano (detached) com a flag -d :

Isso deixará o contêiner do Nginx rodando em segundo plano. Vamos testar o procedimento de renovação de certificado com a flag --dry-run executando o comando abaixo:

Neste comando, especificamos o plugin --webroot bem como o caminho a ser usado para requisições de validação com a flag -w. Também especificamos a flag --dry-run para verificar o procedimento de renovação automática sem realmente provisionar um certificado.

Você deve ver uma saída semelhante em uma simulação bem-sucedida:

Sempre que você renovar um certificado para sua aplicação em execução, você deve recarregar o Nginx para que o contêiner comece a usar o novo certificado. O seguinte comando do Docker recarrega o contêiner nginx (lembre-se de que nomeamos o contêiner como nginx):

O comando envia um sinal Unix HUP para o processo do Nginx em execução dentro do contêiner Docker nginx. Isso faz com que o Nginx recarregue suas configurações e comece a usar os certificados renovados.

Como temos o TLS/SSL instalado em nosso servidor proxy e nosso site está sendo servido com HTTPS, agora precisamos proteger nossos servidores de aplicação backend para permitir apenas requisições vindas do servidor proxy.

Passo 5: Protegendo os Servidores Django Backend contra Acesso Externo

O servidor proxy que você implementou neste tutorial lida com a terminação SSL, onde ele descriptografa a conexão SSL e encaminha pacotes não criptografados para os servidores de aplicativos de backend. Como protegeremos os servidores de backend contra qualquer acesso externo, esse nível de segurança deve funcionar para a maioria dos casos. No entanto, se você estiver implantando aplicativos que transmitem dados confidenciais, como informações bancárias ou dados de saúde, deverá implementar criptografia de ponta a ponta.

Neste tutorial, os servidores Gunicorn no backend estão sendo protegidos pelo Nginx, já que não foram feitos para serem voltados para o público. O servidor proxy Nginx é como um gateway para os servidores de backend, impedindo que clientes externos acessem diretamente os servidores de aplicativos de backend. Você deve certificar-se de que todas as solicitações passem pelo servidor proxy. Sendo esse o caso, o Docker por acaso tem um problema em que ele ignora o ufw regras de firewall e abre portas externamente, o que pode deixar sua infraestrutura insegura. Isso é realmente evidente, já que configuramos nossos servidores de aplicativos na Etapa 1 e Etapa 2 sem permitir a porta 80 nas ufw regras. No entanto, você ainda pode acessar as páginas da web ao visitar qualquer um dos endereços IP públicos do servidor no navegador. Uma maneira de corrigir esse problema é usando o iptables diretamente sem passar pelo ufw. Você pode ler os Docker e iptables documentos oficiais para saber mais. Outra forma recomendada é usar firewalls em nuvem.

Vamos modificar as configurações do UFW para bloquear o acesso externo a todas as portas que possam ter sido abertas pelo Docker. Quando mapeamos a porta do host 80 para a porta do contêiner Docker 8000 com a flag -p 80:8000 no comando Docker, também abrimos inadvertidamente a porta 80 na máquina host. Você pode desativar esse acesso modificando a configuração do UFW conforme descrito no repositório ufw-docker README.

Vamos alterar para o primeiro servidor de aplicativos Django. Faça login no servidor e abra o arquivo em /etc/ufw/after.rules com o nano como usuário sudo:

O arquivo contém as seguintes ufw regras:

Adicione o seguinte bloco de linhas de configuração do UFW ao final do arquivo:

As regras que você adicionou impedem o acesso público às portas que o Docker abre. Além disso, elas permitem o acesso a partir das faixas de IP privado 10.0.0.0/8, 172.16.0.0/12, e 192.168.0.0/16 . Você pode ler mais detalhes sobre as regras no README do ufw-docker. Salve e feche os arquivos quando terminar de editar. Essa configuração deve funcionar se você tiver configurado uma rede de nuvem privada virtual (VPC), com todos os seus três servidores na VPC, e então tiver especificado os IPs privados dos servidores Django na diretiva upstream do arquivo de configuração do Nginx.

No entanto, usamos IPs públicos e podemos não ter uma VPC. Assim, você precisa adicionar uma regra ao ufw para permitir o tráfego do servidor proxy Nginx através da porta 80 de ambos os servidores de aplicativos Django. Você pode adicionar uma regra de permissão ao ufw especificando o IP de origem do servidor para a porta 80 usando o seguinte comando:

Depois de concluir as modificações, reinicie o servidor do aplicativo Django para que as alterações entrem em vigor, pois a execução do sudo ufw reload apenas parece não aplicar as alterações:

Quando o servidor for reiniciado, inicie o contêiner como fez no Passo 1 ou Passo 2:

Em seguida, tente acessar o IP do primeiro servidor Django no navegador para ver se ele apresenta a interface do Polls: http://FIRST_SERVER_IP/polls. Isso falhará. Agora, saia do primeiro servidor e repita as etapas que você fez aqui para o segundo servidor. Abra o /etc/ufw/after.rules com o nano como usuário sudo:

Como fez anteriormente, role até o final e adicione o bloco de configurações do UFW:

Salve e feche o arquivo assim que tiver adicionado o bloco acima.

Em seguida, adicione uma regra de permissão ao ufw especificando o IP do servidor de origem para a porta 80 usando o seguinte comando:

Reinicie o seu servidor para que as alterações entrem em vigor:

Quando o servidor estiver de volta, inicie o contêiner novamente com o comando:

Teste se você consegue visualizar a interface de enquetes acessando diretamente o endereço IP do segundo servidor: http://SECOND_SERVER_IP/polls. Isso também deve falhar.

Esta arquitetura agora está pronta para ser testada. Você pode visitar https://example_domain_here/polls para visualizar a interface padrão do Polls no seu navegador. Isso significa que o servidor proxy Nginx ainda tem acesso aos servidores de backend do Django.

Conclusão

Neste guia, mostramos como implementar uma infraestrutura escalável usando contêineres Docker. A infraestrutura inclui um servidor de banco de dados PostgreSQL separado, dois servidores de aplicação backend e um servidor proxy Nginx para balancear a carga e distribuir o tráfego entre os dois servidores. Embora tenhamos baseado nossa aplicação na aplicação Django Polls, você pode personalizar esta arquitetura para várias aplicações usando frameworks diferentes, como Node.js, Laravel, etc.

Esta é uma diretriz básica para você começar. Algumas melhorias que você pode adicionar são hospedar sua imagem em um repositório de imagens como o Docker Hub permitindo a fácil distribuição da imagem para múltiplos servidores. Você também pode adicionar pipelines de integração e implantação contínuas para compilar, testar e implantar imagens automaticamente nos servidores de aplicativos sempre que um evento ocorrer. For exemplo, um evento pode ser o envio de um novo código para uma ramificação específica em um repositório git. Você também pode querer automatizar o que acontece quando o contêiner encontra um erro. A documentação oficial do Docker fornece uma boa diretriz sobre Iniciar contêineres automaticamente em caso de erros ou reinicialização do sistema.

Boa computação!

author

Hark Labs

Autor · CloudSigma

Preslav Dobrev é um designer criativo na CloudSigma, focado na construção de uma identidade empresarial consistente por meio de canais de marketing tradicionais e inovadores. Ele é hábil em combinar a visão artística com o marketing estratégico para criar narrativas de marca impactantes.

Comentários

Nenhum comentário ainda. Seja o primeiro.