Kubernetesのmanifestをgitで管理するとき、Secretの扱いが課題になりがちです。
基本的にデプロイに必要なKubernetesリソースのyamlは全てGitに管理したいですが、Secretリソースは機密情報がbase64エンコードまたは平文で保存されており、
KubernetesのSecretをサポートするプロジェクトとして、
が有名です。
External SecretsではSecretの内容をHashicorp VaultやAWS Secrets Managerといった外部のSecret Managerに管理します。ExternalSecretリソースに外部Secret Managerの参照を定義し、そこからSecretオブジェクトを生成します。ExternalSecretリソースのYAMLはgit管理可能です。
Sealed SecretsではSealedSecretリソースにクライアントサイドで暗号化したSecretの内容を設定し、サーバーサイドで復号してSecretオブジェクトを生成します。暗号化にはkubeseal CLIを使用します。SealedSecretリソースのYAMLはgit管理可能です。
どちらも汎用的にSecretを扱えますが、動機が"自動生成したパスワードをSecretに設定したい"だけであれば、
手間が大きく、やりたいことに対して複雑に感じます。
サーバーサイドでパスワードや証明書を自動生成したいだけであれば、Quarks Secretがシンプルです。
Quarks Secretの簡単な使い方を紹介します。
Note: QuarksはSuseが主に開発を進めている、Cloud FoundryのBOSHでデプロイしていたソフトウェアをKubernetesにデプロイするために変換するController群です。
その中でもQuarks SecretはBOSHで使われていた機密情報自動生成機能を移行したもので、この部分だけスタンドアローンで利用できます。
同様なツールとしてはsecretgen-controllerがあります。
Quarks Secretのインストール
インストールはHelmで行います。
helm repo add quarks https://cloudfoundry-incubator.github.io/quarks-helm/
helm install qsecret quarks/quarks-secret --namespace quarks --create-namespace --wait
インストールが完了したら、quarkssecrets.quarks.cloudfoundry.orgというカスタムリソースが出来ます。
$ kubectl get crd | grep quarks
quarkssecrets.quarks.cloudfoundry.org 2020-09-19T13:27:52Z
このQuarksSecretオブジェクトを作成するとSecretオブジェクトを自動生成できます。自動生成できるSecretの種類は
passwordcertificatetlssshrsabasic-authdockerconfigjsoncopytemplatedconfig
デフォルトではstaging Namespaceのみ監視対象になります。
他のNamespaceも対象としたい場合は、Namespaceリソースのquarks.cloudfoundry.org/monitoredラベルにqsecret-quarks-secret(Helmでインストールした場合のデフォルト値)を設定する必要があります。
この設定がないとQuarksSecretオブジェクトを作成してもSecretは生成されません。
ここではdefault Namespaceにそのラベルを設定します。
kubectl patch namespace default --type=json -p '[{"op": "add", "path": "/metadata/labels", "value": {"quarks.cloudfoundry.org/monitored": "qsecret-quarks-secret"}}]'
Passwordの自動生成
次のdemo.ymlを作成します。
apiVersion: quarks.cloudfoundry.org/v1alpha1
kind: QuarksSecret
metadata:
name: demo
spec:
type: password
secretName: demo
$ kubectl apply -f demo.yml
quarkssecret.quarks.cloudfoundry.org/demo created
QuarksSecretリソースを作成するとdemoという名前のSecretができていることがわかります。
$ kubectl get secret,quarkssecret demo
NAME TYPE DATA AGE
secret/demo Opaque 1 5s
NAME COPIED GENERATED
quarkssecret.quarks.cloudfoundry.org/demo true true
生成されたSecretの値(password)を確認します。
$ kubectl get secret demo -o go-template='{{index .data "password" | base64decode}}'
niBhcLk5NKBOSDeA6cS1APeKcmIFppNGKcyCRuZCq8lnJf9PfMbhC15ezuAbeTfE
Passwordのローテート
Passwordはローテート可能です。次のようなrotate.ymlを作成します。
apiVersion: v1
kind: ConfigMap
metadata:
name: rotate
labels:
quarks.cloudfoundry.org/secret-rotation: "true"
data:
secrets: '["demo"]'
$ kubectl apply -f rotate.yml
quarkssecret.quarks.cloudfoundry.org/demo created
生成されたpasswordの値を確認します。
$ kubectl get secret demo -o go-template='{{index .data "password" | base64decode}}'
57BzENCpDmqFBVrnxOoMnlZzEvfA0iGY2S5cmV9cJVK2p4cwWbtgCY35wmKOcXGN
Basic Authの自動生成
ユーザー名、パスワードをまとめて生成できます。次のauth.ymlを作成します。
apiVersion: quarks.cloudfoundry.org/v1alpha1
kind: QuarksSecret
metadata:
name: auth
spec:
type: basic-auth
secretName: auth
$ kubectl apply -f auth.yml
quarkssecret.quarks.cloudfoundry.org/auth created
kubernetes.io/basic-auth TypeのSecretが生成されます。
$ kubectl get secret,quarkssecret auth
NAME TYPE DATA AGE
secret/auth kubernetes.io/basic-auth 2 33s
NAME COPIED GENERATED
quarkssecret.quarks.cloudfoundry.org/auth true true
生成されたSecretの値(username及びpassword)を確認します。
$ kubectl get secret auth -o go-template='{{index .data "username" | base64decode}}'
wfdfcank0141g7B6hCbl7953uzFWofYp8RKQAgsEyVbEeSPPCj2Q46wf5lGHfbWY
$ kubectl get secret auth -o go-template='{{index .data "password" | base64decode}}'
ZtsfXWGfqqDHlMjBJchLHDER3uN2m8nvmq2P6qon5zQvVkWoG5tYAtQQbrH9V0Iq
アプリケーションのサンプル
実用例として、gitで管理するアプリケーションのマニフェストサンプルを示します。
hello.ymlを作成します。このファイルはgitで管理できますし、これ以外のファイルは管理する必要がありません。
apiVersion: v1
kind: Namespace
metadata:
name: demo
labels:
quarks.cloudfoundry.org/monitored: qsecret-quarks-secret
---
apiVersion: quarks.cloudfoundry.org/v1alpha1
kind: QuarksSecret
metadata:
name: hello-password
namespace: demo
spec:
type: password
secretName: hello-password
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: demo
name: demo
labels:
app: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: hello
image: ghcr.io/making/hello
env:
- name: SPRING_SECURITY_USER_PASSWORD
valueFrom:
secretKeyRef:
name: hello-password
key: password
---
apiVersion: v1
kind: Service
metadata:
namespace: demo
name: demo
labels:
app: demo
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8080
selector:
app: demo
type: ClusterIP
kubectl apply -f hello.yml
ServiceをPort Forwardします。
kubectl port-forward -n demo service/demo 8080:80
生成されたパスワードを取得します。
PASSWORD=$(kubectl get secret -n demo hello-password -o go-template='{{index .data "password" | base64decode}}')
パスワード有無でそれぞれアクセスします。
$ curl localhost:8080 -v
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 401
< Set-Cookie: JSESSIONID=C88CB80B86495DBF086159105F93FBE2; Path=/; HttpOnly
< WWW-Authenticate: Basic realm="Realm"
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Sat, 19 Sep 2020 14:40:55 GMT
<
{"timestamp":"2020-09-19T14:40:55.679+00:00","status":401,"error":"Unauthorized","message":"","path":"/"}
$ curl localhost:8080 -u user:${PASSWORD} -v
> GET / HTTP/1.1
> Host: localhost:8080
> Authorization: Basic dXNlcjpHY1hSall6c2J5MzRCaENGb3FUclk0Nm94VjhicFQ5UUNJRFBIUkY2U05ROXFBb0RHSm9lazRlUE1JblVsblZO
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200
< Set-Cookie: JSESSIONID=6AE2EF09AEFA5F44EA4C32F4A3A5A9D6; Path=/; HttpOnly
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 15
< Date: Sat, 19 Sep 2020 14:52:04 GMT
<
Hello World!
Quarks Secretで簡単にサーバーサイドでパスワードを自動生成できました。
シンプルな例であれば、SecretだけGitとは別に管理するという面倒くさいことをしなくても済みます。