Xử lý các yêu cầu http và https bằng một cổng duy nhất với nginx


16

Tôi đã tự hỏi nếu nginx có thể xử lý các yêu cầu http và https trên cùng một cổng . [*]

Đây là những gì tôi đang cố gắng làm. Tôi đang chạy một máy chủ web (lighttpd) xử lý các yêu cầu http và chương trình C phục vụ một phần cụ thể của cây tài liệu thông qua https. Hai quá trình này chạy trên cùng một máy chủ.

Ở cấp độ tường lửa, tôi chỉ có thể có một cổng chuyển tiếp lưu lượng truy cập vào máy chủ này . Vì vậy, những gì tôi muốn làm là thiết lập nginx trên máy chủ này để nó lắng nghe các yêu cầu trên một cổng duy nhất và sau đó:

A) chuyển hướng tất cả các yêu cầu http://myhost.com/ * để chúng truy cập localhost: 8080 (nơi lighttpd đang lắng nghe)

B) nếu người dùng yêu cầu URL bắt đầu bằng, ví dụ: https: // myhost.com/app, nó sẽ gửi yêu cầu đó đến localhost: 8008 (chương trình C). Lưu ý rằng trong trường hợp này, lưu lượng giữa trình duyệt từ xa và nginx phải được mã hóa.

Bạn có nghĩ rằng điều này có thể có thể? Nếu có, làm thế nào nó có thể được thực hiện?

Tôi biết làm thế nào để làm điều này bằng cách sử dụng hai cổng khác nhau. Thách thức mà tôi gặp phải là thực hiện điều này chỉ với một cổng duy nhất (thật không may, tôi không có quyền kiểm soát cấu hình tường lửa trên môi trường cụ thể này, vì vậy đó là hạn chế mà tôi không thể tránh được). Sử dụng các kỹ thuật như đảo ngược cổng thông qua ssh để vượt qua tường lửa sẽ không hoạt động, bởi vì điều này sẽ hoạt động cho người dùng từ xa không có gì nhiều hơn trình duyệt web và liên kết internet.

Nếu điều này vượt quá khả năng của nginx, bạn có biết sản phẩm nào khác có thể đáp ứng yêu cầu này không? (cho đến nay tôi đã không thành công trong việc thiết lập điều này với lighttpd và pound). Tôi cũng muốn tránh Apache (mặc dù tôi sẵn sàng sử dụng nó nếu đó là lựa chọn duy nhất có thể).

Cảm ơn trước, Alex

[*] Để rõ ràng, tôi đang nói về việc xử lý các kết nối HTTP được mã hóa không được mã hóa thông qua cùng một cổng. Việc mã hóa được thực hiện thông qua SSL hoặc TLS không thành vấn đề.


Các yêu cầu HTTPS theo mặc định đến cổng 443, vì vậy ngay cả khi bạn có thể làm việc này (và tôi nghĩ rằng có thể với một chút tin tặc), bạn cần sử dụng yourhost.comyourhost.com:80 làm liên kết (hoặc yourhost.com:443yourhost.com ).
Zanchey

Ok, tôi là người mới tại Server Fault và tôi không biết liệu tôi có thể xóa câu hỏi của mình không. Vì có vẻ như vấn đề chưa được giải quyết rõ ràng, thay vào đó tôi sẽ mở một câu hỏi mới. Dù sao, cảm ơn rất nhiều cho tất cả những người đã đóng góp với các đề xuất hữu ích cho vấn đề này.
alemartini

Câu trả lời:


17

Đối với những người có thể đang tìm kiếm:

Thêm ssl on;error_page 497 $request_uri;định nghĩa máy chủ của bạn.


8
Cải thiện nhỏ cho câu trả lời của HoverHells: Thêm ssl on;error_page 497 =200 $request_uri;định nghĩa máy chủ của bạn. Điều này sẽ thay đổi mã trạng thái thành 200.
Mawi12345

Điều này phục vụ câu trả lời lỗi giải thích tại sao giải pháp này hoạt động.
SiliconMind

17

Theo bài viết trên wikipedia về mã trạng thái, Nginx có mã lỗi tùy chỉnh khi lưu lượng http được gửi đến cổng https (mã lỗi 497)

Và theo tài liệu nginx trên error_page , bạn có thể xác định URI sẽ được hiển thị cho một lỗi cụ thể.
Do đó, chúng ta có thể tạo một uri mà khách hàng sẽ được gửi đến khi mã lỗi 497 được đưa ra.

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

Tuy nhiên, nếu khách hàng đưa ra yêu cầu thông qua bất kỳ phương thức nào khác ngoại trừ GET, yêu cầu đó sẽ được chuyển thành GET. Do đó, để duy trì phương thức yêu cầu mà khách hàng đã truy cập thông qua; chúng tôi sử dụng chuyển hướng xử lý lỗi như được hiển thị trong tài liệu nginx trên error_page

Và đó là lý do tại sao chúng tôi sử dụng 301 =307chuyển hướng.

Sử dụng tệp nginx.conf được hiển thị ở đây, chúng tôi có thể nghe http và https trên cùng một cổng


4

Nếu bạn muốn thực sự thông minh, bạn có thể sử dụng một proxy proxy kết nối để đánh hơi vài byte đầu tiên của luồng dữ liệu đến và tắt kết nối dựa trên nội dung của byte 0: nếu đó là 0x16 (SSL / TLS ' bắt tay 'byte), chuyển kết nối sang phía SSL, nếu đó là ký tự chữ cái, hãy thực hiện HTTP bình thường. Nhận xét của tôi về đánh số cổng áp dụng .


2

Có, điều đó là có thể, nhưng cần vá mã nguồn nginx (HoverHell có giải pháp mà không cần vá). Nginx coi điều này là cấu hình sai thay vì cấu hình hợp lệ.

Biến $ ssl_session_id có thể được sử dụng để khác nhau giữa kết nối đơn giản và ssl.

Bản vá chống 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)

Cấu hình máy chủ:

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

Tôi không nghĩ có bất cứ thứ gì có thể xử lý hai giao thức khác nhau trên một cổng ...

Tôi tò mò về lý do tại sao bạn chỉ có thể chuyển tiếp một cổng, nhưng điều đó sang một bên ... nó không lý tưởng nhưng nếu tôi ở trong đôi giày của bạn, tôi sẽ phục vụ mọi thứ qua https.


Xin chào Wil, và cảm ơn câu trả lời của bạn! Tôi nghĩ rằng phục vụ mọi thứ qua https có thể là một tùy chọn, mặc dù tôi muốn có thể thiết lập điều này theo cách mà tôi đã mô tả. Có lẽ điều này có thể được thực hiện nếu máy chủ web phía trước (hoạt động như một proxy ngược) có thể thiết lập một phiên http bình thường và sau đó nâng cấp nó lên https mà không thay đổi cổng. Tôi nghĩ rằng hành vi này được mô tả trong RFC2817 (nâng cấp lên TLS với HTTP / 1.1) nhưng tôi không chắc liệu nginx hoặc các máy chủ web khác có biết cách xử lý tiêu chuẩn đó không.
alemartini

Tôi không có thời gian để đọc toàn bộ RFC (và không chắc là tôi đủ thông minh để hiểu nó!) Nhưng bạn có đang nói về cuộc đàm phán tiêu chuẩn trước khi phiên an toàn được thiết lập hoặc thổi toàn bộ các phiên khác nhau không? Tôi nghĩ rằng tôi hiểu thêm một chút - máy chủ đang phục vụ trên hai cổng và đó là proxy đề cập đến yêu cầu - nghe có vẻ hay nhưng tôi chưa bao giờ thấy nó được thực hiện. Có lẽ một giải pháp có thể thông qua việc tạo một trang web bảo mật duy nhất trên một cổng và có toàn bộ thư mục ảo chỉ đơn giản là kế thừa / nhập trang web kia? Nó không khắc phục được tất cả các vấn đề, nhưng có thể chỉ hoạt động: S
William Hilsum

1

Bạn không thể hỗ trợ cả HTTP và HTTPS trên cùng một cổng, bởi vì cả hai đầu của kết nối đều mong muốn nói một ngôn ngữ nhất định và chúng không đủ thông minh để xử lý nếu đầu kia đang nói điều gì đó khác.

Như nhận xét của bạn về câu trả lời của Wil đã đề xuất, bạn có thể sử dụng nâng cấp TLS (Tôi tin rằng các bản phát hành nginx mới hơn hỗ trợ nó, mặc dù tôi chưa thử), nhưng đó không chạy HTTP và HTTPS, đó chỉ là chạy HTTP với nâng cấp TLS. Vấn đề vẫn là hỗ trợ trình duyệt - hầu hết các trình duyệt (vẫn) không hỗ trợ. Tuy nhiên, nếu bạn có một nhóm khách hàng hạn chế, thì đây là một khả năng.


1
Lưu lượng HTTP được mã hóa và không được mã hóa có thể được xử lý thông qua một cổng duy nhất. Những gì tôi muốn biết là nếu điều này là có thể bằng cách sử dụng nginx hoặc các sản phẩm khác (như lighttpd) hoạt động như các proxy ngược. Rất có thể loại thiết lập này có thể được xử lý bởi Apache, nhưng tôi đã quên đề cập đến trong câu hỏi ban đầu của mình rằng tôi không muốn sử dụng Apache (mặc dù tôi sẽ làm điều đó nếu không có lựa chọn nào khác trên Linux nền tảng để thực hiện điều này).
alemartini

Như tôi đã nói trong câu trả lời của mình, "Tôi tin rằng hỗ trợ phát hành nginx mới hơn [nâng cấp TLS], mặc dù tôi chưa thử". Nếu bạn cần tôi đọc hướng dẫn cho bạn, thì bạn đã hết may mắn.
womble

Tôi xin lỗi nếu tôi có ấn tượng rằng tôi cần ai đó đọc hướng dẫn cho tôi. Dường như vấn đề (và các câu hỏi về nó) không được mô tả chính xác (lỗi của tôi), dẫn đến những cách hiểu khác nhau về những gì tôi đang hỏi hoặc cần. Vì vậy, tôi quyết định mở một câu hỏi mới về vấn đề này và cố gắng tránh bất kỳ sự nhầm lẫn nào có thể liên quan đến vấn đề hoặc các câu hỏi cụ thể về nó. Dù sao, cảm ơn bạn đã dành thời gian và chia sẻ cái nhìn sâu sắc của bạn.
alemartini

0

Tôi không chắc chắn làm thế nào nó kéo nó ra, nhưng CUPSD phản hồi cả http và https trên cổng 631. Nếu nginx không thể làm điều đó ngay bây giờ, có lẽ họ có thể học hỏi từ cách nhóm CUPS kéo nó ra, nhưng CUPS nằm dưới GPL, vì vậy nginx có thể phải xem xét thay đổi giấy phép nếu họ muốn thực hiện khả năng đó và không thể tìm thấy mã để thực hiện ở nơi khác.


0

Về lý thuyết, bạn có thể có một trang web có thể truy cập thông qua HTTP, có thể mở WebSocket trên https: 443 tới bất cứ nơi nào nó muốn. Cái bắt tay ban đầu của WebSocket là HTTP. Vì vậy, vâng, có thể làm cho một trang tìm kiếm không an toàn thực sự có khả năng giao tiếp an toàn. Bạn có thể làm điều này với Thư viện Netty .


0

Điều này cuối cùng có thể làm đúng kể từ 1.15.2. Xem thông tin ở đây .

Trong nginx.conf của bạn, hãy thêm một khối như thế này (bên ngoài khối 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;
    }
}

Sau đó, bạn có thể tạo khối máy chủ bình thường của mình, nhưng lắng nghe trên các cổng khác nhau này:

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

Bằng cách này, khối luồng có thể đọc trước và phát hiện xem nó có phải là TLS hay không (trên cổng 8080 trong ví dụ này), và sau đó proxy chuyển nó đến đúng cổng máy chủ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.