运行多证书反向代理,充当NOSTR中继转发器快四个月了,今天准备下线这个服务。随便把配制思路分享一下。
NOSTR因为众所周知的原因,国内是无法访问的,而NOSTR需要中继才能存活,本来NOSTR的设计思路就是天然的
去中心社交模式。但是现在因为没有使用分布式方式发布和订阅中继,反而让NOSTR的分布式社交变得有名无实 了。
国内访问那些著名的中继器比如说wss://relay.damus.io, wss://offchain.pub等等。
那么如果有VPS且有域名和证书的情况下,使用Nginx是很容易架设一个反向代理服务器,充当中继转发(Forwarder of Relay)。
这样子,中继还是那些中继。但是因为转发器的存在,让中继器拥有了无数个“分身”。
- 域名的申请,跳过,这个非常容易几美金就能完成的事情。
- 证书的申请,本文跳过,最简单的是使用certbot来完成,但是官方的certbot手册真的很一般,很容易出错。而且它的基于域名认证的扩展也不够及时,还需要用户自己经验足够丰富。还不如使用稍为麻烦但是可靠的http文件认证模式。但是范域名证书不支持http文件认证,必须用DNS认证。好在DNS认证可以通过手
这里,需要证书,是因为wss协议的要求。其实建立连接之后,Nginx还需要offload原wss请求中的Host,然后重新封装新的wss请求,然后将新的wss请求转发到中继器去。
同时,因为Nginx默认情况下,一般还会提供正常的https访问,所以这就要求在ssl的443端口,提供https以及中继转发的wss多个服务。为了区别这些服务,必须Nginx开启ngx_stream_ssl_preread_module模块,这个模块提供SNI和ALPN功能,在本文的应用场景中,只需要SNI支持,它允许客户端连接的时候,通知Ningx连接的是那个服务,是https的服务?还是wss的中继转发服务。这样Nginx就会根据客户端连接的通知,选择不同的处理逻辑:
- 如果是wss中继转发服务,则进行wss的offload,重新组装wss请求转发到对应的中继器。这个offload主要是替换wss请求中的Host头。
- 如果是普通的https服务,则按照正常的https流程,交给本地的https应用来处理。
- 如果还有其它的服务,比如说Trojan服务那就一样的专交给Trojan服务。
在这里,可以看到,其实Nginx的的处理层不是在http层来进行的,而是在strea层进行的。
换句话说nginx需要先配制stream块,然后配制http块。
stream {
log_format stream_log '$remote_addr $ssl_preread_server_name [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time "$upstream" "$upstream_addr" '
'"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';
access_log logs/stream.log stream_log;
#access_log off;
map $ssl_preread_server_name $upstream{
~t\. trojan; #begin with t.xxx.yyy.. means trojan traffic
~\.relay\.center relay; #nostr relay
default web;
}
upstream trojan{
server 127.0.0.1:8443;
}
upstream web{
server 127.0.0.1:1443;
}
upstream relay{
server 127.0.0.1:1443;
}
server {
listen 443 reuseport;
ssl_preread on;
proxy_pass $upstream;
#proxy_ssl on;
}
}
在这里Nginx所使用到的变量$ssl_preread_server_name,就是ngx_stream_ssl_preread_module模块提供的。它通过在建立ssl连接的时候,提前读取Client Hello中的server_name来给$ssl_preread_server_name赋值,这个时候ssl的握手建立还没有完成,只是收到了客户端的hello数据包,服务器没有返回任何ssl数据包,需要根据配制逻辑,选择不同的服务器证书来完成ssl握手,然后才能进入到https层面的工作。
在这里根据$ssl_preread_server_name定义了不同的正则表达式,然后使用proxy_pass选择不同的后端。
map $ssl_preread_server_name $upstream{
~t\. trojan; #begin with t.xxx.yyy.. means trojan traffic
~\.relay\.center relay; #nostr relay
default web;
}
然后后端的配制,就使用普通的https既可,
中继转发的时候,需要动态的把请求代理出去既可。
需要注意的三个地方:
- 客户端证书需要自己生成,很多地方都可以,openssl也应该可以
- proxy_ssl_protocals的协议版本,最好把最新的1.3版本加上支持,否则有的服务器可能会拒绝连接,比如说damus.io
- 最后需要把普通的https升级为wss,这个用proxy_set_header指令既可完成。
至于如何混用trojan流量,等有机会再说。这样子Nginx就一肩挑三担:
- 本地的https服务
- 远程基于WSS的NOSTR中继服务器
- Trojan流量