使用单个端口通过Nginx处理HTTP和https请求


16

我想知道nginx是否能够在同一端口上处理http和https请求。[*]

这就是我想要做的。我正在运行用于处理http请求的Web服务器(lighttpd),以及一个通过https服务文档树的特定部分的C程序。这两个进程在同一服务器上运行。

在防火墙级别,我只能有一个端口将流量转发到该服务器。因此,我想在此服务器上设置nginx,以便它在单个端口上侦听请求,然后:

A)重定向所有http://myhost.com/ *请求,以便它们转到localhost:8080(lighttpd正在侦听)

B)如果用户请求以例如https:// myhost.com/app开头的URL,则会将该请求发送到localhost:8008(C程序)。请注意,在这种情况下,必须对远程浏览器和nginx之间的通信进行加密。

您认为这可能吗?如果是,该怎么办?

我知道如何使用两个不同的端口来执行此操作。我面临的挑战是仅使用一个端口来完成此操作(不幸的是,我无法控制此特定环境上的防火墙配置,因此这是我无法避免的限制)。使用诸如通过ssh进行反向端口转发来绕过防火墙的技术也不起作用,因为这仅适用于仅具有Web浏览器和Internet链接的远程用户。

如果这超出了nginx的功能,您是否知道其他任何可以满足此要求的产品?(到目前为止,我在使用lighttpd和pound进行设置方面一直没有成功)。我也希望避免使用Apache(尽管如果它是唯一的选择,我愿意使用它)。

预先感谢Alex

[*]要清楚一点,我说的是通过同一端口处理加密未加密的HTTP连接。加密是通过SSL还是TLS无关紧要。


HTTPS请求默认情况下会到达端口443,因此即使您可以正常工作(并且我认为可能有一点黑客行为),也需要使用yourhost.comyourhost.com:80作为链接(或yourhost.com:443yourhost.com)。
Zanchey

好的,我是Server Fault的新手,我不知道是否可以删除自己的问题。既然问题似乎还不够清楚,我将打开一个新问题。无论如何,非常感谢为这个问题提供有用建议的每个人。
alemartini

Answers:


18

根据Wikipedia关于状态码的文章,当将HTTP流量发送到https端口时,Nginx具有自定义错误码(错误代码497)

并根据 error_page nginx文档,您可以定义将针对特定错误显示的URI。
因此,我们可以创建一个uri,将在引发错误代码497时将客户端发送到该uri。

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。从而保留客户端通过的请求方法;我们使用错误处理重定向,如下所示 error_page上的nginx文档所示

这就是为什么我们使用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连接之间进行区别。

针对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服务一切都是一种选择,尽管我希望能够按照我所描述的方式进行设置。如果前端Web服务器(充当反向代理)可以建立正常的http会话,然后在不更改端口的情况下将其升级为https,则可以这样做。我认为此行为在RFC2817中进行了描述(使用HTTP / 1.1升级到TLS),但是我不确定nginx或其他Web服务器是否知道如何处理该标准。
alemartini

我没有时间阅读整个RFC(并且不确定我是否足够聪明才能理解它!),但是您是在建立安全会话还是在完全不同的会话之前谈论标准协商?我想我了解的更多了-服务器在两个端口上服务,并且代理是引用请求的服务器-听起来很酷,但我从未见过。也许可以通过在一个端口上创建一个安全站点并拥有一个简单地继承/导入另一个网站的完整虚拟目录来解决问题?它不能解决所有问题,但可以解决问题:S
William Hilsum,2009年

1

您不能在同一端口上同时支持HTTP和HTTPS,因为连接的两端都希望使用某种语言,并且如果另一端正在讲其他内容,则它们不够灵巧。

正如您对Wil的答案的评论所建议的那样,您可以使用TLS升级(我相信较新的nginx版本支持它,尽管我没有尝试过),但是它没有运行HTTP和HTTPS,而只是通过TLS升级运行HTTP。问题仍然是浏览器支持-大多数浏览器(仍然)不支持它。如果您的客户数量有限,那么这是有可能的。


1
加密和未加密的HTTP流量可以通过单个端口处理。我想知道的是,是否可以通过使用nginx或其他产品(例如lighttpd)作为反向代理。这种设置很可能可以由Apache处理,但是我忘了在我最初的问题中提到我宁愿不必使用Apache(尽管如果Linux上没有其他选择,我会这样做)平台来完成此任务)。
alemartini 2009年

正如我在回答中所说的:“尽管我还没有尝试过,但我相信较新的Nginx版本支持[TLS升级]”。如果您需要我为您阅读手册,那么您很不幸。
womble

如果给我留下我需要有人为我阅读手册的印象,我感到抱歉。似乎问题(及其相关问题)描述得不够准确(我的错误),导致对我所要询问或需要的东西的解释不同。因此,我决定就此问题打开一个新问题,并尝试避免对该问题或有关此问题的任何可能的混淆。无论如何,感谢您的宝贵时间并分享您的见解。
alemartini

0

我不确定它如何实现,但是CUPSD在端口631上同时响应http和https。如果nginx现在无法做到这一点,也许他们可以向CUPS团队学习如何实现它,但是CUPS在GPL,因此,nginx如果确实想实现这种功能并且找不到在其他地方这样做的代码,则可能不得不考虑更改其许可证。


0

从理论上讲,您可以拥有一个可通过HTTP访问的网页,该网页能够将https:443上的WebSocket打开到它想要的任何位置。WebSocket的初始握手是HTTP。因此,是的,有可能使外观不安全的页面实际上能够进行安全的通信。您可以使用Netty Library进行此操作。


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),然后代理将其本地传递给正确的服务器端口。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.