Kubernetes secrets 加密處理的3種方式 | IDCF

來(lái)源:DevSecOps SIG 作者:小馬哥
前 言
一、Sealed Secrets

$ kubectl -n kube-system get secret | grep sealed-secret
sealed-secrets-controller-token-qv2n5 kubernetes.io/service-account-token 3 6d4h
sealed-secrets-key2l7vf kubernetes.io/tls 2 6d4h
SealedSecrets 和 Secret 兩者的關(guān)系與 Deployment 和 Pod 之間的關(guān)系類(lèi)似。
安裝于集群側(cè)的 controller 客戶(hù)端工具 kubeseal
$ kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.14.1/controller.yaml$ kubectl -n kube-system get pods | grep seal
sealed-secrets-controller-64b74f67b-4wtj7 1/1 Running 0 153m$ brew install kubeseal
通過(guò)查看 kubeseal 的版本來(lái)確定是否安裝成功:
$ kubeseal --version
kubeseal version: v0.14.1apiVersion: v1
data:
username: eGlhb21hZ2U=
password: cGFzc3cwcmQ=
token: MWU0ZGdyNWZncmgzcmZmZ3JodG9ubmhyaHI=
kind: Secret
metadata:
name: seal-test-secret
namespace: test
type: Opaque$ kubeseal < test-secret.yaml > test-seal-secret.yaml{
"kind": "SealedSecret",
"apiVersion": "bitnami.com/v1alpha1",
"metadata": {
"name": "seal-test-secret",
"namespace": "test",
"creationTimestamp": null
},
"spec": {
"template": {
"metadata": {
"name": "seal-test-secret",
"namespace": "test",
"creationTimestamp": null
},
"type": "Opaque"
},
"encryptedData": {
"password": "AgCHLFSlGFpX2B9QDhWbMTfT83aopXMisR5XnUPZNcbvvnQzqgyG8fBVknT8LCNF5ExtUCCcNLsvWRrZY+9BJqf5dlBl6DkLV1acuPicP0vuGaUQwmc5BY/5Bj53Oj9uMYLNdVHoQ3E6kQgeJPa5v4rvwRXsB0EneYPcT88KyMg+tn4OY9JH+hpg2XMXZudyyZsocE852J5nfN4P7WZQYaG2eIBqRSQvQXUflQQvZ5wBCkTvmaZYfxz+Lxuf3wDWdSlPjcgSVnn5tWNP7u0ErdVy8LwTL5HzJdcoSviDysTq3VVA8W9Nmn0CM3QS0R0Nbi3JfalUdxfBMK+yb7t6Z72oJyoxGfCa07iKnkM37SSw4vl1nXiYy3FMWuzDtWLVk6XzjBZR2ChoeClqbGDg8KeSWZg/rO3Xku98vCmCa004OetJKBMc9Db3q+gX53ThdU70VvRol9rLPFBPHB8NTjD+Bu0Ss4XzIzZzi8J+Ov5xE7G8LnPLSZtZQyD/qGZK4n7pU1YNLROJ+fz1W5edPdpb5szUOqs1bpFfGleUiPZo1sGA0f1EsDvJShptgtT44YzGRkkgrP1LGp2AVIpnt9meE5WNCoSEPZJVx7wWMV9CHMOyyUi8zi+oG/S2NkI3rc2sC8AFp0DqP9m/HaX1GG+6vw9oHAbhxpR4v3mDyBIq+/8UEMtkybIEDQGHqyQ5CfRow+A80cA4Hw==",
"token": "AgBRi4NZunaJtHyP5aAoWmGtEXBipbFIb/n4ep8wdg+eka5xbDeLZwNCLofbUL+u0pP/CHSJeWl62mVPJhZdOKE+Su0b8a78im0+xsochaMQf0AI1GGL/Fo08HI8paP8k404gwAtonocIFSis3YooU0nyVD+lYH+k09FGABI+RmVLc2XkuIr96TTL4xsdSM5L0Ks2SFQKcQ42JfFWtNdXz6lr/IODsZop0/xAk6ffbsGGmCUjwusUU3Wp0MR25ntYT8ySuO6W7xkfGozEFzztteBJs28SHLf5HUi6BbYVnsZibrFF3BZP5aNnBg2TIgo3+dbX6EPHM904By3Z9XTBxsQfH6p1VoyUf0EGKZnUnJFezFtN9m2tyKbV/Z/5vCh9kVp6Yn/BE/AwGAH7kqqjPtHTnZiq+Xy1UwV4/eHkxGAvSAR3Z6wTQCt/rwqGrQi2eGpIcyjxTwlPYaVjfx3L+1tnBR966lGLnhwX0I6b6whXAm3hRb1AhYFnuyF/CoG/PEmQsMU5GfkroQkb5LL+UeCYKbjvMvgCe2hFxfh3dcGJ9E3dad9W0rSKrPd5t/dR1kDtItHau36+G9PSVyqRD1yt0MS2vLLUQu7t2RhiIPrl20fkbnum9JAfmLlgliHIiQPHASL32CXB6EzsgqRX6w8TmWNOSvlR7LU8JZtd4Gmiw9wGBh7JEGodkaH6lc5ndQluykC18RUXtuLft+S4dnQCApHX6FoIGZjug==",
"username": "AgCgBQc/fhGqB0YBGfXzhybC6YXJeLkOZyi7Z7Y+HjfnYSg4Q/Zh8Kn7UEbq9CwEl+CtagARjKmLfhIcAqFWS8+h8j4A2xNq7gzLnv+eCo0vFDPTddDVvdb6ixmRvF5rzD1gZ2vxWzlWVqk7x0wt8wCE90S0yu40j+JOaqH35Ir3kb4NgTMXk6Yqlidw06r3P2cqbZ0jBleOFf5eRfiu0ZquU5PJ/J7t9Pecx9S8mlitTtFPlvpVprNPB+XPSz2uwcwNW9i5OBUgR3PXsOjILLog8SiWYyk7bHaWnJtZ+JVEi9isy4EiwyrDY5kHRK2kB9Nnf6a9zz2krP7W+w9a3qXJkv8GP9D2+FN9Pj+2WP4r0hz7JL0i5q9bcc5HgBKP946u87z2lEjv2ioUAghaG/zwol3q+tKv0i6pPe0guGRCdpMlXa1Z1deOBJvxJXanTrIwi7dVc/bCsRGMRyYwD6hWhe1JjxgBjc/YbbBj8JJVdHrc2tGYFBU9qG2Kv3cAZMRrMXvKUkTK8JiMVzN0/DHEtdNv1PW4U3hlAqt5b62WahyzdHNVqHycwe+Ogz0BfTdohlxftv5qQYx0SEynXaIY+WltRnCnYrY1Kg1/DmsWYCGy++TO+6cEEwISPe/FM1peidsXVf5S3DCUQWE6aMK/6XDzukZoTjor/8JPkHc56Pk1Paty0yrP+YdL5R5m3IERzHoD"
}
}
}$ kubectl -n test apply -f test-seal-secret.yaml接著查看 SealedSecret 和 secret:
$ kubectl -n test get SealedSecret,secret
sealedsecret.bitnami.com/seal-test-secret 4s
secret/seal-test-secret Opaque 3 3s在如下的 Deployment 中以環(huán)境變量的形式引用上述生成的 secret:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: devops
name: devops
spec:
replicas: 1
selector:
matchLabels:
app: devops
template:
metadata:
labels:
app: devops
spec:
restartPolicy: Always
containers:
- name: devops
image: dllhb/devopsday:v0.6
imagePullPolicy: Always
envFrom:
- secretRef:
name: test-secret
ports:
- containerPort: 9999
name: http
protocol: TCP使用下面命令部署:
$ kubectl -n test apply -f test-deploy.yaml
deployment.apps/devops created查看 pod 狀態(tài),并查看環(huán)境變量(secret 是以環(huán)境變量的形式注入 pod 內(nèi)的):
$ kubectl -n test get pods
devops-b48df6659-gmjtr 1/1 Running 0 21s
$ kubectl -n test exec -it devops-b48df6659-gmjtr sh
env | grep -E "username|password|token"
username=xiaomage
token=1e4dgr5fgrh3rffgrhtonnhrhr
password=passw0rd說(shuō)明 secret 注入成功。其他的 secret 類(lèi)型,比如 tls、dockerconfigjson 等都可以用上面的方式進(jìn)行使用。
最后就可以將包含 secret 信息且經(jīng)過(guò)加密的文件 test-seal-secret.yaml 推送至版本管理系統(tǒng),比如 GitHub。
二、Helm Secrets
Helm Secrets 是 Helm 的一個(gè)插件,用來(lái)對(duì)于 Helm Chart 中的敏感信息進(jìn)行加密。
2.1 原理
Helm Secrets Plugin 可以使用多種加密方式來(lái)對(duì)敏感信息進(jìn)行加解密(本文介紹 sops)。加密時(shí)使用helm secrets enc 命令對(duì)需要加密的文件內(nèi)容進(jìn)行加密;解密時(shí)helm secrets使用dec將加密內(nèi)容進(jìn)行解密,并添加在 values.yaml 文件中,后續(xù)的使用直接取用 values.yaml 文件中的值即可。
2.2 加密

helm secerts enc 對(duì)位于helm_vars目錄下的secrets文件加密時(shí),helm secrets plugin會(huì)使用 public key 對(duì)內(nèi)容進(jìn)行加密。2.3 解密

helm secrets install/upgrade命令對(duì)應(yīng)用程序進(jìn)行安裝或更新的過(guò)程中,需要指定經(jīng)過(guò)加密后的 secret 文件(-f 指定,通常位于 helm_vars目錄下),helm secrets plugin 會(huì)選擇 private key 對(duì)加密的數(shù)據(jù)進(jìn)行解密,解密后的數(shù)據(jù)在 values.yaml 文件中可找到,在 helm chart 的 template 目錄下的 secret 文件只需要引用相關(guān)的值即可(.Values.xxx)。2.4 安裝
安裝的前提條件:
helm(>2.3)
sops
sops 是一個(gè)加密文件的編輯器,支持 YAML、JSON、ENV、INI 和二進(jìn)制格式,并使用 AWS KMS、GCP KMS、Azure Key Vault 和 PGP 進(jìn)行加密。
PGP(Pretty Good Privacy)是一種常用的加密方式。在 1990s(1991 年)由 Phil Zimmermann 所開(kāi)發(fā),現(xiàn)在歸屬于 Symantec 公司,它是商業(yè)軟件,需要付費(fèi)才能使用。而 GPG(GNU Privacy Guard)是一種基于 Open PGP 標(biāo)準(zhǔn)的加密方式。它是開(kāi)源且免費(fèi)的。所以本文的演示將使用 GPG 的方式。
gpg --full-generate-key并輸入必要的參數(shù)即可生成 key,如下:$ gpg --full-generate-key
gpg (GnuPG) 2.2.12; Copyright (C) 2018 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Please select what kind of key you want:
(1) RSA and RSA (default)
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0) 1y
Key expires at Sat Jan 8 12:12:10 2022 UTC
Is this correct? (y/N) y
GnuPG needs to construct a user ID to identify your key.
Real name: xiaomage
Email address: [email protected]
Comment: gpg key generation
You selected this USER-ID:
"xiaomage (gpg key generation) <[email protected]>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: key 8BA2C5716B5C007F marked as ultimately trusted
gpg: revocation certificate stored as '/root/.gnupg/openpgp-revocs.d/BCEB5797691E6C95E33A465D8BA2C5716B5C007F.rev'
public and secret key created and signed.
pub rsa4096 2021-01-08 [SC] [expires: 2022-01-08]
BCEB5797691E6C95E33A465D8BA2C5716B5C007F
uid xiaomage (gpg key generation) <[email protected]>
sub rsa4096 2021-01-08 [E] [expires: 2022-01-08]可以查看生成的private key和public key:
private key 查看
gpg -K(gpg --list-secret-keys)
/root/.gnupg/pubring.kbx
------------------------
sec rsa4096 2021-01-08 [SC] [expires: 2022-01-08]
BCEB5797691E6C95E33A465D8BA2C5716B5C007F
uid [ultimate] xiaomage (gpg key generation) <[email protected]>
ssb rsa4096 2021-01-08 [E] [expires: 2022-01-08]public key 查看
gpg -k(gpg --list-keys)
gpg: checking the trustdb
gpg: marginals needed: 3 completes needed: 1 trust model: pgp
gpg: depth: 0 valid: 2 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 2u
gpg: next trustdb check due at 2022-01-08
/root/.gnupg/pubring.kbx
------------------------
pub rsa4096 2021-01-08 [SC] [expires: 2022-01-08]
BCEB5797691E6C95E33A465D8BA2C5716B5C007F
uid [ultimate] xiaomage (gpg key generation) <[email protected]>
sub rsa4096 2021-01-08 [E] [expires: 2022-01-08]在用 sops 加密數(shù)據(jù)之前,先創(chuàng)建一個(gè).sops.yaml文件,寫(xiě)一個(gè)加密的規(guī)則,比如只加密 key 是 username 和 password 的敏感信息,而且需要指定 gpg 的 fingerprint。
cat << EOF > .sops.yaml
creation_rules:
- encrypted_regex: '^(username|password)$'
pgp: 'B1C77B2CCF5575FAF0DA6B882CA51446C98C9D85'
EOF接著,將下面的敏感信息寫(xiě)入一個(gè)yaml文件:
cat << EOF > secrets.yaml
username: xiaomage
password: passw0rd
EOF現(xiàn)在就可以用 sops 來(lái)加密數(shù)據(jù)了:
$ sops -e secrets.yaml
username: ENC[AES256_GCM,data:s6pInMY3eGM=,iv:5Q7JsntVoKjseD3ApWcgmYeedmGXj2A1/PyGCNFHGdE=,tag:vInq3NBLxvVWXsoVUD46Rw==,type:str]
password: ENC[AES256_GCM,data:Ua7de2w6Jgw=,iv:qYIjTW1D0dh20NA8FGu4XEGI16kvYGAWIk4iu3r/Gdg=,tag:b33tpsP1vCgqlpyCEDP88Q==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
lastmodified: '2021-02-06T12:08:57Z'
mac: ENC[AES256_GCM,data:QHHDRSO2PyJt0/OA67ex0R39gEjWEnwg0MSnBac8QtLNh3ncY+9D8IZw/WqVnbcaiPta2Pem96yJZTZP4pum9ZX446iRKldsAXNqS4+tmlfowpMWI+1DgOa1QCkhSDH9U/2URA1dzyn3cZLPFzb5Ai6YUEQ93sRjlPI+kHXl16c=,iv:jhFM/uJSeChikUv777qgYVDFCHQhQeXlUSjiHx5X8Ow=,tag:6QTo5CsXQoqr0fK1B947ug==,type:str]
pgp:
- created_at: '2021-02-06T12:08:51Z'
enc: |
-----BEGIN PGP MESSAGE-----
hQIMA/AYjF0OZ4PLAQ/+LRc5vgpRhOez8q9up8t+3OVM5QdnMwSYiuwLvjfInqqk
K19jUfUhwXDGGtSMlTotYlTWqWCiSm7sYeqFB0/Lx9lCZY5BhCrVnK7u7m8azpWU
osCQNmJehflqqnBmn82nblOGnDjM/FYkcnz4+NHUPNyYV5tWjzw9s8i/WhDeuNrf
IPnGKRCGJunWlHDP3yWMo7bnCNU/TmuRiSpf7lQLsp/U71M5t1X8RajatO7DPecq
caq3VZ+Ynx0Qcgyt+aHugZw5Sw9oFOT4WVqwLlC/NKvrjtY8pCQ1HtY5/agLHrDw
Hn2Phz1aQ+l4EAarphXCiYAFw/LHD2tisbQoApXe5tud9CjiyMu/14qhQalLgQxA
yGcMmhEH7Ke4bubaA0ZPo8hBXAkxfdeicSzB/e1IkUP4LtlQiwPldDcDShB6MROH
sK3RpELhSaNdfQZxqDVN0CgjRS0/AjboWejjrLQHD1hVcUDAU2WTyfvIaSxKpHIx
ONo5sTvzYOjU/BRTLn0EujRP414xadOtt+4gEQDrGacYAokuiK2ev0dinHo32EWY
j/vsb0o3whNRpBEGMZTUrl9HSkt58FQZsmu5JnL3ZYKiujHFoQS/aOcxD0slUxhC
PoCnce6PgmB78RHOLHaXkTrORc+6oMpCGN8/K1hjXE+eH/kk4jv8yVLwmbg9XjLS
XgFTcQYs6nVTSoWVea62kRN4qlC/XTJ6D91HXRX5UyB3qrZ3k+w9TOlM9quYYI/B
E0FqbFVSKT3ekPQqF91a7tV01FIxpfr4Mvzy2+8xsXiAQtDm52PSlk9eovkAMqU=
=nafU
-----END PGP MESSAGE-----
fp: B1C77B2CCF5575FAF0DA6B882CA51446C98C9D85
encrypted_regex: ^(username|password)$
version: 3.6.1可以看到 username 和 password 的值都被加密了。
2.5 安裝使用 Helm Secrets plugin
執(zhí)行以下命令安裝 helm-secrets plugin:
$ helm plugin install https://github.com/zendesk/helm-secrets
首先,創(chuàng)建一個(gè)演示用的 helm chart,并在根目錄下創(chuàng)建一個(gè)名為helm_vars的目錄,用來(lái)存放用來(lái)加密的 secret,如下所示:
$ tree
.
└── devsecops
├── Chart.yaml
├── charts
├── helm_vars
│ ├── secrets.yaml
│ ├── tls.crt
│ └── tls.key
├── templates
│ ├── NOTES.txt
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── secrets.yaml
│ ├── service.yaml
│ ├── serviceaccount.yaml
│ └── tests
│ └── test-connection.yaml
└── values.yaml將需要加密的信息寫(xiě)入secrets.yaml文件里面,比如:
cat << EOF > secrets.yaml
secret_data:
username: xiaomage
password: passw0rd
EOF使用 helm secrets 來(lái)加密上述文件:
$ helm secrets enc secrets.test.yaml
Encrypting secrets.test.yaml
Encrypted secrets.test.yaml查看加密后的文件內(nèi)容:
secret_data:
username: ENC[AES256_GCM,data:O/1pyNsL3Gc=,iv:HZ0MrGWaBxM37cIkp/JdsA5gRzw6aJFfBR19rno3h5I=,tag:2SiMs46lonnwECc8RHfT/Q==,type:str]
password: ENC[AES256_GCM,data:l15XlhZ4CsM=,iv:TMbV6+Rh2wGpMlHi7zJsHWM6IxMK2hBuMKsD82p8LiY=,tag:N4Kbftl//B1U2R9Khsduzg==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
lastmodified: '2021-02-07T06:19:15Z'
mac: ENC[AES256_GCM,data:dSXjEbKyBXVtqqSqshGXKUwDJcMVZrDf2GxFj0Oor3FDnNeS+bTY4Yubv1J0XlzU6yxO0Y87NzVN84unkF/Ph95JJV2opk6a0VTtaxKYOFUVneyY5WQ2glHEntX+aEq1lJkW1Sd34i/tvWeSABemIX4M2xcIOdIaCHgzk//vi9w=,iv:febius/ashzpdfKStJnQYVG/3FrVaYw102q87P9+egQ=,tag:/MUXrxhhOk6F8MS5wi7cLQ==,type:str]
pgp:
- created_at: '2021-02-07T06:19:08Z'
enc: |
-----BEGIN PGP MESSAGE-----
hQIMA7Oc9Dk1ccccARAAk7l23omTBRThnP7YC5AHdqzEO8Lapxc8ycWg5tsbM8eE
JaRFn4u3/+dQdpL6xlHv1wu0kmrZUgG8P41WmNDIKb2GtAlHQk+bjjV2IU0lCEj7
9UZXuAyhxHtVjHMBnzjppFh+6L0nH2K5AGaJWATwhO9M6CqmdCFnWJx7vAPfVQZF
Li9zqHK/YsbwgEWKs0bVvJ1btB7u4J5olKagYaZhaFaLzwjbtXmEqDUpfmPkooNr
7kPSVe8IMv/+MUaJY6uYNTBGWGrije4bY4A+hA/dUj4yN0gqqd796oc9GuN1MJSO
cAAoiTW2Vrw3OdyP7PIJVuxlS9gXnxtBOjo+p/Ij91ELq+DnC+6bGS9UIeF+Y1RD
h4siwx7I7hzk9tp+tXmsfdJit+usK6raPzYkcBgZVF8woKZsp2/qxloYyIFJ0sbK
MO67+dcAg+AX0M0/u33t1BAMTt/LJ1V2ZQUl+yzjRSKfZ2bCmd/skkE3VZx2ls44
LMngWZG7EzE39Onw9PB3ukXD7W+X+BThc2AJzVotrpDWbSI2/anoM9TMJjYfBjyU
xBuTuoviT5ENdm14bGomww9G+Ean3dyC2vWoHhY2KfuPlSxZ6mDIDm5zAPkZZl5A
QHjtaPT5qymPCpqy2X3yvK76zyJhfWYFIHguOy3JlDxiONC9DH1M6OVWoC69pPzS
XgEtII9fTeLXFU5Jy9gJa5nNKEQY87OkSXl3TFAiQ9OmgDbuUHZuvQzlecsKwR2s
mS7P7Z3Bb+eRakQ41Gzw4B7wmOrm2w0t4guVJDNIP/gQB0XBO1XZj4RsbMKn070=
=yQ2R
-----END PGP MESSAGE-----
fp: B1C77B2CCF5575FAF0DA6B882CA51446C98C9D85
encrypted_regex: ^(data|username|password|.dockerconfigjson|token|token1|key|crt)$
version: 3.6.1需要注意的是,此時(shí)只是加密了需要加密的內(nèi)容,但是這些內(nèi)容該怎么用呢?其實(shí)也比較簡(jiǎn)單,就是:正常用。
舉例來(lái)說(shuō),在 helm chart 中,正常用 secret 的方式如下:
apiVersion: v1
kind: Secret
metadata:
name: test
labels:
app: devsecops
type: Opaque
data:
{{- range $key, $value := .Values.secret_data}}
{{ $key }} : {{ $value | b64enc | quote}}
{{- end}}.Values.secret_data就是來(lái)自于上面helm_vars目錄下的加密文件。......
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
envFrom:
- secretRef:
name: test
......接下來(lái)可以使用下面命令將前文的 chart 進(jìn)行安裝:
$ helm secrets install test . --namespace test -f helm_vars/secrets.yaml -f values.yaml需要注意的是,在上面的命令中,必須指定helm_vars/secrets.yaml文件。接著查看生成的pod、secret:
$ kubectl -n test get pods,secret
pod/test-devsecops-7876ffc8b7-967xr 1/1 Running 0 6s
secret/test Opaque 2 8s由于上面的 secret 是以環(huán)境變量的形式注入到 pod 里面的,可以查看進(jìn)行驗(yàn)證:
$ kubectl -n test exec -it test-devsecops-7876ffc8b7-967xr sh
$ env | grep -E 'username|password'
username=xiaomage
password=passw0rd可以看到 secret 解密成功,并成功注入 pod。
最后就可以將加密后的文件上傳至源碼管理系統(tǒng)了(比如用 git push 至 GitHub)。
三、Kamus
Kamus 是一款開(kāi)源的采用零信任的 secret 加解密方式為 Kubernetes 應(yīng)用程序安全處理 secrets 的工具。Kamus 提供兩種方式來(lái)對(duì) Kubernetes secrets 進(jìn)行加密,即
使用 init container:將 secrets 加密后存儲(chǔ)為 Kubernetes configmap,然后在應(yīng)用程序的部署中添加一個(gè) init container,通過(guò) init container 將 configmap 中的加密數(shù)據(jù)解密至指定文件,應(yīng)用程序再?gòu)拇宋募x取解密后的 secret 信息。
使用 KamusSecret:創(chuàng)建一個(gè) KamusSecret 對(duì)象(充分利用了 Kubernetes 的擴(kuò)展機(jī)制),此對(duì)象包含了加密之后的數(shù)據(jù),在此對(duì)象創(chuàng)建的過(guò)程中,會(huì)創(chuàng)建一個(gè)同名的 secret 對(duì)象,secret 中的數(shù)據(jù)是經(jīng)過(guò) base64 編碼之后的數(shù)據(jù),可以被 Kubernetes 其他對(duì)象引用。
3.1 原理
Kamus 同樣是使用客戶(hù)端工具 kamus-cli 對(duì)于需要加密的數(shù)據(jù)進(jìn)行加密(區(qū)別于 Sealed Secrets 的是,Kamus 的數(shù)據(jù)需要單獨(dú)逐一加密,而不是全部存放在 secret 文件里面一次性加密,下面會(huì)看到),下面依舊以加解密的方式分別闡述。
3.2 加密

用 kamus-cli 對(duì)需要加密的數(shù)據(jù)逐一加密時(shí),位于集群上的 controller 會(huì)選擇 public key 并使用 kamus encryptor 對(duì)數(shù)據(jù)進(jìn)行加密,隨后將加密內(nèi)容保存在 configmap 中(以 init container 的方式使用)或者 KamusSecret 中(以 secret 的方式使用)。
3.3 解密

當(dāng)將加密后的內(nèi)容進(jìn)行部署時(shí)(kubectl apply/create),位于集群上的 controller 會(huì)選擇 private key,并使用 kamus decryptor 對(duì)于數(shù)據(jù)進(jìn)行解密。
如果是使用 KamusSecret 存儲(chǔ)的數(shù)據(jù),則 controller 將生成一個(gè)與 KamusSecret 對(duì)象同名的 Secret 對(duì)象,此 Secret 中存放由經(jīng)過(guò) base64 編碼后的信息;
如果是使用 configmap 的形式,則此 configmap 會(huì)以 volume 的形式掛載到 pod 內(nèi),隨后在 pod 中使用 init container 來(lái)調(diào)用 kamus-decryptor 的 api 將加密信息解密,并存放到指定的文件中,隨后 pod 內(nèi)的應(yīng)用程序可以通過(guò)讀取此文件內(nèi)容來(lái)獲取敏感信息。
KamusSecret 和 Secret 兩者的關(guān)系與 Deployment 和 Pod 之間的關(guān)系類(lèi)似。
3.4 安裝
kamus 的安裝包括 controller 和客戶(hù)端工具 kamus-cli。
安裝 controller
$ helm repo add soluto https://charts.soluto.io
$ helm install kamus --namespace kamus soluto/kamus檢查 pod 狀態(tài):
$ kubectl -n kamus get pods
NAME READY STATUS RESTARTS AGE
kamus-controller-55d959895d-hdklf 1/1 Running 0 9m30s
kamus-decryptor-5974b6ff47-5pkbr 1/1 Running 0 7m34s
kamus-decryptor-5974b6ff47-c4jt4 1/1 Running 0 7m34s
kamus-encryptor-f75dd457-fwp8r 1/1 Running 0 9m28s
kamus-encryptor-f75dd457-p9rnx 1/1 Running 0 9m29s安裝客戶(hù)端
$ npm install -g @soluto-asurion/kamus-cli檢查安裝是否成功:
$ kamus-cli -V
0.3.03.5 使用
下面分別以init container和 KamusSecret 的方式來(lái)演示使用方式。
1)以init container的方式
$ kubectl -n test create sa xiaomage$ kamus-cli encrypt --secret xiaomage --service-account xiaomage --namespace test --kamus-url http://localhost:9999 --allow-insecure-url
[info kamus-cli]: Encryption started...
[info kamus-cli]: service account: xiaomage
[info kamus-cli]: namespace: test
[warn kamus-cli]: Auth options were not provided, will try to encrypt without authentication to kamus
[info kamus-cli]: Successfully encrypted data to xiaomage service account in test namespace
[info kamus-cli]: Encrypted data:
CxujUBK4jgdjt+wP5mXyDA==:+CoXRDJVtW3EZ2FpterVTA==返回的CxujUBK4jgdjt+wP5mXyDA==:+CoXRDJVtW3EZ2FpterVTA== 就是 xiaomage 這個(gè)值經(jīng)過(guò)加密后的值,用同樣的方法,可以將 passw0rd 進(jìn)行加密:
$ kamus-cli encrypt --secret passw0rd --service-account xiaomage --namespace test --kamus-url http://localhost:9999 --allow-insecure-url
[info kamus-cli]: Encryption started...
[info kamus-cli]: service account: xiaomage
[info kamus-cli]: namespace: test
[warn kamus-cli]: Auth options were not provided, will try to encrypt without authentication to kamus
[info kamus-cli]: Successfully encrypted data to xiaomage service account in test namespace
[info kamus-cli]: Encrypted data:
ChmNEPM8Nj7Huh1YwO5xOA==:r9MHhEyTIEaQ4hw837lA9w==ChmNEPM8Nj7Huh1YwO5xOA==:r9MHhEyTIEaQ4hw837lA9w== 就是 passw0rd 這個(gè)值經(jīng)過(guò)加密后的值,將上述兩個(gè)加密后的值放在 configmap 中:apiVersion: v1
kind: ConfigMap
metadata:
name: kamus-encrypted-secrets-cm
namespace: test
data:
username: CxujUBK4jgdjt+wP5mXyDA==:+CoXRDJVtW3EZ2FpterVTA==
password: ChmNEPM8Nj7Huh1YwO5xOA==:r9MHhEyTIEaQ4hw837lA9w==接下來(lái),將上述 configmap 以 volume 的方式掛在到 pod 中,隨后使用 init container 來(lái)解密數(shù)據(jù),且將數(shù)據(jù)存放在一個(gè) config.json 文件中:
apiVersion: v1
kind: Pod
metadata:
namespace: test
name: kamus-pod
spec:
serviceAccountName: xiaomage
automountServiceAccountToken: true
initContainers:
- name: "kamus-init"
image: "soluto/kamus-init-container:latest"
imagePullPolicy: IfNotPresent
env:
- name: KAMUS_URL
value: http://kamus-decryptor.kamus.svc.cluster.local/
volumeMounts:
- name: encrypted-secrets
mountPath: /encrypted-secrets
- name: decrypted-secrets
mountPath: /decrypted-secrets
args: ["-e","/encrypted-secrets","-d","/decrypted-secrets", "-n", "config.json"]
containers:
- name: kamus-test
image: dllhb/devopsday:v0.6
imagePullPolicy: IfNotPresent
volumeMounts:
- name: decrypted-secrets
mountPath: /secrets
volumes:
- name: encrypted-secrets
configMap:
name: kamus-encrypted-secrets-cm
- name: decrypted-secrets
emptyDir:
medium: Memory需要注意的是,需要指定 kamus 的地址,即 decryptor 的地址,可根據(jù)自己的安裝情況自行指定。
$ kubectl -n test apply -f configmap.yaml
$ kubectl -n test apply -f kamus-deploy.yaml
$ kubectl -n test get pods,cm
NAME READY STATUS RESTARTS AGE
pod/kamus-pods 1/1 Running 0 4h3m
NAME DATA AGE
configmap/kamus-encrypted-secrets-cm 2 30s進(jìn)入 pod 查看解密后的數(shù)據(jù):
$kubectl -n test exec -it kamus-deploy sh
$ cat /secrets/config.json
{
"password":"passw0rd",
"username":"username"
}apiVersion: "soluto.com/v1alpha2"
kind: KamusSecret
metadata:
name: kamus-test
namespace: test
type: Opaque
stringData:
username: CxujUBK4jgdjt+wP5mXyDA==:+CoXRDJVtW3EZ2FpterVTA==
password: ChmNEPM8Nj7Huh1YwO5xOA==:r9MHhEyTIEaQ4hw837lA9w==
serviceAccount: xiaomage創(chuàng)建 KamusSecret 對(duì)象:
$ kubectl -n test apply -f kamus-secrets.yaml
查看生成的 KamusSecret 和 Secret:
$ kubectl -n test get KamusSecret,secret
NAME AGE
kamussecret.soluto.com/kamus-test 60s
NAME TYPE DATA AGE
secret/kamus-test Opaque 2 59s可以看到 KamusSecret 生成了一個(gè)和自己同名的 secret,接著查看 secret 的內(nèi)容:
apiVersion: v1
data:
password: cGFzc3cwcmQ=
username: eGlhb21hZ2U=解碼后為:
password: passw0rd
username: xiaomage此時(shí),可以像正常方式在 pod 中引用此 secret(像前文的 Sealed Secret 章節(jié)所演示的一樣,再次不再贅述)。
最后就可以將加密后的文件上傳至源碼管理系統(tǒng)了(比如 git push 至 GitHub)。
寫(xiě)在最后
其實(shí),安全處理 Kubernetes secret 的方式不僅僅上面的三種形式,還可以利用諸如 vault 等來(lái)管理應(yīng)用程序部署中的敏感信息。但是不同的工具、不同的方式,其背后的思想和思路都差不太多。
總結(jié)起來(lái),差不多有以下幾點(diǎn):
充分利用 Kubernetes 的擴(kuò)展能力,自定義一個(gè) secret 對(duì)象用來(lái)存儲(chǔ)加密后的數(shù)據(jù)(諸如 Sealed Secrets 的 SealedSecret 對(duì)象;Kamus 的 KamusSecret),讓這些擴(kuò)展的對(duì)象生成對(duì)應(yīng)的 secret 對(duì)象,再正常使用 secret 對(duì)象。
利用外部的敏感信息管理工具(諸如 vault 或者其他云廠商提供的服務(wù))。
sops 是一種使用方便的加密工具,也是上述工具實(shí)現(xiàn)加密的秘密武器。
沒(méi)有一勞永逸的安全,只有永不止步的行動(dòng)。任何改變都是重要的。
參考
https://github.com/bitnami-labs/sealed-secrets
https://github.com/Soluto/kamus
https://kamus.soluto.io/
https://blog.solutotlv.com/can-kubernetes-keep-a-secret/
https://en.sokube.ch/post/lightweight-kubernetes-gitops-secrets
https://github.com/mozilla/sops#showing-diffs-in-cleartext-in-git

4月,【冬哥有話說(shuō)】DevOps之庖丁解牛,拆解DevOps的工具及具體實(shí)戰(zhàn)。今晚8點(diǎn),第②期,薄濤和魏振龍兩位老師分享《持續(xù)交付中的版本管理與基于Azure DevOps擴(kuò)展框架的插件開(kāi)發(fā)》,關(guān)注公眾號(hào)回復(fù)“解?!笨色@取直播地址

