通过SSL连接将转发请求连接到转发HTTP代理?

时间:2011-07-06 09:59:30

标签: apache http ssl proxy

我正在编写HTTP代理,但我无法理解通过TLS发出CONNECT请求的一些细节。为了获得更好的图片,我正在尝试使用Apache来观察它与客户端的交互方式。这是我的默认虚拟主机。

NameVirtualHost *:443
<VirtualHost>
  ServerName example.com
  DocumentRoot htdocs/example.com  
  ProxyRequests On
  AllowConnect 22
  SSLEngine on
  SSLCertificateFile /root/ssl/example.com-startssl.pem
  SSLCertificateKeyFile /root/ssl/example.com-startssl.key
  SSLCertificateChainFile /root/ssl/sub.class1.server.ca.pem
  SSLStrictSNIVHostCheck off
</VirtualHost>

Apache和我的客户之间的对话是这样的。

一个。客户端连接到example.com:443并在TLS握手中发送example.com

湾客户端发送HTTP请求。

CONNECT 192.168.1.1:22 HTTP/1.1
Host: example.com
Proxy-Connection: Keep-Alive

℃。 Apache说HTTP/1.1 400 Bad Request。 Apache错误日志说

Hostname example.com provided via SNI and hostname 192.168.1.1
provided via HTTP are different. 

似乎Apache没有查看Host标头,只是看到它存在,因为HTTP / 1.1需要它。如果客户端发送Host: foo,我会得到相同的失败行为。如果我在没有TLS的情况下向example.com:80发出HTTP请求,那么Apache会将我连接到192.168.1.1:22。

我不完全理解这种行为。 CONNECT请求有问题吗?我似乎无法找到解释所有这些内容的RFC的相关部分。

4 个答案:

答案 0 :(得分:35)

目前尚不清楚您是否尝试将Apache Httpd用作代理服务器,这可以解释您获得的400状态代码。 CONNECT由客户端使用,并发送到代理服务器(可能是Apache Httpd,但通常不是),而不是发送到目标Web服务器。

在客户端和终端服务器之间建立TLS连接之前,客户端和代理服务器之间使用

CONNECT。客户端(C)连接到代理(P)proxy.example.com并发送此请求(包括空行):

C->P: CONNECT www.example.com:443 HTTP/1.1
C->P: Host: www.example.com:443
C->P:

代理打开与www.example.com:443(P-S)的TCP连接,并以200状态代码响应客户端,接受请求:

P->C: 200 OK
P->C: 

此后,客户端和代理(C-P)之间的连接保持打开状态。代理服务器中继与P-S之间的C-P连接上的所有内容。客户端通过在该通道上启动TLS握手,将其活动(P-S)连接升级为SSL / TLS连接。由于现在所有内容都被中继到服务器,就好像TLS交换是直接使用www.example.com:443完成的。

代理在握手中不起作用(因此与SNI一起)。 TLS握手实际上直接在客户端和终端服务器之间发生。

如果您正在编写代理服务器,则在CONNECT请求中读取允许客户端连接到HTTPS服务器所需的所有操作,建立从代理到终端服务器的连接(在CONNECT请求),向客户端发送200 OK回复,然后将您从客户端读取的所有内容转发给服务器,反之亦然。

RFC 2616CONNECT视为建立简单隧道(​​即它)的一种方式。在RFC 2817中有更多关于它的内容,尽管很少使用RFC 2817的其余部分(在非代理HTTP连接中升级到TLS)。

看起来你要做的是通过TLS在客户端(C)和代理(P)之间建立连接。没关系,但客户端不会使用CONNECT连接到外部Web服务器(除非它也是连接到HTTPS服务器)。

答案 1 :(得分:3)

来自RFC 2616(第14.23节):

  

Host request-header字段指定Internet主机和端口      请求的资源编号,从原始编号获得      用户或引用资源给出的URI(通常是HTTP URL,      如第3.2.2节所述。主机字段值必须表示      由...给出的源服务器或网关的命名权限      原始网址。

我的理解是您需要将地址从CONNECT行复制到HOST行。总而言之,资源的地址是192.168.1.1,并且您通过example.com连接的事实不会从RFC的角度改变任何内容。

答案 2 :(得分:3)

你做的一切都是正确的。这是Apache的错误。最近刚刚添加了对TLS上的CONNECT的支持(https://issues.apache.org/bugzilla/show_bug.cgi?id=29744),还有一些事情需要解决。你遇到的问题就是其中之一。

答案 3 :(得分:2)

很少在TLS(https)中看到CONNECT方法。我实际上不知道任何客户是谁(我会有兴趣知道它做了什么,因为我认为它实际上是一个很好的功能)。

通常,客户端使用http(plain tcp)连接到代理,并将CONNECT方法(和主机头)发送到主机:443。然后代理将与端点建立透明连接,然后客户端通过SSL发送SSL握手。

在这种情况下,数据受到“端到端”的保护。

没有真正指定CONNECT方法,它只在HTTP RFC中保留。但通常它非常简单,因此可以互操作。 Method指定host [:port]。主机:标头可以简单地被忽略。可能需要一些额外的代理身份验证标头。当连接的主体开始时,代理不再需要解析(有些会这样做,因为它们会检查有效的SSL握手)。