DEV Community

Cover image for Nginx, SSL e mTLS: Um Guia Prático para Criar um Ambiente mais seguro
Jorge Rafael
Jorge Rafael

Posted on

Nginx, SSL e mTLS: Um Guia Prático para Criar um Ambiente mais seguro

Olaaaaaa pessoas, muito boa tarde/manhã/noite, espero que vocês estejam bem.

Trilha sonora enquanto eu escrevia esse artigo:

https://open.spotify.com/intl-pt/track/5dpDnd3Nnw5dRql80RcI7M?si=41eec3a4d5eb45e9


Neste artigo, vamos fornecer um combo de informações. Aprenderemos desde a configuração de um servidor HTTPS usando o Nginx, passando pela criação de um certificado autoassinado e, por fim, configuraremos o mesmo servidor para ser um Mutual TLS ou apenas mTLS.

Nota: Nesta primeira parte, aprenderemos como configurar um ambiente HTTPS usando o Nginx.

Para este tutorial, estou usando o Docker com o Docker Compose para orquestrar nosso ambiente local, facilitando assim o processo de configuração do ambiente. Afinal, em 2023, ninguém mais configura um servidor Nginx diretamente na máquina.

Configurando o ambiente

Começaremos criando nosso diretório de projeto:

mkdir projeto-mtls
Enter fullscreen mode Exit fullscreen mode

Dentro da pasta, criaremos um arquivo docker-compose.yaml e uma pasta nginx que conterá tudo relacionado ao nosso servidor:

cd projeto-mtls
touch docker-compose.yaml # Arquivo do ambiente docker nosso.
mkdir nginx # Pasta que vai guardar tudo relacionado ao nginx
mkdir nginx/certs # Pasta que vai guardar nossos certificados.
Enter fullscreen mode Exit fullscreen mode

Feito isso, agora começaremos a configurar nosso servidor.

Vamos criar um arquivo nginx.conf:

touch nginx.conf
Enter fullscreen mode Exit fullscreen mode

Nele, configuraremos um servidor "simples" que retornará uma página HTML quando acessado em http://localhost:

server {
    listen 80;
    server_name localhost;

    location / {
        default_type text/html;
        return 200 "<!DOCTYPE html><h2>Hello World!</h2>\n";
    }
}
Enter fullscreen mode Exit fullscreen mode

Agora, para testarmos se tudo está funcionando, configuraremos nosso docker-compose.yaml da seguinte forma:

version: "3.8"
services:
  nginx:
    container_name: proxy-mtls
    restart: always
    image: nginx
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      - ./nginx/certs:/etc/nginx/certs
    ports:
      - "80:80"
      - "443:443"
    networks:
      - sandbox

networks:
  sandbox:
    driver: bridge
Enter fullscreen mode Exit fullscreen mode

Mapearemos a pasta nginx para dentro do container, para que as configurações e certificados sejam acessíveis no servidor:

docker-compose up # Inicializa os containers do Docker Compose.
Enter fullscreen mode Exit fullscreen mode

Com o servidor ativo, acesse http://localhost:

Página HTML, com o texto "Hello World" apenas

Configurando o SSL

Agora que temos nosso servidor HTTP, precisamos configurar o SSL para começarmos a usar HTTPS nas requisições.

Para isso, precisamos gerar um certificado autoassinado.

Nota: Um certificado digital normalmente requer uma autoridade de certificação (CA) para validar sua autenticidade. No entanto, em casos de desenvolvimento, podemos atuar como a própria CA e assinar nosso próprio certificado, conhecido como "certificado autoassinado". Esse tipo de certificado não é válido em produção, mas é suficiente para ambientes de desenvolvimento.

Gerando a chave privada do certificado

Para gerar nosso certificado, usaremos o utilitário OpenSSL, amplamente utilizado para criptografia e segurança. Utilizaremos o comando a seguir para gerar uma chave privada:

openssl genpkey -algorithm RSA -out certs/localhost.key
Enter fullscreen mode Exit fullscreen mode

Este comando utiliza o algoritmo RSA, amplamente empregado em sistemas de segurança, como SSL/TLS, para criptografar comunicações na web. O resultado será salvo em certs/localhost.key.

Gerando a Solicitação de Assinatura de Certificado (CSR)

Agora, usaremos o OpenSSL para gerar uma Solicitação de Assinatura de Certificado (CSR):

openssl req -new -key certs/localhost.key -out certs/localhost.csr
Enter fullscreen mode Exit fullscreen mode

Essa solicitação contém informações necessárias para emitir nosso certificado. Informamos a chave privada a ser usada (certs/localhost.key) e o arquivo de saída (certs/localhost.csr).

Tela do terminal, com as informações necessárias para gerar um CSR

Ao executar esse comando, você precisará fornecer várias informações sobre a autoridade do certificado e detalhes de contato, que serão enviados à CA para autenticar o certificado da empresa.

Dica bonus sobre geração de CSR

Ver dica

Se você precisa gerar vários certificados ou deseja ter um "modelo" para facilitar a geração de certificados no futuro, pode criar um arquivo de configuração contendo configurações padrão. Isso agilizará o processo de geração de certificados.

Vamos criar um arquivo chamado template.csr.conf para ser nosso modelo:

touch template.csr.conf

Dentro deste arquivo, colocaremos as seguintes informações:

[req]
default_bits = 2048
default_keyfile = localhost.key
distinguished_name = req_distinguished_name
req_extensions = req_ext
x509_extensions = v3_ca
req_extensions = v3_req

[req_distinguished_name]
countryName = Country Name (2 letter code)
countryName_default = BR
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Sao Paulo
localityName = Locality Name (eg, city)
localityName_default = Sao Paulo
organizationName = Organization Name (eg, company)
organizationName_default = Localhost Ltda
organizationalUnitName = organizationalunit
organizationalUnitName_default = Development
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_default = localhost
commonName_max = 64

[req_ext]
subjectAltName = @alt_names

[v3_ca]
subjectAltName = @alt_names

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

[alt_names]
DNS.1 = 127.0.0.1
DNS.2 = localhost

Nota: Eu não vou explicar aqui detalhes sobre esse arquivo, mas um ponto legal é que você pode configurar vários endereços no [alt_names] para que esse certificado suporte vários dominios.

Agora, execute o comando novamente, mas desta vez passando o arquivo de configuração:

openssl req -new -key certs/localhost.key -out certs/localhost.csr -config template.csr.conf

Observe que, mesmo pedindo que você preencha informações, os dados pré-definidos são os que definimos no nosso modelo, economizando tempo durante o processo.


Gerando o Certificado Autoassinado

Após criar a chave privada e a solicitação de assinatura de certificado, chegou a hora de atuar como CA e assinar o certificado. Use o seguinte comando:

openssl x509 -req -days 365 -in certs/localhost.csr -signkey certs/localhost.key -out  certs/localhost.crt
Enter fullscreen mode Exit fullscreen mode

Este comando gera o certificado a partir da solicitação de assinatura de certificado (CSR). O certificado terá validade de 1 ano e será salvo em certs/localhost.crt.

Entenda o comando: x509 é usado para manipular certificados digitais. -req indica que estamos trabalhando com uma solicitação de assinatura de certificado. -days 365 define a validade do certificado em 1 ano. Os próximos três argumentos (-in, -signkey, -out) especificam o caminho da solicitação de assinatura de certificado, da chave privada e do arquivo de saída.

Testando o SSL

Após gerar o certificado, precisamos testá-lo. Para isso, faremos algumas alterações no nosso arquivo nginx.conf para indicar o uso de HTTPS em vez de HTTP:

server {
    listen 80;
    server_name localhost;

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name localhost;

    ssl_certificate /etc/nginx/certs/localhost.crt;
    ssl_certificate_key /etc/nginx/certs/localhost.key;

    location / {
        default_type text/html;
        return 200 "<!DOCTYPE html><h2>Hello World with HTTPS</h2>\n";
    }
}
Enter fullscreen mode Exit fullscreen mode

Agora, qualquer requisição para http://localhost será redirecionada para https://localhost. Observe que estamos usando os atributos ssl_certificate e ssl_certificate_key para apontar o certificado e a chave do nosso servidor.

Salve as alterações e reinicie o servidor:

docker-compose down
docker-compose up
Enter fullscreen mode Exit fullscreen mode

Ao acessar https://localhost, você receberá um aviso do navegador informando que o certificado de autoridade é inválido.

Aviso de SSL inválido no navegador Chrome.

Isso é esperado, uma vez que nós mesmos assinamos o certificado. Para continuar, clique em "Avançado" e depois em "Prosseguir para localhost (não seguro)".

Aviso de SSL inválido

Pronto, agora temos nosso servidor HTTPS funcionando:

Tela do Chrome, escrita Hello World with HTTPS

Até este ponto, já temos um servidor HTTPS funcionando perfeitamente. Agora, vamos para a parte do Mutual TLS ou mTLS.

Fechando as Portas com mTLS

Nota: O mTLS, ou Mutual Transport Layer Security, é uma extensão do protocolo TLS que autentica tanto o cliente quanto o servidor durante a comunicação na internet. Isso é feito por meio da troca de certificados digitais, onde cada parte verifica a identidade da outra. Esse nível adicional de autenticação reforça a segurança da comunicação, especialmente em ambientes sensíveis à segurança, como serviços bancários online e sistemas corporativos, protegendo contra ataques de intermediários mal-intencionados e garantindo uma conexão confiável e criptografada.

Para começar, criaremos um certificado para o cliente, seguindo os mesmos passos que fizemos para criar o certificado do servidor:

mkdir nginx/certs/clients # Vai guardar o certificado do nosso cliente
Enter fullscreen mode Exit fullscreen mode
openssl genpkey -algorithm RSA -out certs/clients/certificado-do-cliente.key
openssl req -new -key certs/clients/certificado-do-cliente.key -out certs/clients/certificado-do-cliente.csr -config template.csr.conf
openssl x509 -req -days 365 -in certs/clients/certificado-do-cliente.csr -signkey certs/clients/certificado-do-cliente.key -out  certs/clients/certificado-do-cliente.crt
Enter fullscreen mode Exit fullscreen mode

Após gerar o certificado do cliente, precisaremos configurar nosso servidor Nginx para trabalhar com mTLS. Para isso, faremos algumas atualizações em nosso arquivo de configuração:


server {
    listen 443 ssl;
    server_name localhost;

    ssl_certificate /etc/nginx/certs/localhost.crt;
    ssl_certificate_key /etc/nginx/certs/localhost.key;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384';

    ssl_client_certificate /etc/nginx/certs/clients/certificado-do-cliente.crt;
    ssl_verify_client on;

    location / {
        default_type text/html;
        return 200 "<!DOCTYPE html><h2>Hello World Secury</h2>\n";
    }
}

Enter fullscreen mode Exit fullscreen mode

Agora, adicionamos algumas configurações adicionais ao nosso proxy. ssl_client_certificate especifica o certificado do cliente (que o cliente deve usar ao fazer chamadas para o nosso serviço), e ssl_verify_client ativa a verificação.

Com essa pequena mudança, garantimos que apenas o cliente que possua este certificado conseguirá fazer requisições para nosso ambiente.

Requisições sem o certificado resultarão em um erro:

Requisição no Postman, sem fechar o MTLS, informando que não foi possível finalizar a chamada

Agora, ao passar o certificado na requisição, a chamada será bem-sucedida:

Página html, escrito Hello World with mTLS

Considerações finais

Espero que este artigo seja útil para quem deseja estudar configurações de ambientes e segurança.

Até a próxima.

Se tiver alguma dúvida ou comentário, deixe-os aqui, e vamos compartilhar conhecimento.

Top comments (0)