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