Skip to content

Direct/Freedom outbound: Add ipsBlocked (supports IP, CIDR, "geoip:", "ext:") and apply a default safe policy#5947

Merged
RPRX merged 20 commits intomainfrom
direct-blocks
Apr 15, 2026
Merged

Direct/Freedom outbound: Add ipsBlocked (supports IP, CIDR, "geoip:", "ext:") and apply a default safe policy#5947
RPRX merged 20 commits intomainfrom
direct-blocks

Conversation

@Meo597
Copy link
Copy Markdown
Member

@Meo597 Meo597 commented Apr 15, 2026

@Meo597 Meo597 mentioned this pull request Apr 15, 2026
@Fangliding
Copy link
Copy Markdown
Member

虽然我不喜欢这个但是为了不恶心我自己的眼球其实有一个东西叫 Conn.RemoteAddr()

@Fangliding
Copy link
Copy Markdown
Member

以及UDP估计是没法管了 一个包匹一次那确实炸裂

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 15, 2026

UDP 就第一个包就行,目前内网服务用 QUIC 的比较少应该,基本上都裸 TCP,或者 RESTful/gRPC

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 15, 2026

说起来 Xray 的 UDP 路由也是只看第一个包,配置路由 block private 也能被绕过其实

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

@Fangliding 你直接动手吧,这块我除了之前 review 老哥 PR 的时候看过几次,基本全忘了现在,根本不熟

@Fangliding
Copy link
Copy Markdown
Member

UDP 就第一个包就行,目前内网服务用 QUIC 的比较少应该,基本上都裸 TCP,或者 RESTful/gRPC

首先攻击向量来源于精心伪造请求的黑客(假设这种东西存在) 那用一个常规请求骗过去后续再往本地发是很自然的事情 所以匹首个目标没用 虽然我说了很多次掩耳盗铃但是这是最掩耳盗铃的 要么就认为认为UDP不会有威胁 少写两行代码

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 15, 2026

那这个直接改名 tcpIPsBlocked 吧,明确不管 UDP 了,用户也知道

UDP 每个包都管的话那确实炸裂,感觉没人这么干,其实可以精心构造 QUIC/BT 请求来绕过机场审计,只是机场用户不会

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

如果 json 里没有 blockIP 就默认封 private
写个 "blockIP": [] 就全部 open 了

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 15, 2026

改一下名,还有你这没判断 inbound name,等下客户端炸了,VLESS Reverse 也加一下 inbound name "vless"

其实 UDP 每个包都管也不是不行,以后可以出个非默认的 udpIPsBlocked,写明会造成 UDP 性能大幅下降就行

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

为啥你们都看不起 ipmatcher
每次匹配最多五十纳秒

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

func buildIPMatcher(rawRules ...string) IPMatcher {
	rules, err := ParseIPRules(rawRules)
	common.Must(err)

	matcher, err := newIPRegistry().BuildIPMatcher(rules)
	common.Must(err)

	return matcher
}

func BenchmarkIPMatcherPrivate(b *testing.B) {
	matcher := buildIPMatcher(
		"0.0.0.0/8",
		"10.0.0.0/8",
		"100.64.0.0/10",
		"127.0.0.0/8",
		"169.254.0.0/16",
		"172.16.0.0/12",
		"192.0.0.0/24",
		"192.0.2.0/24",
		"192.88.99.0/24",
		"192.168.0.0/16",
		"198.18.0.0/15",
		"198.51.100.0/24",
		"203.0.113.0/24",
		"224.0.0.0/3",
		"::/127",
		"fc00::/7",
		"fe80::/10",
		"ff00::/8",
	)
	ip := net.IP{8, 8, 8, 8}

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		_ = matcher.Match(ip)
	}
}
BenchmarkIPMatcherPrivate-4   	22099935	        55.17 ns/op	       0 B/op	       0 allocs/op

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 15, 2026

额说起来匹配 IP 的话应该挺快的,耗时相比于用户态->系统态可以忽略不计?那要不给 UDP 每个包也默认安排上

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

改一下名,还有你这没判断 inbound name,等下客户端炸了,VLESS Reverse 也加一下 inbound name "vless"

我以为你是要默认 block 所有的 freedom
如果仅 tag 是 direct / vless 的话,我得在别的地方加默认配置
我改一下

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 15, 2026

不是,ctx 里能取出 inbound 判断 name 的

This reverts commit f5145f7.
@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 15, 2026

Freedom 出站判断入站是 VLESS、VMess、Trojan、Shadowsocks、Hysteria、WireGuard 这六个才默认 block private ip

不反向判断 Socks/HTTP 是怕有些没 ctx inbound name 的内部请求被 block 了

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 15, 2026

给每个 UDP 包都安排上,改名 ipsBlocked 吧,这个比路由更彻底,毕竟路由就匹配一次,如果每个包都来一次的话确实难绷

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

okk 我还要想一下配置文件该怎么弄
因为 infra/conf 那边没法知道入站 tag

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 15, 2026

不用啊,freedom 出站代码里读 ctx 判断一下就行了,不用搞配置检测

然后我想了下路由那边把 ip 加 map 也行,不过那个改起来麻烦很多

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 15, 2026

如果用户没配置 ipsBlocked 就判断 ctx,如果用户配置了(或者没成员)就不用判断

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

关键在于没成员。。。

因为 infra/conf 那边传到 core 必须是个合法的 ip 规则,要么就是空数组也行
这就搞得没法判断用户是不是写了 "ipsBlocked": []

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 15, 2026

算了,没成员的情况相当于这功能没开直接跳过代码就行

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

然后这里面的东西又不能写个魔法字符串,会报错的。除非硬改 geodata 太丑了

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

除非用户去写个 255.255.255.255
这样相当于全放开了

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 15, 2026

代码里全改一下"target IP is blocked: "->"blocked target IP: "

想了下应该暂时不需要 domainsBlocked

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

我再跑一下 test

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 15, 2026

WG 入站那个 source 我看了下代码,还真不确定是 TUN 那种还是客户端 IP,你搭个 WG 测一下

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

一堆测试都被 block 了,等我这老爷机跑完一轮,我挨个加策略
硅脂在疯狂燃烧

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

return defaultPrivateBlockIPMatcher 的时候加个日志告诉用户吧?要什么级别的

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

一亿个测试都被安全策略拦截,我批量替换了直接

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

发版吧,发完回头会有一堆人说小鸡连不上的

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

对外只暴露 443
没有面板
xray 自动更新
意思 ssh 要先连上 xray

然后就只能 console 了

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 15, 2026

info 吧,逐包拦截的日志改为 debug,不然被刷屏

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

说到 debug 我记得有不少热点代码都有这玩意
我很久以前看过这个的实现,级别不够都会拼字符串,我没测过开销

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 15, 2026

光生产不消费,跟某国经济一样

@RPRX RPRX changed the title Freedom: Add ipsBlocked (supports IP, CIDR, "geoip:", "ext:") and apply a default safe policy Direct/Freedom outbound: Add ipsBlocked (supports IP, CIDR, "geoip:", "ext:") and apply a default safe policy Apr 15, 2026
@RPRX RPRX merged commit 310b764 into main Apr 15, 2026
78 checks passed
@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 15, 2026

WG 入站那个 source 我看了下代码,还真不确定是 TUN 那种还是客户端 IP,你搭个 WG 测一下

保险起见我先把 WG 从 XUDP Global ID 的判断里摘掉吧,也就是 WG 入加 VLESS/VMess/Mux 出的 XUDP 没有 Global ID 了

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 16, 2026

说到 debug 我记得有不少热点代码都有这玩意 我很久以前看过这个的实现,级别不够都会拼字符串,我没测过开销

确实是一个能优化的点,改成 call log 加回调,确实需要再去拼接字符串吧,感觉应该不至于代码写那里就先拼接吧?

@Meo597 Meo597 deleted the direct-blocks branch April 16, 2026 06:27
@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 16, 2026

不知道为啥不用现成的轮子,这里的实现的确有点辣眼睛
除了拼接字符串,里面应该还有一些类似 java 栈追踪的东西吧?后者更重

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 16, 2026

ipmatcher 的 cidr 应该支持取反语义
再支持下端口就更好了

比如反向代理只想放通 127.0.0.1:80 现在办不到
甚至想 block 内网,只放通 127 都得写一堆规则

@Fangliding
Copy link
Copy Markdown
Member

重新在freedom做一套路由。。

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 16, 2026

重新在freedom做一套路由。。

最多算个 acl 吧

xray-enterprise 这不就来了吗
这波福利给到家人们,原价 $0
surge 存量用户凭订单只要 $288

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 16, 2026

总之这个 PR 所实现的 TCP 只看 dial 后的 remoteAddr,以及 UDP 逐包匹配确实封得更彻底,没有死角了

再做绝些就是把 UDP 回包也过滤一下,来自那些地址的话就 drop 掉,@Meo597 PR 一下吧

要论割韭菜那确实还得是 Surge,一个无关紧要的新功能让韭菜们续订一年,Xray 一个月都能更新它一年的量还多

@Meo597
Copy link
Copy Markdown
Member Author

Meo597 commented Apr 16, 2026

回包过滤意义不大吧
我想了半天也就是某个应用崩了,攻击者疯狂开连接或许能收到一点包
没有价值啊

@RPRX
Copy link
Copy Markdown
Member

RPRX commented Apr 16, 2026

怎么说呢,从哲学的角度是彻底杜绝与这些 ipsBlocked 地址的 任何 有效数据传输,这下真变成哲学问题了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants