การจัดการคำขอ http และ https โดยใช้พอร์ตเดียวกับ nginx


16

ฉันสงสัยว่า nginx สามารถจัดการคำขอ http และ https บนพอร์ตเดียวกันได้หรือไม่ [*]

นี่คือสิ่งที่ฉันพยายามจะทำ ฉันกำลังเรียกใช้เว็บเซิร์ฟเวอร์ (lighttpd) จัดการคำขอ http และโปรแกรม C ที่ให้บริการเฉพาะส่วนของแผนผังเอกสารผ่าน https กระบวนการทั้งสองนี้ทำงานบนเซิร์ฟเวอร์เดียวกัน

ที่ระดับไฟร์วอลล์ฉันสามารถมีพอร์ตเดียวที่ส่งต่อทราฟฟิกไปยังเซิร์ฟเวอร์นี้ได้ ดังนั้นสิ่งที่ฉันต้องการจะทำคือการตั้งค่า nginx บนเซิร์ฟเวอร์นี้เพื่อให้ฟังการร้องขอบนพอร์ตเดียวแล้ว:

A) เปลี่ยนเส้นทางคำขอhttp://myhost.com/ * ทั้งหมดเพื่อให้ไปที่ localhost: 8080 (โดยที่ lighttpd กำลังฟัง)

B) หากผู้ใช้ร้องขอ URL ที่ขึ้นต้นด้วยเช่น https: // myhost.com/app ก็จะส่งคำขอนั้นไปยัง localhost: 8008 (โปรแกรม C) โปรดทราบว่าในกรณีนี้การรับส่งข้อมูลระหว่างเบราว์เซอร์ระยะไกลและ nginx จะต้องได้รับการเข้ารหัส

คุณคิดว่าเป็นไปได้ไหม ถ้าเป็นเช่นนั้นจะทำอย่างไร?

ฉันรู้วิธีการใช้สองพอร์ตที่แตกต่างกัน ความท้าทายที่ฉันเผชิญทำด้วยพอร์ตเพียงพอร์ตเดียว (โชคไม่ดีที่ฉันไม่สามารถควบคุมการกำหนดค่าไฟร์วอลล์ในสภาพแวดล้อมนี้ได้ดังนั้นจึงเป็นข้อ จำกัด ที่ฉันไม่สามารถหลีกเลี่ยงได้) การใช้เทคนิคอย่าง reverse port fowarding ผ่าน ssh เพื่อข้ามไฟร์วอลล์จะไม่ทำงานเช่นนี้เพราะมันจะใช้ได้กับผู้ใช้ระยะไกลที่ไม่มีอะไรมากกว่าเว็บเบราว์เซอร์และลิงค์อินเทอร์เน็ต

หากเกินความสามารถของ nginx คุณรู้จักผลิตภัณฑ์อื่น ๆ ที่สามารถตอบสนองความต้องการนี้ได้หรือไม่? (จนถึงตอนนี้ฉันไม่ประสบความสำเร็จในการตั้งค่าด้วย lighttpd และปอนด์) ฉันยังต้องการหลีกเลี่ยง Apache (แม้ว่าฉันยินดีที่จะใช้หากเป็นทางเลือกเดียวที่เป็นไปได้)

ขอบคุณล่วงหน้าอเล็กซ์

[*] เพื่อความชัดเจนฉันกำลังพูดถึงการจัดการการเชื่อมต่อ HTTP ที่เข้ารหัสและไม่เข้ารหัสผ่านพอร์ตเดียวกัน ไม่สำคัญว่าการเข้ารหัสจะทำผ่าน SSL หรือ TLS


คำขอ HTTPS ไปที่พอร์ต 443 โดยค่าเริ่มต้นดังนั้นแม้ว่าคุณจะสามารถทำงานได้ (และฉันคิดว่ามันเป็นไปได้ด้วยการแฮ็กบิต) คุณจะต้องใช้yourhost.comและyourhost.com:80เป็นลิงก์ (หรือyourhost.com:443และyourhost.com )
Zanchey

ตกลงฉันใหม่ที่ Server Fault และฉันไม่รู้ว่าฉันสามารถลบคำถามของตัวเองได้หรือไม่ เนื่องจากดูเหมือนว่าปัญหายังไม่ได้กำหนดอย่างชัดเจนเพียงพอฉันจะเปิดคำถามใหม่แทน อย่างไรก็ตามขอขอบคุณทุกคนที่มีส่วนร่วมกับคำแนะนำที่เป็นประโยชน์สำหรับปัญหานี้
alemartini

คำตอบ:


18

ตามบทความวิกิพีเดียเกี่ยวกับรหัสสถานะ Nginx มีรหัสข้อผิดพลาดที่กำหนดเองเมื่อการรับส่งข้อมูล HTTP ถูกส่งไปยังพอร์ต https (รหัสข้อผิดพลาด 497)

และตามเอกสาร nginx บน error_pageคุณสามารถกำหนด URI ที่จะแสดงสำหรับข้อผิดพลาดเฉพาะ
ดังนั้นเราสามารถสร้าง uri ที่ลูกค้าจะถูกส่งไปเมื่อรหัสข้อผิดพลาด 497 ถูกยกขึ้น

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;
    }
}

อย่างไรก็ตามหากลูกค้าทำการร้องขอผ่านวิธีอื่นใดยกเว้น GET คำขอนั้นจะเปลี่ยนเป็น GET ดังนั้นเพื่อรักษาวิธีการร้องขอที่ลูกค้าเข้ามาผ่าน; เราใช้การประมวลผลข้อผิดพลาดในการเปลี่ยนเส้นทางดังที่แสดงในnginx docs บน error_page

และนั่นคือสาเหตุที่เราใช้การ301 =307เปลี่ยนเส้นทาง

เมื่อใช้ไฟล์ nginx.conf ที่แสดงที่นี่เราสามารถฟัง http และ https ในพอร์ตเดียวกันได้


17

สำหรับผู้ที่อาจกำลังค้นหา:

เพิ่มssl on;และerror_page 497 $request_uri;นิยามเซิร์ฟเวอร์ของคุณ


8
คำตอบ HoverHells ปรับปรุงเล็กน้อยเพียงเล็กน้อย: เพิ่มssl on;และerror_page 497 =200 $request_uri;นิยามเซิร์ฟเวอร์ของคุณ สิ่งนี้จะเปลี่ยนรหัสสถานะเป็น 200
Mawi12345

คำตอบข้อผิดพลาดที่ให้บริการนี้อธิบายว่าทำไมโซลูชันนี้จึงใช้งานได้
SiliconMind

4

หากคุณต้องการให้ฉลาดจริง ๆ คุณสามารถใช้การเชื่อมต่อพร็อกซีเพื่อดักข้อมูลไบต์คู่แรกของสตรีมข้อมูลขาเข้าและส่งการเชื่อมต่อตามเนื้อหาของไบต์ 0: ถ้าเป็น 0x16 (SSL / TLS ' จับมือ 'ไบต์) ผ่านการเชื่อมต่อไปยังด้าน SSL หากเป็นตัวอักษรตัวอักษรทำ HTTP ปกติ ความคิดเห็นของฉันเกี่ยวกับหมายเลขพอร์ตใช้


2

ใช่เป็นไปได้ แต่ต้องการการแก้ไขซอร์สโค้ดของ nginx (HoverHell มีวิธีแก้ปัญหาโดยไม่ต้องแก้ไข) Nginx ถือว่าสิ่งนี้เป็นการกำหนดค่าที่ผิดแทนที่จะเป็นการกำหนดค่าที่ถูกต้อง

ตัวแปร $ ssl_session_id สามารถใช้แตกต่างกันระหว่างการเชื่อมต่อแบบธรรมดาและแบบ SSL

Patch กับ nginx-0.7.65:

--- 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)

กำหนดค่าเซิร์ฟเวอร์:

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

ฉันไม่คิดว่าจะมีสิ่งใดที่สามารถรองรับโปรโตคอลที่แตกต่างกันสองรายการในพอร์ตเดียว ...

ฉันอยากรู้ว่าทำไมคุณสามารถส่งต่อพอร์ตเดียว แต่นอกเหนือจากนั้น ... มันไม่เหมาะ แต่ถ้าฉันอยู่ในรองเท้าของคุณฉันจะให้บริการทุกอย่างผ่าน https


สวัสดีวิลและขอขอบคุณสำหรับคำตอบของคุณ! ฉันคิดว่าการให้บริการทุกอย่างผ่าน https อาจเป็นตัวเลือกแม้ว่าฉันจะสามารถตั้งค่านี้ได้ตามวิธีที่ฉันได้อธิบายไว้ อาจทำเช่นนี้ได้หากเว็บเซิร์ฟเวอร์ด้านหน้า (ทำหน้าที่เป็น reverse proxy) สามารถสร้างเซสชัน http ปกติแล้วอัปเกรดเป็น https โดยไม่เปลี่ยนพอร์ต ฉันคิดว่าพฤติกรรมนี้อธิบายไว้ใน RFC2817 (อัปเกรดเป็น TLS ด้วย HTTP / 1.1) แต่ฉันไม่แน่ใจว่า nginx หรือเว็บเซิร์ฟเวอร์อื่นรู้วิธีจัดการกับมาตรฐานนั้นหรือไม่
alemartini

ฉันไม่มีเวลาอ่าน RFC ทั้งหมด (และไม่แน่ใจว่าฉันฉลาดพอที่จะเข้าใจมัน!) แต่คุณกำลังพูดถึงการเจรจาต่อรองมาตรฐานก่อนที่จะมีการสร้างเซสชันที่ปลอดภัยหรือเซสชันที่แตกต่างเต็มไปหมด? ฉันคิดว่าฉันเข้าใจมากกว่านี้ - เซิร์ฟเวอร์ให้บริการในสองพอร์ตและเป็นพร็อกซีที่อ้างถึงคำขอ - ฟังดูดี แต่ฉันไม่เคยเห็นมันมาก่อน บางทีวิธีแก้ไขอาจเกิดจากการสร้างเว็บไซต์ที่ปลอดภัยเพียงแห่งเดียวบนพอร์ตหนึ่งและมีไดเรกทอรีเสมือนทั้งหมดที่เพียงแค่สืบทอด / นำเข้าเว็บไซต์อื่นหรือไม่ ไม่สามารถแก้ไขปัญหาได้ทั้งหมด แต่อาจใช้ได้: S
William Hilsum

1

คุณไม่สามารถรองรับทั้ง HTTP และ HTTPS บนพอร์ตเดียวกันเนื่องจากปลายทั้งสองของการเชื่อมต่อคาดว่าจะพูดภาษาหนึ่งและพวกเขาก็ไม่ฉลาดพอที่จะทำงานได้หากอีกฝ่ายกำลังพูดอย่างอื่น

ตามความคิดเห็นของคุณสำหรับคำตอบของ Wil คุณสามารถใช้การอัพเกรด TLS (ฉันเชื่อว่า nginx รุ่นใหม่รองรับแม้ว่าฉันจะไม่ได้ลอง) แต่ก็ไม่ได้ใช้ HTTP และ HTTPS นั่นเป็นเพียงการใช้ HTTP กับการอัพเกรด TLS ปัญหายังคงรองรับเบราว์เซอร์เบราว์เซอร์ส่วนใหญ่ (ยัง) ไม่รองรับ หากคุณมีกลุ่มลูกค้าที่ จำกัด ดังนั้นนี่คือความเป็นไปได้


1
การรับส่งข้อมูล HTTP ที่เข้ารหัสและไม่เข้ารหัสสามารถจัดการผ่านพอร์ตเดียว สิ่งที่ฉันอยากรู้คือถ้าเป็นไปได้โดยใช้ nginx หรือผลิตภัณฑ์อื่น ๆ (เช่น lighttpd) ทำหน้าที่เป็นพร็อกซี่ย้อนกลับ อาจเป็นไปได้ว่า Apache สามารถจัดการการตั้งค่าประเภทนี้ได้ แต่ฉันลืมที่จะพูดถึงคำถามเดิมของฉันว่าฉันไม่ต้องการใช้ Apache (แม้ว่าฉันจะเลือกใช้ตัวเลือกนี้บน Linux ก็ตาม) แพลตฟอร์มเพื่อบรรลุสิ่งนี้)
alemartini

ดังที่ฉันพูดในคำตอบของฉัน "ฉันเชื่อว่า nginx release ใหม่รองรับ [อัพเกรด TLS] แม้ว่าฉันจะไม่ได้ลอง" หากคุณต้องการให้ฉันอ่านคู่มือสำหรับคุณคุณก็โชคไม่ดี
womble

ฉันขอโทษถ้าฉันรู้สึกว่าฉันต้องการใครซักคนที่จะอ่านคู่มือสำหรับฉัน ดูเหมือนว่าปัญหา (และคำถามเกี่ยวกับมัน) ไม่ได้อธิบายอย่างชัดเจนเพียงพอ (ความผิดพลาดของฉัน) นำไปสู่การตีความที่แตกต่างกันของสิ่งที่ฉันถามหรือต้องการ ดังนั้นฉันจึงตัดสินใจเปิดคำถามใหม่เกี่ยวกับปัญหานี้และพยายามหลีกเลี่ยงความสับสนที่เป็นไปได้เกี่ยวกับปัญหาหรือคำถามเฉพาะเกี่ยวกับปัญหานี้ อย่างไรก็ตามขอขอบคุณสำหรับเวลาและการแบ่งปันข้อมูลเชิงลึกของคุณ
alemartini

0

ฉันไม่แน่ใจว่ามันดึงออกมาได้อย่างไร แต่ CUPSD ตอบกลับทั้ง http และ https บนพอร์ต 631 ถ้า nginx ไม่สามารถทำได้ในตอนนี้บางทีพวกเขาสามารถเรียนรู้จากวิธีที่ทีม CUPS ดึงออกมา แต่ CUPS อยู่ภายใต้ GPL ดังนั้น nginx อาจต้องดูการเปลี่ยนแปลงใบอนุญาตหากพวกเขาต้องการที่จะใช้ความสามารถดังกล่าวและไม่สามารถหารหัสที่จะทำเช่นนั้นได้ที่อื่น


0

ในทางทฤษฎีคุณสามารถมีเว็บเพจที่สามารถเข้าถึงได้ผ่าน HTTP ซึ่งสามารถเปิดWebSocketบน https: 443 ไปยังทุกที่ที่ต้องการ HandSake เริ่มต้น WebSocket คือ HTTP ใช่มันเป็นไปได้ที่จะทำให้หน้าการมองที่ไม่ปลอดภัยสามารถสื่อสารได้อย่างปลอดภัย คุณสามารถทำเช่นนี้กับห้องสมุด Netty


0

ในที่สุดมันก็เป็นไปได้ที่จะทำอย่างถูกต้องตั้งแต่ 1.15.2 ดูข้อมูลที่นี่

ใน nginx.conf ของคุณเพิ่มบล็อกแบบนี้ (นอกบล็อก http):

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;
    }
}

จากนั้นคุณสามารถสร้างเซิร์ฟเวอร์บล็อกปกติ แต่ฟังพอร์ตต่าง ๆ เหล่านี้:

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

ด้วยวิธีนี้บล็อกสตรีมสามารถโหลดล่วงหน้าและตรวจสอบว่าเป็น TLS หรือไม่ (บนพอร์ต 8080 ในตัวอย่างนี้) จากนั้นพร็อกซีจะส่งผ่านไปยังพอร์ตเซิร์ฟเวอร์ที่ถูกต้องภายในเครื่อง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.