使用ExternalSecret在EKS中将Vault的数据(kv)作为密钥加载进来

外部秘密是指的是什么?

1654482450879.png

通常情况下,在使用EKS的ExternalSecret时,我们通常会从SecretManager中获取值,但这次是从Vault中获取值的验证记录。

安装外部密钥操作员

按照官方网站的“入门指南”,安装外部密钥操作员。

helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets \
   external-secrets/external-secrets \
    -n external-secrets \
    --create-namespace \
    --set installCRDs=true

请安装Vault。

我也要安装Vault。请参考官方步骤获取详细信息,这里只是简单记录一下我完成的步骤。

helm repo add hashicorp https://helm.releases.hashicorp.com
helm show values hashicorp/vault > vault-values.yaml
helm upgrade -i -f vault-values.yaml vault -n vault --create-namespace hashicorp/vault

将vault-values.yaml文件更改为使用Ingress。
在Vault启动后执行解封操作。如果不进行解封操作,Pod将持续抛出以下错误,并无法完成启动。

2022-08-04T03:28:40.124Z [INFO]  core: seal configuration missing, not initialized
2022-08-04T03:28:45.090Z [INFO]  core: security barrier not initialized

可以通过使用`vault operator init`命令来获取解封(unseal)所需的密钥和令牌。

kubectl exec -ti vault-0 -n vault -- vault operator init

這個初始的根令牌會用於不同的用途,所以需要備份保存。使用 unseal key 來進行解封。

kubectl exec -ti vault-0 -n vault -- vault operator unseal

进行3次解封操作后,将获得以下的输出,使Vault可用。

Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    5
Threshold       3
Version         1.10.3
Storage Type    file
Cluster Name    vault-cluster-44f5b753
Cluster ID      d4b134d3-6888-37a1-dba4-3b7063a61a80
HA Enabled      false

Vault的设置

在这里,将样本数据投入到Vault内,并创建访问策略。
此处的描述基本上是参考以下信息的。

    • https://www.vaultproject.io/docs/auth/kubernetes

 

    https://learn.hashicorp.com/tutorials/vault/agent-kubernetes

从本地PC使用vault命令访问Helm Vault的入口点。

export VAULT_ADDR=https://vault.mydomain.info
vault login

由於默認情況下未啟用KeyValue引擎,所以將其啟用。

vault secrets enable kv

输入Key和Value的示例数据。

vault kv put kv/mysecret password=himitsu

确认。

$ vault kv get kv/mysecret
====== Data ======
Key         Value
---         -----
password    himitsu

接下来要创建政策。

cat <<EOF > /tmp/vault_sample_policy.hcl
path "*" {
    capabilities = ["read", "list"]
}
EOF

分配策略。按照教程,将策略命名为myapp-kv-ro。

vault policy write myapp-kv-ro /tmp/vault_sample_policy.hcl

创建一个可以操作Kubernetes中资源的ServiceAccount。这次根据教程在默认的Namespace中创建了该账户。由于在权限上遇到了一些问题,所以我将其设为了cluster-admin,但我认为可以更细化权限。

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: vault-auth
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: role-tokenreview-binding
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: vault-auth
  namespace: default
EOF

启用Kubernetes的身份验证,并添加认证信息。

vault auth enable kubernetes
export SA_SECRET_NAME=$(kubectl get secrets --output=json \
    | jq -r '.items[].metadata | select(.name|startswith("vault-auth-")).name')
export SA_JWT_TOKEN=$(kubectl get secret $SA_SECRET_NAME \
    --output 'go-template={{ .data.token }}' | base64 --decode)
export SA_CA_CRT=$(kubectl config view --raw --minify --flatten \
    --output 'jsonpath={.clusters[].cluster.certificate-authority-data}' | base64 --decode)
export K8S_HOST=$(kubectl config view --raw --minify --flatten \
    --output 'jsonpath={.clusters[].cluster.server}')
vault write auth/kubernetes/config \
     token_reviewer_jwt="$SA_JWT_TOKEN" \
     kubernetes_host="$K8S_HOST" \
     kubernetes_ca_cert="$SA_CA_CRT" \
     issuer="https://kubernetes.default.svc.cluster.local"

只要没有问题,应该会得到以下信息作为vault write auth/kubernetes/config的结果。

Success! Data written to: auth/kubernetes/config

接下来创建一个角色。角色名称为hoge。

vault write auth/kubernetes/role/hoge \
     bound_service_account_names=vault-auth \
     bound_service_account_namespaces=default \
     policies=myapp-kv-ro \
     ttl=24h

此外,如果您更改了ServiceAccount的名称、Namespace或策略名称,请相应地修改并执行。

vault write auth/kubernetes/login role=hoge jwt=$SA_JWT_TOKEN iss=https://kubernetes.default.svc.cluster.local

将Vault的Secrets部署到EKS上。

使用SecretStore资源与Vault进行连接。

cat << EOF | kubectl apply -f -
 apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: vault-backend
 spec:
   provider:
     vault:
       server: "https://vault.mydomain.info"
       path: "kv"
       version: "v1"
       namespace: "default"
       auth:
         kubernetes:
           mountPath: "kubernetes"
           role: "hoge"
           serviceAccountRef:
             name: "vault-auth"
EOF

另外,如果將版本設置為”v2″,在創建ExternalSecret時可能會出現以下錯誤,導致Secret無法成功創建。我也遇到了這個問題,卡在這裡一段時間。

"cannot read secret data from Vault: Error making API request.\n\nNamespace: default\nURL: GET https://vault.mydomain.info/v1/kv/data/mysecret\nCode: 404.

因此,我参考了这篇文章并进行了v1的修改,以避免这个问题。

※补充:
通过GUI创建KV时成为v2版本,通过CLI创建也是v2版本。通过GUI创建的人需要选择v2版本或不指定版本。

确认状态。

$ kubectl get secretstore
NAME            AGE   STATUS
vault-backend   8s    Valid

另外,如果有设置错误等问题,就会出现以下情况。

$ kubectl get secretstore.external-secrets.io/vault-backend
NAME            AGE     STATUS
vault-backend   9m50s   InvalidProviderConfig

在这种情况下,如果忘记设置ServiceAccount,则external-secrets的Pod内会出现类似于Code: 500. Errors: * service account name not authorized的错误。请根据错误信息确认发生了什么。

接下来,将ExternalSecret作为Kubernetes的Secret资源进行导入。

cat <<EOF | kubectl apply -f -
 apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: vault-example
 spec:
   secretStoreRef:
     name: vault-backend
     kind: SecretStore
   target:
     name: vault-example-secret
   data:
   - secretKey: fuga
     remoteRef:
       key: mysecret
       property: password
EOF

spec.target.name是创建的Secret的名称,spec.data.secretKey是创建的Secret中的Key。spec.data.remoteRef用于设置Vault端的路径(去除SecretStore指定的路径部分)和Key-Value的key(属性)。

如果在创建上述资源后,状态变为SecretSynced,则表示一切正常。

$ kubectl get externalsecret
NAME            STORE           REFRESH INTERVAL   STATUS
vault-example   vault-backend   1h                 SecretSynced

可以察觉到已创建了Secret,并且可以从Vault中提取值。

$ kubectl get secret vault-example-secret -o jsonpath={.data.fuga} | base64 -d
himitsu
广告
将在 10 秒后关闭
bannerAds