---
title: Quarks SecretでKubernetesのSecretをサーバーサイドで自動生成させる
tags: ["Kubernetes", "Quarks", "Quarks Secret"]
categories: ["Dev", "CaaS", "Kubernetes", "Secret", "QuarksSecret"]
date: 2020-09-19T14:55:56Z
updated: 2020-09-19T15:22:18Z
---

Kubernetesのmanifestをgitで管理するとき、`Secret`の扱いが課題になりがちです。
基本的にデプロイに必要なKubernetesリソースのyamlは全てGitに管理したいですが、
`Secret`リソースは機密情報がbase64エンコードまたは平文で保存されており、

Kubernetesの`Secret`をサポートするプロジェクトとして、

* [External Secrets](https://github.com/godaddy/kubernetes-external-secrets)
* [Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets)

が有名です。

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](https://quarks.suse.dev/docs/quarks-secret/)がシンプルです。

Quarks Secretの簡単な使い方を紹介します。

> Note: [Quarks](https://quarks.suse.dev)はSuseが主に開発を進めている、Cloud FoundryのBOSHでデプロイしていたソフトウェアをKubernetesにデプロイするために変換するController群です。<br>その中でもQuarks SecretはBOSHで使われていた機密情報自動生成機能を移行したもので、この部分だけスタンドアローンで利用できます。

同様なツールとしては[secretgen-controller](https://github.com/k14s/secretgen-controller)があります。

<!-- toc -->

### 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`の種類は

* `password`
* `certificate`
* `tls`
* `ssh`
* `rsa`
* `basic-auth`
* `dockerconfigjson`
* `copy`
* `templatedconfig`

です。個々の使い方は[ドキュメント](https://quarks.suse.dev/docs/quarks-secret/)と[サンプル](https://github.com/cloudfoundry-incubator/quarks-secret/tree/master/docs/examples)で学べます。

デフォルトでは`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`を作成します。

```yaml
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`を作成します。

```yaml
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`を作成します。

```yaml
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で管理できますし、これ以外のファイルは管理する必要がありません。

```yaml
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とは別に管理するという面倒くさいことをしなくても済みます。
