前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Kubernetes 的小秘密——从 Secret 到 Bank Vault

Kubernetes 的小秘密——从 Secret 到 Bank Vault

作者头像
崔秀龙
发布2024-04-15 17:23:19
1190
发布2024-04-15 17:23:19
举报
文章被收录于专栏:伪架构师伪架构师

Kubernetes 提供了 Secret 对象用于承载少量的机密/敏感数据,在实际使用中,有几种常规或者非常规的方式能够获取到 Secret 的内容:

  1. Pod 加载(自己的或者不是自己的)Secret 为环境变量或者文件
  2. 使用 Kubernetes API(或者 kubectl)获取 Secret 对象内容
  3. 连接 ETCD 读取其中保存的 Secret 明文
  4. 在 CICD 工具中截获含有明文的 Secret 对象 YAML
  5. 在加载了 Secret 的容器中直接读取环境变量或者机密文件

上述泄露途径有几个方式可以进行消减:

  1. 制定细粒度的 RBAC 策略,防止未授权的 Secret 访问以及 Exec 访问
  2. API Server 使用加密参数(EncryptionConfiguration),在 ETCD 中存储密文
  3. 使用 Scratch 等超精简基础镜像,杜绝无用访问
  4. 使用策略引擎,防止不当的加载行为
    • 只有特定的 Pod/容器可以加载特定的 Secret
    • 禁止随意加载主机卷,防止 Kubernetes 组件的身份证书被冒用

除了上述的原生方案之外,还有一些补充手段也是有帮助的,例如:

  • Bitnami 的 Sealed Secret 工具,使用密钥对机密信息进行加密,只有在进入集群之后才会还原为目标 Secret,防止在供应链中泄露信息。
  • Vault 提供了一个 Sidecar,能把 Vault 中存储的机密信息,直接在 Pod 中生成相应的敏感信息文件
  • Secrets Store CSI Driver 项目,能从 Vault、Azure 等设施获取信息,注入 Pod 或者生成 Secret。

Bank Vault

Bank Vault 是个 Vault 周边项目,它大大的降低了 Vault 的落地难度,通过 Webhook 注入,Sidecar 等方式,为 Kubernetes 集群中的工作负载提供了方便的 Vault 接入手段。下图表示了它和原生 Vault 的相对优势:

部署

Bank Vault 提供了一个 Operator,能够非常方便的部署 Vault 服务极其相关的 Webhook。所以首先从 Helm 安装 Operator 开始。

代码语言:javascript
复制
$ helm upgrade --install --wait vault-operator \
oci://ghcr.io/bank-vaults/helm-charts/vault-operatorRelease "vault-operator" does not exist. Installing it now.
Pulled: ghcr.io/bank-vaults/helm-charts/vault-operator:1.22.1
Digest: sha256:f9d976c39f96942ae52b26a3ab923f173109de64a87c3161fed2470f7bcfa86f
NAME: vault-operator
LAST DEPLOYED: Sat Apr  6 13:54:32 2024
...

接下来使用 Kustomize 生成 Vault 所需的 RBAC 对象:

代码语言:javascript
复制
$ kubectl kustomize https://github.com/bank-vaults/vault-operator/deploy/rbac | kubectl apply -f -
serviceaccount/vault created
role.rbac.authorization.k8s.io/vault created
role.rbac.authorization.k8s.io/leader-election-role created
rolebinding.rbac.authorization.k8s.io/leader-election-rolebinding created
rolebinding.rbac.authorization.k8s.io/vault created
clusterrolebinding.rbac.authorization.k8s.io/vault-auth-delegator created

最后创建 Vault 实例:

代码语言:javascript
复制
$ kubectl apply -f https://raw.githubusercontent.com/bank-vaults/vault-operator/v1.21.0/deploy/examples/cr-raft.yaml
vault.vault.banzaicloud.com/vault created

创建结束后,会出现几个 Pod,分别是 vault-operatorvault-configurer 以及三个有状态 vault 实例。

连接到 Vault

首先是新开一个终端窗口,使用端口转发方式暴露 Vault 服务:

代码语言:javascript
复制
$ kubectl port-forward vault-0 8200 &
...
Forwarding from 127.0.0.1:8200 -> 8200
Forwarding from [::1]:8200 -> 8200

然后是给 Vault 客户端准备接入端点和 CA:

代码语言:javascript
复制
# 端点就是 kubectl 转发的端口
$ export VAULT_ADDR=https://127.0.0.1:8200
# 导出证书,并记录到环境变量里
$ kubectl get secret vault-tls -o jsonpath="{.data.ca\.crt}" | base64 --decode > $PWD/vault-ca.crt
export VAULT_CACERT=$PWD/vault-ca.crt

检查一下 vault 的连接:

代码语言:javascript
复制
$ vault status
Key                     Value
---                     -----
Seal Type               shamir
Initialized             true
Sealed                  false
...

用环境变量保存凭据:

代码语言:javascript
复制
export VAULT_TOKEN=$(kubectl get secrets vault-unseal-keys -o jsonpath={.data.vault-root} | base64 --decode)

部署 Webhook

Vault 服务启动并连接之后,就可以开始着手部署功能部分了,前面提到过,Bank Vault 是用 Webhook 实现功能的,所以接下来部署的就是 Webhook 了:

代码语言:javascript
复制
$ kubectl create namespace vault-infra
$ kubectl label namespace vault-infra name=vault-infranamespace/vault-infra created
namespace/vault-infra labeled
$ helm upgrade --install --wait vault-secrets-webhook \
oci://ghcr.io/bank-vaults/helm-charts/vault-secrets-webhook \
--namespace vault-infra
...
LAST DEPLOYED: Sat Apr  6 14:45:05 2024
NAMESPACE: vault-infra
STATUS: deployed

部署完成之后发现生成了两个 Webhook。查看代码,可以看到:

  • pods.vault-secrets-webhook
    • 会被 Pod 的创建事件触发
    • 跳过 kube-system 和刚创建的 vault-infra 两个命名空间
    • 跳过 security.banzaicloud.io/mutate 标签为 skip 的 Pod
  • secrets.vault-secrets-webhook
    • 会被 Secret 的创建和更新事件触发
    • 跳过 kube-system 和刚创建的 vault-infra 两个命名空间
    • 跳过 security.banzaicloud.io/mutate 标签为 skip 的 Secret

写入测试数据

向 Vault 写入一个密钥:

代码语言:javascript
复制
vault kv put secret/demosecret/aws AWS_SECRET_ACCESS_KEY=s3cr3t======= Secret Path =======
secret/data/demosecret/aws======= Metadata =======
Key                Value
---                -----
created_time       2024-04-06T07:12:27.042649134Z
...

用环境变量读取 Vault 内容

创建一个 Pod,看看 Webhook 会对他做什么。

代码语言:javascript
复制
apiVersion: v1
kind: Pod
metadata:
name: vault-test-pod
labels:
app.kubernetes.io/name: vault
annotations:
vault.security.banzaicloud.io/vault-addr: "https://vault:8200"
vault.security.banzaicloud.io/vault-role: "default"
vault.security.banzaicloud.io/vault-skip-verify: "false"
vault.security.banzaicloud.io/vault-tls-secret: "vault-tls"
vault.security.banzaicloud.io/vault-agent: "false"
vault.security.banzaicloud.io/vault-path: "kubernetes"
spec:
serviceAccountName: default
containers:
- name: alpine
image: alpine
command: ["sh", "-c", "echo $AWS_SECRET_ACCESS_KEY && echo going to sleep... && sleep 10000"]
env:
- name: AWS_SECRET_ACCESS_KEY
value: vault:secret/data/demosecret/aws#AWS_SECRET_ACCESS_KEY

创建成功之后,看看 Pod 的日志:

代码语言:javascript
复制
$ kubectl logs -f vault-test-pod
Defaulted container "alpine" out of: alpine, copy-vault-env (init)
...
s3cr3t
going to sleep...

这里输出了我们之前写入 Vault 的密钥值,然而回头看看,我们的 Pod 定义里,并没有引用 Secret,只是定义了一个值为 vault:secret/data/demosecret/aws#AWS_SECRET_ACCESS_KEY 的环境变量,command 节中的命令行直接输出这个环境变量,就能够输出保存在 Vault 中的内容了。但是进入 Pod 的 Shell,会发现环境变量没有变化:

代码语言:javascript
复制
$ kubectl exec -it vault-test-pod -- env | grep -i aws
Defaulted container "alpine" out of: alpine, copy-vault-env (init)
AWS_SECRET_ACCESS_KEY=vault:secret/data/demosecret/aws#AWS_SECRET_ACCESS_KEY

所以 Pod 中被注入了什么呢?

首先是注入了一个初始化容器,在临时卷里面复制了一个 vault-env 命令

用卷加载了 Configmap,其中包含了访问 Vault 所需的 CA

加载了

根据我们前面的注解,生成了一系列的 VAULT* 环境变量

最重要的,它劫持了原有的启动命令,在前面加入了一个 /vault/vault-env,启动命令就变成了:

代码语言:javascript
复制
    - args:
- sh
- -c
- echo $AWS_SECRET_ACCESS_KEY && echo going to sleep... && sleep 10000
command:
- /vault/vault-env

所以可以推测——/vault/vault-env 充当了 sh 的父进程,在其中根据环境变量 AWS_SECRET_ACCESS_KEY 的值获取了保存在 Vault 中的机密内容。

用机密数据渲染配置文件

看看下面的 Configmap

代码语言:javascript
复制
apiVersion: v1
kind: ConfigMap
metadata:
labels:
app.kubernetes.io/name: my-app
my-app.kubernetes.io/name: my-app-vault-agent
branches: "true"
name: my-app-vault-agent
data:
config.hcl: |
vault {
// This is needed until https://github.com/hashicorp/vault/issues/7889
// gets fixed, otherwise it is automated by the webhook.
ca_cert = "/vault/tls/ca.crt"
}
auto_auth {
method "kubernetes" {
mount_path = "auth/kubernetes"
config = {
role = "default"
}
}
sink "file" {
config = {
path = "/vault/.vault-token"
}
}
}
template {
contents = <<EOH
{{- with secret "secret/data/demosecret/aws" }}
token: {{ .Data.data.AWS_SECRET_ACCESS_KEY }}
{{ end }}
EOH
destination = "/tmp/config"
// command     = "/bin/sh -c \"kill -HUP $(pidof sleep) || true\""
}

上面的配置文件指示了如何对接 Vault,从 secret/data/demosecret/aws 拉取 AWS_SECRET_ACCESS_KEY 中的值,渲染到 template 一节中的模板里面。只要在 Pod 的注解中加入 vault.security.banzaicloud.io/vault-agent-configmap: "my-app-vault-agent"。就可以在这个容器中加入 Sidecar,使用 Sidecar 在 destination 字段指定的配置文件里保存渲染结果。如果 command 有赋值,还可以发出命令,通知业务应用刷新配置。

加入该注解的 Pod 运行后,可以在这个 Pod 的指定文件中看到渲染结果,例如:

代码语言:javascript
复制
$ kubectl get pods | grep vault-agent-pod
vault-agent-pod                     2/2     Running   0              9m8s
$ kubectl exec -it vault-agent-pod -- cat /tmp/config
Defaulted container "vault-agent" out of: vault-agent, alpinetoken: s3cr3t

后记

Bank Valut 这个项目虽然已经有 2000 Star 了,不过文档还弱的很,甚至 Blog 全挂了也没人理。但是这个思路还是有点意思。虽说有点像屠龙技,不过被安全同学卡脖子的时候,这种使用父进程遮盖环境变量,或者用轮转方式刷新配置文件的玩法,都算是个可行的解法。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-04-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 伪架构师 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Bank Vault
    • 部署
      • 连接到 Vault
        • 部署 Webhook
          • 写入测试数据
            • 用环境变量读取 Vault 内容
              • 用机密数据渲染配置文件
              • 后记
              相关产品与服务
              容器服务
              腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档