smartdns
smartdns copied to clipboard
按客户端分组并设定差异化的DNS解析策略
需求应用场景 路由,多路网络连接,但是会需要根据客户端设定不同的DNS策略,允许特定分组使用特定 DNS 服务器解析特定请求,也需要针对不同设备设定不同的 DNS 劫持规则。
可以起多个smartdns 实例,加载不同配置,但是那就没有讨论的意义了。
此场景不使用 smartdns 的 speed-check,仅仅是用于流量分流。
类型: A 高自由度; B 严控; C 默认但是不暴露网络架构。
例如, 家庭网络:自己的设备 A,子女的设备 B,其他设备 C。
子女设备可能过滤成人、游戏、短视频等域名,也可能不允许使用 VPN。其他设备,例如来客,不暴露家里的 VPN。
公司网段:研发、内网服务器A、访客BC、一般员工BC。
- 访客可能限定不允许解析内网域名,也可能限定只允许使用运营商dns解析,不暴露网络架构,或兼有;
- 一般员工允许解析内网域名,也可能限定例如购物、视频平台等的域名,也可能不允许使用公司 VPN 或兼有;
- 研发或服务器可能不限制,又要允许使用公司 VPN。
本地默认 DNS 使用运营商 DNS。
流量选择: 1 ipset + nat ,将不同请求转给不同的 bind 端口,bind 需要新增参数标记流量 2 新增配置命令,根据客户端 IP 匹配
全局: A B C 都开启国内的白名单,例如主流域名强制使用运营商 DNS 解析,domain-set + nameserver 已解决 A B C 都开启去广告策略。domain-set + address # 已解决。
分组: A 可以开激进策略,全局之后所有交给 VPN 处理,例如可能直接交给 clash 去远程取,或者speed check B 按客户端分组执行域名或列表的策略,其他列表交本地默认DNS,不走VPN; C 全局之后,所有都交给本地默认 DNS
建议的方案
我理想中是类似 nginx 这种风格的配置,相互之间隔离,但是看了源码发现是 getopt,也是个相当好的方案。
实现层面,相当于规则匹配的配置需要增加 ACL。
# 默认 src group = default
bind :xxxx [-src-group 来源组]
# 假定 % 是 ANY
## 第一种,全 inline
domain-rules /domain/ -allow-src-group xxx -allow-src-group yyy -deny-src-group %
domain-rules /domain/-disallow-src-group xxx -allow-src-group %
address /domain/ -allow-src-group xxx -allow-src-group yyy -deny-src-group %
nameserver /domain/ -allow-src-group xxx -allow-src-group yyy -deny-src-group %
...
## 第二种,ACL
acl -name xxxyyy -include-src-group xxx -include-src-group yyy -exclude-src-group %
acl -name xxxyyy_blacklist -include-src-group % -exclude-src-group xxx -exclude-src-group yyy
domain-rules /domain/ -acl xxxyyy
nameserver /domain/ -acl xxxyyy_blacklist
address
...
设备信息 Linux
就是不同客户端不同规则吧
是的。
一开始我想用 nameserver /domain/group- 去试,但是试了几种组合都没成功。看了下源码,_config_domain_rule_add 里 域名的各类规则只有一条,毕竟设定是服务器组,逻辑上也最多也就适合和 bind 的组做一个简单的排除。
要增加一个client-group参数,用于指定客户端查询时,使用的group组。 对应的address, nameserver等,也要增加group组。
这个要细化设计下配置的方法。
我想到的场景中,客户的 IP 可能出现在多个 client group 里,所以我在原始 issue 里提了两种命令行参数方式,一种全inline,一种预定义 ACL。我自己会倾向于后者,因为我担心前一种会导致树的空间占用过大,也会需要每次去匹配各种参数组合。
针对 ACL 的方案,我有个可能的算法逻辑,不知道是不是适合。
1,客户在使用的时候同一个 ip 或网段可能会出现在多个组合里。但是我猜测 smartdns 的绝大多数用户还是会在局域网场景下,家庭用户可能个位数数量的组合就能满足需求,企业用户可能两位数,所以我假定最多支持 63 个 ACL,加一个预设的默认。
每个客户端来请求的时候,可以先去匹配所有 ACL,计算是否匹配 0/1;前边我假定了63 + 1 个 ACL,所以假定有个 int64 的 mask 变量,启动时,可以按 ACL 的某些字段排序,例如 name,编号 0~62,default 是 63,将匹配的结果 set 为这个int64 的各位。
那在程序运行中,客户 IP 和 这个 int64 的关系应该是确定的;如果匹配的计算消耗过大,也可以考虑 cache 这个数据。
这里 default 也可以考虑预设 int64_max;或者不要 default,就用现有的默认规则。
这里我不确定实现层面能不能支持让客户去配置最大多少个字节的 int 去管理 ACL,例如 4/8/16 。
client.ip -> ACL 1, 3, 5 -> ACL mask bits [00101010]
2,域名配置的树里,增加一个额外的 ACL mask 的列表。配置加载时,根据配置命令的 ACL 参数,把域名的 mask bits 也存下来。于是域名的配置节点里可能会多一个不定长的数组。
从存储的角度,实际上大量域名会有重复的 ACL mask 列表,所以再加一层映射,把不定长的数组拿出来,配置节点上只存大数组的索引或者节点的指针。但是这个感觉会是牺牲启动的速度换运行的速度。
这里还会遇到一个问题,客户可能会配置错误,例如一个域名出现在两个 domain-set 里,而这两个domain-set 配置了不同的 ACL,导致一个客户IP + 请求域名的组合有多个 server group 的选项。我的想法是配置不检查,执行时匹配到什么算什么,只记录匹配日志,客户自己开日志去查原因。
这个列表我觉得应该是有序的,例如按 mask 升序,default 理论上永远是最大的一个,而匹配逻辑是 domain.group-acl-rules[x].mask & client.acl > 0,匹配到就返回对应的组;如果不需要 default,ACL mask 列表为空或者匹配不上就执行现有的默认规则。
stored_rules: Combinations of ACLs
{
# index => sorted masks
[0] => [mask 1, mask 2, mask 4, mask n],
[1] => [mask 2],
...
}
mask n object:
{
.mask = 0x0000,
.group = "xxxxx"
}
# config tree
domain node .group-acl-rule -> stored_rules[index]
我最近也碰到了类似需求。我感觉可以先让bind支持下ip-transparent,这样用户自己就能通过iptable/nftables的tproxy灵活解决这个需求了。
我最近也碰到了类似需求。我感觉可以先让bind支持下ip-transparent,这样用户自己就能通过iptable/nftables的tproxy灵活解决这个需求了。
如果用户可以用iptable,可以根据客户端ip将dns请求重定向到不同端口,服务端用多进程多配置来解决
我最近也碰到了类似需求。我感觉可以先让bind支持下ip-transparent,这样用户自己就能通过iptable/nftables的tproxy灵活解决这个需求了。
如果用户可以用iptable,可以根据客户端ip将dns请求重定向到不同端口,服务端用多进程多配置来解决
如果你说的“重定向“是指iptables/nftables里的redirect的话,这东西不支持udp吧?而且用这个的话dns server好像要改的更多,tproxy支持udp而且对dns server的改动很小,基本上只需要在bind的时候往socket option里面加个IP_TRANSPARENT。 多进程大概也是不需要的,因为目前smartdns已经支持bind多个地址,而且每个bind支持设置不同-group来分流。
但是tproxy,可能有回流问题(请求的服务器ip和应答的服务器ip不符
最新代码添加了相关的功能。
配置方法:
方法一:
group-begin client
server 127.0.0.1:61053 -e
client-rules 127.0.0.1
address /a.com/1.1.1.1
domain-rules /b.com/ -address 4.5.6.7
group-end
方案二:
conf-file /path/to/conf.conf -group client
# /path/to/conf.conf文件内容
client-rules 127.0.0.1
address /a.com/1.1.1.1
domain-rules /b.com/ -address 4.5.6.7