Dapr | State Store Encryption

之前源码系列文章讲到过, Dapr 可以对集群内部提供 kv store (键值存储) 的功能. 但是很多时候我们希望有更好的安全性, 因为存储功能总是需要外部软件提供, 所以 dapr 提供了键值存储自动加密的功能.
注意: 这个功能目前还处于 Preview feature 阶段, 如果要使用需要在全局配置中开启这个特性. 本文源码对应 dapr v1.5.1 版本.
基本思路⌗
dapr 选用 AES-256-GCM 对称加密算法进行数据加解密, 因为用户应用都是通过 dapr API 来读取, 新增或者修改数据, 所以 dapr 在写入/修改 handler 中增加加密逻辑, 在读取 handler 中增加解密逻辑就可以实现此功能了.
源码解析⌗
此功能使用时需要在 state store component 配置中增加 primaryEncryptionKey 作为秘钥, 如果没有配置则当做不加密处理. 所以在 component 初始化阶段 dapr runtime 会根据配置标记该 store 的加解密配置.
注意: 由于使用 AES-256-GCM 加密算法, 所以秘钥必须为 32 bytes, 并且需要是 hex 格式.
写入/更新⌗
以 HTTP API 为例:
接着查看 encryption.TryEncryptValue 方法:
可以看到最终存在外部存储中的值为 base64(aes256gcm(data)) + || + primaryEncryptionKey name 的形式.
为什么不直接存储加密后的值而是在后面还拼接了秘钥名称?
因为此功能提供了一个 Key rotation 的功能, 也就是为了方便用户修改秘钥. 所以加密功能其实是可以配置 primaryEncryptionKey 和 secondaryEncryptionKey 两个秘钥, 更新秘钥时, 需要将老的秘钥配置为 secondaryEncryptionKey. 加密永远会使用 primaryEncryptionKey, 新增数据会直接使用新秘钥, 而老数据只有在更新时才会用新秘钥加密. 因此存储中会同时存在新老秘钥加密的数据, 为了能够正常解密, dapr 才将加密使用秘钥名称存储在了每条数据中.
读取⌗
以 HTTP API 为例, 读取实现非常简单, 如果发现请求的 store 配置了加密功能, 增加一步解密操作.
接着查看 encryption.TryDecryptValue 方法:
总结⌗
这个新特性实现是非常简单的, 但是 primaryEncryptionKey 和 secondaryEncryptionKey 的设计减轻了更换秘钥的难度值得学习.
参考资料⌗
