惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
The GitHub Blog
The GitHub Blog
Recent Announcements
Recent Announcements
MyScale Blog
MyScale Blog
P
Proofpoint News Feed
S
Security @ Cisco Blogs
N
News and Events Feed by Topic
H
Hacker News: Front Page
Attack and Defense Labs
Attack and Defense Labs
S
Secure Thoughts
Microsoft Security Blog
Microsoft Security Blog
N
Netflix TechBlog - Medium
U
Unit 42
Stack Overflow Blog
Stack Overflow Blog
T
Threat Research - Cisco Blogs
Google Online Security Blog
Google Online Security Blog
Spread Privacy
Spread Privacy
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
L
LINUX DO - 热门话题
T
Tenable Blog
博客园 - 叶小钗
D
DataBreaches.Net
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园_首页
人人都是产品经理
人人都是产品经理
aimingoo的专栏
aimingoo的专栏
C
Check Point Blog
博客园 - 三生石上(FineUI控件)
量子位
P
Proofpoint News Feed
H
Help Net Security
Blog — PlanetScale
Blog — PlanetScale
宝玉的分享
宝玉的分享
Recorded Future
Recorded Future
The Register - Security
The Register - Security
F
Fortinet All Blogs
Engineering at Meta
Engineering at Meta
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Last Week in AI
Last Week in AI
S
Schneier on Security
V
Vulnerabilities – Threatpost
雷峰网
雷峰网
Microsoft Azure Blog
Microsoft Azure Blog
G
GRAHAM CLULEY
G
Google Developers Blog
月光博客
月光博客
V
V2EX
T
Troy Hunt's Blog
A
Arctic Wolf

I'm OWenT

国产大模型(GLM 5.1、Kimi K2.6)真实场景效果和 Coding Plan 额度测试 新版本libatapp的连接管理——从etcd服务发现到拓扑驱动的自动重连 新版本libatbus的设计变更——从树形路由到拓扑驱动 Protobuf又一坑 - C++标准和ABI兼容性 AI真好用-给Blog主题统一加mermaid,chart.js,excalidraw,draw.io的多种引入方式支持 给内网部署Squid-通用HTTP下载缓存 UE使用CodeChecker和clang-tidy生成静态分析报告 找出UE的循环依赖 C++小协程栈和临时变量及作用域的栈溢出问题分析 游戏服务的可观测性能力建设(C++生态) 指标上报的多线程优化和多拉取源点优化 协程(libcopp)的Channel功能和CPU命中率优化 通用RPC代码生成器 实现strong_rc_ptr(比shared_ptr更快的引用计数智能指针) 手夯一个STL allocator和对象内存分析组件 std::condition_variable 的信号丢失问题 踩坑一处(GCC)STL std::async 实现BUG导致的crash问题 GCC 14的一个warning to error BUG 给xresloader(Excel导表工具)增强UE读表支持(包含蓝图,Blueprint) Opentelemetry社区在gRPC的几个链接问题(静态库和动态库混用,musl工具链,符号裁剪) Excel转表工具(xresloader)的新验证器(验证外部Excel和文本数据,唯一性和自定义规则) protobuf v22和gRPC v1.55版本升级的依赖变化和upb适配 关于protobuf近期版本(v20/v3.20+)和 gRPC v1.54版本在某些编译环境下的一些链接和编译问题 xresloader-Excel导表工具链的近期变更汇总 打通游戏服务端框架的C++20协程改造的最后一环 Opentelemetry-cpp的Logs模块标准更新(涉及近期版本:1.8-1.9的BREAK CHANGES) 给cmake-toolset和工具链(curl等)加HTTP/2和HTTP/3支持 关于opentelemetry-cpp社区对于C++ Head Only组件单例和符号可见性的讨论小记 填个转表工具 xresloader 去年的坑(数组尾部裁剪) 集成 upb 和 lua binding 的踩坑小记 libcopp对C++20协程的接入和接口设计 再度优化GCC、LLVM、Clang、libc++、libc++abi等套件的构建脚本 游戏服务的分布式事务优化(二)- 事务管理 游戏服务的分布式事务优化(一)- Write Ahead Log(WAL) 模块 记录一些bazel适配用编译选项 测试现代化硬件C++浮点数性能和一致性 适配Boringssl和OpenSSL 3.0 近期cmake-toolset的一些适配问题 C++20 Text Formatting/fmtlib 适配问题小记 再次重构LLVM+Clang+libcxx+libc++abi+其他相关工具的构建流程 重构基于CMake的构建工具链 新版GCC和LLVM+Clang终于Release啦 折腾一下nftables下的双拨 [C++20] Module partitions和符号交叉引用(声明和实现分离) [Rust] 实现一个线程安全且迭代器可以保存的链表 基于protobuf的代码生成 几个使用protobuf中C++接口的Arena的坑 Amazon Aurora DB存储引擎论文阅读小记 近期对libatapp的一些优化调整(增加服务发现和连接管理,支持yaml等) xresloader转表工具链增加了一些新功能(map,oneof支持,输出矩阵,基于模板引擎的加载代码生成等) 在游戏服务器中使用分布式事务 libcopp接入C++20 Coroutine和一些过渡期的设计 libatbus 的大幅优化 nftables初体验 容器配置开发环境小计 PALM Tree - 适合多核并发架构的B+树 - 论文阅读小记 跨平台协程库 - libcopp 简介 C++20 Coroutine 性能测试 (附带和libcopp/libco/libgo/goroutine/linux ucontext对比) 尝鲜Github Action 一些xresloader(转表工具)的改进 protobuf、flatbuffer、msgpack 针对小数据包的简单对比 协程框架(libcopp) 小幅优化 Excel转表工具(xresloader) 增加protobuf插件功能和集成 UnrealEngine 支持 Anna(支持任意扩展和超高性能的KV数据库系统)阅读笔记 C++20 Coroutine libcopp merge boost.context 1.69.0 Google去中心化分布式系统论文三件套(Percolator、Spanner、F1)读后感 Rust玩具-企业微信机器人通用服务 使用ELK辅助监控开发测试环境服务质量和问题定位 2018年的新通用伪随机数算法(xoshiro / xoroshiro)的C++(head only)实现 Webpack+vue+boostrap+ejs构建Web版GM工具 Rust的第二次接触-写个小服务器程序 理解和适配AEAD加密套件 atsf4g-co的进化:协程框架v2、对象路由系统和一些其他细节优化 协程框架(libcopp)v2优化、自适应栈池和同类库的Benchmark对比 可执行文件压缩 初识Rust 使用restructedtext编写xresloader文档 atframework的etcd模块化重构 C++的backtrace ECDH椭圆双曲线(比DH快10倍的密钥交换)算法简介和封装 protobuf-net的动态Message实现 pbc的proto3接入 atgateway内置协议流程优化-加密、算法协商和ECDH 整理一波软件源镜像同步工具+DevOps工具 Blog切换到Hugo libcopp v2的第一波优化完成 libcopp(v2) vs goroutine性能测试 libcopp的线程安全、栈池和merge boost.context 1.64.0 GCC 7和LLVM+Clang+libc++abi 4.0的构建脚本 libatbus的几个藏得很深的bug 用cmake交叉编译到iOS和Android 开源项目得一些小维护 atapp的c binding和c#适配 对象路由系统设计 2016年总结 近期的一个协程流程BUG 重写了llvm+clang+libc++和libc++abi的构建脚本 atsf4g完整游戏工程示例|I'm OWenT atframework基本框架已经完成|I'm OWenT
又开新坑之 coredns 插件: nftables和filter
owent · 2022-10-04 · via I'm OWenT

blog-website

背景

传说中的下一代 iptablesnftables 已经出来了好长时间了。现在主流发行版的内核也都已经更新到了对 nftables 支持足够好的版本。 在2年多前我也初步体验过了 nftables ,当时写了个 《nftables初体验》 。并且开始使用 nftables 来实现对家里软路由的管理。 而去年的时候,我也尝试用 nftables 实现了双拨(详见: 《折腾一下nftables下的双拨》)并且可以搭配TPROXY透明代理使用。

但是由于 nftables 的生态建设仍然时落后于 iptables ,导致我一直没能完全把家里的软路由全部迁移到 nftables 上来。 剩下的部分主要有两处,一处是桥接的重路由,另一个是我原来的策略路由会根据域名解析动态地写 ipset ,然后根据 ipset 来决定是否走tproxy。 前者之前和别人讨论过说是我之前测试时候用地地址不对,理论上是有解的(我还没测试)。 而后者由于 nftables 有自己的 set 结构,且并没有指令可以访问 ipset,并且我之前用的无论是 dnsmasq 还是 smartdns 都不支持直接写 nftables,所以就一直没有完全替换。

其实最新版本的 dnsmasq 在今年9约发布的版本(2.8.7)里已经支持写入 nftables 了,而 smartdns 去年就有PR支持 nftables,但是知道今天都还没合入。 然而由于之前无论是 dnsmasqsmartdns,我在使用上都碰到了一些问题。所以现在是全面替换成了 coredns 作为软路由的DNS服务,然后仅仅把 dnsmasq 用作 ipv4 的DHCP服务。

dnsmasq 是碰到过一些ipv6的兼容性问题,这个问题存在了很久都没有解决。而 2.8.7 版本又迟迟不发布。另一个问题是 dnsmasq 内部很多操作都是链表实现,如果我们有个巨大的匹配规则,会导致CPU居高不下。后来有Patch加了缓存来减少这个开销,但是本质上还是 O(n) 复杂度,感觉很不优雅。 而 smartdns 仅支持 A 和 AAAA 记录。如果把它当作DNS服务的入口,对PTR、ANY等记录解析是由问题的,导致有些app网络不正常。所以官方主页也是建议把它放在已有的DNS服务后面当加速DNS使用,而不是替代现有DNS服务。

corednsCNCF 基金会下的项目,也是现在 Kubernetes 中默认的DNS服务。基于 Caddy , 使用 golang 编写。所以它其实消耗内存相对较高。 但是我家里软路由是 x86_64 的,不在乎那点内存。受益于 Kubernetes 社区和 golang 的灵活性,它的可靠性和可扩展性都特别好。编写插件的上手难度相当地低,所以我就打算自己写插件满足我的需求。

我的需求其实也比较简单:

  • 根据域名解析结果写入 nftables 的set。
  • 如果域名能同时解析出 ipv4 地址和 ipv6 地址,则只返回ipv4地址。

    我的用于tproxy的机器只有ipv4地址,所以有个特殊的需要是尽可能用ipv4,实在没有的话才用ipv6。

  • 实现 dnsmasqsmartdns 里都有的 bogus-nxdomain 功能。

针对这两个需求,我拆分成了2个插件,一个仅用于操作 nftablescoredns-nftables ;另一个相当于是对返回数据的过滤,所以叫 coredns-filter

coredns 的插件行为编写只需要实现一个Handle,实现这两个接口即可:

Handler interface {
  ServeDNS(context.Context, dns.ResponseWriter, *dns.Msg) (int, error)
  Name() string
}

然后初始化的时候需要实现

func init() {
  plugin.Register("插件名", setup)
}

func setup(c *caddy.Controller) error {
  handle := NewHandler()    // 创建插件实例
  err := parse(c, &handle)  // 解析配置
  if err != nil {
    return plugin.Error("插件名", err)
  }

  // 设置串联插件的回调(设置下一个插件)
  dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
    handle.Next = next
    return &handle
  })

  log.Debug("Add 插件名 plugin to dnsserver")

  return nil
}

coredns 的插件是静态的,就是增减插件都需要把插件名字和地址写进 plugin.cfg 里然后重新编译。而 coredns 的插件触发顺序是按 plugin.cfg 里的顺序,和用户写配置的顺序无关。 实际上 coredns 会对每个Zone建立一个插件的链表,然后从尾部开始触发插件的 setup 接口。

比如,如果 plugin.cfg 里插件 A , B 的配置顺序是

那么会先调用 Bfunc(next plugin.Handler) plugin.Handler ,然后在调用 Afunc(next plugin.Handler) plugin.Handler 时候 next 传入 B 的返回值。

而执行的时候,几乎所有的插件都是使用 plugin.NextOrFailure(m.Name(), m.Next, ctx, w, r) 来先触发下一跳,通过协程的等待机制等下一跳完成在执行自己的逻辑。 如果需要修改下一跳的返回结果,可以通过 coredns 提供的 nonwriter 模块对插件链的解析结果进行劫持再重新写入。

这些操作最终使得整体像是一个链表按配置的顺序执行,实际上它是一个栈。

当然也可以不用这种标准方式执行,比如说我们也可以像 smartdns 一样并发发起多个请求,返回最快的那个。(说不定哪天有时间了我可以再写个插件完成这个功能)

其中 ServeDNS 接口的返回值如果无错误的话要返回 DNS解析的 RCODE 。

所以实现插件只要把我们要加的功能插入到合适的位置。

coredns-nftables

coredns-nftables 用于操作 nftables。先预留了也许以后还可以由其他操作,所以插件名字就叫 nftables 。只是当前版本还是仅支持添加 set 。

golang 其实没有成熟的能够直接操作 nftables 的库,所幸 Google 有一个没有Release的库 https://github.com/google/nftables ,可以直接用。

然后由于这个库是对解析出的最终结果写入set,对于返回 CNAME 的域名,其实需要进一步递归解析,这时候可以使用 coredns 的外部插件 finalize

那么由于我们要对 finalize 的最终结果做处理,所以我们的这个插件要挂在 finalize 的后面。

我测试的时候发现操作 nftables 的时候延迟还是比较大的,能有上百毫秒。所以为了不拖慢域名解析的速度,我把这个插件写成了先写出和返回解析结果,然后后台起了个 goroutine 去执行 nftables 的写入。同时也为了降低不必要的开销,建议是把这个插件放在 cache 插件后,并且超时时间大于 cache 的超时时间。

sed -i.bak -r '/finalize:.*/d' plugin.cfg
sed -i.bak '/cache:.*/a finalize:github.com/tmeckel/coredns-finalizer' plugin.cfg
go get github.com/tmeckel/coredns-finalizer

sed -i.bak -r '/nftables:.*/d' plugin.cfg
sed -i.bak '/cache:.*/i nftables:github.com/owent/coredns-nftables' plugin.cfg
go get github.com/owent/coredns-nftables

go generate

最终的配置结构如下:

nftables [ip/ip6]... {
  set add element <TABLE_NAME> <SET_NAME> [ip/ip6/auto] [interval] [timeout]
  [connection timeout <timeout>]
}

nftables [inet/bridge/arp/netdev]... {
  set add element <TABLE_NAME> <SET_NAME> <ip/ip6> [interval] [timeout]
  [connection timeout <timeout>]
}

这里还利用了 nftables 的 set 自带的超时机制去设置定时淘汰。 对于 ipip6 的 family 类型,可以设置 set 类型为 auto,这样就只会写入符合条件对的ip。 而对于其他的 family 类型,就必须自己设置建立 set 时要使用哪种类型了。当然如果set本身就已存在会用已存在的set的类型。

coredns-filter

coredns-filter 插件就更简单一些了。当然也是建议放在 cachecoredns-nftables 插件后,但是应该在 finalize 前。这样能减少 coredns-nftables 不必要的开销。

整个配置的语法大概是:

filter [command options...] {
  [command options...]
}

然后这里的指令可以是:

  • prefer <none/ipv4/ipv6> : 不过滤/优先使用ipv4/优先使用ipv6
  • bogus-nxdomain [ip address/ip prefix...] : 反污染的ip地址或前缀

比如:

example.org {
    whoami
    forward . 8.8.8.8
    filter prefer ipv4 {
      bogus-nxdomain 127.0.0.1/30 123.125.81.12
    }
}

当然ipv6也是可以的

最后

以上两个插件我开源在了 https://github.com/owent/coredns-nftables 和 https://github.com/owent/coredns-filter 。

另外我还做了个 coredns 的docker镜像放在了 docker.io/owt5008137/coredns 。 每周三拉取最新版本的 coredns 代码并在 cache 插件后面依次插入 coredns-nftablescoredns-filterfinalize ,并在 forward 插件前插入了 alternate 插件。alternate 相当于反向的 bogus-nxdomain 功能。

最终大概这个样子:

cache
nftables
filter
finalizer
# ...
alternate
forward

有需要的小伙伴可以通过 podman/docker pull owt5008137/coredns 或者 podman/docker pull docker.io/owt5008137/coredns 自取。

也欢迎有兴趣的小伙伴们互相交流。