前言
Xray已经出来一段时间了,之前一直使用VLESS+tls+ws方式科学上网,其实都够用,没什么问题。但是了解了下XTLS黑科技,再看看Xray性能测试,这货简直爆炸,性能是TLS的数倍。
其实之前折腾过一次,但是Xray要监听443端口(不监听也行,但是不完美啊),影响做站。虽然有解决方法,但是不够完整,我一不小心就跳坑里面去了。Xray已经带了SNI回落功能(教程看这里:通过 SNI 回落功能实现伪装与按域名分流),Nginx也带SNI功能,我还是觉得用Nginx的比较好,免得折腾Xray影响网站运行。
这里我不会写如何安装Xray,因为教程实在是太多了,嫌麻烦还有一键脚本:Xray_onekey(V2ray脚本别用这个作者的哈,特别是服务器有站点的!!!Xray脚本这个大佬的还不错)。
坑
因为服务器上有几个站点,装完Xray后实现SNI分流,过程非常顺利,但是chrome上有问题,几个二级域名访问的内容全部和第一个访问的域名一样。
例如:我先访问 a.example.com 然后在访问 b.example.com,b.example.com 响应内容和 a.example.com 一模一样,然后再访问 c.example.com 还是和 a.example.com 一样。开chrome隐身模式直接访问 c.example.com 正常,然后访问 a.example.com 或 b.example.com 内容又全是 c.example.com 的(不一定会每次都这样,要看浏览器是否使用现有连接或服务器是否断开了之前的连接)。
这个问题折腾我很长时间,最后发现和HTTP/2有关,我所有站点不使用HTTP/2就会正常,只要有一个站点使用了HTTP/2,你先访问HTTP/2站点,那么后面的其他子域名都会变成它的内容。这个情况没有仔细测试证明,无法确定。
最开始我使用Xray的SNI也是这样,我还以为是Xray SNI的问题,之后换成Nginx使用SNI还是这样。chrome中有这个问题,用手机safari浏览器完全正常。
经过网上一番搜刮,终于找到了问题根源:通配符域名证书。
首先你得了解什么是SNI,个人理解:SNI就是通过证书区分服务。多个二级域名使用通配符域名证书的话,相同的证书无法区分具体想访问的服务,因为它们的证书都一样。
用下人家的图(原作者文章:Multiplex TLS Traffic with SNI Routing):
*由于原始服务器10.0.3.2使用的TLS证书具有通配符名称 .example.com,因此Web浏览器可以建立服务器名称为 b.example.com 的 TLS连接,并对HTTP/2请求使用相同的连接到 a.example.com。这可能会在网站和应用程序中导致未定义的行为。**
因为HTTP/2会保持TCP连接,导致你访问了 a.example.com 后,在访问 b.example.com 的时候使用的同一个连接,SNI跟失效了一样,直接使用该连接访问之前的服务。我在Xray的调试日志中发现访问错误的域名没有获取到,是空的!
两个域名使用同一个连接(红框内是连接ID),可以很清楚的看到两个域名使用同一个连接,返回的内容也是一模一样:
解决办法两种:
- 不使用HTTP/2。不确定是否HTTP2的问题,但是你可以试一试
- 不使用通配符域名证书,站点域名证书一个个申请。
配置Nginx SNI
使用Nginx首先得安装(如果服务器自带就不用折腾了),不会安装Nginx可以看我之前的文章:Ubuntu 安装nginx(如果使用Xray_onekey脚本,那他已经帮你全部弄好了,你都不用折腾)。
nginx.conf配置追加:
stream {
map $ssl_preread_server_name $orzlee {
a.example.com a;
b.example.com b;
c.example.com c;
}
upstream a {
server 127.0.0.1:60088; ### A站点监听端口,xray xtls伪装站点
}
upstream xtls {
server 127.0.0.1:4443; ### 你的Xray端口
}
upstream b {
server 127.0.0.1:60001; ### B站点监听端口,业务站点
}
upstream c {
server 127.0.0.1:60002; ### C站点监听端口,业务站点
}
server {
listen 443 reuseport;
listen [::]:443 reuseport;
proxy_pass $orzlee;
ssl_preread on;
proxy_protocol on;
}
server {
### Xray XTLS
listen 127.0.0.1:60088 proxy_protocol;
proxy_pass xtls;
}
}
a.example.com.conf配置(伪装站点):
server
{
listen 80;
listen [::]:80;
server_name a.example.com;
return 301 https://$http_host$request_uri;
}
server
{
### 伪造站点由Xray处理SSL
listen 127.0.0.1:60003 proxy_protocol; ### xray http/1.1
listen 127.0.0.1:60004 http2 proxy_protocol; ### xray http/2
set_real_ip_from 127.0.0.1;
real_ip_header proxy_protocol;
port_in_redirect off; ###重定向去掉端口号
server_name a.example.com;
index index.html index.htm index.php default.php default.htm default.html;
root /var/www/a.example.com;
...
}
b.example.com.conf配置(其他业务站点配置照葫芦画瓢):
server
{
listen 80;
server_name b.example.com;
rewrite ^(.*) https://b.example.com$1 permanent;
}
server
{
listen 127.0.0.1:60001 proxy_protocol ssl http2;
set_real_ip_from 127.0.0.1; ### 获取真实客户IP,不然全是127.0.0.1
real_ip_header proxy_protocol;
port_in_redirect off; ###重定向去掉端口号。不然类似 try_files nginx帮你重定向时,你浏览器上都带了60003这样的端口
ssl_certificate b.example.com.cer; ### 证书换成你的
ssl_certificate_key b.example.com.key; ### 证书换成你的
server_name b.example.com;
root /var/www/b.example.com;
index index.php index.html index.htm;
...
}
配置Xray
服务端配置:
{
"log": {
"access": "/var/log/xray/access.log",
"error": "/var/log/xray/error.log",
"loglevel": "warning"
},
"inbounds": [
{
"listen": "127.0.0.1",
"port": 4443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "你的UUID",
"flow": "xtls-rprx-direct"
}
],
"decryption": "none",
"fallbacks": [
{
"dest": 60003,
"xver": 1
},
{
"dest": 60004,
"alpn": "h2",
"xver": 1
}
]
},
"streamSettings": {
"network": "tcp",
"security": "xtls",
"xtlsSettings": {
"alpn": [
"h2",
"http/1.1"
],
"minVersion": "1.3",
"certificates": [
{
"certificateFile": "b.example.com.cer 记得换证书",
"keyFile": "b.example.com.key 记得换证书"
}
]
}
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
}
],
"outbounds": [
{
"protocol": "freedom"
}
]
}
客户端看作者说明吧,值得一看:Outbounds 可用协议列表-VLESS,flow
仔细看看。
结语
本来折腾Xray的,结果Nginx折腾大半天。虽然掉坑里了,总算是爬出来了,受益匪浅!
通配符证书用起来省事,碰到坑了一下还不好找问题。就今天这个坑上网找都不知道怎么描述,基本上找不到相关问题,比较冷门。之前参考其他博主Nginx SNI配置发现有其他同学也有这样的问题,但是无从查起,不了了之!
遇到这个坑不长记性都难。
请问在AWS上刚建的节点,nginx前置+xray的,刚建完可以用,过一会就用不了了,用ping.pe测试国内国外还都能ping通,这是啥情况
能ping通不能代表端口没封,换个端口试试!
按照这样配置之后,访问a.example.com access日志里面拿到的都是127的ip。使用tcpdump捕获流量的时候 看到的443-60088 是带有proxy的 从60088-4443 就丢了原始的客户端真实ip了。请问这种情况是需要如何修改呢?
你可以在以下配置中配置proxy_set_header试试,我没有试过,你可以试试看:
server {
Xray XTLS listen 127.0.0.1:60088 proxy_protocol; proxy_pass xtls; proxy_set_header Host $host; 保留代理之前的host proxy_set_header X-Real-IP $remote_addr; 保留代理之前的真实客户端ip proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header HTTP_X_FORWARDED_FOR $remote_addr; 在多级代理的情况下,记录每次代理之前的客户端真实ip proxy_set_header X-Forwarded-Proto $scheme; 表示客户端真实的协议(http还是https) proxy_redirect default; 指定修改被代理服务器返回的响应头中的location头域跟refresh头域数值 }你可以参考一下https://cloud.tencent.com/developer/article/1908285,我这评论格式有点乱。
你的access日志里 访问a.example.com能拿到真实ip吗?在这个地方没办法配置proxy_set_header nginx -t会报错
stream {
server {
listen 127.0.0.1:60088 proxy_protocol;
}
}
nginx: [emerg] "proxy_set_header" directive is not allowed here
我的没问题,你配置文件里面有这两行吗?获取真实IP:
set_real_ip_from 127.0.0.1;
real_ip_header proxy_protocol;
nginx.conf配置如下
server { listen 443 reuseport; proxy_pass $sni_name; ssl_preread on; proxy_protocol on; } server { listen 127.0.0.1:52000 proxy_protocol; proxy_pass xtls; }stream {
map $ssl_preread_server_name $sniname {
example.com ;
xray.example.com xray;
a.example.com a;
b.example.com b;
}
upstream _ {
server 127.0.0.1:51000;
}
upstream xray {
server 127.0.0.1:52000;
}
upstream xtls {
server 127.0.0.1:52002;
}
upstream a {
server 127.0.0.1:51000;
}
upstream b {
server 127.0.0.1:51000;
}
}
http {
include /etc/nginx/conf.d/*.conf;set_real_ip_from 127.0.0.1;
real_ip_header proxy_protocol;
port_in_redirect off;
}
xray.conf配置
proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header HTTP_X_FORWARDED_FOR $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; server_name xray.example.com; ssl_certificate /opt/sslCert/fullchain.pem; ssl_certificate_key /opt/sslCert/key.pem; ssl_session_timeout 5m; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; if ($host != 'xray.example.com'){ return 404; }server {
listen 127.0.0.1:52101 proxy_protocol;
listen 127.0.0.1:52102 http2 proxy_protocol;
}
fallback配置
"fallbacks": [
{
"name": "",
"alpn": "",
"path": "",
"dest": "52101",
"xver": 1
},
{
"name": "",
"alpn": "h2",
"path": "",
"dest": "52102",
"xver": 1
}
]
这时候请求xray.example.com nginx的access日志显示是127.0.0.1
有的 这样配置在http里
http {
set_real_ip_from 127.0.0.1; # 获取真实客户IP,不然全是127.0.0.1
real_ip_header proxy_protocol;
port_in_redirect off; # 重定向去掉端口号
server {
....
}
}
不好意思,之前没有看清楚你的问题。其实我的xray伪装站点没有记录日志。
你的问题是nginx->xray是能获取的客户端IP的,但是xray->伪装站点获取不到,xray->伪装站点的流量是xray转发过去的,所以你的伪装站点获取的客户端IP是127.0.0.1本机IP。你想要伪装站点获取到客户真实IP的话可能要去看看xray的文档,和nginx获取真实客户端IP的方法,本文确实没有提及相关信息。可能要你自己去多去找找了。
謝謝分享,是有質量既教學。网上東拼西溝的起碼有20篇,浪費了我很多時間才找到這裡。
我設置最新的xtls-rprx-vision,大致跟了教程,各功能都實現了,但xray fallback過來的沒有真IP傳到去nginx后端服務,不知是不是vision的問題。其他直接由nginx sni分流的,都沒有問題。
感谢博主分享,但是我遇到个问题,站点虽然都能打开了,但是v2rayng却连不上,不知道客户端需要怎么配置
已经搞好了,但是又出现一个问题,就是连接上一会之后报错了,然后重启了lnmp,出现这个错误:Starting nginx... nginx: [emerg] bind() to 127.0.0.1:8443 failed (99: Cannot assign requested address)
failed
8443端口被占用了
但是我查看端口并没有看到8443端口被占用,而且,我更换了别的端口6004,又提示一样的信息了,又提示6004被占用
加了这句XRAY就不正常代理了。如果遇到同样问题,去掉试试。
你好,我想请教一下安装了宝塔nginx也是同样配置吗?我今天尝试了一下发现xray可以运行,但是无法访问我的网页,不论是伪装网站还是业务网站
宝塔我没有用过,不太清楚怎么操作!建议还是直接修改配置文件比较直观!
为什么xray回落会到其他端口?上游也没分配啊,根本打不开给xray设置的简易网站,不是很理解这个跳转是怎么跳转的。
不知道你怎么设置的。想实现什么效果!
我是想着看看专门为xray搭建的简易网页(就是回落的地方),就是这个效果。
PS:我不应该加个啊的语气,不像请教的语气2333.
如果要简单回落安文档操作就可以实现,不用nginx,用xray监听440和80端口。如果还有其他站点按我的文章操作也是没问题的!
来还愿了。您回复我当天就实验成功了,运行至今很稳定,没出差错。感谢大大
请问一下,用上面的配置,nginx使用的是www.aaa.net和www.bbb.com,xtls伪装在www.aaa.net,访问的网站,证书没问题,但是访问www.bbb.com还是一样显示www.aaa.net的证书,已经禁用了所有http2的相关参数了,貌似nginx里面的certificate配置没生效似的,www.aaa.net的nginx配置里面压根没有certificate,都是xray里面处理的,按理www.bbb.com的证书和xray没啥关系吧
stream {
SNI map map $ssl_preread_server_name $backend_name { www.bbb.com bbb; www.aaa.net aaa; } # xray upstream vless { server 127.0.0.1:8443; } # xray 伪装站点 upstream aaa { server 127.0.0.1:10000; } # website upstream bbb { server 127.0.0.1:9090; } server { listen 443; listen [::]:443; ssl_preread on; proxy_protocol on; proxy_pass $backend_name; }server {
xray vless listen 127.0.0.1:10000 proxy_protocol; proxy_pass vless;}
}
HTTP2可能没有关系,文章我已经修改过了!
你的证书是不是单个域名都申请了?伪装站点的证书在Xray配置里面配置!
其实这个根本问题就是把通配符证书换成指定域名证书,然后在nginx其他站点中使用指定域名证书就可以解决问题!
如果你是两个不同的域名证书不会引起这个问题的,但是一个证书发放给多个不同域名使用的话那就会出现这个问题!你可以打开站点看看证书是不是颁发给对应域名使用的,如果里面包含你多个站点域名的话这个证书就不行!
感谢回复这么及时!
2个不同的域名,但是分别申请的.a.net和.b.com的通配符证书,xray里面配置的.a.net的证书,nginx里伪装网站没有配证书,没有加ssl选项,另外一个站点单独配置的.b.com的通配符证书
所以打开www.a.net时证书是xray里面指定的,但是打开www.b.com时还是*.a.net的证书……
我也有点懵圈,我再仔细检查一下nginx配置文件,看看是否有哪里出错
抱歉,问题解决了,不是nginx或xray的配置问题,是acme生成的证书拷贝到目的文件夹时用了append搞错了,第一次拷贝时把文件名敲错了,后面再次改正之后,脚本里面有个append模式……然后就一个文件里面混杂了2个key和cert,但是nginx只识别了前面那部分,append上去的直接忽略了……
想问一下伪装网站a.example.com的证书是在哪里配置的呢?只看到说它的SSl由Xray配置,但是没看到Xray里有导入a.example.com的证书?
"certificates": [
{
"certificateFile": "b.example.com.cer 记得换证书",# 不需要在这前面加入a.example.com的证书么?
"keyFile": "b.example.com.key 记得换证书"
}
]
xray伪装站点按照xray文档操作就好了,nginx是因为多个站用nginx相对熟悉方便!伪装站证书还是在xray配置!
哦哦,我刚去看了下nginx和xray的配置文档,明白什么意思了,谢谢
主要是看下面那个问增加一个业务网址证书时候,是不是应该把c写在b后面有点晕了。最后还是只有伪装站点的证书写进xray配置文件由xray处理,其它所有站点nginx处理就行
xray的文档里没写怎么用nginx做SNI,所以您这篇文章帮助挺大的,再次感谢
如果再有个c.example.com的业务网址,streamSettings里的certificates该怎么写?就直接把c的证书和b的写到一块去?不是很懂这点
照着例子加就好了,证书写到站点配置里面,nginx.conf只是分流!
也就是说c的nginx配置照着b的写,然后xray的配置里certificates写成下面这个:
"certificates": [
{
"certificateFile": "b.example.com.cer",
"keyFile": "b.example.com.key"
},
{
"certificateFile": "c.example.com.cer",
"keyFile": "c.example.com.key"
}
]
我理解对吗?
是的
nginx.conf的SNI分流那里,是否可以使用不同的域名,例如伪装是xx.a.com,其他网站是y1.b.com,y2.b.com
这个都可以,只要域名解析在你的服务器就行!
多谢老大。另外,还有一个问题,我同时安装了transmissionBT,装好以后的默认地址是http://ip:9001,没有用vless之前,我是可以用proxy_set_header,隐藏地址和端口,同时https加密。但现在我不知道怎么弄了,用80端口http访问,可以隐藏地址和端口,正常访问,但是上SSL,采用其他普通web网站相同的办法,就报错。
什么错?用SNI直接分流过去不行吗?
非常感谢!!试遍了网上各种乱七八糟的教程,终于用这篇,完美解决!!楼主功德无量
这个是您吗https://qoant.com/2021/05/xray-nginx-sni/,不是的话就抄袭的很搞笑了居然还搞了个许可协议:)
我写的居然还会被抄袭!!!无所谓了,搞这种事的人太多了!
上面这个没看懂啥意思。
我现在做法是haproxy做ssl sni分流
后端xray+nginx做站点及回落+caddy naive及伪装
caddy做站点开销比nginx大,效率比nginx差
不用upstream nginx转发xtls流量xray无法识别为代理,使用upstream是为了复用443端口让xray使用,更好的伪装xray。上面的配置是用nginx使用内部端口配置站点或者xray,upstream流量转发,达到80或者443端口复用的目的。
upstream a {}为啥不直接server 127.0.0.1:4443呢?
为啥先转60088,然后60088转转4443?
主要是这段没看懂
一个是自己的网站,另一个是xray,有可能还有更多的网站或其他服务在一台服务器上,这个配置是区分其他服务的端口!不是你想象的转来转去!所有流量都是走80或443进来,识别流量请求域名转发到指定的端口!
xray 的服务端证书配置是不是应该用 a.example.com.cer 的证书呀?
xray也一样,无论是nginx还是xray都不能用通配符域名证书,其实问题是在浏览器上,只能服务端做处理兼容浏览器!
你好,我遇到了相同的问题,https://github.com/fatedier/frp/issues/2181#issuecomment-867605135 请问关于这个问题有进一步的解决办法吗
你的问题和那个问题不一样,你的多站点错乱使用了通配符域名证书,他的问题是nginx加上代理协议会导致frp错误!你的问题只需要吧通配符域名证书换成单个域名证书就行了,所有站点都需要换成单域名证书!
是的,我的问题和 issue 的问题不一样,但是和你的这篇博客的问题是一样的
找到了些资料 https://levelup.gitconnected.com/multiplex-tls-traffic-with-sni-routing-ece1e4e43e56
https://stackoverflow.com/questions/44162263/request-cached-when-using-http-2
现在除了不用通配符证书好像没什么别的办法了,但是我要分流的网站用单个证书不太好维护,所以我换成了用 Nginx 反代 HTTP 了,不分流 HTTPS 网站了
这样也可以
我找到不是办法的办法,我稍微整理了下贴出来。
在 map $ssl_preread_server_name 中使用 default 作为所有业务站点的中转。这样可以使用泛域名证书,同时所有业务站点也可以使用统一的SSL配置(包括证书)。而且不用每增加一个业务站点就在 stream 里增加上游后端;同时业务站点之间的访问不会导致内容一样。但是仅这样的话,回落站点和业务站点先后访问还是有问题。 给回落站点单独申请证书,并将证书配置到Xray中,同时也在回落站点的Nginx配置中配置单独证书的SSL。虽然回落站点不用配置也能SSL,但是配置上单独证书后,可以保证回落站点和所有业务站点的先后访问没有问题。 我算是Nginx小白,也不知道这样配置会不会有效率问题。不知道格式会不会乱,所以上传到图床作为图片。
解释一下,免得被认为是病毒、广告之类的
代码:stream { map $ssl_preread_server_name $backend { x.domain.ltd xray; # xray回落站点 default https; # 正常的业务站点,除了 x.domain.ltd 之外,其他都分流到 https。 } upstream xray { # xray xtls伪装站点 server 127.0.0.1:4430; # 也可以配置Xray使用进程的方式,但是要保证Nginx和Xray权限一致 } upstream https { server unix:/dev/shm/https.sock; # 使用进程方式更有效率 } server { listen 443 reuseport; proxy_pass $backend; ssl_preread on; proxy_protocol on; } } http { set_real_ip_from unix:; # 从进程获取真实IP real_ip_header proxy_protocol; port_in_redirect off; # 重定向去掉端口号 # Xray回落站点 server { listen 80; # 可以单独建立强制跳转到HTTPS的虚拟站点,为示例简洁,统一放置。 listen 127.0.0.1:6001 proxy_protocol; # 回落 HTTP/1.1 listen 127.0.0.1:6002 http2 proxy_protocol; # 回落 HTTP/2 server_name x.domain.ltd; # 本来回落站点的SSL已由Xray处理,会自动支持的。 # 明确配置SSL,并统一使用其他业务站点一样的监听端口(进程) listen unix:/dev/shm/https.sock ssl http2 proxy_protocol; # 配置证书,用回落站点域名的独立证书 # 和正常的业务站点一样,也可以使用泛域名证书。但会导致先访问回落站点和后访问回落站点时的证书不一致。 ssl_certificate /usr/local/nginx/conf/ssl/x.domain.ltd.crt; ssl_certificate_key /usr/local/nginx/conf/ssl/x.domain.ltd.key; # 其他站点配置 ... } # 业务站点1 server { listen 80; # 可以单独建立强制跳转到HTTPS的虚拟站点,为示例简洁,统一放置。 listen unix:/dev/shm/https.sock ssl http2 proxy_protocol; server_name domain.ltd www.domain.ltd; # 证书位置 ssl_certificate /usr/local/nginx/conf/ssl/domain.ltd.crt; # 该证书包括了 domain.ltd 和 *.domain.ltd ssl_certificate_key /usr/local/nginx/conf/ssl/domain.ltd.key; # 其他SSL 配置 ... # 其他站点配置 ... } # 业务站点2 server { listen 80; listen unix:/dev/shm/https.sock ssl http2 proxy_protocol; set_real_ip_from unix:; server_name blog.domain.ltd; } # HTTP模块其他配置 ... }
详细的解释和思路:https://pic.imgdb.cn/item/62190c8f2ab3f51d91b00c27.png
PS:
站长的这个教程对我启发很大,但我先看到的是盗文的那个,解决不了SNI问题后一直搜,搜到这篇原文。
看到很多朋友都一样的困扰,我在摸索出来后就回来贴在这里了。
如果站长觉得我这样贴图不合适,但内容有用,可以整理一下直接放在自己的站上的,不要客气。我又不弄博客之类的,链接都不用给我加。
如果觉得要原文或是要删除,可以给我发个邮件,毕竟写了这么多。。。。
感谢评论,解决问题的方法永远不止一个,找到最适合自己的就好了!写这篇文章也就是目前对我来说最合适的方法了,也感谢你提供的其他思路!