Eu tenho o Varnish rodando atrás de um proxy reverso (rodando em localhost, para descarregamento de SSL). O proxy define o cabeçalho X-Forwarded-For ou se adiciona a ele se o cabeçalho já existir.
Quando faço verificações de ACL, claro, quero verificar o IP do cliente original, não o IP do meu proxy, por isso não posso usar o campo client.ip
. Com o std
vmod, posso fazer o seguinte:
vcl 4.0;
import std;
sub vcl_recv {
if (std.ip(regsub(req.http.X-Forwarded-For, ", 127.0.0.1$", ""), "0.0.0.0") ~ my_acl) {
...do stuff...
}
}
Em outras palavras, eu aparei o IP do proxy (127.0.0.1) do cabeçalho antes de executá-lo através de std.ip
e compará-lo à minha ACL. Isso funciona bem, exceto ...
Isso falha quando o cabeçalho X-Forwarded-For já está definido antes de chegar ao meu proxy. Nesse caso, o cabeçalho XFF contém três ou mais endereços IP. Aparar o último ainda deixa mais de um e std.ip
engasga com isso, atrasando o pedido por vários segundos e, é claro, não checando o ACL.
Eu preciso ter certeza de que o cabeçalho XFF contém apenas um IP (IPv4 ou IPv6) depois que eu cortar o proxy. Este deve ser o IP do cliente.
Por exemplo:
X-Forwarded-For: 10.10.1.1, 10.10.2.2, 2001:a031:100a:dead:beef:1234:1234:1234, 127.0.0.1
deve se tornar
X-Forwarded-For: 2001:a031:100a:dead:beef:1234:1234:1234, 127.0.0.1
Como não posso confiar em nenhum cabeçalho XFF que venha de fora, gostaria de descartar tudo, menos o ip do cliente que o meu proxy viu. Meu proxy não suporta a modificação do cabeçalho XFF, então eu preciso fazê-lo em verniz.
O primeiro ponto no fluxo do verniz quando posso interagir com os cabeçalhos é em vcl_recv()
e nesse ponto o verniz já adicionou o client.ip
ao final da lista.
Eu esperava usar uma expressão regular para capturar os dois últimos itens (IPv4 ou IPv6) em um grupo de captura numerado ($ 1) e simplesmente substituir o cabeçalho pelo grupo de captura. Assim:
vcl 4.0;
import std;
sub vcl_recv {
set req.http.X-Forwarded-For = regsub(req.http.X-Forwarded-For, "([a-f0-9:.]+, [a-f0-9:.]+)$", "");
}
O terceiro argumento (a string que substitui os caracteres correspondidos pelo regex) não funciona. O cabeçalho resultante é exatamente o mesmo de antes, embora o regex capture apenas os últimos dois endereços IP.
Como posso jogar fora tudo menos os dois últimos endereços IP do cabeçalho XFF?