Http ve https isteklerini nginx ile tek bir bağlantı noktası kullanarak işleme


16

nginx aynı bağlantı noktasında http ve https isteklerini işleyebiliyor mu diye merak ediyordum . [*]

Yapmaya çalıştığım şey bu. Ben http istekleri işleme bir web sunucusu (lighttpd) ve https yoluyla belge ağacının belirli bir bölümünü sunan bir C programı çalıştırıyorum. Bu iki işlem aynı sunucuda çalışır.

Güvenlik duvarı düzeyinde, bu sunucuya trafik yönlendiren yalnızca bir bağlantı noktasına sahip olabilirim . Yani ne yapmak istiyorum bu sunucuda nginx tek bir bağlantı noktasındaki istekleri dinler ve daha sonra ayarlamaktır:

A) tüm http://myhost.com/ * isteklerini localhost'a yönlendirecek şekilde yönlendirir : 8080 (lighttpd dinliyor)

B) bir kullanıcı, örneğin https: // myhost.com/app ile başlayan bir URL isterse, bu isteği localhost: 8008'e (C programı) gönderir. Bu durumda, uzak tarayıcı ile nginx arasındaki trafiğin şifrelenmesi gerektiğini unutmayın.

Bunun mümkün olabileceğini düşünüyor musun? Eğer öyleyse, nasıl yapılabilir?

Bunu iki farklı bağlantı noktasını kullanarak nasıl yapacağımı biliyorum. Karşılaştığım zorluk bunu tek bir bağlantı noktasıyla yapıyor (maalesef, bu ortamdaki güvenlik duvarı yapılandırması üzerinde kontrole sahip değilim, bu yüzden kaçınamayacağım bir kısıtlama). Güvenlik duvarını atlamak için ssh üzerinden ters bağlantı noktası kullanmak gibi teknikler de işe yaramaz, çünkü bu bir web tarayıcısından ve internet bağlantısından başka bir şeyleri olmayan uzak kullanıcılar için çalışmalıdır.

Bu nginx özelliklerinin ötesindeyse, bu gereksinimleri karşılayabilecek başka bir ürün biliyor musunuz? (Şimdiye kadar bunu lighttpd ve pound ile ayarlamada başarısız oldum). Ayrıca Apache'den kaçınmayı tercih ederim (her ne kadar tek seçenekse kullanmaya istekli olsam da).

Şimdiden teşekkürler, Alex

[*] Açık olmak gerekirse, aynı port üzerinden şifrelenmiş ve şifrelenmemiş HTTP bağlantılarını ele almaktan bahsediyorum . Şifrelemenin SSL veya TLS üzerinden yapılması önemli değildir.


HTTPS istekleri Eğer bu çalışma almak böylece bile, varsayılan olarak port 443 gidin (ve bunu hackery bir parça mümkün olduğunu düşünüyorum), kullanmak gerekirdi yourhost.com ve yourhost.com:80 bağlantılar gibi (ya da yourhost.com:443 ve yourhost.com ).
05'da Zanchey

Tamam, Sunucu Hatası'nda yeniyim ve kendi sorumu silebilir miyim bilmiyorum. Sorun yeterince net bir şekilde formüle edilmemiş gibi göründüğünden, bunun yerine yeni bir soru açacağım. Her neyse, bu sorun için yararlı önerilerle katkıda bulunan herkese çok teşekkürler.
alemartini

Yanıtlar:


18

Durum kodları hakkındaki wikipedia makalesine göre, http trafiği https bağlantı noktasına gönderildiğinde Nginx'in özel bir hata kodu var (hata kodu 497)

Ve error_page'deki nginx belgelerine göre , belirli bir hata için gösterilecek bir URI tanımlayabilirsiniz.
Böylece, 497 hata kodu yükseltildiğinde istemcilerin gönderileceği bir uri oluşturabiliriz.

nginx.conf

#lets assume your IP address is 89.89.89.89 and also that you want nginx to listen on port 7000 and your app is running on port 3000

server {
    listen 7000 ssl;

    ssl_certificate /path/to/ssl_certificate.cer;
    ssl_certificate_key /path/to/ssl_certificate_key.key;
    ssl_client_certificate /path/to/ssl_client_certificate.cer;

    error_page 497 301 =307 https://89.89.89.89:7000$request_uri;

    location / {
        proxy_pass http://89.89.89.89:3000/;

        proxy_pass_header Server;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Protocol $scheme;
    }
}

Ancak bir istemci bir GET dışında başka bir yöntemle istekte bulunursa, bu istek bir GET'e dönüştürülecektir. Böylece müşterinin geldiği istek yöntemini korumak için; hata_sayfasında nginx belgelerinde gösterildiği gibi hata işleme yönlendirmeleri kullanıyoruz

Bu yüzden 301 =307yönlendirmeyi kullanıyoruz.

Burada gösterilen nginx.conf dosyasını kullanarak, http ve https'nin aynı bağlantı noktasını dinlemesini sağlayabiliriz


17

Arayanlar için:

Sunucu tanımınıza ssl on;ve ekleyin error_page 497 $request_uri;.


8
HoverHells cevabında küçük bir gelişme: Sunucu tanımınıza ssl on;ve ekleyin error_page 497 =200 $request_uri;. Bu durum kodunu 200 olarak değiştirecektir.
Mawi12345 19:12

Bu servis hatası yanıtı , bu çözümün neden çalıştığını açıklar.
SiliconMind

4

Gerçekten akıllı olmak istiyorsanız, gelen veri akışının ilk çift baytını koklamak için bir bağlantı proxy'si kullanabilir ve bağlantıyı bayt 0'ın içeriğine göre dağıtabilirsiniz: 0x16 ise (SSL / TLS ' el sıkışma 'bayt), bağlantıyı SSL tarafına geçirin, eğer alfabetik bir karakterse, normal HTTP yapın. Bağlantı noktası numaralandırma hakkındaki yorumum geçerlidir .


2

Evet, mümkün, ancak nginx kaynak kodunu yamalamak gerekiyor (HoverHell, yama olmadan çözümü var). Nginx bunu geçerli yapılandırma yerine yanlış yapılandırma olarak ele alır.

Düz ve SSL bağlantısı arasında değişiklik yapmak için $ ssl_session_id değişkeni kullanılabilir.

Nginx-0.7.65'e karşı yama:

--- src/http/ngx_http_request.c-orig    2011-05-03 15:47:09.000000000 +0200
+++ src/http/ngx_http_request.c 2011-05-03 15:44:01.000000000 +0200
@@ -1545,12 +1545,14 @@

    c = r->connection;

+    /* disable plain http over https port warning
     if (r->plain_http) {
         ngx_log_error(NGX_LOG_INFO, c->log, 0,
                       "client sent plain HTTP request to HTTPS port");
         ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);
         return;
     }
+    */

#if (NGX_HTTP_SSL)

Sunucu yapılandırması:

server {
    listen 80;
    index index.html;

    location / {
        root html;
        if ($ssl_session_id) {
            root html_ssl;
        }
    }

    ssl on;
    ssl_certificate cert.crt;
    ssl_certificate_key cert.key;
}

1

Tek bir bağlantı noktasında iki farklı protokolü işleyebilecek bir şey olduğunu sanmıyorum ...

Neden sadece bir bağlantı noktasını iletebildiğinizi merak ediyorum, ama bu bir yana ... ideal değil ama ayakkabılarınızda olsaydım, https üzerinden her şeye hizmet ederdim.


Merhaba Wil ve cevabınız için teşekkürler! Her şeyi ben https üzerinden hizmet bir seçenek olabilir düşünüyorum, ancak ben bunu tarif ettiğim şekilde ayarlamak mümkün olmak istiyorum. Belki de ön web sunucusu (ters proxy olarak işlev görür) normal bir http oturumu oluşturabilir ve daha sonra bağlantı noktalarını değiştirmeden https'ye yükseltebilirse bu yapılabilir. Bu davranış RFC2817 (HTTP / 1.1 withing TLS yükseltme) açıklanmıştır düşünüyorum ama nginx veya diğer web sunucuları bu standart ile başa çıkmak için nasıl biliyorum emin değilim.
alemartini

Bütün bir RFC okumak için zamanım yok (ve bunu anlayabilecek kadar zeki olduğumdan emin değilim!) Ama güvenli oturum kurulmadan veya tam olarak farklı oturumlardan önce standart görüşmeden mi bahsediyorsunuz? Sanırım biraz daha anlıyorum - sunucu iki bağlantı noktasında hizmet veriyor ve isteği ifade eden proxy - harika geliyor ama bunu hiç görmedim. Belki de bir çözüm, tek bir bağlantı noktasında tek bir güvenli site oluşturmak ve diğer web sitesini devralan / ithal eden bütün bir sanal dizine sahip olmak olabilir. Tüm sorunların üstesinden gelmiyor, ama sadece işe yarayabilir: S
William Hilsum

1

Aynı bağlantı noktası üzerinden hem HTTP hem de HTTPS'yi destekleyemezsiniz, çünkü bağlantının her iki ucu da belirli bir dil konuşmayı beklemektedir ve diğer uç başka bir şey konuşuyorsa çalışacak kadar zeki değildir.

Wil cevabı yorumunuz önerilen gibi, olabilir (ben denemedim rağmen yeni nginx bültenleri, bunu destekleyecek inanmak), ama bu sadece TLS yükseltme HTTP çalışıyor ki HTTP ve HTTPS koşmuyor TLS yükseltmesi kullanın. Sorun hala tarayıcı desteği - çoğu tarayıcı (hala) desteklemiyor. Sınırlı bir müşteri havuzunuz varsa, bu bir olasılıktır.


1
Şifrelenmiş ve şifrelenmemiş HTTP trafiği tek bir bağlantı noktasından işlenebilir. Ne bilmek istiyorum, bu nginx veya ters proxy gibi davranan diğer ürünler (lighttpd gibi) kullanarak mümkün olup olmadığını. Muhtemelen bu tür bir kurulum Apache tarafından ele alınabilir, ancak orijinal sorumda Apache'yi kullanmak zorunda kalmamayı tercih ettiğimi söylemeyi unuttum (Linux'ta başka bir seçenek yoksa bunu yapardım. bunu gerçekleştirmek için bir platform).
alemartini

Cevabımda söylediğim gibi, "denememe rağmen yeni nginx sürüm desteğinin [TLS yükseltmesi] olduğuna inanıyorum." El kitabını sizin için okumam gerekiyorsa, şansınız kalmadı.
womble

Benim için bir kılavuzu okumak için birine ihtiyacım olduğu izlenimini verdiysem özür dilerim. Görünüşe göre sorun (ve bununla ilgili sorular) tam olarak yeterince açıklanmadı (benim hatam), bu da sorduğum veya ihtiyaç duyduğum şeylerin farklı yorumlarına yol açtı. Bu yüzden bu konu hakkında yeni bir soru açmaya karar verdim ve sorunla ilgili herhangi bir karışıklıktan kaçınmaya çalıştım. Her neyse, zaman ayırdığınız ve görüşlerinizi paylaştığınız için teşekkürler.
alemartini

0

Nasıl çektiğinden emin değilim, ancak CUPSD 631 numaralı bağlantı noktasında hem http hem de https'ye yanıt veriyor. GPL, bu nedenle nginx, böyle bir yeteneği uygulamak istiyorsa ve başka bir yerde yapmak için kodu bulamıyorsa lisansını değiştirmeye bakmak zorunda kalabilir.


0

Teorik olarak, https: 443 üzerinde istediği her yere bir WebSocket açabilen HTTP üzerinden erişilebilen bir web sayfanız olabilir . WebSocket başlangıç ​​anlaşması HTTP'dir. Bu nedenle, evet, güvenli iletişim kurabilen güvenli olmayan görünümlü bir sayfa yapmak mümkündür. Bunu Netty Kütüphanesi ile yapabilirsiniz .


0

Bu nihayet 1.15.2'den beri düzgün bir şekilde yapılabilir. Buradaki bilgilere bakın .

Nginx.conf dosyasında şöyle bir blok ekleyin (http bloğunun dışında):

stream {
    upstream http {
        server localhost:8000;
    }

    upstream https {
        server localhost:8001;
    }

    map $ssl_preread_protocol $upstream {
        default https;
        "" http;
    }

    server {
        listen 8080;
        listen [::]:8080;
        proxy_pass $upstream;
        ssl_preread on;
    }
}

Ardından, normal sunucu bloğunuzu oluşturabilir, ancak bu farklı bağlantı noktalarını dinleyebilirsiniz:

server {
    listen 8000;
    listen [::]:8000;
    listen 8001 ssl;
    listen [::]:8001 ssl;
...

Bu şekilde akış bloğu, TLS olup olmadığını önceden okuyabilir ve algılayabilir (bu örnekte 8080 bağlantı noktasında) ve ardından proxy yerel olarak doğru sunucu bağlantı noktasına iletir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.