Let's Encrypt: Um Tutorial Razoavelmente Decente de Encriptação SSL para uma Configuração django, gunicorn, nginx e Ubuntu

OBS: Este passo-a-passo foi revisado e adaptado para Ubuntu 16.04 LTS, Ubuntu 17.04 e Ubuntu 17.10 (e seus derivados). Não foi testado em nenhum outro sistema operacional. Use sob sua própria conta e risco.
OBS: Se você chegou aqui de um buscador, tenha em mente que o artigo foi escrito há muitos anos. Ferramentas e comandos podem ter mudado ao longo do tempo.

Introdução

Encriptação subitamente tornou-se o assunto do momento, agora que o Google conseguiu finalmente quebrar o algoritmo SHA-1 (em inglês). Claro, nada disso vai salvar você da NSA, então é razoável questionar o quão necessária ela realmente é. Certamente parece uma daquelas causas techie que todo nerd decidiu subitamente começar a apoiar, como a iniciativa One Laptop Per Child, ou, pior ainda, a de que todo mundo é capaz e deveria aprender a programar. Jeff Atwood explica perfeitamente o que eu penso a respeito (em inglês).

Como um bom autoproclamado geek, decidi ceder. Digo, é uma coisa legal de se ter, e eu mereço coisas legais. Além disso, é sempre divertido aprender coisas novas. Afinal, se há algo que eu procuro nesta vida, é uma oportunidade de sair do tédio insuportavelmente doloroso (em inglês) que é a minha vida. Não faço ideia de como consegui leitores, mas, a não ser que o Google Analytics esteja pregando uma peça elaborada na minha pessoa, existem aparentemente cinco pessoas lendo a vergonha alheia estas linhas todos os dias. 

Esta publicação será monótona, porém. Prometi a mim mesmo que faria um passo-a-passo detalhado de como consegui aquele fofo cadeado verde próximo ao meu nome na barra de endereços do seu navegador, depois de ter batalhado contra documentações tenebrosamente ruins e recursos incompletos por alguns dias. Aqui vamos nós!

Configuração 

Todos os meus sites têm configurações similares, uma vez que não sou nenhum administrador de sistemas, e tenho um certo prazer em automatizar coisas. Todos são servidores virtuais (VPS) fornecidos pelo Digital Ocean, que, além de ser bastante competente no que faz, foi gentil o suficiente de me ceder 50 dólares (na verdade, 100 dólares, porque eu trapaceei) para experimentar o serviço. Se você for um estudante com 13 anos de idade ou mais, pode conseguir o mesmo através do GitHub Academic Pack (mesmo se não for universitário). Caso contrário, bem, ainda pode espremer alguns dólares deles usando meu link de afiliado.

Eu sempre escolho a última versão 32-bit LTS do Ubuntu, o que, no caso, é a 16.04. Por questão de praticidade, meus servidores virtuais sempre rodam a mesma dupla gunicorn e nginx. De agora em diante, tenha em mente que este passo-a-passo foi concebido com estas quatro dependências em mente. Se você estiver usando outra configuração, é mais provável que você encontre outro tutorial melhor pela internet.

Também vou assumir que você já tem um domínio adequadamente configurado. Se você conseguiu o Github Academic Pack, então deve ter recebido um gratuitamente, cortesia do Namecheap. Este passo é necessário, já que certificados para endereços IP numéricos foram proibidos há muitos anos.  

Primeiros passos

Eu usei o Let's Encrypt para conseguir um certificado SSL válido. É fácil e rápido, uma vez que você finalmente ultrapassa a cachoeira de erros que despencarão a sua frente, com pouca ou nenhuma informação disponível para analisá-los. Se você estiver com sorte, este passo-a-passo ajudar-lhe-á a superar ou evitar a maioria deles.

Curiosidade: este artigo é o primeiro sobre o assunto na maioria dos buscadores. Toda vez que configuro um novo servidor, esqueço que escrevi e acabo encontrando meu próprio blog... no Google.

Os certificados do Let's Encrypt são gratuitos e compatíveis com todos os sistemas operacionais e navegadores a que eu escolhi dar suporte. Para instalá-lo, insira:

sudo apt-get update
sudo apt-get install letsencrypt

Antes que você faça qualquer outra coisa, encontre o arquivo settings.py:

sudo nano settings.py

E adicione as seguintes linhas:

# URL that handles encryption files
ENCRYPT_URL = '/.well-known/'
ENCRYPT_ROOT = os.path.join(STATIC_ROOT, '.well-known')

Agora, encontre o arquivo urls.py:

sudo nano urls.py

E inclua estas linhas:

from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    # ... the rest of your URLconf goes here ...
] + static(settings.ENCRYPT_URL, document_root=settings.ENCRYPT_ROOT)

Em seguida, encontre o arquivo de configuração do nginx para o seu site. Deve ser algo como:

sudo nano /etc/nginx/sites-available/yourwebsite.com  # substitua 'yourwebsite.com' por seu site

Abaixo da linha server_name yourwebsite.com;, adicione o seguinte para fazer o nginx autorizar o Let's Encrypt a encontrar os seus certificados:

location ~ /.well-known { allow all; }

Rode sudo nginx -t para ter certeza de que você não cometeu nenhum engano. Você deve obter o seguinte como resposta:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok 
nginx: configuration file /etc/nginx/nginx.conf test is successful

Se você receber alguma mensagem diferente, reze para o São Google ajudar a encontrar a solução. Não se preocupe muito, o mais provável é que tenha sido apenas algum erro de digitação. Não chegamos à parte realmente complicada ainda.

Reinicie o nginx com o comando sudo systemctl restart nginx. Espere alguns segundos, depois entre com os comandos abaixo para ensinar ao firewall a permitir o tráfego HTTP (porta 80) e HTTPS (porta 443):

sudo ufw allow 80
sudo ufw allow 443
sudo ufw allow 'Nginx-Full' # ou sudo ufw allow 'Nginx Full'

Você deve receber uma destas duas mensagens: Allow rule ou Skipped rule, qualquer uma das duas é um resultado positivo. Se você chegou até aqui, está pronto para fazer o trabalho da NSA um pouquinho mais difícil. Hora de começar a encriptação propriamente dita.

Conseguindo os certificados

Você precisa decidir onde quer guardar os arquivos de encriptação. Onde quer que seja, precisa ser na mesma pasta que você informou ao django. Eu coloquei a minha na pasta static, porque é, ao mesmo tempo, mais fácil para o nginx servir e para o django encontrar. Execute o comando:

sudo letsencrypt certonly -a webroot --webroot-path=/caminho/até/website/arquivos -d www.yourwebsite.com -d yourwebsite.com

Uma tela estranha aparecerá, pedindo seu endereço de email. Após, pedirá que você aceite os termos de uso, então pressione Agree. Este passo deve levar alguns minutos, controle sua ansiedade.

É aqui também que a maioria dos erros ocorre. É humanamente a qualquer guia cobrir tudo, mas, felizmente, os mais comuns estão claros os suficiente.

Dica: erros 404 costumam vir de erros nos arquivos do nginx ou nas configurações e/ou URLs do Django. Eles precisam estar coerentes um com o outro, e coincidir com o diretório onde /.well-known/ está localizado. 

Outra: erros 403 vêm de desconfiguração do firewall (execute sudo ufw status para conferir) ou do nginx. Retorne lá e confira.

Uma vez que estes problemas estejam superados e a mensagem de êxito apareça, execute:

sudo letsencrypt renew --dry-run --agree-tos

--dry-run é um código comumente para simulações. Este comando testa se as condições para renovar o certificado estão válidas. Se tudo der certo, você deve receber uma mensagem avisando que está cedo demais e, talvez, outra que pede seu endereço de e-mail (que pode ser ignorada).

O próximo passo é bastante lento. Se você precisa dormir e/ou almoçar, este é o momento ideal:

sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096

Um número de Diffie-Hellman será gerado para criar uma chave secreta para você. Dizem as más línguas que a NSA já é capaz de quebrar chaves de 1024 bits, então 2048 bits deve ser o próximo alvo. 4096 bits, portanto, é o valor mais equilibrado em termos de segurança e tempo de execução.

Uma vez terminado o comando, seu site deve estar apropriado para os padrões modernos de segurança na web. Na próxima seção, vamos aparar arestas para evitar efeitos imprevisíveis potencialmente indesejáveis.

Concluindo

Crie o seguinte arquivo:

sudo nano /etc/nginx/snippets/ssl-www.yourwebsite.com.conf

E escreva nele estas duas linhas:

ssl_certificate /etc/letsencrypt/live/www.yourwebsite.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.yourwebsite.com/privkey.pem;

Confirme que ambos os arquivos existem usando o comando file nome_do_arquivo. Talvez você precise consertar os nomes nas configurações acima.

Após, crie o seguinte arquivo:

sudo nano /etc/nginx/snippets/ssl-params.conf

E copie esta configuração que copiei descaradamente de alguém que entende mais do que eu sobre o assunto:

# from https://cipherli.st/
# and https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Disable preloading HSTS for now.  You can use the commented out header line that includes
# the "preload" directive if you understand the implications.
#add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

ssl_dhparam /etc/ssl/certs/dhparam.pem;

Esta é uma configuração básica de segurança suficiente para a maioria das aplicações web. Desabilitar o HSTS permite que usuários em hotspots wifi abertos acessem seu site. O DNS do resolver está em IPv4 (este blog não tem IPv6 ainda) e é fornecido pelo Google DNS.

Vamos fazer mudanças adicionais à configuração do nginx. Como há o risco delas quebrarem permanentemente uma configuração que está funcional, o senso comum nos obriga a fazer uma cópia:

sudo cp /etc/nginx/sites-available/yourwebsite /etc/nginx/sites-available/yourwebsite.bak

Uma vez que os certificados foram emitidos com sucesso e estão operantes, não há por que continuar usando o protocolo HTTP inseguro (porta 80). Para tanto, edite este arquivo:

sudo nano /etc/nginx/sites-available/yourwebsite

A cada requisição, de agora em diante, haverá um redirecionamento 301 para a porta 443 (protocolo HTTPS). Edite as entradas existentes para ficarem assim:

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name yourwebsite.com www.yourwebsite.com;
    return 301 https://$server_name$request_uri;
}

server {

    # SSL configuration
    listen 443 ssl http2 default_server;
    listen [::]:443 ssl http2 default_server;
    include snippets/ssl-example.com.conf;
    include snippets/ssl-params.conf;

# other stuff
}

Como sempre, execute sudo nginx -t logo ao final para garantir que não houve erros de sintaxe. Você deve confirmar que recebeu esta mensagem:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Caso haja uma mensagem de erro sobre default_server, uma forma fácil de contorná-la é mudando o nome do arquivo de configuração padrão do nginx:

sudo mv /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak

Teste novamente a sintaxe com sudo nginx -t e, a seguir, reinicie o nginx: 

sudo systemctl restart nginx

Espere um minuto inteiro e, a seguir, teste sua implementação neste site: https://www.ssllabs.com/ssltest/analyze.html. Leva alguns minutos, mas, se tudo estiver correto, você deve receber um A+. Pode ser que não funcione de primeira. Neste caso, reinicie o servidor e repita o processo.

Uma vez concluído, seu site está completamente encriptado e pronto para a web. De forma a continuar desta forma, configure um par de cron jobs que renove seu certificado automaticamente todo mês. O próprio Let's Encrypt sugere isso como uma camada a mais de confiabilidade:

sudo nano /etc/cron.monthly/renew1.sh
sudo nano /etc/cron.monthly/renew2.sh

Em ambos os arquivos, copie e cole o mesmo script e deixe o Ubuntu fazer a mágica:

sudo letsencrypt renew
sudo systemctl reload nginx
sudo reboot # opcional

Para terminar, o Django precisa ser ensinado a se comportar sob HTTPS. Ative o ambiente virtual no mesmo diretório onde os arquivos settings.py e urls.py estão:

source ../venv/bin/activate

Um (venv) ou similar deve ter aparecido no início da linha. Neste caso, execute:

pip install django-sslify

A instalação leva alguns minutos, a depender da conexão com a rede. Uma vez concluída, abra settings.py:

sudo nano settings.py

Procure pela variável MIDDLEWARE_CLASSES. Digite ali mesmo, na primeira linha:

MIDDLEWARE_CLASSES = ( 'sslify.middleware.SSLifyMiddleware',
# ... outras coisas ...
)

E está pronto. É complicado e cansativo da primeira vez, mas vai ficando cada vez mais fácil (e este tutorial já me ajudou muitas vezes). Quando fiz pela primeira vez, gostaria que ele estivesse disponível. Creio que não seja o único: segundo o Analytics, esta é a terceira publicação que mais recebe visitantes do Google, e a primeira quando considerados só os textos em língua inglesa.

Últimas considerações

Não vou mentir: manter sistemas é muito monótono. Desenvolver é uma das minhas paixões; configurar o ambiente para minhas aplicações rodarem é só um requisito irritante. A alternativa mais simples é o Heroku, claro, e é nele que costumo fazer testes mais rápidos quando não preciso que o site esteja disponível 24 horas por dia.

O Heroku fica muito caro muito rapidamente, porém. No fim das contas, a escalabilidade de ter um servidor virtual com custo previsível pesa muito menos no bolso. Apesar disso, se eu mesmo não tivesse escrito este tutorial, provavelmente continuaria por lá para a vasta maioria das minhas necessidades.

Com o tempo, já configurei um script em bash que faz a maior parte da digitação para mim, embora não me livre dos erros de conexão e falhas ocasionais. A stack Django/gunicorn/nginx/Ubuntu é uma zona de conforto muito cômoda, neste sentido, por ser fácil de reproduzir em muitos cenários, e deve continuar sendo por mais alguns anos. Até lá, espero que o workflow que delineei aqui tenha ajudado outras pessoas (além de mim mesmo) a superar a preguiça e/ou o medo de colocar suas aplicações na nuvem. Encriptação por si só não adiciona muito ao site - ainda mais com um processo tão enfadonho -, mas só precisa ser executado uma vez e permite a construção de aplicações com senhas e nomes de usuário.

Ou qualquer outra coisa que eu precise dizer a mim mesmo para me convencer de que o trabalho valeu a pena e mereço uma boa noite de sono. 

Etiqueta: tools linux server 

 

Comentários

Não há comentários no momento.

Novo Comentário