FreeBSD 上的 nginx kTLS 支持
昨天,FreeBSD 13.2 正式发布了。 kTLS 是在 FreeBSD 13.0 中由 Netflix 赞助的新功能, 过去,TLS/SSL 应用程序对于通讯内容的加解密通常是在用户态进行的,这样一来, 系统在读取出数据和发出数据之前需要进行额外的上下文切换,并在内核和用户地址空间之间来回复制缓冲区来满足加解密的需要。 有了 kTLS 之后,内核可以在 sendfile(2) 时直接在内核上下文中完成加密和填充到网络协议栈的操作,从而减少了上下文切换和不必要的复制操作。
在进行加密和解密操作时, kTLS 假定内核有直接映射 (direct map) 支持。在这些平台中, 内核直接将物理内存全部「直接」映射到内核地址空间,这样一来,在内核需要访问某一物理地址时, 其 pmap_map 实现便完全无需操作页表, 因此可以大幅提高性能。FreeBSD 支持的主流 64 位平台均支持这一特性(参见平台对应的 pmap_map 实现)。
FreeBSD 附带的 OpenSSL 在 arm64/aarch64 和 amd64/amd64 平台上默认启用了 WITH_OPENSSL_KTLS
,
无需重新编译即可使用。
整体上,FreeBSD 的 kTLS 提供了三种模式:软件(software),网卡(ifnet) 和 TOE。 软件模式中,kTLS会使用软件加密(这包括加速指令如 AES-NI,也包括协处理器如 qat(4) 等等)在 socket 缓冲区层进行加解密。网卡 ifnet 模式中,加解密操作是由网卡完成的, 一些比较贵的网卡,如 mlx5en(4) 和 cxgbe(4) T6 上集成了相关功能,其中前者同时支持发送和接收。而 TOE (TCP 卸载引擎, 由网卡完成完整的 TCP/IP 操作)模式中,网卡除了完成加解密之外也负责 TCP。
我个人的网站使用了 AES256-GCM,在 FreeBSD 13.2 中加入了收发的支持,因此这次升级完系统之后打算试一试。
由于我没有使用高端的网卡,加上这个网站反正流量也不大,因此我使用软件模式,首先是加载 ktls_ocf
内核模块:
kldload ktls_ocf
在 /etc/rc.conf
中加入: kld_list="ktls_ocf"
令下次启动时也启用它。
此处 ocf
是指内核的 OpenCrypto Framework,它会自行决定是否使用类似 aesni 这样的加速机制。
然后是启用 kTLS 支持:
echo "kern.ipc.tls.enable=1" >> /etc/sysctl.conf
service sysctl start
接下来是告诉 nginx 可以用 kTLS 了。在 nginx 配置中的 server 小节中添加:
ssl_conf_command Options KTLS;
然后重启 nginx。此后,在 sysctl kern.ipc.tls.stats
中可以观察到由 OCF 完成的加密解密。