Série Nginx 6#: Controlando acesso
Tags: nginx, security, webdev, webserver
Published: 2021-10-01 16:59:08

Opa!! Vamos avançar em nossa série. Hoje aprenderemos como controlar o acesso com filtro de IP e autenticação interna.

-------------

## Bloqueando acessos

Você pode bloquear um IP (ou faixa de IP) utilizando a diretiva `deny`. Além do IP, é possível também utilizar o <abbr title="Classes Inter-Domain Routing">CIDR</abbr> para bloquear requisições.

```conf
# Syntax: deny address | CIDR | unix: | all;
# Default: —
# Context: http, server, location, limit_except
#
# Bloqueia um IP específico
deny 127.0.0.1;

# Bloqueia faixa 142.250.218.0 - 142.250.218.255
deny 142.250.218.0/24;

# Bloqueia uma faixa de IP do tipo IPV6
deny 2001:0db8::/32;

# Bloqueia todos os acessos
deny all;
```

**Bloqueando acesso a arquivos**

Com o `deny`, também é possível bloquear acesso aos arquivos ou a um diretório. Basta utilizar a diretiva dentro do contexto `location`, por exemplo.

```conf
location ~ \.(log|zip|tar|gz|htaccess|bkp|old|txt)$ {
    deny all;
}
```

-------------

## Permitindo acesso a determinados IPs

Além de bloquear, você pode apenas permitir determinados IPs ou faixa de ips. Isso é útil para limitar o acesso a determinada rede.

```conf
# Syntax: allow address | CIDR | unix: | all;
# Default: —
# Context: http, server, location, limit_except
#
# Bloqueia acesso local
allow 127.0.0.1;

# Permite acesso da faixa de IP 162.158.214.0 - 162.158.214.255
allow 162.158.214.0/24;

# Permite acesso a uma faixa IPV6
allow 2001:0db8::/32;

# Bloqueia todos os acessos
deny all;
```

> Não faz sentido usar apenas `allow`, visto que — por padrão — todos os acessos são permitidos.

**Resolvendo problema com CDN**

Quando você utiliza um serviço como CloudFlare (ou similar), eles recebem a requisição e a repassam para seu servidor. Isso faz com que o IP recebido pelo Nginx não seja o real, o do cliente; mas sim o IP do servidor da CloudFlare. Como resolver isso?

Para resolver, você precisa configurar duas diretivas: `set_real_ip_from` e `real_ip_header`.

Na primeira, você definirá os endereços de IP confiável (*True-Client-IP*) que sabem o IP do cliente, ou seja, você configurará com os IPs da CloudFlare.

Na segunda, você informará o *header* que contém o endereço real do cliente. Normalmente é *CF-Connecting-IP* ou *X-Forwarded-For*.

Como esses dados são mutáveis, deixarei o *link* com as configurações atuais.

- [CloudFlare](https://support.cloudflare.com/hc/en-us/articles/200170786-Restoring-original-visitor-IPs#R8FGMtY5b3k5cez)

- [CloudFront](https://ip-ranges.amazonaws.com/ip-ranges.json)

- [Fastly](https://docs.fastly.com/signalsciences/faq/real-client-ip-addresses/#nginx---http_realip_module)

- [Sucuri](https://docs.sucuri.net/website-firewall/troubleshooting/same-ip-for-all-users/#nginx)

-------------

## Auth Request

O sistema de subautorização do Nginx funciona através do módulo `ngx_http_auth_request_module`. Ele permite que o desenvolvedor crie um sistema de autorização baseado em sub-requisições (requisições internas).

Essas requisições devem retornar o *status code* 200 (2xx), 401 ou 403. A depender do código de *status* retornado, o Nginx liberará (ou não) a solicitação. Caso a autorização seja negada, o Nginx retornará o erro para o usuário.

**Verificar suporte**

Antes de começar, verifique se o Nginx foi compilado com o suporte ao módulo.

```bash
nginx -V 2>&1 | grep --color http_auth_request_module
```

Se não tiver configurado, compile o Nginx com a *flag* `--with-http_auth_request_module`

> Observação: É importante que você saiba compilar o código fonte do Nginx. Caso não tenha esse conhecimento, acesse a postagem [Série Nginx #2: Compilando o Nginx](https://dev.to/valdeirpsr/serie-nginx-2-compilando-o-nginx-3f9o)

**Configurando rota**

Para configurar a autorização, utilizaremos a diretiva `auth_request`.

> Iremos configurar a autorização para uma rota, mas você pode configurar para um servidor virtual (no contexto `server`) ou todos os sites (no contexto `http`), beleza? 👍

```conf
server {
    # Outras configurações

    location /upload {
        # Syntax: auth_request uri | off;
        # Default: auth_request off;
        # Context: http, server, location
        #
        auth_request /auth_upload;

        proxy_pass http://upload-server;
    }
}
```

Perfeito! Agora vamos configurar a localização de autorização.

```conf
location = /auth_upload {
    proxy_pass http://auth-server;
}
```

Com isso já conseguimos realizar a validação. Lembrando que o *status code* do *proxy* `http://auth-server` **deve** retornar 2xx, 401 ou 403.

**Bloqueando acesso direto**

Em alguns sistemas, você pode querer bloquear o acesso direto à URL ou ao servidor de autenticação ou autorização. O Nginx oferece esse suporte com a diretiva `internal`, ou seja, o Nginx impedirá que o usuário acesse a URL `/auth_upload` e apenas ele fará o acesso.

```conf
location = /auth_upload {
    internal;

    proxy_pass http://auth-server;
}
```

No nosso exemplo, utilizarei os dados enviados na requisição POST para realizar a verificação da autorização, por isso é necessário configurar as diretivas `proxy_pass_request_body` e `proxy_method`.

Caso seu sistema de autorização não faça uso dos dados enviados no corpo da requisição, ignore-a.

```conf
location = /auth_upload {
    internal;

    proxy_pass http://auth-server;
    proxy_pass_request_body on;
    proxy_method POST;
}
```

Pronto! Com isso, o nosso sistema deverá funcionar corretamente.

**Código para verificação da autorização**

> O código foi feito em Node e o servidor iniciado com Docker. Não explicarei o funcionamento porque a ideia é focar apenas no servidor web.

Utilizei o [padrão adotado pelo S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/RESTAuthentication.html), da AWS, para realizar a autorização ou negação. Ele basicamente verificará o método da requisição, *content type*, *hash* do arquivo e a data de envio e depois comparará com o *hash* enviado no *header Authorization*.

{% gist https://gist.github.com/valdeirpsr/152662e6df2d7f3ef775f52d4dae0367 file=auth_server.js %}

**Realizando Upload**

```bash
token="2HmVrZRYii8ZWrfIAFHLmlCYtqqv5nSASLCzZMHUg6M5vRlarfjumNDz9ZR5NHoY"

date_req=$(date -R)

hash=$(echo -en "POST\n$(md5sum anitta.mp4 | cut -d' ' -f1)\nvideo/mp4\n$date_req" | openssl sha1 -hmac "<user-key>" -hex)

http --form POST \
  php.valdeirpsr.dev/upload \
  Authorization:"AWS $token:$hash" \
  Date:"$date_req" \
  file@anitta.mp4
```

**Como é feito o upload**

1. O arquivo é enviado para a URL `/upload`;
2. O Nginx recebe a requisição;
3. O Nginx realiza uma subrequisição para autorização em `/auth_upload`;
4. O servidor `http://auth-server` realiza a validação e retorna o *status code* *200* ou *401*.
5. O Nginx recebe a resposta da subrequisição e verifica o código retornado;
6. Se o código for 401 ou 403, o Nginx retorna um erro para o cliente; caso retorne 2xx, prossegue com a requisição de upload.

-------------

## Conclusão

É isso aew, meu povo! Finalizamos mais uma etapa da série de Nginx. Caso tenham dúvidas e/ou sugestões, basta deixarem o comentário. Beleza?

Até a próxima!