Niech nginx przekaże nazwę hosta nadrzędnego podczas odwrotnego przepuszczania

89

Prowadzę kilka kontenerów dokerów z nazwami hostów:

web1.local web2.local web3.local

Trasowanie do nich wykonane na podstawie nazwy hosta przez nginx. Mam proxy przed tą konfiguracją (na innym komputerze podłączonym do Internetu), w którym definiuję upstream jako:

    upstream main {
      server web1.local:80;
      server web2.local:80;
      server web3.local:80;
    }

I rzeczywisty opis hosta wirtualnego:

    server {
      listen 80;
      server_name example.com;
      location / {
        proxy_pass http://main;
      }
    }

Ponieważ kontenery otrzymują nazwę hosta „main” zamiast „web1.local”, nie odpowiadają one poprawnie na żądanie.

Pytanie: jak mogę powiedzieć nginx, aby przekazał nazwę serwera nadrzędnego zamiast nazwy nadrzędnej grupy serwerów w nagłówku Host: podczas proxy żądania?

pavel_karoukin
źródło
3
Nie sądzę, że możesz. Dlaczego nie ustawiasz serwerów zaplecza tak, aby odpowiadały na main lub example.com? To nie tak, że backend nie wie, kto to jest. Odwrotność jest łatwo możliwa: proxy_set_header Host $ host; zastąpi każdą zmienną Host wracającą z nadrzędnego nazwą hosta z pierwotnego żądania.
Andrew Domaszek
Właściwe jest, aby naprawić aplikację.
Michael Hampton

Odpowiedzi:

109

W rzeczywistości możesz to zrobić za pomocą proxy_set_header.

Aby uzyskać więcej informacji, spójrz tutaj: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header lub zobacz przykładowy przypadek użycia tutaj: https://stackoverflow.com/questions/12847771/configure-nginx- with-proxy-pass

Do powyższej konfiguracji dodałem dynamiczne podejście:

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            $host;
    proxy_set_header X-Forwarded-For $remote_addr;
  }
}

Oto przykład ze statyczną nazwą hosta:

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            www.example.com;
    proxy_set_header X-Forwarded-For $remote_addr;
  }
}
Jens Bradler
źródło
7
proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for; wydaje się lepszy
sivann
1
@pavel: rozumiem Właściwie zrobiłem też trochę badań i testów. Wydaje się, że nie ma prostego podejścia do spełnienia wymagań. Tak więc nawet „drańskie” rozwiązanie jest rozwiązaniem. Nie lubię pytać, dlaczego chcesz to zrobić. Jestem pewien, że masz swoje powody. :-)
Jens Bradler
@JensBradler Wydajesz się być ekspertem ode mnie, więc czy możesz mi powiedzieć, co myślisz o moim rozwiązaniu? Chcę zrobić to samo, ponieważ prowadzę dwie kopie mojej witryny z dwóch kont na moim usługodawcy internetowym: site1.myisp.comi site2.myisp.comodpowiadają one tylko na ich nazwy. Teraz jestem właścicielem mojej nazwy domeny i chciałbym korzystać z mojej witryny internetowej dostawcy usług internetowych w celu zrównoważenia obciążenia moich serwerów. Czy to nie jest dobry powód? Dziękuję bardzo;)
ncenerar
1
@ncenerar Możesz to zrobić, ale doprowadzi Cię to do jednego punktu awarii: modułu równoważenia obciążenia. Jeśli dotyczy to równoważenia obciążenia (a nie redundancji), można również użyć równoważenia obciążenia opartego na DNS w połączeniu z przełączaniem awaryjnym DNS.
Jens Bradler
2
Ta odpowiedź odzwierciedla porady oficjalnego bloga .
Bernard Rosset,
28

Miałem ten sam problem i ostatecznie go rozwiązałem, używając dwóch poziomów proxy. Oto, co możesz zrobić dla swojej sytuacji (tak myślę):

server {
  listen      8001 default_server;
  server_name web1.example.com;
  location / {
    proxy_pass       http://web1.local:80;
    proxy_set_header Host web1.local:80;
  }
}

server {
  listen      8002 default_server;
  server_name web2.example.com;
  location / {
    proxy_pass       http://web2.local:80;
    proxy_set_header Host web2.local:80;
  }
}

server {
  listen      8003 default_server;
  server_name web3.example.com;
  location / {
    proxy_pass       http://web3.local:80;
    proxy_set_header Host web3.local:80;
  }
}

upstream main {
  server 127.0.0.1:8001;
  server 127.0.0.1:8002;
  server 127.0.0.1:8003;
}

server {
  listen      80;
  server_name example.com;
  location / {
    proxy_pass http://main;
  }
}

Jak widać, sztuczka polega na utworzeniu lokalnego serwera odpowiadającego na konkretny port, który będzie proxy serwera, przepisując odpowiedni Host dla każdego serwera. Następnie możesz użyć tych lokalnych serwerów na swoim serwerze upstream, a na końcu użyć tego na serwerze proxy.

ncenerar
źródło
Początkowo stosowałem podejście Lua, ale teraz całkowicie przełączyłem się na HAProxy, który pozwala robić to, co chciałem ze standardową konfiguracją.
pavel_karoukin
3

Tak więc po przeczytaniu całej dokumentacji nginx (tak naprawdę nie mogłem parsować kodu dla upstream module = () wymyśliłem to draństwo rozwiązanie. Niestety to rozwiązanie nie śledzi awarii hostów, ale po prostu wybiera losowy i przekierowuje do niego żądanie. Więc muszę skonfigurować monitorowanie, aby upewnić się, że wszystkie backendy są uruchomione.

server {
        listen 80;
        server_name example.com;
        resolver 127.0.0.1;

        location / {
                set $upstream "";
                rewrite_by_lua '
                        local upstreams = {
                                "http://web1.dokku.localdomain",
                                "http://web2.dokku.localdomain",
                                "http://web3.dokku.localdomain",
                                "http://web4.dokku.localdomain"
                        }
                        ngx.var.upstream = upstreams[ math.random( #upstreams ) ] 
                ';
                proxy_pass $upstream;
        }
}
pavel_karoukin
źródło
2

Podajemy adres nadrzędny jako osobny nagłówek taki jak ten

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            $host;
    proxy_set_header X-Forwarded-For $remote_addr;
    add_header       X-Upstream      $upstream_addr;
  }
}

Co jeśli spróbujesz?

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            $upstream_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    add_header       X-Host          $host;
  }
}
dalore
źródło
2

Chociaż cel wydaje się logiczny, nginx nie zmieni nagłówka Host: tak, aby pasował do strumienia nadrzędnego . Zamiast tego traktuje upstreamnazwy domen jak CNAMEw DNS - jako sposób na uzyskanie adresu IP.

Nagłówki (i treść) żądania są ustalane przed wybraniem w górę. Proces wysyłania może zmienić żądanie pośrednie, jeśli okaże się, że dany przekaz nie odpowiada, ale żądanie się nie zmienia.

GreenReaper
źródło
0

Hmm Mam podobną konfigurację, w której po prostu zrobiłem

location / {
    ... 
    proxy_set_header X-Forwarded-Host $http_host;
    proxy_pass ...;
}

Użycie $http_host(nagłówka hosta HTTP z przychodzącego żądania) tutaj zamiast $host(konfiguracja nazwy hosta serwera) powoduje, że ten sam nagłówek hosta przekazywany przez klienta jest przekazywany w górę, w moich testach.

Zobacz także https://stackoverflow.com/questions/14352690/change-host-header-in-nginx-reverse-proxy .

lyngvi
źródło
0

Ponieważ inne osoby publikowały już przy użyciu zmiennej skryptu (np. $ Upstream), możesz ustawić ją tak, jak chcesz, i to rozwiąże problem, bez dodatkowego hakowania nagłówka.

Zmienne skryptu obsługi programu proxy Proxy Pass w inny sposób, jeśli wartość nie jest warunkowa (nie ma $ w nazwie), jest cofana do fazy wysyłania na etapie konfiguracji i używana później.

Prostym sposobem na ominięcie tego problemu i uzyskanie większości zalet (bezpłatnej wersji) w górę byłoby użycie czegoś takiego Split_Clients:

split_clients $request_uri $my_upstream {
              33%          server1.domainX.com;
              33%          server2.domainX.com;
# Always use DOT at end entry if you wonder why, read the SC code.
              *            server3.domainX.com;  
}
location / {
    ... 
    proxy_pass http://$my_upstream;
}

Powyższy przykład wygląda prawie tak samo jak w górę. Istnieją inne moduły wykonujące mapowanie, np. Chash_map_module , ale ponieważ są one poza drzewem, będziesz musiał zbudować je samodzielnie, co nie jest możliwe w niektórych przypadkach użycia /

Mazeryt
źródło