Linux Kernel TLS(KTLS), ULP 和OpenSSL的大坑
前几天GSoC的工作,了解了一下KTLS、ULP以及和OpenSSL的交互,踩了很多坑。同时因为技术比较新,几乎没有中文文档,英文的都很少,在此记录一下,也算是贡献一点中文资料吧……
什么是Kernel TLS(KTLS)
首先我们来看看一个使用OpenSSL的简单web server(摘自原论文):
目前的TLS加密一般是在用户态,也是由于各种历史原因和http的支持,都是先建立TCP通信,然后再在用户态进行握手等操作。比如上图发送一个文件,利用system call从硬盘读出内容,然后回到用户态,OpenSSL加密后再通过system call发出去,这一来一回需要进出内核态2次、伴随着文件内容也复制了2次,虽然硬盘访问占大头,但这个开销还是可观的。
为什么不在Kernel态实现OpenSSL?
好问题,参考这里,简单来说就是现在版本的OpenSSL使用了很多用户态独有的东西,加上历史包袱和各种重构使得这件事难度极大。
虽然通过mmap之类的可以避免一次复制,然而还是要进出内核,复制加密数据,Zero Copy的诱惑是巨大的,所以有了 sendfile
这个好东西,不熟悉的可以参考这里
但是TLS却用不了这个福利,因为加密是在用户态的, sendfile
只能发送原数据,所以Facebook才提出了KTLS的技术,原论文。
简单来说就是下面这个图
既然OpenSSL无法放到内核态,不如单独把TLS加解密放进去,这样就可以利用 sendfile
达到零拷贝和不出内核了。
唯一的KTLS中文介绍在这里大家可以看看。
ULP
同样是上面那个大佬的博客介绍了ULP。总的来说就是一个专门针对KTLS的框架(当然也可以干别的)
一些技术性问题
然而很可惜,上面大佬接触是在16-17年,KTLS和ULP还不是很成熟,现在虽然基本原理没变,但是接口啥的现在都变了很多。原论文用的是GNUTLS,有一个API gnutls_record_get_state
可以拿到cipher的参数,但是OpenSSL没有这个,之前大佬写的这个OpenSSL的版本由于更新并不能跑起来,而且在Linux 4.14以后内置了KTLS,Beta版的OpenSSL也内置支持了,所以之前很多代码也跑不起来了,我们需要解决怎么用新的API……
如何在OpenSSL中使用KTLS
自己写可以参考Linux官方文档,如果要用Beta版的OpenSSL需要自己编译,由于没有文档中间的麻烦很多,通过读ktls相关的代码以及调试OpenSSL打印输出终于找到了正确的姿势:
- 确保Linux内核和Header版本>4.17,可以这么看
uname -a
应该>4.17,cat /usr/include/linux/version.h | grep LINUX_VERSION_CODE
应该>266496。注意如果用Docker也请再次确认,我在这里浪费了很多时间调试,总是不明原因返回-1… - 确认
tls
内核模块开启,一般的发行版内核编译应该用的是m
模式,可以检查一下lsmod |grep tls
,如果没有输出,请使用sudo modprobe tls
,确保运行lsmod
命令可以找到tls
模块 - 下载OpenSSL,master分支,编译:
1
2
3./config enable-ktls
make build_sw -j4
make install_sw
这样基本上是可以用的,如果要验证可以在 include/internal/ktls.h
第322行左右加上 fprintf(stderr, "[KTLS]%s[/KTLS]\n", msg.msg_iov->iov_base);
这种,如果连接中打印出来的是明文那就确认确实使用了KTLS,内核直接返回的明文。
注意目前版本KTLS只支持 TLS_AES_128_GCM_SHA256
的cipher,且只有TLS1.2有效,不过已经有了PR,如果合并了那可能就支持其他的了可以看看。
BUG
目前master分支kTLS存在内存泄露的BUG,具体表现是多次连接后就会时不时出现乱码甚至挂掉,提交了issue和mailbox不过好像没什么人理……
参考资料
- 内核态OpenSSL:https://stackoverflow.com/questions/50166948/is-it-possible-to-port-openssl-into-linux-driver
- sendfile技术:https://www.jianshu.com/p/028cf0008ca5
- KTLS原论文:https://netdevconf.info/1.2/papers/ktls.pdf
- Linux KTLS 官方文档:https://www.kernel.org/doc/html/latest/networking/tls.html
- KTLS中文介绍:https://blog.csdn.net/dog250/article/details/53868519
- ULP中文介绍:https://blog.csdn.net/dog250/article/details/78231773
- KTLS and Golang:https://blog.filippo.io/playing-with-kernel-tls-in-linux-4-13-and-go/