mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-08-06 20:49:46 +00:00
plan: update
This commit is contained in:
+45
-49
@@ -7,6 +7,20 @@
|
||||
name = "localhost" # optional, replaces hostname, default to system hostname
|
||||
domain = "localdomain" # optional, replaces tld_dns_zone, default to et.net
|
||||
|
||||
addresses = [
|
||||
"100.100.100.101:53",
|
||||
] # optional, default to [ "100.100.100.101:53" ]
|
||||
# any UDP packet or ICMP packet to these addresses will be hijacked by the dns server
|
||||
# the server does *not* bind to/listen on these addresses!
|
||||
|
||||
listeners = [
|
||||
] # optional, default to empty
|
||||
# let the dns server bind to these addresses
|
||||
# could be useful when no_tun = true
|
||||
|
||||
# these two options supersede accept_dns
|
||||
# setting both of them to empty is equivalent to set accept_dns = false, but zones are still broadcasted
|
||||
|
||||
# this policy applies to all zones with origin "example.com"
|
||||
[dns."example.com".import]
|
||||
whitelist = ["*"]
|
||||
@@ -40,25 +54,6 @@ forwarders = [
|
||||
]
|
||||
```
|
||||
|
||||
可能会支持的配置
|
||||
|
||||
```toml
|
||||
[dns]
|
||||
addresses = [
|
||||
"100.100.100.101:53",
|
||||
] # optional, default to [ "100.100.100.101:53" ]
|
||||
# any UDP packet or ICMP packet to these addresses will be hijacked by the dns server
|
||||
# the server does *not* bind to/listen on these addresses!
|
||||
|
||||
listeners = [
|
||||
] # optional, default to empty
|
||||
# let the dns server bind to these addresses
|
||||
# could be useful when no_tun = true
|
||||
|
||||
# these two options supersede accept_dns
|
||||
# setting both of them to empty is equivalent to set accept_dns = false, but zones are still broadcasted
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><h2>计划和进展</h2></summary>
|
||||
|
||||
@@ -66,9 +61,9 @@ listeners = [
|
||||
|
||||
## protobuf
|
||||
|
||||
- `ZoneConfigPb`:包含所有 Zone 配置,以及一个 ID,该 ID 在读取 TOML 时生成
|
||||
- `GetExportConfigResponse`:包含全部 `ZoneConfigPb`(特别地,包含专用 zone)、该 peer 的 fqdn
|
||||
- `HeartbeatRequest`: DnsClient 发送的心跳,包含:id、digest、`Option<Snapshot>`
|
||||
- `ZoneData`:包含 Zone 配置,以及一个 ID,该 ID 在读取 TOML 时生成
|
||||
- `GetExportConfigResponse` (`DnsExportConfig`):包含全部 export 的 `ZoneData`(特别地,包含专用 zone)、该 peer 的 fqdn
|
||||
- `HeartbeatRequest`: DnsNode 发送的心跳,包含:id、digest、`Option<Snapshot>`
|
||||
- `DnsSnapshot`: 所有 DnsServer 需要的配置
|
||||
|
||||
## RoutePeerInfo
|
||||
@@ -80,47 +75,51 @@ listeners = [
|
||||
|
||||
## DnsRunner
|
||||
|
||||
用于启动/监护 DnsClient 和 DnsServer
|
||||
每个 EasyTier 实例都会启动一个 DnsClient,但是一台机器上的所有实例共享一个唯一的 DnsServer。
|
||||
用于启动/监护 DnsNode 和 DnsServer
|
||||
每个 EasyTier 实例都会启动一个 DnsNode,但是一台机器上的所有实例共享一个唯一的 DnsServer。
|
||||
|
||||
每个实例启动时:
|
||||
|
||||
1. 读取 TOML 配置,然后用这个配置启动一个 DnsClient,交给 DnsClient 一个用于尝试重启 server 的 notify
|
||||
2. 尝试绑定 DNS_SERVER_RPC_ADDR 监听 RPC 请求
|
||||
1. 一台机器上所有 EasyTier 实例一起尝试绑定 DNS_SERVER_RPC_ADDR,绑定成功的那个就启动 DnsServer(当然也启动 DnsClient),失败的那些就只有 DnsClient
|
||||
2. 这个启动 DnsServer 的操作其实是个循环,每隔一小段时间或者 DnsClient rpc 失败(notify)后立刻尝试 bind,如果 bind 成功就说明 DnsServer 真挂了,那就自己在这个已有的 SocketAddr 上启动 DnsServer(忽略 bind 失败或启动失败,启动失败就直接释放 socket),这样才能保证服务不断
|
||||
1. - [ ] 读取 TOML 配置,然后用这个配置启动一个 DnsNode,交给 DnsNode 一个用于尝试重启 server 的 notify
|
||||
2. - [ ] 尝试绑定 DNS_SERVER_RPC_ADDR 监听 RPC 请求
|
||||
1. 一台机器上所有 EasyTier 实例一起尝试绑定 DNS_SERVER_RPC_ADDR,绑定成功的那个就启动 DnsServer(当然也启动 DnsNode),失败的那些就只有 DnsNode
|
||||
2. 这个启动 DnsServer 的操作其实是个循环,每隔一小段时间或者 DnsNode rpc 失败(notify)后立刻尝试 bind,如果 bind 成功就说明 DnsServer 真挂了,那就自己在这个已有的 SocketAddr 上启动 DnsServer(忽略 bind 失败或启动失败,启动失败就直接释放 socket),这样才能保证服务不断
|
||||
|
||||
|
||||
## DnsClient
|
||||
## DnsNode
|
||||
|
||||
1. 启动时(配置更新时?)将 listeners 和 addresses 添加进 delta
|
||||
2. 使用自己的 name 和 domain 创建一个专用 zone,让 name 指向自身 IP,并监听 IP 地址变化事件(为 DNS 一致性避免使用 127.0.0.1 作为 IP,若没有 IP 则不创建这个 zone)
|
||||
3. 每次获得 RoutePeerInfo 时,读取其中的 dns 字段(和一些别的身份标记字段),这是远程 Peer 的 dns 配置(不含 addresses 和 listeners)的 digest,接收后检查 digest 和本地配置是否一致,如果一致,不做修改,否则标记 dirty,下一次心跳时将重建快照
|
||||
4. 每隔一小段时间向 DnsServer 发送心跳和当前 digest:
|
||||
1. - [ ] 监听配置更新/IP 地址变化,重建快照
|
||||
2. - [x] (`GlobalCtx` 的扩展 trait) 使用自己的 name 和 domain 创建一个专用 zone,让 name 指向自身 IP(为 DNS 一致性避免使用 127.0.0.1 作为 IP,若没有 IP 则不创建这个 zone)
|
||||
3. - [ ] 每次获得 RoutePeerInfo 时,读取其中的 dns 字段(和一些别的身份标记字段),这是远程 Peer 的 dns 配置(不含 addresses 和 listeners)的 digest
|
||||
- [x] 接收后检查 digest 和本地配置是否一致,如果一致,不做修改,否则标记 dirty,下一次心跳时将重建快照
|
||||
4. - [x] 每隔一小段时间向 DnsServer 发送心跳和当前 digest:
|
||||
1. 如果没有 dirty 标记,心跳不含 snapshot;
|
||||
2. 如果有 dirty 标记,重建 snapshot 并在心跳中包含;
|
||||
3. 如果 DnsServer 返回 resync,立刻重新发送带有 Snapshot 的心跳
|
||||
5. 一个 RPC 接口,供 Peer 拉取 DNS 配置
|
||||
5. - [x] 一个 RPC 接口,供 Peer 拉取 DNS 配置
|
||||
|
||||
## DnsServer
|
||||
|
||||
1. 提供一个 RPC 接口接受 DnsClient 的心跳,如果心跳 digest 和本地不符则返回 resync
|
||||
2. 收到含有 snapshot 的心跳时替换本地配置;如果 snapshot 中的 listeners 或者 addresses 不同则 rebind
|
||||
3. 持续检查是否有过期(丢失心跳)的 DnsClient,需要把这些 DnsClient 提供的所有配置清除
|
||||
4. 启动时自动添加 root zone,并把它的 forwarder 设置为系统 DNS
|
||||
5. 使用 snapshot 更新 zone。不用合并同名 zone,直接用 Zone 结构体提供的 ChainedAuthority 按顺序插入 Catalog 就行,不过注意要先插入 MemoryAuthority,这些都是 records,后插入 ForwardAuthority,这都是 forwarders
|
||||
6. 更新 zone 的时候自动去掉 forwarder 中导致回环的那些,就是把 addresses 和 listeners 去掉(root zone 也需要这个逻辑)
|
||||
7. 内部接口,控制 DnsServer 是否 bind 到某些 socket(也就是配置中的 listeners)
|
||||
8. Listeners 绑定失败打印日志(失败一个打印一次然后就跳过),即便这时 addresses 为空也不要停机。(否则释放 socket 绑定后会有 instance 抢占 socket 试图启动 server,然后就死循环)
|
||||
9. 内部接口,更新 addresses,并且给 tun 添加删除这些 addresses 的路由。目前这些用来 hijack 的 addresses 都是只支持 udp 简单查询,就是一个 UDP 包查询,tcp 完全不管。但是可以支持除了 53 之外的端口,这个不难。
|
||||
10. 启动时,往 packet pipeline 上挂一个 filter,和目前 magic dns 的操作一样,给 addresses 添加路由并劫持所有目的为配置中 addresses 的 UDP 包,直接作为 DNS request 读取并交给 DnsServer 解析(这个 addresses 可能还得 append 到 resolv.conf 之类的地方)
|
||||
11. Addresses 和 Listeners 更新时需要检查所有 zone 的 forwarder,之前为了避免回环可能去掉了一些 forwarder,或者有新的 forwarder 要去除
|
||||
1. - [x] 提供一个 RPC 接口接受 DnsNode 的心跳,如果心跳 digest 和本地不符则返回 resync
|
||||
2. - [x] 收到含有 snapshot 的心跳时替换本地配置;如果 snapshot 中的 listeners 或者 addresses 不同则 rebind
|
||||
3. - [x] (`moka::Cache`) 持续检查是否有过期(丢失心跳)的 DnsNode,需要把这些 DnsNode 提供的所有配置清除
|
||||
4. - [x] 每次更新 zone 时自动添加 root zone
|
||||
- [x] (`Zone::system`) 并把它的 forwarder 设置为系统 DNS
|
||||
5. - [x] 使用 snapshot 更新 zone。不用合并同名 zone,直接用 Zone 结构体提供的 ChainedAuthority 按顺序插入 Catalog 就行,不过注意要先插入 MemoryAuthority,这些都是 records,后插入 ForwardAuthority,这都是 forwarders
|
||||
6. - [x] 更新 zone 的时候自动去掉 forwarder 中导致回环的那些,就是把 addresses 和 listeners 去掉(root zone 也需要这个逻辑)
|
||||
7. - [x] 内部接口,控制 DnsServer 是否 bind 到某些 socket(也就是配置中的 listeners)
|
||||
8. - [x] Listeners 绑定失败打印日志(失败一个打印一次然后就跳过),即便这时 addresses 为空也不要停机。(否则释放 socket 绑定后会有 instance 抢占 socket 试图启动 server,然后就死循环)
|
||||
9. - [x] 内部接口,更新 addresses。目前这些用来 hijack 的 addresses 都是只支持 udp 简单查询,就是一个 UDP 包查询,tcp 完全不管。但是可以支持除了 53 之外的端口,这个不难。
|
||||
- [ ] 并且给 tun 添加删除这些 addresses 的路由
|
||||
10. - [x] 启动时,往 packet pipeline 上挂一个 filter,和目前 magic dns 的操作一样,给 addresses 添加路由并劫持所有目的为配置中 addresses 的 UDP 包,直接作为 DNS request 读取并交给 DnsServer 解析
|
||||
- [ ] 这个 addresses 可能还得 append 到 resolv.conf 之类的地方
|
||||
11. - [x] Addresses 和 Listeners 更新时~~需要检查所有 zone 的 forwarder~~直接更新所有 zone,之前为了避免回环可能去掉了一些 forwarder,或者有新的 forwarder 要去除
|
||||
|
||||
此外,还有以下几个设计要点:
|
||||
|
||||
- Zone 允许只有 forwarder,这时候就是纯转发器
|
||||
- Zone 允许没有 forwarder,这时候要检查是不是有 SOA 和 NS 记录,如果没有可能需要添加?
|
||||
- 另一种方案是 DnsClient 挂 filter,自己处理 UDP 劫持,用某种方式(如 RPC)把 DNS 请求代理给 DnsServer,该方案的优势在于完全解耦 DnsServer 的实现,特别是解决了 DnsServer 所在实例可能 no_tun 的问题,缺点是:
|
||||
- 另一种方案是 DnsNode 挂 filter,自己处理 UDP 劫持,用某种方式(如 RPC)把 DNS 请求代理给 DnsServer,该方案的优势在于完全解耦 DnsServer 的实现,特别是解决了 DnsServer 所在实例可能 no_tun 的问题,缺点是:
|
||||
- 性能更差
|
||||
- 操作路由表或 /etc/resolv.conf 时会有多个 instance 同时修改,修改结果没有确定性
|
||||
- DnsServer 仍然需要得知 addresses 以进行回环检测
|
||||
@@ -143,9 +142,6 @@ listeners = [
|
||||
## TODO
|
||||
|
||||
- [x] 配置解析
|
||||
- [ ] DnsClient
|
||||
- [ ] DnsServer
|
||||
- [ ] 用 `RoutePeerInfo` 传播配置 hash,用 RPC 拉取配置
|
||||
- [ ] cli 输出状态
|
||||
|
||||
</details>
|
||||
|
||||
Reference in New Issue
Block a user