Kubernetes の secrets や configMap を管理する際に気をつけることがいくつかあります。
例えば manifests に base64 エンコードされた値が記載されてしまうため、GitOps の場合はリポジトリに平文同然の manifests が置かれることになり、あまりセキュアとは言えません。
この場合 sealed-secrets などを使って暗号化するという方法などがあります。
sealed-secrets も扱いやすいのですが、 kubectl get secrets -o yaml
などすると平文で取得できてしまいます。
「暗号化したまま保存し Pod のアプリケーションから平文の値を取得できるようにしたい」「できるだけ Kubernetes との連携が簡単なものを選びたい」というときに kamus が便利そうでした。
Install
helm でインストールします。また、CLIクライアントも合わせてインストールします。
$ helm repo add soluto https://charts.soluto.io
$ helm upgrade --install kamus soluto/kamus
$ npm install -g @soluto-asurion/kamus-cli
暗号化する
ここでは super-secret
という文字列を暗号化します。
暗号化するエンドポイントとやり取りするために port-forward
しておきます。
$ export POD_NAME=(kubectl get pods --namespace default -l "app=kamus,release=kamus,component=encryptor" -o jsonpath="{.items[0].metadata.name}")
$ kubectl port-forward $POD_NAME 12345:9999
$ kamus-cli encrypt \
--secret super-secret \
--service-account kamus-sa \
--namespace default \
--kamus-url http://localhost:12345 --allow-insecure-url
[info kamus-cli]: Encryption started...
[info kamus-cli]: service account: kamus-sa
[info kamus-cli]: namespace: default
[warn kamus-cli]: Auth options were not provided, will try to encrypt without authentication to kamus
[info kamus-cli]: Successfully encrypted data to kamus-sa service account in default namespace
[info kamus-cli]: Encrypted data:
A0VrZdae6LQD+yOeXtBPhw==:Ge7M4yTZtaynIrmPtRhUng==
最後に出力された A0VrZdae6LQD+yOeXtBPhw==:Ge7M4yTZtaynIrmPtRhUng==
が super-secret
を暗号化したものになります。
これを configMap として保存し、アプリケーションから復号化してみます。
apiVersion: v1
kind: ConfigMap
metadata:
name: encrypted-secrets
data:
token: A0VrZdae6LQD+yOeXtBPhw==:Ge7M4yTZtaynIrmPtRhUng==
復号化してアプリケーションから利用する
復号化するためには Kamus Decrypt API を使用します。今回は soluto/kamus-init-container を initContainer で起動し configMap を VolumeMount します。
復号化した後は /secrets/config.json
に JSON 形式置くようになっています。これは soluto/kamus-init-container がやっているだけで、SA Token を使って Decrypt API に投げれば復号結果を入手できるので、アプリケーション内でこの処理をしても良さそうです。
apiVersion: v1
kind: ServiceAccount
metadata:
name: kamus-sa
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: kamus-sample-app
labels:
app: kamus-sample-app
spec:
template:
metadata:
labels:
app: kamus-sample-app
spec:
serviceAccountName: kamus-sa
automountServiceAccountToken: true
initContainers:
- name: "kamus-init"
image: "soluto/kamus-init-container:latest"
imagePullPolicy: IfNotPresent
env:
- name: KAMUS_URL
value: http://kamus-decryptor.default.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: app
image: alpine:latest
command: ["sleep", "300"]
imagePullPolicy: IfNotPresent
volumeMounts:
- name: decrypted-secrets
mountPath: /secrets
volumes:
- name: encrypted-secrets
configMap:
name: encrypted-secrets
- name: decrypted-secrets
emptyDir:
medium: Memory
apply して確認すると、ちゃんと復号化できていることが分かります。
$ kubectl apply -f deployment.yml
$ kubectl exec -it kamus-sample-app-5f8f4b7df9-mb6nm sh
/ # ls /secrets/
config.json
/ # cat /secrets/config.json
{
"token":"super-secret"
}
Decrypt API を叩いてアプリケーションから復号化する
kamus Decrypt API を使えばアプリケーションから直接復号化できると書きましたが、これは次のようにすることで可能です。
$ export TOKEN=`cat /var/run/secrets/kubernetes.io/serviceaccount/token`
$ export KAMUS_URL=http://kamus-decryptor.default.svc.cluster.local
$ curl -s -X POST -H "Content-Type: application/json" -H "Authorization: Bearer ${TOKEN}" $KAMUS_URL/api/v1/decrypt --data '{"data": "ctxOYCcBhJY3ldvX8a16wA==:36mZddDCKj5/dvtm/HrCgw=="}'
super-secret