最近网络持续抽风,套cloudflare和github的网页持续上不去,错误的特征就是超时,ping可以通,但443端口无反应,连端口关闭的信息都没有……
在排查故障时看到了一些关于SSL
、HTTPS
、SNI
、nginx
的东西,再加上内网有一个树莓派。前几天刚刚安排上smartdns
做内网dns服务器,于是就安排了一下nginx的HTTPS
反向(正向)代理。这nginx前置都快糊客户端脸上不应该叫正向代理? →_→
nginx可以利用ngx_http_proxy_module
来进行反向代理。
通常的配置是这样的:
server{
listen 443 ssl http2;
server_name cfwk.dogcraft.top;
access_log /var/log/nginx/tuu3.log;
ssl_certificate /home/pi/testdog.pem;
ssl_certificate_key /home/pi/dogww.key;
access_log /var/log/nginx/indexpage.log ;
location /{
proxy_pass https://162.159.211.103:8443/;
proxy_set_header Host $http_host;
#proxy_set_header Host "sdd.dogcraft.top";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header User-Agent $http_user_agent;
proxy_set_header Accept-Encoding '';
#proxy_buffering off;
#proxy_ssl_verify off;
#proxy_ssl_name yyxxxxxx.dogcraft.top;
#proxy_ssl_server_name on;
}
}
其中proxy_pass https://162.159.211.103:8443/;
这个是后端服务器的地址。这个配置在不代理cloudflare的网站时是没有问题的。
在内网树莓派上建立这样一个代理用来代理www.dogcraft.top
也就是直接部署在阿里云上的主页。
server{
listen 443 ssl http2;
server_name cfwk.dogcraft.top;
access_log /var/log/nginx/tuu3.log;
ssl_certificate /home/pi/testdog.pem;
ssl_certificate_key /home/pi/dogww.key;
access_log /var/log/nginx/indexpage.log ;
location /{
proxy_pass https://101.37.173.241/;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header User-Agent $http_user_agent;
proxy_set_header Accept-Encoding '';
}
}
然后在本地的/etc/hosts
把cfwk.dogcraft.top
的地址指向树莓派的内网ip,然后这样访问cfwk.dogcraft.top
就会出现www.dogcraft.top
的页面,树莓派是自身带有Let's Encrypt
发的证书,所以浏览器不会报证书错误的提醒。
现在改动一个地方
server{
listen 443 ssl http2;
server_name cfwk.dogcraft.top;
access_log /var/log/nginx/tuu3.log;
ssl_certificate /home/pi/testdog.pem;
ssl_certificate_key /home/pi/dogww.key;
access_log /var/log/nginx/indexpage.log ;
location /{
proxy_pass https://101.37.173.241/;
proxy_set_header Host "dogcraft.top";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header User-Agent $http_user_agent;
proxy_set_header Accept-Encoding '';
}
}
把 proxy_set_header Host "dogcraft.top";
给安排上,这个时候再访问https://cfwk.dogcraft.top
出现的页面就是dogcraft.top
这个页面了,这就是一个比较有意思的事情了。
像nginx
这类可以安排多个网站的基础设施是通过Host
头来区分不同的服务的,如果没有指定或者里面没有这个host,就会返回一个默认的host(在监听端口那里标有default的那一个)。现在有了两处配置,分别是proxy_pass
和Host
。其中proxy_pass
决定把http请求安排到那个IP地址之上,'Host'头决定nginx把http请求发送给哪一个后端。
现在的情况比较简单,现在如果用一个套上了一层cloudflare的网站作为后端那就麻烦了,先是直接把配置搬过去
server{
listen 443 ssl http2;
server_name cfwk.dogcraft.top;
access_log /var/log/nginx/tuu3.log;
ssl_certificate /home/pi/testdog.pem;
ssl_certificate_key /home/pi/dogww.key;
access_log /var/log/nginx/indexpage.log ;
location /{
proxy_pass https://sig.dogcraft.top/;#这个是套了一层cloudflare的地址,源站和www.dogcraft.top在一个地方。
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header User-Agent $http_user_agent;
proxy_set_header Accept-Encoding '';
}
}
这样会出现502错误,原因是cloudflare的ip对应的服务器上同时具有很多网站,同样也是具有ssl证书,不同的网站证书肯定是不同的,但是你访问这个ip时cloudflare是不知道你要看那个网站的,所以你需要告诉cloudflare你要哪个网站的证书。这个过程是用明文完成的,因为这个时候还没有完成SSL握手,也无从加密。这是整个HTTPS加密体系中最薄弱的环节,现在知道了这个事情之后感觉这个问题是对TSL与HTTPS整个完全加密体系的嘲讽,最关键与基础的部分居然用明文。毕竟https近几年才大规模普及,当时设计体系的人也难以预料到这个情况,尤其是套上cloudflare这种CDN的网站,像www.dogcraft.top
这种不套上cdn的就没有这个问题,大不了给你个默认证书,反正都是*.dogcraft.top
这种的通配符证书,只要不出三级域名就完全没有问题。现在说是TLS1.3
版本要修复这个问题,安排成ESNI
还需要EDNS
的支持,但这个标准的普及不知道要到那年那月了……(更多信息可以查一下RSA、SNI等)
按照网上的解决方案,安排成这样就行了:
server{
listen 443 ssl http2;
server_name cfwk.dogcraft.top;
access_log /var/log/nginx/tuu3.log;
ssl_certificate /home/pi/testdog.pem;
ssl_certificate_key /home/pi/dogww.key;
access_log /var/log/nginx/indexpage.log ;
location /{
proxy_pass https://sig.dogcraft.top/;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header User-Agent $http_user_agent;
proxy_set_header Accept-Encoding '';
proxy_ssl_name sig.dogcraft.top;
proxy_ssl_server_name on;
}
}
即添加上两行proxy_ssl_name sig.dogcraft.top; proxy_ssl_server_name on;
然后nginx会向后端的服务器发送SNI信息,然后就可以获得到正确的证书了。
然后现在有了三个配置项目。分别是proxy_pass
`Host`proxy_ssl_name
。这三者并不一定是一致的。
配置项目 | 值 | 备注 |
---|---|---|
proxy_pass | 104.28.28.226 | cloudflare给安排的节点ip |
Host | sig.dogcraft.top | 套了一层cloudflare |
proxy_ssl_name | sig.dogcraft.top |
结果:正常访问到sig.dogcraft.top
配置项目 | 值 | 备注 |
---|---|---|
proxy_pass | 104.16.175.255 | 网上搜到的cloudflare自定义ip |
Host | sig.dogcraft.top | 套了一层cloudflare |
proxy_ssl_name | sig.dogcraft.top |
结果:正常访问到sig.dogcraft.top
配置项目 | 值 | 备注 |
---|---|---|
proxy_pass | 104.16.175.255 | 网上搜到的cloudflare自定义ip |
Host | www.dogcraft.top | 在cloudflare仅dns,不套cdn的地址 |
proxy_ssl_name | sig.dogcraft.top | 套了一层cloudflare的二级域名 |
结果:返回www.dogcraft.top
的内容
配置项目 | 值 | 备注 |
---|---|---|
proxy_pass | 104.16.175.255 | 网上搜到的cloudflare自定义ip |
Host | www.dogcraft.top | 在cloudflare仅dns,不套cdn的地址 |
proxy_ssl_name | dogcraft.top | 在cloudflare仅dns,不套cdn的地址 |
结果:返回www.dogcraft.top
的内容
配置项目 | 值 | 备注 |
---|---|---|
proxy_pass | XXX.XX.xx | 网上搜到的解析到同一个IP地址的别人的域名 |
Host | www.dogcraft.top | 在cloudflare仅dns,不套cdn的地址 |
proxy_ssl_name | dogcraft.top | 在cloudflare仅dns,不套cdn的地址 |
结果:返回www.dogcraft.top
的内容
配置项目 | 值 | 备注 |
---|---|---|
proxy_pass | 104.16.175.255 | 网上搜到的cloudflare自定义ip |
Host | XXX.XX.xx | 网上搜到的解析到同一个IP地址的别人的域名 |
proxy_ssl_name | dogcraft.top | 在cloudflare仅dns,不套cdn的地址 |
结果:返回cloudflare的403
配置项目 | 值 | 备注 |
---|---|---|
proxy_pass | 104.16.175.255 | 网上搜到的cloudflare自定义ip |
Host | dogcraft.top | 在cloudflare仅dns,不套cdn的地址 |
proxy_ssl_name | XXX.XX.xx | 网上搜到的解析到同一个IP地址的别人的域名 |
结果:返回cloudflare的403
配置项目 | 值 | 备注 |
---|---|---|
proxy_pass | 104.16.175.255:8443 | 网上搜到的cloudflare自定义ip |
Host | s.dogcraft.top | 套了一层cloudflare,并在非标准的https端口上工作 |
proxy_ssl_name | dogcraft.top | 在cloudflare仅dns,不套cdn的地址 |
结果:返回s.dogcraft.top:8443
的内容
配置项目 | 值 | 备注 |
---|---|---|
proxy_pass | 104.16.175.255:8443 | 网上搜到的cloudflare自定义ip |
Host | r.dogcraft.top | 没套cloudflare,并在非标准的https端口上工作 |
proxy_ssl_name | dogcraft.top | 在cloudflare仅dns,不套cdn的地址 |
结果:返回r.dogcraft.top:8443
的内容
配置项目 | 值 | 备注 |
---|---|---|
proxy_pass | 104.16.175.255 | 网上搜到的cloudflare自定义ip |
Host | sdjkiusk.dogcraft.top | 不解析到任何地址的域名 |
proxy_ssl_name | sig.dogcraft.top | 套了一层cloudflare的二级域名 |
结果:返回cloudflare的Origin DNS error
错误
配置项目 | 值 | 备注 |
---|---|---|
proxy_pass | 104.16.175.255 | 网上搜到的cloudflare自定义ip |
Host | sig.dogcraft.top | 套了一层cloudflare的二级域名 |
proxy_ssl_name | sdjkiusk.dogcraft.top | 不解析到任何地址的域名 |
结果:正常访问到sig.dogcraft.top
配置项目 | 值 | 备注 |
---|---|---|
proxy_pass | 104.16.175.255 | 网上搜到的cloudflare自定义ip |
Host | sig.dogcraft.top | 套了一层cloudflare的二级域名 |
proxy_ssl_name | sksui.sdjkiusk.dogcraft.top | 不解析到任何地址的域名 |
结果:返回nginx的502报错,查看日志是SSL握手出了问题。
结果总结。只要以dns方式接入cloudflare,无论是不是用仅dns还是代理,通过cloudflare的ip都可以访问到。proxy_pass
控制着cloudflare的节点ip以及回源所用的端口号。proxy_ssl_name
控制着发回哪个站的证书,Host
则是控制访问哪个站,如果两者不在同一个域名下则会返回403。
配合内网的smartdns
可以对网站进行偷梁换柱,可以把百度替换成bing,如果这样做就需要自签https证书与自建CA了。
参考资料: