nginx try_files redireciona usando esquema errado quando está atrás do balanceador de carga de terminação SSL (haproxy)

8

Eu tenho um servidor nginx 1.6.2 em execução como back-end atrás de um balanceador de carga que faz a terminação SSL. Toda a comunicação com os servidores back-end passa por HTTP.

Diagrama do que está acontecendo:

          /--http---> frontend:80  --\
client --+                            +--http---> backend:8000
          \--https--> frontend:443 --/

                      LOAD BALANCER                BACKENDS

Para fins de teste, só tenho um back-end no momento. O balanceador de carga executa o HAProxy 1.5, sobre o qual tenho algum controle.

Eu tenho uma try_filesdiretiva bastante típica no meu serverbloco no backend nginx config:

server {
    server_name frontend;
    ...
    try_files $uri $uri/ =404;
    ...
}

Agora, por padrão, quando eu acesso um diretório sem uma barra à direita, por exemplo https://frontend/somedir, o nginx deseja enviar um redirecionamento HTTP 301 para um URL absoluto como http://frontend:8000/somedir/.

Eu posso fazer o nginx omitir o número da porta 8000 usando port_in_redirect off.

No entanto, não consigo corrigir o http://esquema no início do redirecionamento que o nginx está gerando. O melhor que posso fazer com o nginx é um redirecionamento de https://frontend/somedirpara http://frontend/somedir/, eliminando efetivamente o SSL!

O balanceador de carga está enviando um X-Forwarded-Protocabeçalho, mas não vejo como o nginx consultá-lo ao criar seu redirecionamento; de fato, existe uma resposta de 2012 dizendo que o nginx não pode fazer isso de imediato, e a solução é substituir o balanceador de carga pelo nginx. IMHO isso é muito trivial para justificar uma mudança drástica na pilha.

Alguma coisa mudou desde 2012 aqui? Eu realmente não quero reescrever esses redirecionamentos no nível HAProxy: os redirecionamentos HTTPS intencionais para HTTP reais do aplicativo Web podem ser "re-HTTPSed" se eu sempre reescrever o esquema para que o Location:cabeçalho de resposta seja o mesmo que o esquema o pedido foi feito com.

EDITAR:

Aqui está uma configuração minimizada mostrando que o nginx produz Location:URLs absolutas . Observe que não há reescritas.

user nobody nobody;
worker_processes auto;
worker_rlimit_nofile 4096;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
    multi_accept on;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    # TODO: Tune fastcgi_buffers/other buffers

    # Configure keepalive connections
    keepalive_timeout 15;
    keepalive_requests 1000;

    # Hide server version.
    server_tokens off;

    # Do not allow any directory indexes anywhere.
    # This is the default, but it is here for extra paranoia.
    autoindex off;

    # gzip text content.
    gzip on;
    gzip_vary on;
    gzip_disable "msie6";
    gzip_comp_level 2;
    gzip_min_length 1024;
    gzip_types  text/css
                text/plain
                text/xml
                application/json
                application/javascript;

    server {
        listen 8000 default_server;

        root /usr/share/nginx/html;
        index index.html index.htm;

        server_name localhost;

        location / {
            try_files $uri $uri/ =404;
        }
    }
}

E se você usar curl para visualizar os cabeçalhos - observe que eu criei um diretório testdirem /usr/share/nginx/html:

[myuser@dev nginx]$ curl -i http://localhost:8000/testdir
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Thu, 26 Mar 2015 14:35:49 GMT
Content-Type: text/html
Content-Length: 178
Location: http://localhost:8000/testdir/
Connection: keep-alive

<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>
mutron
fonte
nginx wants to send an HTTP 301 redirect to an absolute URL- o nginx não faz isso, a menos que você tenha uma regra de reescrita explícita para isso - mostre sua configuração completa.
AD7six
Eu adicionei a configuração, aparada para ilustrar esse problema. Não faço reescritas explícitas.
precisa
1
Você deve configurar seu balanceador para substituir o protocolo no Location:cabeçalho, pois o nginx não tem idéia do que era o protocolo original.
Alexey Ten
2
@AlexeyTen você gostaria de postar isso como resposta para que eu possa aprová-lo? Também não encontrei uma maneira de fazer isso no nginx; portanto, uma alteração no balanceador de carga realmente parece ser a única maneira.
MuTron
1
Este ainda é um bug da versão mais recente do nginX! Alguém sabe se já existe um relatório de erro? Não foi possível encontrar um.
Roel van Duijnhoven 17/05/19

Respostas:

2

Avance para 2019 e você pode:

absolute_redirect off; 
port_in_redirect off;

Dos documentos paraabsolute_redirect :

Se desativado, os redirecionamentos emitidos pelo nginx serão relativos.

Danila Vershinin
fonte
-1

Você precisa dizer o balanceador de carga para reescrever httpa httpsnos pedidos assim:

proxy_redirect http:// $scheme://;
HostFission
fonte