我有一个运行多个虚拟主机的 apache 2.4.18 服务器。
/etc/apache2/sites-enabled/000-default.conf:
<VirtualHost *:80>
DocumentRoot /var/www/html
Redirect 400 /
</VirtualHost>
/etc/apache2/sites-enabled/000-default-ssl.conf:
<VirtualHost _default_:443>
DocumentRoot /var/www/html
SSLEngine on
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
</VirtualHost>
/etc/apache2/sites-enabled/001-custom.conf:
<VirtualHost _default_:443>
ServerName www.example.org
Include /etc/apache2/letsencrypt/main.conf
SSLCertificateFile /etc/letsencrypt/live/example.org/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.org/privkey.pem
Include /etc/apache2/proxytunnel/main.conf
</VirtualHost>
<VirtualHost *:80>
ServerName www.example.org
Redirect permanent / https://www.example.org/
</VirtualHost>
/etc/apache2/proxytunnel/main.conf:
ProxyRequests On
AllowConnect 2222
<Proxy *>
Order deny,allow
Deny from all
</Proxy>
<Proxy 127.0.0.1>
Order deny,allow
Allow from all
</Proxy>
我还在同一台机器的 2222 端口上运行一个 SSH 服务器。
如果我从远程计算机使用 proxytunnel 通过 SSL 访问 SSH 服务器,并使用 apache 作为代理服务器:
proxytunnel -v -E -p www.example.org:443 -d 127.0.0.1:2222
我收到以下错误:
SSL client to proxy enabled
Local proxy www.example.org resolves to xxx.xxx.xxx.xxx
Connected to www.example.org:443 (local proxy)
Tunneling to 127.0.0.1:2222 (destination)
Communication with local proxy:
-> CONNECT 127.0.0.1:2222 HTTP/1.1
-> Host: 127.0.0.1:2222
-> Proxy-Connection: Keep-Alive
<- HTTP/1.1 405 Method Not Allowed
HTTP return code: 405 Method Not Allowed
<- Date: Thu, 14 Apr 2016 00:55:57 GMT
<- Server: Apache/2.4.18 (Debian)
<- Allow: GET,HEAD,POST,OPTIONS
<- Content-Length: 309
<- Content-Type: text/html; charset=iso-8859-1
但是如果我将该文件包含/etc/apache2/proxytunnel/main.conf
在我的/etc/apache2/sites-enabled/000-default-ssl.conf
虚拟主机中,它就可以工作......
SSL client to proxy enabled
Local proxy www.example.org resolves to xxx.xxx.xxx.xxx
Connected to www.example.org:443 (local proxy)
Tunneling to 127.0.0.1:2222 (destination)
Communication with local proxy:
-> CONNECT 127.0.0.1:2222 HTTP/1.1
-> Host: 127.0.0.1:2222
-> Proxy-Connection: Keep-Alive
<- HTTP/1.0 200 Connection Established
<- Proxy-agent: Apache/2.4.18 (Debian)
Tunnel established.
SSH-2.0-OpenSSH_7.2p2 Debian-2
因此,我的结论是,要使 CONNECT 请求通过 SSL 在 apache 服务器上工作,AllowCONNECT
除了发出请求的实际 vhost 之外,还必须将指令放在 apache 代理端口的默认 vhost 中。
请注意,使用 HTTP 上的 CONNECT(而不是 HTTPS)且在代理隧道中禁用加密时,不会重现此行为。此外,问题并非来自代理隧道,因为请求已正确转发到 apache 并出现在服务器日志中:是 apache 主动拒绝了它,因为它认为 HTTP 方法(CONNECT)在此服务器上无效(尽管在相关的虚拟主机中允许)。
答案1
此外,问题不是来自代理隧道,因为请求被正确转发到 apache,并且出现在服务器日志中
这是错误的,在我使用的版本中,proxytunnel 不支持 SNI(最新提交从本文发布之日起从他们的存储库中提取,所以如果我相信他们的发布标签的话,这个版本比 1.9.1 稍微早一点)。
结果是,无论我尝试连接到哪个主机,apache 选择的 vhost 都是默认 vhost。
答案2
我想根据自己的经验提供反馈来补充这个问题。我还不能发表评论。
我在 Windows 7 上使用 Proxytunnel 1.9.9(大约 2018 年 4 月),它是我使用 Cygwin 32 位从源代码编译的。
经过测试,我发现 Proxytunnel 无法将 -p 参数中的主机名与 Apache2 Virtualhost 中的 ServerName 正确匹配。使用 Proxytunnel 通过一个 http 或 https 本地代理建立隧道会失败,除非包含 AllowCONNECT 方法的虚拟主机是默认虚拟主机。
列出 Apache 中启用的虚拟主机并查看哪个是默认的:
apachectl -S
在我的设置中,我将每个虚拟主机放在 sites-available 目录中的自己的文件中。运行以下命令:
ls -1
sites-available 目录中列出的第一个文件是 apache2 将设为默认虚拟主机的虚拟主机。这样,我将 http(或 https)正向代理替换为默认和第二行虚拟主机...通过更改其名称,使其不会出现在目录列表中的第一个。
我发现这个用于 http 本地代理的命令:
proxytunnel -v -p http-forward-proxy.com:80 -d 192.168.0.10:22
或者对于 https 本地代理使用此命令:
proxytunnel -v -E -C root.pem https-forward-proxy.com:443 -d 192.168.0.10:22
如果 AllowCONNECT 方法在默认虚拟主机中,则给我 OpenSSH 横幅,否则,给我 HTTP 405 错误...即使非默认端口 80 虚拟主机包含:
ServerName http-forward-proxy.com
或者在代理到 https 正向代理的情况下,当非默认端口 443 虚拟主机包含以下内容:
ServerName https-forward-proxy.com
我查看了 Apache2 日志并确认处理总是转到默认虚拟主机。
我还在 Proxytunnel GitHub 页面上发布了有关 HTTP 案例的“问题” https://github.com/proxytunnel/proxytunnel/issues/31
然而
我使用带有 sslh 的 Proxytunnel 客户端和 stunnel 服务器端。在这种情况下,我发现 Proxytunnel 命令中的主机名与 sslh 中的 sni_hostnames{} 功能匹配,因此在这种情况下,主机名似乎至少在加密和使用 SNI 时传输正确。
答案3
感谢您提出问题并写下结论。我现在有一个可用的设置,感谢您!使用 apache 默认 vhost 效果很好。
不过,我想知道 proxytunnel 的 SNI 是否真的是问题所在。就我而言,我有https://app1.example.com这是 apache 的默认虚拟主机和另一个虚拟主机https://example.com。我的 letsencrypt 证书的 commonname 是 app1.example.com,subjectaltname 是 example.com。
如果我将其添加ProxyRequests on
到 example.com,它根本不起作用。如果我将其移动到 app1.example.com,它就可以正常工作。
据我了解,如果多个虚拟主机在单个 IP 上使用一个证书,则不需要 SNI。
另外,我使用 curl 进行测试,如curl -v -x https://example.com https://somethingelse.com
。 Curl 支持 SNI,我使用 wireshark 检查过:它确实在握手扩展中为 CONNECT 请求和 GET 请求发送了正确的主机名。我仍然得到405 Method Not Allowed
。
因此,问题似乎出在 mod_proxy 未正确激活。您认为呢?
我在使用 apache 2.4.25。