機密資料
有兩種機密資料,解密的地點不同,但可以放在同一份 secrets.ejson 裡。
- registry 密碼,
docker login要用的,由 kran 在你的機器上解密。 - 應用程式的機密資料,由 krane 在叢集裡解密。
ejson
ejson 只加密 JSON 檔裡的值、不加密鍵,所以整份檔案可以進版控。
任何人都能用檔案裡附的公鑰新增一筆值,但只有私鑰讀得出來。以底線開頭的鍵維持明文,_public_key
和 _type 因此看得見。
$ ejson keygen
Public Key:
8b0a1f5c9d3e47a2b6c8d0e2f4a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2c
Private Key:
b1d3f5a7c9e1b3d5f7a9c1e3b5d7f9a1c3e5b7d9f1a3c5e7b9d1f3a5c7e9b1d3
$ ejson encrypt config/deploy/secrets.ejson
Wrote 842 bytes to config/deploy/secrets.ejson
ejson keygen 會把私鑰寫進 /opt/ejson/keys,ejson decrypt 也是去那裡找;EJSON_KEYDIR 可以改
這個目錄。
registry 密碼
只有要讓 kran 自己登入時才需要;不設 registry.username 和 registry.password,推送就用 docker
已有的登入。
可以是純字串,也可以是任何 ERB 運算式,因為這個檔案在解析成 YAML 之前會先跑 ERB:
registry:
password: <%= ENV["GITHUB_TOKEN"] %>
registry:
password: <%= %x(aws ecr get-login-password --region us-east-1).strip %>
registry:
password: <%= %x(op read op://deploy/ghcr/token).strip %>
也可以是解密後 secrets.ejson 裡的一條點分路徑:
registry:
password:
ejson: registry.password
kran 會對 krane.secrets(預設 config/deploy/secrets.ejson)跑 ejson decrypt,再沿路徑取值,
用的是你機器上的 ejson 執行檔與私鑰。
$ kran deploy
ERROR: registry.password not found in config/deploy/secrets.ejson
# 連檔案都沒設定時
$ kran deploy
ERROR: registry.password refers to ejson but krane.secrets is not set and config/deploy/secrets.ejson does not exist
krane 怎麼讀 secrets.ejson
secrets.ejson 就放在模板目錄裡,跟 manifest 擺在一起:
config/
├── kran.yml
└── deploy/
├── deployment.yaml.erb
└── secrets.ejson
檔名必須剛好是 secrets.ejson,kran 用一個獨立的 -f 傳進去:
krane render -f config/deploy ... \
| krane deploy storefront prod-east \
-f config/deploy/secrets.ejson -
krane 只看一個頂層鍵,kubernetes_secrets 底下每一項都會變成一個 Secret:
{
"_public_key": "8b0a1f5c9d3e47a2b6c8d0e2f4a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2c",
"kubernetes_secrets": {
"rails-app": {
"_type": "Opaque",
"data": {
"SECRET_KEY_BASE": "EJ[1:...]",
"DATABASE_URL": "EJ[1:...]"
}
}
}
}
- 項目名稱就是 Secret 的名稱。
_type原封不動寫進 Secret 的type欄位。data底下每個值先解密,再做 base64 編碼。
envFrom:
- secretRef:
name: rails-app
kubernetes_secrets 以外的鍵 krane 不理,也不會在叢集裡產生東西。
ejson-keys Secret
krane 讀的是目標 namespace 裡一個叫 ejson-keys 的 Secret:每個 data 鍵是公鑰,值是對應的私鑰。
$ kubectl --context prod-east --namespace storefront \
> create secret generic ejson-keys \
> --from-literal=8b0a1f5c9d3e47a2b6c8d0e2f4a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2c=<私鑰>
secret/ejson-keys created
# 沒有它的話,krane 在套用任何東西之前就會停住
$ kran deploy
Secret `ejson-keys` not found in namespace `storefront`
要用
create,絕對不要用apply。 krane 會 prune 掉不是自己這次部署產生的資源,而帶著 last-applied-configuration annotation 的 Secret 就會被當成那一類:它會在下次部署時被刪掉,之後 每次部署都解不開密。
用同一份檔案產生 image pull secret
kubernetes.io/dockerconfigjson 這個 Secret 型別,用單一個 .dockerconfigjson 鍵裝一份 docker
設定檔。
{
"_public_key": "8b0a1f5c9d3e47a2b6c8d0e2f4a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2c",
"kubernetes_secrets": {
"registry": {
"_type": "kubernetes.io/dockerconfigjson",
"data": {
".dockerconfigjson": "EJ[1:...]"
}
}
}
}
被加密的那個值,是整份 docker 設定序列化成的一個 JSON 字串:
{"auths":{"ghcr.io":{"username":"acme-deploy","password":"ghp_notarealtoken","auth":"YWNtZS1kZXBsb3k6Z2hwX25vdGFyZWFsdG9rZW4="}}}
auth 是 username:password 的 base64:
$ printf '%s' 'acme-deploy:ghp_notarealtoken' | base64
YWNtZS1kZXBsb3k6Z2hwX25vdGFyZWFsdG9rZW4=
跑過 ejson encrypt 之後,krane 每次部署都會建出這個 Secret:
spec:
imagePullSecrets:
- name: registry
containers:
- name: web
image: <%= image %>
一組密碼兩邊共用
把密碼當成同一個 Secret 裡的普通鍵,再讓 kran 指過去:
{
"_public_key": "8b0a1f5c9d3e47a2b6c8d0e2f4a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2c",
"kubernetes_secrets": {
"registry": {
"_type": "Opaque",
"data": {
"password": "EJ[1:...]"
}
}
}
}
registry:
username: acme-deploy
password:
ejson: kubernetes_secrets.registry.data.password
kran 在本機解密拿去 docker login,krane 則在 namespace 裡建一個叫 registry 的 Opaque Secret。
不想要那個 Secret 的話,把這個鍵移出 kubernetes_secrets:
{
"_public_key": "8b0a1f5c9d3e47a2b6c8d0e2f4a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2c",
"registry": {
"password": "EJ[1:...]"
}
}
registry:
password:
ejson: registry.password
檔案仍然是合法的 ejson,多出來的那個鍵只給 kran 用。
不會出現在輸出裡的資料
$ kran deploy --dry-run
printf '%s' '[REDACTED]' | docker login ghcr.io -u acme-deploy --password-stdin
真正的密碼是寫進 docker login 的標準輸入,所以不會出現在 process 列表、shell 歷史或 CI log 裡。