今天是2025年8月15日,大噩耗啊:
external-secrets / external-secrets
⭐ 5416 | 🔀 1032 | Go 97.6%
External Secrets Operator 项目暂停发布
由于维护团队规模过小,External Secrets Operator 决定暂停所有官方版本发布,直到重建足够的维护团队。
Key Takeaways
- 项目暂停所有官方版本发布,包括新功能、补丁和容器镜像。
- 将继续审核和合并社区的 PR,但不会提供 GitHub Discussions、Slack 或问题评论的支持。
- 需要至少五名长期维护者来确保项目的可持续发展。
- 鼓励依赖该项目的公司或团队积极参与贡献。
- 贡献者可通过填写表单或查看治理文档加入项目。
居然暂停发布了,那就抓紧时间把Vault跟它的集成记录下来:
一、首先必须在集群里安装Vault,然后安装external -secrets,都用helm装即可
helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update
helm install vault hashicorp/vault --set "server.dev.enabled=true" --set "injector.enabled=true" --namespace default
helm repo add external-secrets https://charts.external-secrets.io
helm repo update
helm install external-secrets external-secrets/external-secrets --namespace external-secrets --create-namespace --set installCRDs=true
二、k8s授权,k8的授权体系就是 sa -> role -> rolebinding
那kubernetes会把pod中sa的Token注入到文件:/var/run/secrets/kubernetes.io/serviceaccount/token
那调用k8s api中的token reviewer是可以对Token令牌进行验证的,我们先建立一系列k82资源
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-secrets-sa
namespace: external-secrets
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: external-secrets-clusterrole
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["authorization.k8s.io"]
resources: ["selfsubjectrulesreviews"]
verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: external-secrets-clusterrolebinding
subjects:
- kind: ServiceAccount
name: external-secrets-sa
namespace: external-secrets
roleRef:
kind: ClusterRole
name: external-secrets-clusterrole
apiGroup: rbac.authorization.k8s.io
如上,我们建立了一个sa,然后有api group的权限,同时授予了整个集群级别的权限
三、登录vault,配置vault使用k8s的认证方式进行身份认证
那 enable kubernetes 其实设置了 vault 可以使用kubernetes的认证方式来进行vault-role角色的身份认证
# 进入容器
kubectl exec -it vault-0 -- /bin/sh
# 登录,要输入的token可以从日志里看到
vault login root
# 开启kubernetes认证方式
vault auth enable kubernetes
# 每个pod里面都有sa,k8s会将这个sa的token注入到文件/var/run/secrets/kubernetes.io/serviceaccount/token
# 那就用这个sa的token,调用k8s token_reviewer进行身份认证
# 认证通过vault就会颁发一个对应vault-role的vault-token令牌出来
# 用vault-token就可以去vault里读取到vault-secret的内容了
vault write auth/kubernetes/config \
token_reviewer_jwt=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
kubernetes_host="https://kubernetes.default.svc:443" \
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
# 拿token的另一种方法:
# kubectl get secret $(kubectl get serviceaccount external-secrets-sa -o jsonpath="{.secrets[0].name}") -o jsonpath="{.data.token}" | base64 --decode
# vault的认证体系跟AWS一样
# 先建policy,后建role
# 所以先建一个读secret的策略
vault policy write external-secrets-policy - <<EOF
path "secret/data/*" {
capabilities = ["read", "list"]
}
EOF
# 然后建立一个vault的role,将这个vault-role绑定到k8s的sa账号上
# 那k8s token review认证通过就可以获得相应vault-role的权限
vault write auth/kubernetes/role/external-secrets-operator \
bound_service_account_names=external-secrets-sa \
bound_service_account_namespaces=external-secrets \
policies=external-secrets-policy \
ttl=24h
# 放数据进去vault
vault kv put secret/my-app MY_SECRET_NAME="Fuck Dingdang"
vault kv get secret/my-app
四、external-secret同步的设定
external-secret相当于中间人,他有很多自定义的资源,来自动进行vault-secret和k8s-secret之间的同步
ClusterSecretStore指定了vault server、vault-role、k8s sa的关系
---
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: vault-cluster-secretstore
spec:
provider:
vault:
server: "http://vault.default.svc.cluster.local:8200" # 请根据你的vault服务地址调整
path: "secret" # KV引擎挂载路径,与你vault kv put命令中路径对应
version: "v2" # KV版本为v2
auth:
kubernetes:
mountPath: "kubernetes" # 你启用的k8s auth路径
role: "external-secrets-operator" # 你在vault中创建的role名称
serviceAccountRef:
name: external-secrets-sa # 你为ESO创建的ServiceAccount名称
namespace: external-secrets # ServiceAccount所在命名空间
ExternalSecret 指定了利用vault-role获得了vault-secret,放置到最终要用到的k8s-secret中
注意有刷新,每小时会刷新一次的
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: demo-external-secret
namespace: default
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-cluster-secretstore
kind: ClusterSecretStore
target:
name: demo-secret
creationPolicy: Owner
data:
- secretKey: MY_SECRET_NAME
remoteRef:
key: my-app
property: MY_SECRET_NAME # Vault中具体的键名
通过以上两个资源,那vault里存放的secret就被同步到k8s的secret中了
五、实际部署一个应用看看
那我们随便起个deployment
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-deployment
namespace: default # 使用目标命名空间,可以修改为你的实际命名空间
labels:
app: demo
spec:
replicas: 1 # 副本数量,可以根据需要调整
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: demo-container
image: nginx:latest
imagePullPolicy: IfNotPresent # 如果本地已经存在镜像则不拉取
ports:
- containerPort: 8080 # 容器暴露的端口
env:
- name: MY_SECRET_NAME
valueFrom:
secretKeyRef:
name: demo-secret # 引用的 Secret 名称
key: MY_SECRET_NAME # Secret 中的键名
---
apiVersion: v1
kind: Secret
metadata:
name: demo-secret
namespace: default # 使用目标命名空间,可以修改为你的实际命名空间
type: Opaque
data:
MY_SECRET_NAME: AABBCC
实在太复杂痛苦了,最终的环境变量应该如下:
MY_SECRET_NAME="Fuck Dingdang"
但这没有结束呢。
六、安装配置reloader自动轮换
如果secret被改了,如AK的Secret自动轮换了,那pod也必须跟着自动重启以加载新的变量
# 安装reloader
kubectl apply -f https://raw.githubusercontent.com/stakater/Reloader/master/deployments/kubernetes/reloader.yaml
在deploy中container的部分新增annotations:
metadata:
annotations:
reloader.stakater.com/auto: "true"
如下:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-deployment
namespace: default
annotations:
reloader.stakater.com/auto: "true"
labels:
app: demo
spec:
replicas: 1 # 副本数量,可以根据需要调整
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: demo-container
image: nginx:latest
imagePullPolicy: IfNotPresent # 如果本地已经存在镜像则不拉取
ports:
- containerPort: 80
env:
- name: MY_SECRET_NAME
valueFrom:
secretKeyRef:
name: demo-secret # 引用的 Secret 名称
key: MY_SECRET_NAME # Secret 中的键名
这样ENV环境变量改变的时候就可以自动重起pod
七、另一种注入式的做法vault injector
Deployment新增annotations:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "demo-role"
vault.hashicorp.com/agent-inject-secret-MY_SECRET_NAME: "secret/data/demo"
vault.hashicorp.com/agent-inject-template-MY_SECRET_NAME: |
{{- with secret "secret/data/demo" -}}
export MY_SECRET_NAME="{{ .Data.data.MY_SECRET_NAME }}"
{{- end -}}
完整如下:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-deployment
namespace: default
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "demo-role"
vault.hashicorp.com/agent-inject-secret-MY_SECRET_NAME: "secret/data/demo"
vault.hashicorp.com/agent-inject-template-MY_SECRET_NAME: |
{{- with secret "secret/data/demo" -}}
export MY_SECRET_NAME="{{ .Data.data.MY_SECRET_NAME }}"
{{- end }}
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "demo-role"
vault.hashicorp.com/agent-inject-secret-MY_SECRET_NAME: "secret/data/demo"
vault.hashicorp.com/agent-inject-template-MY_SECRET_NAME: |
{{- with secret "secret/data/demo" -}}
export MY_SECRET_NAME="{{ .Data.data.MY_SECRET_NAME }}"
{{- end }}
spec:
serviceAccountName: demo-sa
containers:
- name: demo-container
image: nginx:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
如上也可以做到secret有改动,就自动重启
八、补全一下用k8s Token凭据验证vault角色的过程
# 先建立一个SA
apiVersion: v1
kind: ServiceAccount
metadata:
name: demo-sa
namespace: default
# 然后定义一个Deployment,在spec附上这个SA
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
namespace: default
spec:
serviceAccountName: demo-sa
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- env:
- name: VAULT_ADDR
value: "http://vault.default.svc.cluster.local:8200" # 替换为 Vault 的服务地址
image: nginx:latest
imagePullPolicy: IfNotPresent
name: nginx
# 登录vault服务(开发模式,登录用户是root)
vault login root
# 启用 Kubernetes Auth 模式
vault auth enable kubernetes
# vault 使用 sa Token进行身份识别
vault write auth/kubernetes/config \
token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
kubernetes_host="https://kubernetes.default.svc:443" \
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
# 创建一个policy
vault policy write demo-policy - <<EOF
path "secret/data/*" {
capabilities = ["read", "list"]
}
EOF
# 创建一个vault-role,绑定到sa
vault write auth/kubernetes/role/demo-role \
bound_service_account_names=demo-sa \
bound_service_account_namespaces=default \
policies=demo-policy \
# 存放测试kv数据
vault kv put secret/demo MY_SECRET_NAME="BBCCDDEE"
vault kv get secret/demo
# 启动一个deployment
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
namespace: default
spec:
serviceAccountName: demo-sa
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- env:
- name: VAULT_ADDR
value: "http://vault.default.svc.cluster.local:8200" # 替换为 Vault 的服务地址
image: nginx:latest
imagePullPolicy: IfNotPresent
name: nginx
# 进入deployment的pod中
kubectl exec -it nginx -- /bin/sh
# 拿到本地sa的Token
export TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
# 注意pod里有环境变量VAULT_ADDR,如果没有要定义一下
# 然后用curl拿vault-token
curl -X POST "${VAULT_ADDR}/v1/auth/kubernetes/login" \
-H "Content-Type: application/json" \
-d '{
"role": "demo-role",
"jwt": "'$TOKEN'"
}'
# curl 返回字符串中的 client_token 字段
export VAULT_TOKEN="hvs.CAESaaaBBBcccDDDEEEfffGGG"
# 用vault_token获得kv的数据
curl -X GET "${VAULT_ADDR}/v1/secret/data/demo" -H "X-Vault-Token: $VAULT_TOKEN"
所以,其实vault有api接口,可以直接调用curl来认证并获取数据,这就极大的扩展了vault的接口范围。
好了,就到这里吧!