Nginx, SSL e mTLS: Um Guia Prático para Criar um Ambiente mais seguro
Tags: ssl, https, kurybr, nginx
Published: 2023-09-25 17:28:11

---
title: Nginx, SSL e mTLS: Um Guia Prático para Criar um Ambiente mais seguro
published: true
description: Aprenda a configurar um ambiente HTTPS utilizando Docker, aprenda também a gerar a chave privada do certificado, criar uma Solicitação de Assinatura de Certificado (CSR) e, finalmente, a assinar o certificado, atuando como sua própria Autoridade de Certificação (CA), e fazer uma conexão mTLS para fins de desenvolvimento.
tags: ssl, https, kurybr, nginx
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ovw9ev52ldm8lc0h8zv.png
# Use a ratio of 100:42 for best results.
# published_at: 2023-09-25 13:04 +0000
---



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:

```shell
mkdir projeto-mtls
```

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

```shell
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.
```

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

Vamos criar um arquivo nginx.conf:

```shell
touch nginx.conf
```

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

```nginx
server {
    listen 80;
    server_name localhost;

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

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

```yaml
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
```

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

```shell
docker-compose up # Inicializa os containers do Docker Compose.
```

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

![Página HTML, com o texto "Hello World" apenas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yix64mg6hbknhmxzpvzp.png)

## 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:

```shell
openssl genpkey -algorithm RSA -out certs/localhost.key
```

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):

```shell
openssl req -new -key certs/localhost.key -out certs/localhost.csr
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gh46x2opxaxv52r4sw0u.png)

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 

{% details 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:

```shell
touch template.csr.conf
```

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

```conf
[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:

```shell
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.
 
{% enddetails %}

### 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:

```shell
openssl x509 -req -days 365 -in certs/localhost.csr -signkey certs/localhost.key -out  certs/localhost.crt
```

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:

```nginx
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";
    }
}
```

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:

```shell
docker-compose down
docker-compose up
```

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.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2jqnp0gp4uwjhislamd4.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pgisrm99vq0lyvdvxdsj.png)

Pronto, agora temos nosso servidor HTTPS funcionando:

![Tela do Chrome, escrita Hello World with HTTPS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8pj9u4p86lad5ozjldjk.png)

*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:

```shell
mkdir nginx/certs/clients # Vai guardar o certificado do nosso cliente
```

```shell
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
```

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:

```nginx

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";
    }
}

```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ghnjyhy4u5729y7tr8o2.png)

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

![Página html, escrito Hello World with mTLS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/96lfi9bifx5s33rt9mdr.png)


### 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.