kubectl patch secret with bash gcloud identity-token, heredoc 和 printf

kubectl patch secret with bash gcloud identity-token, heredoc and printf

我想创建一个简单的 k8s cronjob 来保密地保持 gcp 身份令牌新鲜。

一个比较简单的问题,我无法解决

给出

kubectl patch secret token-test --type json -p=<< END
[
   {
     "op": "replace",
     "path": "/data/token.jwt",
     "value": "$(gcloud auth print-identity-token | base64 )"
   }
]
END

我希望将此应用于 kubectl patch secret token-test --type json -p=$(printf "'%s'" "$json")

我尝试了很多变体,奇怪的是,如果我粘贴 printf 的 heredoc insted 结果,它就会起作用。但是我所有的努力都失败了(还尝试了 json 单行文档)

$ kubectl patch secret token-test --type json -p=$(printf "'%s'" "$json")
error: unable to parse "'[{": yaml: found unexpected end of stream

而这确实有效:

printf "'%s'" "$json"|pbcopy 
kubectl patch secret sudo-token-test --type json -p '[{ "op": "replace","path": "/data/token.jwt","value": "ZX...Zwo="}]'
secret/token-test patched

我无法理解失败时有什么不同。我知道 bash 在字符串处理方面有点棘手,但我不确定这是 bash 问题还是 kubectl 中的问题。

$ json='[{ "op": "replace","path": "/data/token.jwt","value": "'"$(gcloud auth print-identity-token | base64 )"'"}]'
$ kubectl patch secret token-test --type json -p="$json"
secret/token-test patched

通过在 heredoc 中附加插值字符串,问题得到解决。 仍然不知道为什么其他方法失败了。

编辑:

最终结果是放弃 kubectl 并改用 curl,因为它更适合 docker 图像。


# Point to the internal API server hostname
APISERVER=https://kubernetes.default.svc

# Path to ServiceAccount token
SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount

# Read this Pod's namespace
NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)

# Read the ServiceAccount bearer token
TOKEN=$(cat ${SERVICEACCOUNT}/token)

# Reference the internal certificate authority (CA)
CACERT=${SERVICEACCOUNT}/ca.crt
SECRET_NAME=$(cat /etc/scripts/secret-name)

# Explore the API with TOKEN
curl --fail --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" --request PATCH ${APISERVER}/api/v1/namespaces/${NAMESPACE}/secrets/${SECRET_NAME}  \
   -H 'Accept: application/json' \
   -H "Content-Type: application/json-patch+json"  \
   -d '[{ "op": "replace","path": "/data/token.jwt","value": "'"$(gcloud auth print-identity-token | base64 -w0 )"'"}]' \
   --output /dev/null

正如我在 DazWilkin 回答中的评论,调用 gcloud auth print-identity-token 的用户实际上是 k8s 上的服务帐户,通过 GKE 上的工作负载身份验证到 GCP ServiceAccount。

我需要令牌才能在不存储实际凭据的情况下调用 AWS,方法是使用它通过 Workload Identity Federation 调用 AWS

这是一种略有不同的方法,但是,怎么样:

printf "foo" > test

kubectl create secret generic freddie \
--namespace=default \
--from-file=./test

kubectl get secret/freddie \
--namespace=default \
--output=jsonpath="{.data.test}" \
| base64 --decode
foo

X="$(printf "bar" | base64)"

kubectl patch secret/freddie \
--namespace=default \
--patch="{\"data\":{\"test\":\"${X}\"}}"

kubectl get secret/freddie \
--namespace=default \
--output=jsonpath="{.data.test}" \
| base64 --decode
bar

NOTE it's not a best practice to use your user (gcloud auth print-identity-token) credentials in this way. Service Accounts are preferred. Service Accounts are intended for machine (rather than a human) auth and they can be more easily revoked.

User credentials grant the bearer all the powers of the user account (and this is likely extensive).

There's a portable alternative in which you create a Kubernetes secret from a Service Account key:

kubectl create secret generic your-key \
--from-file=your-key.json=/path/to/your-key.json

There's a cool-kids who use GKE-mostly approach called Workload Identity