DEV Community

Cover image for Série Nginx 6#: Controlando acesso
Valdeir S.
Valdeir S.

Posted on

9 2

Série Nginx 6#: Controlando acesso

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 CIDR para bloquear requisições.

# 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;
Enter fullscreen mode Exit fullscreen mode

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.

location ~ \.(log|zip|tar|gz|htaccess|bkp|old|txt)$ {
    deny all;
}
Enter fullscreen mode Exit fullscreen mode

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.

# 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;
Enter fullscreen mode Exit fullscreen mode

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.


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.

nginx -V 2>&1 | grep --color http_auth_request_module
Enter fullscreen mode Exit fullscreen mode

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

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? 👍

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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

location = /auth_upload {
    proxy_pass http://auth-server;
}
Enter fullscreen mode Exit fullscreen mode

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.

location = /auth_upload {
    internal;

    proxy_pass http://auth-server;
}
Enter fullscreen mode Exit fullscreen mode

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.

location = /auth_upload {
    internal;

    proxy_pass http://auth-server;
    proxy_pass_request_body on;
    proxy_method POST;
}
Enter fullscreen mode Exit fullscreen mode

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

const crypto = require('crypto');
const express = require('express');
const fileUpload = require('express-fileupload');
const morgan = require('morgan');
const app = express();
const mockUsers = require('./users.json');
app.use(fileUpload());
app.use(morgan('dev'));
/**
* Valida o cabeçalho Authorization
*/
app.use(function(req, res, next) {
if (req.method.toUpperCase() == 'POST' && req.path == '/auth_upload') {
if (!req.get('authorization')) {
res.status(401).json({ msg: 'Header Authorization is invalid' }).end();
}
}
next();
})
/**
* Valida usuário
*/
app.use(function(req, res, next) {
if (req.method == 'POST' && req.path == '/auth_upload') {
const [, AccessKeyId, Signature] = req.get('authorization').match(/([\w]+):([\w]+)/i)
const userFiltered = mockUsers.filter((item) => item.AccessKeyId == AccessKeyId);
if (!userFiltered || userFiltered.length == 0) {
res.status(401).json({ msg: 'User not found' }).end();
}
req.user = userFiltered[0];
req.signature = Signature;
}
next();
})
app.post('/auth_upload', (req, res, next) => {
if (!req.files?.file) {
res.status(401).end();
}
const StringToSign = `${req.method}\n${req.files.file.md5}\n${req.files.file.mimetype}\n${req.get('date')}`;
const HMacHash = crypto.createHmac('sha1', req.user.YourSecretAccessKey).update(StringToSign).digest('hex');
let statusCode = 401;
if ( req.signature === HMacHash ) {
statusCode = 200;
}
res.status(statusCode).end();
});
app.listen(80, '0.0.0.0', () => console.log(`Start server ${new Date().toLocaleString()}`))
view raw auth_server.js hosted with ❤ by GitHub

Realizando Upload

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
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (0)

Cloudinary image

Video API: manage, encode, and optimize for any device, channel or network condition. Deliver branded video experiences in minutes and get deep engagement insights.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay