Kubernetes secrets 加密处理的3种方式 | IDCF
共 24007字,需浏览 49分钟
·
2021-04-14 09:25
来源: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 两者的关系与 Deployment 和 Pod 之间的关系类似。
安装于集群侧的 controller 客户端工具 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
通过查看 kubeseal 的版本来确定是否安装成功:
$ kubeseal --version
kubeseal version: v0.14.1
apiVersion: 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 中以环境变量的形式引用上述生成的 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 状态,并查看环境变量(secret 是以环境变量的形式注入 pod 内的):
$ 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
说明 secret 注入成功。其他的 secret 类型,比如 tls、dockerconfigjson 等都可以用上面的方式进行使用。
最后就可以将包含 secret 信息且经过加密的文件 test-seal-secret.yaml 推送至版本管理系统,比如 GitHub。
二、Helm Secrets
Helm Secrets 是 Helm 的一个插件,用来对于 Helm Chart 中的敏感信息进行加密。
2.1 原理
Helm Secrets Plugin 可以使用多种加密方式来对敏感信息进行加解密(本文介绍 sops)。加密时使用helm secrets enc
命令对需要加密的文件内容进行加密;解密时helm secrets
使用dec
将加密内容进行解密,并添加在 values.yaml 文件中,后续的使用直接取用 values.yaml 文件中的值即可。
2.2 加密
helm secerts enc
对位于helm_vars
目录下的secrets
文件加密时,helm secrets plugin
会使用 public key 对内容进行加密。2.3 解密
helm secrets install/upgrade
命令对应用程序进行安装或更新的过程中,需要指定经过加密后的 secret 文件(-f 指定,通常位于 helm_vars
目录下),helm secrets plugin
会选择 private key 对加密的数据进行解密,解密后的数据在 values.yaml 文件中可找到,在 helm chart 的 template 目录下的 secret 文件只需要引用相关的值即可(.Values.xxx
)。2.4 安装
安装的前提条件:
helm(>2.3)
sops
sops 是一个加密文件的编辑器,支持 YAML、JSON、ENV、INI 和二进制格式,并使用 AWS KMS、GCP KMS、Azure Key Vault 和 PGP 进行加密。
PGP(Pretty Good Privacy)是一种常用的加密方式。在 1990s(1991 年)由 Phil Zimmermann 所开发,现在归属于 Symantec 公司,它是商业软件,需要付费才能使用。而 GPG(GNU Privacy Guard)是一种基于 Open PGP 标准的加密方式。它是开源且免费的。所以本文的演示将使用 GPG 的方式。
gpg --full-generate-key
并输入必要的参数即可生成 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: devops@xiaomage.com
Comment: gpg key generation
You selected this USER-ID:
"xiaomage (gpg key generation) <devops@xiaomage.com>"
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) <devops@xiaomage.com>
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) <devops@xiaomage.com>
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) <devops@xiaomage.com>
sub rsa4096 2021-01-08 [E] [expires: 2022-01-08]
在用 sops 加密数据之前,先创建一个.sops.yaml
文件,写一个加密的规则,比如只加密 key 是 username 和 password 的敏感信息,而且需要指定 gpg 的 fingerprint。
cat << EOF > .sops.yaml
creation_rules:
- encrypted_regex: '^(username|password)$'
pgp: 'B1C77B2CCF5575FAF0DA6B882CA51446C98C9D85'
EOF
接着,将下面的敏感信息写入一个yaml文件:
cat << EOF > secrets.yaml
username: xiaomage
password: passw0rd
EOF
现在就可以用 sops 来加密数据了:
$ 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
执行以下命令安装 helm-secrets plugin:
$ helm plugin install https://github.com/zendesk/helm-secrets
首先,创建一个演示用的 helm chart,并在根目录下创建一个名为helm_vars的目录,用来存放用来加密的 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
将需要加密的信息写入secrets.yaml文件里面,比如:
cat << EOF > secrets.yaml
secret_data:
username: xiaomage
password: passw0rd
EOF
使用 helm secrets 来加密上述文件:
$ helm secrets enc secrets.test.yaml
Encrypting secrets.test.yaml
Encrypted secrets.test.yaml
查看加密后的文件内容:
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
需要注意的是,此时只是加密了需要加密的内容,但是这些内容该怎么用呢?其实也比较简单,就是:正常用。
举例来说,在 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
就是来自于上面helm_vars
目录下的加密文件。......
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
envFrom:
- secretRef:
name: test
......
接下来可以使用下面命令将前文的 chart 进行安装:
$ 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 是以环境变量的形式注入到 pod 里面的,可以查看进行验证:
$ kubectl -n test exec -it test-devsecops-7876ffc8b7-967xr sh
$ env | grep -E 'username|password'
username=xiaomage
password=passw0rd
可以看到 secret 解密成功,并成功注入 pod。
最后就可以将加密后的文件上传至源码管理系统了(比如用 git push 至 GitHub)。
三、Kamus
Kamus 是一款开源的采用零信任的 secret 加解密方式为 Kubernetes 应用程序安全处理 secrets 的工具。Kamus 提供两种方式来对 Kubernetes secrets 进行加密,即
使用 init container:将 secrets 加密后存储为 Kubernetes configmap,然后在应用程序的部署中添加一个 init container,通过 init container 将 configmap 中的加密数据解密至指定文件,应用程序再从此文件读取解密后的 secret 信息。
使用 KamusSecret:创建一个 KamusSecret 对象(充分利用了 Kubernetes 的扩展机制),此对象包含了加密之后的数据,在此对象创建的过程中,会创建一个同名的 secret 对象,secret 中的数据是经过 base64 编码之后的数据,可以被 Kubernetes 其他对象引用。
3.1 原理
Kamus 同样是使用客户端工具 kamus-cli 对于需要加密的数据进行加密(区别于 Sealed Secrets 的是,Kamus 的数据需要单独逐一加密,而不是全部存放在 secret 文件里面一次性加密,下面会看到),下面依旧以加解密的方式分别阐述。
3.2 加密
用 kamus-cli 对需要加密的数据逐一加密时,位于集群上的 controller 会选择 public key 并使用 kamus encryptor 对数据进行加密,随后将加密内容保存在 configmap 中(以 init container 的方式使用)或者 KamusSecret 中(以 secret 的方式使用)。
3.3 解密
当将加密后的内容进行部署时(kubectl apply/create),位于集群上的 controller 会选择 private key,并使用 kamus decryptor 对于数据进行解密。
如果是使用 KamusSecret 存储的数据,则 controller 将生成一个与 KamusSecret 对象同名的 Secret 对象,此 Secret 中存放由经过 base64 编码后的信息;
如果是使用 configmap 的形式,则此 configmap 会以 volume 的形式挂载到 pod 内,随后在 pod 中使用 init container 来调用 kamus-decryptor 的 api 将加密信息解密,并存放到指定的文件中,随后 pod 内的应用程序可以通过读取此文件内容来获取敏感信息。
KamusSecret 和 Secret 两者的关系与 Deployment 和 Pod 之间的关系类似。
3.4 安装
kamus 的安装包括 controller 和客户端工具 kamus-cli。
安装 controller
$ helm repo add soluto https://charts.soluto.io
$ helm install kamus --namespace kamus soluto/kamus
检查 pod 状态:
$ 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
安装客户端
$ npm install -g @soluto-asurion/kamus-cli
检查安装是否成功:
$ kamus-cli -V
0.3.0
3.5 使用
下面分别以init container和 KamusSecret 的方式来演示使用方式。
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 这个值经过加密后的值,用同样的方法,可以将 passw0rd 进行加密:
$ 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 这个值经过加密后的值,将上述两个加密后的值放在 configmap 中:apiVersion: v1
kind: ConfigMap
metadata:
name: kamus-encrypted-secrets-cm
namespace: test
data:
username: CxujUBK4jgdjt+wP5mXyDA==:+CoXRDJVtW3EZ2FpterVTA==
password: ChmNEPM8Nj7Huh1YwO5xOA==:r9MHhEyTIEaQ4hw837lA9w==
接下来,将上述 configmap 以 volume 的方式挂在到 pod 中,随后使用 init container 来解密数据,且将数据存放在一个 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 的地址,可根据自己的安装情况自行指定。
$ 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
进入 pod 查看解密后的数据:
$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
创建 KamusSecret 对象:
$ 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 生成了一个和自己同名的 secret,接着查看 secret 的内容:
apiVersion: v1
data:
password: cGFzc3cwcmQ=
username: eGlhb21hZ2U=
解码后为:
password: passw0rd
username: xiaomage
此时,可以像正常方式在 pod 中引用此 secret(像前文的 Sealed Secret 章节所演示的一样,再次不再赘述)。
最后就可以将加密后的文件上传至源码管理系统了(比如 git push 至 GitHub)。
写在最后
其实,安全处理 Kubernetes secret 的方式不仅仅上面的三种形式,还可以利用诸如 vault 等来管理应用程序部署中的敏感信息。但是不同的工具、不同的方式,其背后的思想和思路都差不太多。
总结起来,差不多有以下几点:
充分利用 Kubernetes 的扩展能力,自定义一个 secret 对象用来存储加密后的数据(诸如 Sealed Secrets 的 SealedSecret 对象;Kamus 的 KamusSecret),让这些扩展的对象生成对应的 secret 对象,再正常使用 secret 对象。
利用外部的敏感信息管理工具(诸如 vault 或者其他云厂商提供的服务)。
sops 是一种使用方便的加密工具,也是上述工具实现加密的秘密武器。
没有一劳永逸的安全,只有永不止步的行动。任何改变都是重要的。
参考
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月,【冬哥有话说】DevOps之庖丁解牛,拆解DevOps的工具及具体实战。今晚8点,第②期,薄涛和魏振龙两位老师分享《持续交付中的版本管理与基于Azure DevOps扩展框架的插件开发》,关注公众号回复“解牛”可获取直播地址