K8S Secret 和 Vault

发布时间 2023-12-25 03:06:29作者: moon~light



Secret

secret 用于存敏感信息,避免将密码等敏感数据硬编码到配置文件中,比如所有要用到 PG 的 pod 都使用同一个已经配置好的 secret,这样 PG 的用户密码就在 secret 被统一管理了,并且每个 pod 的配置都只需要指向 secret 不会暴露密码数据

主要有两种方式把 secret 传给 Pod

  1. 配置环境变量,设置其来源为某个 secret
  2. 挂载 Volumn,设置其来源为某个 secret
  3. imagePullSecrets

但 Secret 本身并不是很安全

sudo kubectl get secret flux-system -o yaml 命令就能看到数据

在 git 代码库也能看到

虽然值是 base64 编码,但和明文其实差不多

所以前面的做法只是统一了敏感数据的管理,其他 deployment 只引用 secret,减少部分风险

可以采取措施进一步保证安全

  • 使用 RBAC 限制哪些 Pod 或 Account 才能访问 Secret
  • 加密数据,即使被暴露也不会被破解
  • 定期更改密码
  • 使用专门的密钥管理服务,将密钥与应用解耦

最理想的是使用专门的密码管理服务

Vault

Vault 是一个被广泛使用的密码管理服务,包括一个 Vault 服务器,和装在 K8S 上的 vault-agent-injector 等

可以把 Vault 服务器也装在 K8S 上(可以通过 Helm 命令安装)

这样密码等敏感信息就统一存在 Vault 上,由 Vault 管理,只要限制少数有权限的人去配置就可以

然后 Deployment 可以通过不同方法(变量、文件等)指向 Vault 上的配置,这样就不会有 Secret 被暴露的问题

当然如果能登陆上 Vault 或 App 的 Pod 的话还是会暴露的

安装 Vault

sudo helm repo add hashicorp https://helm.releases.hashicorp.com

sudo helm install vault hashicorp/vault --version 0.19.0 --set "server.dev.enabled=true" -n vault

安装好后查看 vault 这个 namespace 下的 pod

$ sudo kubectl get pods -n vault
NAME                                    READY   STATUS    RESTARTS   AGE
vault-0                                 1/1     Running   0          174m
vault-agent-injector-6cd49f8bbd-xflhm   1/1     Running   0          174m

可以看到有两个 pod
vault-0 是 vault 服务器
vault-agent-injector 用于 Deployment 获取 vault 配置的数据

查看 vault 的登录 token

sudo kubectl logs vault-0  -n vault  

Unseal Key: FHrkeZnLwHHMWJScB4iTTa71JA49QlDxh0pIYCM34Mg=
Root Token: root

由于默认没有暴露 HTTP 端口,可以自己配置 NodePort

apiVersion: v1
kind: Service
metadata:
  name: vault-node
  namespace: vault
spec:
  type: NodePort
  selector:
    app.kubernetes.io/instance: vault
    app.kubernetes.io/name: vault
    component: server
  ports:
    - name: http
      port: 8200
      protocol: TCP
      targetPort: 8200
      nodePort: 30820
    - name: https-internal
      port: 8201
      protocol: TCP
      targetPort: 8201
      nodePort: 30821

这样就可以使用 root token 登录 Vault UI
http://localhost:30820/ui

上面可以配置查看数据

也可以直接登录 Pod 配置查看

在 Vault 配置数据

配置 PG 数据库的用户密码

~$ sudo kubectl exec -it vault-0 /bin/sh -n vault
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ $
/ $ vault secrets enable -path=internal kv-v2
Success! Enabled the kv-v2 secrets engine at: internal/
/ $
/ $
/ $ vault kv put internal/database/config username="pg-username" password="pg-password"
Key                Value
---                -----
created_time       2023-12-21T09:32:53.353327656Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1
/ $
/ $ vault kv get internal/database/config
======= Metadata =======
Key                Value
---                -----
created_time       2023-12-21T09:32:53.353327656Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

====== Data ======
Key         Value
---         -----
password    pg-password
username    pg-username
/ $

为了 K8S 能访问到,还要开启 vault 的 kubernetes 认证

/ $ vault auth enable kubernetes
Success! Enabled kubernetes auth method at: kubernetes/

配置 kubernetes 认证

vault write auth/kubernetes/config \
      token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
      kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \
      kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt

Success! Data written to: auth/kubernetes/config

授予用户访问 internal/database/config 的权限

vault policy write internal-app - <<EOH

  path "internal/data/database/config" {
    capabilities = ["read"]
  }
EOH

Success! Uploaded policy: internal-app

关联 service account

vault write auth/kubernetes/role/internal-app \
      bound_service_account_names=internal-app \
      bound_service_account_namespaces=default \
      policies=internal-app \
      ttl=24h

Success! Data written to: auth/kubernetes/role/internal-app

这样就可以用 Deployment 访问 Vault 数据了

Deployment 读取 Vault 数据

创建 service account

apiVersion: v1
kind: ServiceAccount
metadata:
  name: internal-app
  namespace: default

创建 deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vault-demo
  labels:
    app: vault-demo
spec:
  selector:
    matchLabels:
      app: vault-demo
  template:
    metadata:
      labels:
        app: vault-demo
      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "internal-app"
        vault.hashicorp.com/agent-inject-secret-database-config.txt: "internal/data/database/config"
    spec:
      serviceAccountName: internal-app
      containers:
        - name: vault-demo
          image: busybox:1.28.4
          imagePullPolicy: IfNotPresent
          command: [ 'sh', '-c', "while true; do echo hello; sleep 6000; done" ]

annotations 下的 vault.hashicorp.com 几个配置,会被 vault-agent-injector 检查到
然后会依据 role 的权限,把 internal/data/database/config 的数据存到 database-config.txt 文件里(agent-inject-secret- 配置是前缀会被去掉)

部署 app 然后查看

$ sudo kubectl get pods
NAME                          READY   STATUS    RESTARTS   AGE
vault-demo-5bc4656884-cmlqw   2/2     Running   0          9s

Pod 下面有两个 container 分别是 vault-demo 和 vault-agent-init

vault-agent-init 作为 sidecar 把 secret 添加到 vault-demo 应用容器

登录 vault-demo 并读取数据

~$ sudo kubectl exec -it vault-demo-5bc4656884-cmlqw -c vault-demo /bin/sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ #
/ # cat /vault/secrets/database-config.txt
data: map[password:pg-password username:pg-username]
metadata: map[created_time:2023-12-21T09:32:53.353327656Z custom_metadata:<nil> deletion_time: destroyed:false version:1]

这样所有敏感数据都不会暴露在外面,除非能登上 Pod

如果想自定义文件的内容,可以加一个 agent-inject-template-database-config.txt

注意 agent-inject-template- 后面的值要和 agent-inject-secret- 的值一样

      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "internal-app"
        vault.hashicorp.com/agent-inject-secret-database-config.txt: "internal/data/database/config"
        vault.hashicorp.com/agent-inject-template-database-config.txt: |
          {{- with secret "internal/data/database/config" -}}
          export username="{{ .Data.data.username }}"
          export password="{{ .Data.data.password }}"
          {{- end }}

再上 Pod 查看

/ # cat /vault/secrets/database-config.txt
export username="pg-username"
export password="pg-password"

可以看到,变成自定义的格式了

Vault Secrets Operator

Vault Secrets Operator 可以把 Vault 数据导入到 Secrets

sudo helm repo add hashicorp https://helm.releases.hashicorp.com
sudo helm install vault-secrets-operator hashicorp/vault-secrets-operator -n vault

如果下载不了 gcr.io/kubebuilder/kube-rbac-proxy 而无法启动 vault-secrets-operator

尝试设置代理,通过 docker info | grep -i proxy 命令可以查看有没有代理

如果没有,可以添加文件

$ cat /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=xxx"
Environment="HTTPS_PROXY=xxx"
$ 
$ 
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
$ 

或者从其他地方下载,再改名字

docker pull anjia0532/kubebuilder.kube-rbac-proxy:v0.15.0
docker tag anjia0532/kubebuilder.kube-rbac-proxy:v0.15.0 gcr.io/kubebuilder/kube-rbac-proxy:v0.15.0

使用 Vault Secrets Operator 具体例子可以参考
https://github.com/hashicorp/vault-secrets-operator/tree/main/config/samples

里面的 setup.sh 是在 Vault 做配置

还有配置 vault 的链接认证的
secrets_v1beta1_vaultconnection.yaml
secrets_v1beta1_vaultauth.yaml

最后是设置 VaultStaticSecret 和 Pod
secrets_v1beta1_vaultstaticsecret.yaml