我想知道 nginx 是否能够在同一端口处理 http 和 https 请求。[*]
这就是我想做的事情。我正在运行一个处理 http 请求的 Web 服务器 (lighttpd) 和一个通过 https 为文档树的特定部分提供服务的 C 程序。这两个进程在同一台服务器上运行。
在防火墙级别,我只能有一个端口将流量转发到此服务器。 因此,我想要做的是在此服务器上设置 nginx,以便它侦听单个端口上的请求,然后:
A)重定向所有 * 请求,以便它们转到 localhost:8080(lighttpd 正在监听的位置)
B) 如果用户请求以 https://myhost.com/app 开头的 URL,它会将该请求发送到 localhost:8008(C 程序)。请注意,在这种情况下,远程浏览器和 nginx 之间的流量必须加密。
您认为这可能吗?如果可能,该怎么做?
我知道如何使用两个不同的端口来实现这一点。我面临的挑战是只使用一个端口来实现这一点(不幸的是,我无法控制这个特定环境中的防火墙配置,因此这是我无法避免的限制)。使用通过 ssh 进行反向端口转发等技术来绕过防火墙也行不通,因为这应该适用于只有 Web 浏览器和 Internet 链接的远程用户。
如果这超出了 nginx 的能力,您知道有其他产品可以满足此要求吗?(到目前为止,我使用 lighttpd 和 pound 进行设置都失败了)。我还希望避免使用 Apache(尽管如果它是唯一可能的选择,我愿意使用它)。
提前致谢,亚历克斯
[*] 需要说明的是,我指的是通过同一端口处理加密和非加密的 HTTP 连接。加密是通过 SSL 还是 TLS 完成并不重要。
2
9 个回答
9
根据维基百科关于状态代码的文章,当 http 流量发送到 https 端口时,Nginx 有一个自定义错误代码(错误代码 497)
根据,您可以定义一个 URI,该 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。因此,为了保留客户端通过的请求方法;我们使用错误处理重定向,如
这就是我们使用301 =307
重定向的原因。
使用此处显示的 nginx.conf 文件,我们可以让 http 和 https 监听同一个端口
|
对于那些可能正在搜索的人:
将ssl on;
和添加error_page 497 $request_uri;
到您的服务器定义。
2
-
9对 HoverHells 答案的一点改进:将
ssl on;
和添加error_page 497 =200 $request_uri;
到您的服务器定义中。这会将状态代码更改为 200。
– -
这个服务故障解释了该解决方案为何有效。
–
|
从 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),然后代理将其本地传递到正确的服务器端口。
1
-
效果很好;在 ubuntu 18.04 上,必须按照此处的说明安装 nginx 主线包:
–
|
如果您真的想聪明一点,可以使用连接代理来嗅探传入数据流的前几个字节,并根据字节 0 的内容切换连接:如果它是 0x16(SSL/TLS“握手”字节),则将连接传递到 SSL 端,如果它是字母字符,则执行正常 HTTP。。
|
是的,这是可能的,但需要修补 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;
}
|
我认为没有任何东西可以在一个端口上处理两种不同的协议……
我很好奇为什么你只能转发一个端口,但除此之外……这并不理想,但如果我处于你的位置,我会通过 https 提供一切服务。
2
-
嗨,Wil,谢谢你的回答!我认为通过 https 提供所有服务可能是一种选择,尽管我希望能够按照我描述的方式进行设置。如果前端 Web 服务器(充当反向代理)可以建立正常的 http 会话,然后在不更改端口的情况下将其升级到 https,也许可以做到这一点。我认为 RFC2817(使用 HTTP/1.1 升级到 TLS)中描述了此行为,但我不确定 nginx 或其他 Web 服务器是否知道如何处理该标准。
– -
我没有时间阅读整个 RFC(并且不确定我是否足够聪明来理解它!)但您是在谈论安全会话建立之前的标准协商还是完全不同的会话?我想我理解得更多一些 – 服务器在两个端口上提供服务,并且是代理引用请求 – 听起来很酷,但我从未见过这样做。也许解决方案可能是在一个端口上创建一个安全站点,并拥有一个简单地继承/导入其他网站的整个虚拟目录?它不能解决所有问题,但可能有效 :S
–
|
您无法通过同一端口同时支持 HTTP 和 HTTPS,因为连接的两端都希望使用某种语言,而它们还不够聪明,无法判断另一端是否在说其他语言。
正如您对 Wil 的回答的评论所建议的那样,您可以使用 TLS 升级(我相信较新的 nginx 版本支持它,尽管我还没有尝试过),但这不是运行 HTTP 和 HTTPS,这只是运行带有 TLS 升级的 HTTP。问题仍然是浏览器支持——大多数浏览器(仍然)不支持它。但是,如果您的客户端数量有限,那么这是一种可能性。
3
-
1加密和未加密的 HTTP 流量可以通过单个端口处理。我想知道的是,使用 nginx 或其他产品(如 lighttpd)作为反向代理是否可以实现这一点。这种设置很可能可以由 Apache 处理,但我忘了在最初的问题中提到我宁愿不使用 Apache(尽管如果 Linux 平台上没有其他选择来实现这一点,我会这样做)。
– -
正如我在回答中所说,“我相信较新的 nginx 版本支持 [TLS 升级],尽管我还没有尝试过”。如果你需要我为你阅读手册,那么你就没那么幸运了。
–
♦ -
如果我给人的印象是我需要有人帮我读手册,我很抱歉。似乎问题(以及有关它的问题)没有得到足够准确的描述(我的错误),导致对我所问或需要的内容有不同的解释。所以我决定就这个问题提出一个新问题,并尽量避免对问题或有关它的具体问题产生任何可能的混淆。无论如何,感谢您抽出时间并分享您的见解。
–
|
我不确定它是如何实现的,但是 CUPSD 在端口 631 上响应 http 和 https。如果 nginx 现在无法做到这一点,也许他们可以学习 CUPS 团队是如何实现的,但是 CUPS 属于 GPL,所以如果 nginx 确实想实现这样的功能而在其他地方找不到代码,他们可能不得不考虑更改其许可证。
|
理论上,您可以拥有一个可通过 HTTP 访问的网页,该网页能够在 https:443 上打开以连接到任何想要的地址。WebSocket 初始握手是 HTTP。因此,是的,有可能让一个看起来不安全的页面实际上能够进行安全通信。您可以使用来实现这一点。
|
–
–
|