nginx reverse proxy több WEB szerver összegyűjtésére

Helló!

 

A több WEB szerver és a kliens között tűzfal van, így direktben nem elérhetők.

Van egy gép, amin a HTTPS port elérhető és ez a gép eléri a többi WEB szervert is.

Szeretnék egy reverse proxy-t, de nem akar sikerülni! RHEL6 alatti 1.18-as nginx és ha

a location-ba /-t írok, akkor az az egyetlen szerver elérhető lesz, de ha pl. /a vagy bármi

más lesz hogy meg tudjam különböztetni a szervereket, akkor már 404 Not found az eredmény.

Úgy néz ki, mintha a távoli WEB szervertől kapott elérési útvonalakat a helyi nginx root-jából

szeretné kiszolgálni, ami természetesen nem működik és jogos a 404.

Valami ilyenekkel próbálkoztam:

location /a {

      proxy_pass http://<WEBszerver1_IP/;

}

location /b {

       proxy_pass http://<WEBszerver2_IP/;

}

 

Mi az, amit rosszul csinálok, vagy hogyan kell ezt megoldani?

Esetleg a távoli WEB szerverek hivatkozásai nem jók?

 

Köszi!

Hozzászólások

nginx annyira nem megy, de apache-ból kiindulva úgy kellene, hogy nem a locationnal különbözteted meg hanem domainnel. kb így:

 

server {
        server_name domain1.com;
        location / {
        proxy_pass http://web1/};
        }
server {
        server_name domain2.com;
        location / {
        proxy_pass http://web2/};
        }

attól függ. Ha teljesen mindegy neki az url és van ami nem támogat SNI-t, lehet egy db elosztó domain is amin url minták alapján válogatja szét, hogy mit hova. Persze ezzel további problémákat idézhet a saját fejére, mivel van alkalmazás ami nem szeret "alkönyvtár" szerű uri prefix-et kapni. Ezt majd kezelni kell vagy a reverse proxy maga kell újraírja a továbbított url-eket vagy az alkalmazás oldalon kell rá felkészülni / kezelni..

https://goo.gl/muWzKz (digitalocean)

http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite

Célszerűen állítsd be pl a Host header-t, továbbítsd a kliens ipt stbstb.

Esetleg nem ez történik?

http://reverseproxy/a/requesturi ->  http://<WEBszerver1_IP/a/requesturi

miközben pl  az alkalmazás /requesturi -t várna (ilyenkor -is- szükséges a rewrite). Dobhatja így az app azt a 404-t teljesen jogosan. :)

De a logok mindent elárulnak..

https://goo.gl/muWzKz (digitalocean)

A rewrite-hoz nem értek:

1.Nem tudom, pontosan mi is megy ki a cél WEB szerverhez.

2.Nem tudom, mire kellene átírni.

3.Mindezt hogyan?

De a proxy_pass http://IP/; (záró /) ott van, ami azt mondja, hogy dobja a location-ban megadott (általam választónak hívott) URL részt és teszi oda a maradékot. Ez alapvetően jónak is tűnik, de mégsem működik, ahogy kellene. :-(

Azóta ahogy írtam, folyamatosan próbálkozom és valami mozdult, mert most már a /a vagy /b location-ra bejön egyetlen WEB szerver, de nem különbözőek, hanem ugyan az és természetesen az IP különböző ... Nem értem!
Ráadásul úgy néz ki, hogy az WEB szerver jön be a /, /a, /b-re, a / alatt van megadva! 

Üdv:
Ruzsi

Igen, valami ilyesmire gyanítok!

 

Össze tudunk-e dobni egy mintát pl. 2 szerverre?

Az nginx-en "bármit" ki tudok próbálni, csak kicsit be vagyok erdőzve, pedig van már pár szerver, amit így húzok ki a tűzfal mögül.

...

Igen, az nginx bontja ki a HTTPS-t és a belső hálón már HTTP-vel megy a kommunikáció.

Üdv:
Ruzsi

error_log-ot kapcsold debug modba, es akkor latod mi tortenik.

de amugy ha nincs felkeszitve a backend webserver, hogy oda /a/* urlek erkeznek, akkor kell egy rewrite a locationon belul, hogy a "/a"-t leszedje:

https://serverfault.com/questions/379675/nginx-reverse-proxy-url-rewrite

A vegtelen ciklus is vegeter egyszer, csak kelloen eros hardver kell hozza!

Szerkesztve: 2020. 10. 21., sze - 15:14

Ha jól értem a https kibontást a nginx végzi, és titkosítatlan http közlekedik a belső hálón? Jó esetben a http szerver loggolja a kéréseket, ott meg tudod nézni, hogy mit kérnek tőle. Ha az nem loggol, akkor a kéréseket tudod debuggolni akár wireshark-kal is könnyedén, vagy egy loggoló TCP proxyn keresztül. (És akár a nginx is rábírható loggolásra, be lehet kapcsolni valahogy, hogy nagyon részletes logot adjon minden kiszolgálásról. Ez a legésszerűbb talán használni, mert ez az eszköz minden hasonló esetben hasznos lesz, ha egyszer megismered a használatát.) "Szabad szemmel" kiolvasható, hogy mi történik. A legvalószínűbb, hogy az URI lesz olyanra átírva, amire a http szerver - jogosan - azt mondja, hogy olyan nem létezik.

Itt leírja az URI átírásának a szabályait: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass

Elég bonyolult, fontos az is, hogy a minta végén van-e / és hogy a prox_pass parancs végén van-e / ! Minden számít!

Nálam a location a legtöbb esetben /-re végződik, de őszintén szólva nem tudom már megmondani, hogy miért :-) Itt egy működő példa a játszós jenkinsemhez - de itt az eredeti szolgáltatás is a /jenkins/ path alatt van:

# Nginx configuration specific to Jenkins
    # Note that regex takes precedence, so use of "^~" ensures earlier evaluation
    location ^~ /jenkins/ {
        auth_request /auth;
 
        # Convert inbound WAN requests for https://domain.tld/jenkins/ to 
        # local network requests for http://10.0.0.100:8080/jenkins/
        proxy_pass http://127.0.0.1:8089/jenkins/;

Ha ilyen kontextus path alá akarsz tenni egy weboldalt, az az adott oldalon belüli abszolút linkeket el tudja rontani, ha van benne olyan. Például a HTML-ben van ilyen: <img src="/style/images/mylogo.png"/>, akkor ez a hivatkozás nem a /a/style/images/mylogo.png-re fog mutatni sajnos.  Tehát nem elég a headereket átírogatni, de a konkrét HTML tartalomban is meg kell hogy változzanak az abszolút linkek! Ha gondolt erre a weboldal fejlesztője, akkor be lehet valahogy állítani. Ha nem gondolt rá, akkor várhatóan esélytelen: én mostanában játszottam ilyen web szolgáltatásokkal, és ugyan a fejlesztés során gondoltam erre a problémára, mégis rendre benéztem valamit, elsőre sosem működött.

A fenti jenkins példában a Jenkins esetén például magán a Jenkins-en egy konfigurációs paraméter, amit a weben be lehet írni, az adja meg a root URL-t, amin a Jenkins elérhető. Saját http szolgáltatások esetén úgy szoktam megcsinálni, hogy a nginx-től headerben kapja meg a http szerver, hogy mi a saját kontext path-ja: ezzel a trükkel ugyanaz a http szerver több különböző domain alatt is képes működni, és mindegyiken megfelelő választ tud adni, ha úgy van bekonfigurálva.

Ez triviális.

De ha ott a / a proxy_pass végén, akkor nem kaphatna mást, mint amire számít.

Gondolom, az nginx úgy viselkedik a proxy-zandó WEB szerver felé mint egy normál kliens, tehát ha nem irkálom át az URL-t,

akkor nem kaphatna váratlan lekérést. Persze most kap, amire meg is adja a választ, hogy nincs ilyen elérés. Ezzel nincs bajom, csak amikor az nginx el kezdi kiszolgálni az URL elejét a saját root-jából, hozzátéve a WEB szerver felé menő mradékot, ami így már igazán sehol se jó.

A kérdés, hogy miért akarja megpróbálni kiszolgálni a saját root-jából, hozzácsapva a távoli szervernek szánt maradék URL-t.

Üdv:
Ruzsi

A jelenlegi konfig:

 

server {
    listen       80;
    server_name  _szerver_név_;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    #location / {
    #    root   /usr/share/nginx/html;
    #    index  index.html index.htm;
    #}

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

server {
    error_log /var/log/nginx/error.log notice;
    rewrite_log         on;
    listen              443 ssl;
    server_name         _szerver_név_;

    #root                /usr/share/nginx/html;
    ssl_certificate     /etc/pki/tls/certs/......crt;
    ssl_certificate_key /etc/pki/tls/private/......key;
    ssl_dhparam         /etc/pki/tls/certs/dhparam.pem;

    location            /e {
        proxy_pass      http://10.x.y.20/;
    }
    location            /f {
        proxy_pass      http://10.x.y.41/;
    }
    location            /a {
        proxy_pass      http://10.x.y.21/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_redirect    off;
    }
    location            /b {
        proxy_pass      http://10.x.y.42/;
        #proxy_set_header Host $host;
        #proxy_set_header X-Real-IP $remote_addr;
        #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        #proxy_set_header X-Forwarded-Proto https;
        #proxy_redirect    off;
    }
    location            / { #mukodo konfig!
        proxy_pass      http://10.x.y.22/;
    }
}

 

Mindig a .22-es WEB oldala jön be...
 

Üdv:
Ruzsi

az _utolsó_ passzoló

  1. Test the URI against all prefix strings.
  2. The = (equals sign) modifier defines an exact match of the URI and a prefix string. If the exact match is found, the search stops.
  3. If the ^~ (caret-tilde) modifier prepends the longest matching prefix string, the regular expressions are not checked.
  4. Store the longest matching prefix string.
  5. Test the URI against regular expressions.
  6. Stop processing when the first matching regular expression is found and use the corresponding location.
  7. If no regular expression matches, use the location corresponding to the stored prefix string.

https://docs.nginx.com/nginx/admin-guide/web-server/web-server/#locatio…

 

https://goo.gl/muWzKz (digitalocean)

 location ^~ /a/ {
        rewrite ^/a(/.*)$ $1 break;
        proxy_pass      http://10.x.y.21/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_redirect    off;
    }
    location ^~ /b/ {
        rewrite ^/b(/.*)$ $1 break;
        proxy_pass      http://10.x.y.42/;
        #proxy_set_header Host $host;
        #proxy_set_header X-Real-IP $remote_addr;
        #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        #proxy_set_header X-Forwarded-Proto https;
        #proxy_redirect    off;
    }

Próba cseresznye, nem tudtam kipróbálni.

https://goo.gl/muWzKz (digitalocean)

Köszönöm, hogy segíteni próbálsz! :-)

Az error.log-ból kivonat:

2020/10/27 10:39:00 [notice] 416#416: *169 "^/b(/.*)$" matches "/b/", client: <IP>, server: <server_name>, request: "GET /b/ HTTP/1.1", host: "<nginx_IP>"
2020/10/27 10:39:00 [notice] 416#416: *169 rewritten data: "/", args: "", client: <IP>, server: <server_name>, request: "GET /b/ HTTP/1.1", host: "<nginx_IP>"
2020/10/27 10:39:00 [error] 416#416: *169 open() "/etc/nginx/html/css/rhpcommon.css" failed (2: No such file or directory), client: 10.111.78.155, server: rs_utast, request: "GET /css/rhpcommon.css HTTP/1.1", host: "<nginx_IP>", referrer: "https://<nginx_IP>/b/"
2020/10/27 10:39:00 [error] 416#416: *169 open() "/etc/nginx/html/cgi-bin/headergen" failed (2: No such file or directory), client: <IP>, server: <server_name>, request: "GET /cgi-bin/headergen HTTP/1.1", host: "<nginx_IP>", referrer: "https://<nginx_IP>/b/"
2020/10/27 10:39:00 [error] 416#416: *169 "/etc/nginx/html/cgi-bin/mainmenugen/index.html" is not found (2: No such file or directory), client: <IP>, server: <server_name>, request: "GET /cgi-bin/mainmenugen/?/mnt/CFlash-apps/www/MainMenuTree HTTP/1.1", host: "<nginx_IP>", referrer: "https://<nginx_IP>/b/"
2020/10/27 10:39:00 [error] 416#416: *169 open() "/etc/nginx/html/cgi-bin/rtuinfo" failed (2: No such file or directory), client: <IP>, server: <server_name>, request: "GET /cgi-bin/rtuinfo HTTP/1.1", host: "<nginx_IP>", referrer: "https://<nginx_IP>/b/"

 

Valami mozdul, mert lesz 3 frame, benne 1-1 404 Not found és az nginx-es proxy verziószámával, azaz azt támasztja alá, amit a fenti log, hogy a távoli WEB szerver a kérést megértette, válaszolt, de a választ az nginx helyből próbálja kiszolgálni, ami természetesen rossz.

Jól értelmezem a log kivonatot?

(azt azért jó lenne látni itt a log-okban, hogy pontosan mi is megy ki és kinek az nginx-ből ...)

Üdv:
Ruzsi

Itt tisztán látszik hogy mi a baj:

a publikuscím/b/ -t átirányítod a belső hálón lógó eszköznek, ő a path-ot / -ként kapja meg.

A belső hivatkozásai / base-re mutatnak, ezeket adja vissza a html-ben (pl. /cgi-bin/mainmenugen/), viszont ezek neked nem jók, mert te /b/ alatt szolgálod ki, a html-ben ennek így /b/cgi-bin/mainmenugen/ -nek kellene lennie.

Vannak eszközök amik alkalmasak arra hogy /-en kívül szolgáljanak ki tartalmat, ez pont nem az. Egyszerűbb lenne ha ezt az egész /path -es magic -et elvetnéd és a publikus ip címnek több nevet adnál amin keresztül a / -t direktben oda tudod adni az eszköznek

// Happy debugging, suckers
#define true (rand() > 10)

Persze, lehet, ha szerencséd van nem is kell sok helyen, ha meg nincs, hát az meg az "így járt" kategória.

A "találomra a tartalomban cserélgetjük a linkeket és az elérési utakat"-nál szerintem egyszerűbb adni neki egy nevet és oda adni az egész /-t ;)

// Happy debugging, suckers
#define true (rand() > 10)

A name-based miért esett ki? Az SNI miatt? Nem biztos, hogy jól értem, de szerintem az SNI akkor számítana, ha a belső szerverek saját cert-et használó HTTPS lennének. Itt csak az nginx https, mögötte minden http. Ezért lehet, hogy működne name-based. Nem tudom, csak ötlet.

A "választó-url"-es megoldás a mellékelt error.log alapján: amikor browser a "/b/"-t kéri, működik. A "/b/" hivatkozik a "/css/rhpcommon.css"-re, amiben már nincs "/b/", ezért nem működik. Erre fentebb írtak példát.

De a Referer-ben van "/b/" Nem tudom, hogy ezt lehet-e közvetlenül az nginx konfigban használni. Ha nem, akkor a választó-url nélüli kéréseket az nginx, mint webszerver szolgálná ki. Pl. egy php megnézi a Referer-t: ha az "/b/..." formátum, akkor 301, másként 404. Nem hatékony, tehát csak ha nincs más...

Kerd meg a sysadmint aki latott mar nginx confot ez nem helpdesk/manager/dev feladat, vagy meg is tanulhatod. Akar.

http://karikasostor.hu - Az autentikus zajforrás.

A rossz hír, én szerettem volna az eszközöket megjeleníteni a tűzfalon kívül, hogy ne kelljen a teljes X-et átlőni a hálózaton pl. egy Firefox képében.

Ha sonlóra voltak már próbálkozásaim, amik működtek, de ott nem kellett varázsolni. Ilyen volt pl. egy Zabbix szerver átproxy-zása.
Viszont ha ennyire nem triviális, akkor inkább nem jelenik meg ... :-(

Üdv:
Ruzsi

Szia!

Ahhoz hogy ez rendesen működjön a belső webszervereknek kezelnie kell azt a beállítást - hogy a documentroot az ne a  "/" legyen, hanem az amit az előtét webszerveren beállítasz pl:
/a
/b
..
Ha ez nem beállítható a belső webszervereken, akkor olyan problémák adódnak, amit nem lehet megoldani:
Proxy kérések "/a" -val mennek a belső webszerver felé, visszafelé pedig a  "/" -val fog válaszolni, tehát html/js/content kódban:
pl.: /images/images.png lesz nem pedig /a/images/images.png
(statikus fájlokat rewrite-al még meg lehetne oldani, de pl.: JavaScript -ben lévő kódók akkor se fognak lefutni így)

Ha a documentroot nem állítható be a belső webszerveren, akkor még van megoldás:
Az előtét webszervert több porton kell futtatni, egy port pedig egy belső webszerver documentroot - "/" -ja így nem akadnak össze.

ngnix tcp:8081  / -> belső-webszerver1 /
ngnix tcp:8082  / -> belső-webszerver2 /
ngnix tcp:8083  / -> belső-webszerver3 /
...

Nem csak magamnak akarom.

Jelenleg egyetlen lyukon látok be a belső rendszerre. Ezért kellene a proxy ...

IP és port korlát van. Az nginx-es proxy-ig a HTTPS lehetséges, más nem. De ezen nem is akarok változtattatni.

Üdv:
Ruzsi

Ami lehetőséged van még:

 * context path alá rakod az oldalakat, de HTML rewrite módszerrel - valaki fentebb linkelte - a visszaadott HTML-be beleírsz egy "base" tag-et a megfelelő context path-szel: https://www.w3schools.com/TAGs/tag_base.asp - ha működik, akkor "örö e bódottá"

 * Csinálsz egy előválasztó oldalt - például session alapon, amin eldöntheti a user, hogy melyikbe akar belépni. Itt beírsz egy cookiet, és utána a cookie szerint dobod szét az aloldalakra (nem tudom konkrétan hogy kell megcsinálni, de ez az infó rendelkezésre áll a nginx számára, tehát működnie kell). Ha váltani akar a user, akkor a választó oldalra visszamegy manuálisan (pl: /select ), és átírja a cookie-t. Egy böngészővel egyszerre csak egy dolgot lát, de több session-t indítva akár egyszerre is láthatja mindet. Körülményes, de megbízhatóan működik. (Ne felejts el cookie consentet is csinálni hozzá! :-)

 * Csinálsz névszervert, és SNI-vel ágaztatod el szerver név szerint.

 * Ha a háromféle szerver resource nevei között nincs ütközés, csak az "index.html" oldalon (mert épp eltérő technológiájú webldalak, stb), akkor elegendő csak az index oldalra csinálni 1-1 átirányítást: /a -> http://aszerver/ /b -> http://bszerver/, stb. A többi elért resource-ot pedig egyesével leválogatni, hogy melyik szerverre kell továbbítani.  Ez már nagyon perverz, és csak akkor működik, ha szerencsésen együtt állnak a csillagok. Nem javaslom, csak viccből írtam be a listába.