实现 Secret as Code,你可以这样做

简介: # 导语云原生时代下,又一次掀起了 "Infrastructure as Code" 和 "DevOps" 运动的浪潮,当前已经不再局限于基础设施,而是万物皆可代码化,例如(排名不分先后):- 架构即代码(Architecture as Code)- 配置即代码(Configuration as Code)- 环境即代码(Environment as Code)- 基础设施即代码(I

导语

云原生时代下,又一次掀起了 "Infrastructure as Code" 和 "DevOps" 运动的浪潮,当前已经不再局限于基础设施,而是万物皆可代码化,例如(排名不分先后):

  • 架构即代码(Architecture as Code)
  • 配置即代码(Configuration as Code)
  • 环境即代码(Environment as Code)
  • 基础设施即代码(Infrastructure as Code)
  • 策略即代码(Policy as Code)
  • 项目即代码(Project as Code)
  • 安全即代码(Security as Code)
  • ...

其中,安全即代码(Security as Code)从字面上看有点宽泛,但将其视为将安全操作自动地插入到软件生产的每个领域是非常有必要的。例如在开发与测试过程中,可能需要与其他系统交互,其中难免存在凭据和敏感信息的管理。同样的,在配置即代码(Configuration as Code)中,用户名、密码、API 令牌和 TLS 证书等,这些信息是非常常见的存在,但是这些包含对用户、用户组或实体进行身份验证和授权的机密数据,不应该被其他人知道或看到。

Secret Management

那么在 IaC 基础设施平台或者 GitOps 运维平台,在享受着声明式配置带来的便利的我们,该如何保证敏感信息的安全呢?答案就是:保证 Secret 安全的关键在于管理它们的方式。接下来我们一起来看下,在云原生领域推荐的几种敏感信息管理方案。

SealedSecret

SealedSecret 是一个管理 Kubernetes Secret 的方案,将 Secret 加密到 SealedSecret 资源中,它可以安全存储,即使在公共存储库中也是如此。SealedSecret 只能由目标集群中运行的控制器解密,其他任何人(包括原作者)都无法从 SealedSecret 中获得原始 Secret。

原理

SealedSecret 通过以下流程,完成加解密:

加密

  1. 用户使用 kubeseal 加密 mysecret.json
  2. sealed-secrets-controller 获取公钥
  3. 使用公钥加密,结果写入本地文件 mysealedsecret.json

解密

  1. 用户下发 SealedSecret 资源(mysealedsecret.json)
  2. sealed-secrets-controller 发现新对象,获取私钥
  3. 使用私钥解密,创建 k8s Secret 资源

小结

每个 SealedSecret 都使用其自己的随机非对称密钥加密,该密钥特定于 SealedSecret 名称和 Namespace。将加密数据复制粘贴到另一个 Secret 或另一个 Namespace 中将不起作用。

  • ✅ 设置和使用简单
  • ✅ Secret 文件统一管理
  • ❎ 人工管理公钥,加密文件公开
  • ❎ 不能在 Secret 之外使用(例如 ConfigMap)

Helm Secrets

Helm Secrets 是 Helm 的一个插件,能够利用 Helm 模板化 Secrets 资源。基于两种后端:

实现敏感信息管理,本质上是一个工具组合。

原理

Helm Secrets 是用 shell 脚本编写,使用的各个组件是需要用户提前安装,下面以 sops + gpg 为例,简要说明工作原理。

加密

  1. 用户通过 helm secrets enc 命令触发 gpg 加密 secret.yaml
  2. gpg 读取本地公钥
  3. 使用公钥加密,结果写入覆盖源文件 secret.yaml

解密

  1. 用户通过 helm secrets dec 命令触发 gpg 解密 secret.yaml
  2. gpg 读取本地私钥
  3. 使用私钥解密,结果写入到新文件 secret.yaml.dec

小结

Helm Secrets 支持多种加密方式,接除了 k8s Secret 的绑定关系,但是使用复杂,体感不太友好。

  • ✅ 支持多种后端和加密方式
  • ✅ Secret 文件统一管理
  • ❎ 配置复杂
  • ❎ 人工管理公钥,加密文件公开

External Secrets Operator

External Secrets Operator(ESO)是一个 Kubernetes Operator,它支持 AWS Secrets Manager、HashiCorp Vault、Google Secrets Manager、Azure Key Vault 等多种外部 Secret 管理系统。Operator 从外部 API 读取信息并自动将值注入 Kubernetes Secret。

ESO 的目标是将来自外部 API 的机密信息同步到 Kubernetes。ESO 是自定义 API 资源的集合:

  • SecretStore:将身份验证/访问的关注点与工作负载所需的实际 Secret 和配置分开;
  • ExternalSecret:声明要获取的数据。它引用了一个知道如何访问该数据的 SecretStore;
  • ClusterSecretStore:是一个全局的、集群范围的 SecretStore,可以从所有命名空间中引用;

它们为外部 API 提供了一个用户友好的抽象,帮助用户存储 Secret 并管理它的生命周期。

原理

ESO 按照以下列方式同步 ExternalSecrets:

  1. 使用 spec.secretStoreRef 来查找合适的 SecretStore。如果不存在或 spec.controller 字段不匹配,将丢弃不再处理。
  2. 使用 SecretStore.spec 的指定凭据实例化外部 API 客户端。
  3. 根据 ExternalSecret 的要求获取敏感信息,如果有必要,将对敏感信息解码。
  4. 根据 ExternalSecret.target.template 提供的模板创建 Kind=Secret 资源。可以使用来自外部 API 的敏感信息对 Secret.data 进行模板化。
  5. ESO 确保敏感信息与外部 API 保持同步。

小结

ESO 相对 SealedSecret,拓展了外部敏感信息管理系统的支持,但依旧是 k8s Secret 的管理方案。

  • ✅ 支持多种外部 Secret 管理系统
  • ✅ 敏感信息中心化管理,获取灵活
  • ❎ 身份认证暴露
  • ❎ 仅用于 Secret 资源

Secret Store CSI Driver

Secrets Store CSI Driver 通过容器存储接口 (CSI) 卷将 Secret 存储与 Kubernetes 集成。允许 Kubernetes 将外部机密存储中的多个 Secret、密钥和证书作为卷挂载到其 Pod 中。卷挂载成功后,其中的数据将被挂载到容器的文件系统中。

原理

Secret Store CSI Driver 管理敏感信息的原理下图所示:

  1. 在 Pod 首次启动或重启时,Secrets Store CSI Driver 使用 gRPC 与 Provider 通信
  2. 从 SecretProviderClass 自定义资源中指定的外部 Secrets Store 检索敏感信息内容
  3. 将卷作为 tmpfs 挂载到 Pod 中,并将敏感信息写入该卷
  4. 在 Pod 被删除时,会清理并删除相应的卷

小结

Secret Store CSI Driver 与 ESO 方案类似,但挂卷不局限于 k8s Secret 资源,可扩展性更强。

  • ✅ 支持多种 Provider
  • ✅ 敏感信息中心化管理,获取灵活
  • ❎ 身份认证暴露
  • ❎ 卷挂载方式,应用需要增加文件读写相关逻辑

Secret as Code

根据以上各种解决方案的分析和总结,当前对于敏感信息管理基本可以分为两大类:

  • 敏感信息加密
  • 敏感信息托管

敏感信息加密

如果使用加解密的方案,那么有以下几个问题需要思考:

Q1:公私钥管理

如果加解密行为完全本地化,也就是使用了纯客户端方案,那么密钥管理就在本地存储,用户负责密钥管理;如果采用独立的加解密服务,密钥一般是该服务签发,用户只需要使用该服务提供的密钥管理能力即可。但同时也衍生另一个问题,就是如何管理访问该服务的身份认证信息,这也同样是敏感信息的管控。

Q2:解密行为触发

不论采用本地加解密还是独立的加解密服务,用户都是按需解密,因此这个行为是按需驱动;借用 GitOps 的概念,也就是 Push(“推”模式),而不是 Pull(“拉”模式)。

小结

如果采用加解密方案,建议使用独立的加解密服务,本地加解密更适用于产品的入门指南,以最小的代价获得最优的用户体验。而且在数据安全领域,永远有更专业的负责人和团队在提供支持;同时,对于产品化的演进,中心化管理也是必经之路。这里介绍一个 Google 开源的项目:go-cloud,它能让 Go 应用的开发在任何云提供商组合上无缝部署云应用程序。其中 secrets 包以可移植的方式提供对密钥管理服务的访问。

以下是使用 HashiCorp Vault 的 Secret Engine 加解密能力的代码片段(初始化+加密+解密):

初始化:

import (
    "context"

    "gocloud.dev/secrets"
    _ "gocloud.dev/secrets/hashivault"
)

keeper, err := secrets.OpenKeeper(ctx, "hashivault://mykey")
if err != nil {
    return err
}
defer keeper.Close()

加密:

plainText := []byte("Secrets secrets...")
cipherText, err := keeper.Encrypt(ctx, plainText)
if err != nil {
    return err
}

解密 :

var cipherText []byte // obtained from elsewhere and random-looking
plainText, err := keeper.Decrypt(ctx, cipherText)
if err != nil {
    return err
}

敏感信息托管

如果使用托管的方案,同样的,也有几个问题需要思考:

Q1:托管服务的身份验证信息管理

托管服务负责保存敏感信息的原始数据,可以认为它就是一种数据库,形如各大云厂商提供的存储服务,例如:Azure Key Vault、AWS Secrets Manager 等,或者是 Gitlab Secret,都是可以安全存储敏感信息的服务。这些服务,无一例外,均需要身份验证,才可以使用。因此,托管服务的身份认证信息管理是首个要解决的问题。

Q2:敏感信息的引用格式

各个提供存取数据托管服务,也都会提供各自的存取方式。对于 IaC 基础平台或者 GitOps 运维系统来说,预先存入的敏感信息,提供一种真正 User-Friendly 敏感信息引用格式,避免大量的填入参数和晦涩难懂的组织形式,是以用户为核心的平台必须慎之又慎的事情。

Q3:敏感信息的取回

存入托管服务的敏感信息可以很方便,可以是 UI,也就是命令行。但是对于敏感信息的取回,无需人工介入是首要条件。各种托管服务的无一例外会提供自己的一套存取 API,或者是某种语言的 SDK。但哪怕只是通过 REST 接口对接主流云厂商的托管服务,都是一件耗时耗力的任务。

小结

如果采用托管服务,可以考虑在平台侧提供默认的托管服务,同时保持开放性,陆续支持其他主流云厂商的 Secret Manager。对接多个托管服务,可以参考 vals 项目,它是一个开源的管理敏感信息配置的框架,同时支持多种后端服务,例如:Vault、AWS Secrets Manager、GCP Secrets Manager、Azure Key Vault、EnvSubst、GitLab Secrets 等。并且针对不同的后端,它的引用格式在设计上始终遵循 URI 风格。

以下是借助 HashiCorp Vault 的托管能力,取回敏感信息的代码片段:

import "github.com/variantdev/vals"

secretsToCache := 256 // how many secrets to keep in LRU cache
runtime, err := vals.New(secretsToCache)
if err != nil {
  return nil, err
}

valsRendered, err := runtime.Eval(map[string]interface{}{
    "inline": map[string]interface{}{
        "foo": "ref+vault://secret/foo?proto=http&&host=127.0.0.1:8200#/foo",
        "bar": map[string]interface{}{
            "baz": "ref+vault://secret/bar?address=http://127.0.0.1:8200#/bar",
        },
    },
})

总结

Anthos architecture foundations and principles 一文中提到:“行业普遍接受的最佳实践是将敏感信息排除在源码之外,并通过文件、环境变量或使用配置拉取方法将秘密注入应用程序”。因此更推荐使用敏感信息托管服务,但仍然需要明确一点,并不存在一个能够解决所有问题的万能方案。任何方案的技术选型,背后都有着不同业务需求。因此基于场景基于需求,设计一个适合的方案,才是“最好”。

参考链接

相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。     相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
机器学习/深度学习 缓存 PyTorch
PyTorch 2.0 推理速度测试:与 TensorRT 、ONNX Runtime 进行对比
PyTorch 2.0 于 2022 年 12 月上旬在 NeurIPS 2022 上发布,它新增的 torch.compile 组件引起了广泛关注,因为该组件声称比 PyTorch 的先前版本带来更大的计算速度提升。
1439 0
|
并行计算 PyTorch 算法框架/工具
融合AMD与NVIDIA GPU集群的MLOps:异构计算环境中的分布式训练架构实践
本文探讨了如何通过技术手段混合使用AMD与NVIDIA GPU集群以支持PyTorch分布式训练。面对CUDA与ROCm框架互操作性不足的问题,文章提出利用UCC和UCX等统一通信框架实现高效数据传输,并在异构Kubernetes集群中部署任务。通过解决轻度与强度异构环境下的挑战,如计算能力不平衡、内存容量差异及通信性能优化,文章展示了如何无需重构代码即可充分利用异构硬件资源。尽管存在RDMA验证不足、通信性能次优等局限性,但该方案为最大化GPU资源利用率、降低供应商锁定提供了可行路径。源代码已公开,供读者参考实践。
1391 3
融合AMD与NVIDIA GPU集群的MLOps:异构计算环境中的分布式训练架构实践
|
存储 Shell Android开发
基于Android P,自定义Android开机动画的方法
本文详细介绍了基于Android P系统自定义开机动画的步骤,包括动画文件结构、脚本编写、ZIP打包方法以及如何将自定义动画集成到AOSP源码中。
1064 2
基于Android P,自定义Android开机动画的方法
|
Ubuntu
ubuntu20+window双系统启动引导项的修复
ubuntu20+window双系统启动引导项的修复
4238 1
ubuntu20+window双系统启动引导项的修复
|
开发框架 安全 Java
.net和java有什么样的区别?
Java和.NET在本质、编程语言、生态系统与工具、跨平台性、应用领域、性能与效率以及安全性与可靠性等方面都存在明显的区别。选择哪个平台取决于具体的需求、技术栈和目标平台。
1313 7
|
消息中间件 物联网 网络性能优化
MQTT常见问题之MQTT的topic超出上限25个如何解决
MQTT(Message Queuing Telemetry Transport)是一个轻量级的、基于发布/订阅模式的消息协议,广泛用于物联网(IoT)中设备间的通信。以下是MQTT使用过程中可能遇到的一些常见问题及其答案的汇总:
|
机器学习/深度学习 运维 算法
监督算法和无监督算法之间的区别
【8月更文挑战第23天】
1034 0
|
存储 人工智能 算法
Unity 实现A* 寻路算法
Unity 实现A* 寻路算法
965 2
Unity 实现A* 寻路算法
|
Ubuntu 数据安全/隐私保护
饥荒:从0开始搭建服务器
记得刚和朋友联机的时候,都是校园网,我做主机他卡,他做主机我卡。这让我和他玩的都不爽之后到饥荒三个吧求助,得到的答案就是云服务器。然后我就去贴吧求助,终于,在用了一下午的时间后,找到了一个较为简单的方法,那就是用云服务器,再配合hi434785814大神的软件即可搭建饥荒云服务器。
饥荒:从0开始搭建服务器
|
机器学习/深度学习 算法 Python
深度学习基础:为什么神经网络的感知机中的神经元需要偏置项?
深度学习基础:为什么神经网络的感知机中的神经元需要偏置项?
625 0
深度学习基础:为什么神经网络的感知机中的神经元需要偏置项?