kubernetesのSecretsを公式ドキュメントから学ぶ

## はじめに

この記事で取り上げること

- k8sのSecrets機能について
- 最低限の使い方

取り上げないこと

- Kustomizeを使った生成
- Secretsの値を取り扱う上でのベストプラクティス
- Secretsを適切に扱うためのツール

2020/3/10時点の[公式ドキュメント](https://kubernetes.io/docs/concepts/configuration/secret/)を参考にしてます。


## 概要

k8sにはSecretsというConfigMapに似た機能がある。
Podやコンテナに対して機密情報を比較的安全に渡すための機能である。ConfigMapは設定のための環境依存の変数を、SecretsはDBのパスワードやCredentialを取り扱うのが主な用途である。

先日、[「kubernetesのConfigMapを理解する」という記事](https://qiita.com/oguogura/items/68741b91b70962081504)を書いたが、Secretsも生成方法と渡し方はConfigMapと大きく変わらない。

早速まとめていく。


Secretsを作成する方法

- ファイルやディレクトリを指定して作成する方法
- マニフェストファイルから作成する方法

Podやコンテナが読み取る方法

- Secretsをボリュームマウントする方法
- Secretsを環境変数として渡す方法


## Secretsを作成する方法

### ファイルやディレクトリを指定して作成する方法

まずはテストとしての機密ファイルを用意する。すでにある場合はスキップしてOK。

```bash

$ echo -n 'hoge' > ./mysql-username.txt
$ echo -n '1f2d1e2e67df' > ./mysql-password.txt
```

```bash

$ kubectl create secret generic mysql-user-pass --from-file=./mysql-username.txt --from-file=./mysql-password.txt
secret/mysql-user-pass created
```

作成したSecretsを確認する。

```bash

$ kubectl get secrets
Name:         mysql-user-pass
Namespace:    test
Labels:       <none>
Annotations:  
Type:         Opaque

Data
====
mysql-password:  12 bytes
mysql-username:  4 bytes
```

ちなみに`$` `\` `*` `!` のような特殊文字が入っている場合、
`--from-file`で作成するか、 `--from-literal`で作成するかで対応が異なる。

```bash

# --from-fileの場合は特に何もする必要はない（k8s側でよしなにやってくれる）
kubectl create secret generic secrets-2　--from-file=./mysql-password.txt

# --from-literalの場合はエスケープして渡す必要がある
kubectl create secret generic dev-db-secret --from-literal=mysql-username=hoge --from-literal=mysql-password='S!B\*d$zDsb'
```

公式によると

>Note: The commands kubectl get and kubectl describe avoid showing the contents of a secret by default. 
>This is to protect the secret from being exposed accidentally to an onlooker, or from being stored in a terminal log.

とのことなのでたまたま画面を見てしまった人にSecretsを見られないように、getとdescribeでは機密情報がプリントされない。

ただし以下のコマンドを実行すると格納されているsecretが確認できる。

```bash

$ kubectl get secrets -o yaml
- apiVersion: v1
  data:
    mysql-password: MWYyZDFlMmU2N2Rm
    mysql-username: aG9nZQ==
  kind: Secret
  metadata:
    creationTimestamp: "2020-03-10T04:32:57Z"
    name: mysql-user-pass
    namespace: test
    resourceVersion: "8227949"
    selfLink: /api/v1/namespaces/test/secrets/mysql-user-pass
    uid: 375cbbb7-6288-11ea-84cf-062919a0e59e
  type: Opaque
```

平文で格納することも可能だが、base64を使って暗号化された文字列にして格納もできる。

```bash

# base64で暗号化し、それをそのままリダイレクトする
$ echo -n 'hoge' | base64 > ./mysql-username.txt
$ echo -n '1f2d1e2e67df' | base64 > ./mysql-password.txt
```

格納した暗号文も複号できる。

```bash

$ echo -n 'MWYyZDFlMmU2N2Rm' | base64
```

## マニフェストファイルから作成する方法

マニフェストファイルを作成する。

```secrets.yaml
apiVersion: v1
kind: Secret
metadata:
  name: mysql-user-pass
type: Opaque
data:
  mysql-username: aG9nZQ==
  mysql-password: MWYyZDFlMmU2N2Rm
```

```bash

$ kubectl get secrets
NAME                  TYPE                                  DATA   AGE
mysql-user-pass       Opaque                                2      3m51s
```

先程からOpaqueという単語が出てきているが、これはSecretsの種類。
普通に試す分ならOpaqueでOK。


| 種類      | YAML内のtype表記| 説明 |
|-----------------|------------------|------------------|
| Generic |Opaque|一般的なフリースキーマのSecret|
| TLS |kubernetes.io/tls| 証明書として利用するSecret |
| Docker Registry|kubernetes.io/dockerconfigjson|証明書として利用するSecret |
| Service Account |kubernetes.io/service-account-token|PodにService AccountのTokenをマウントするためのSecret|


## Podやコンテナが読み取る方法
### Secretsをボリュームマウントする方法

このような感じでマニフェストファイルを作成。

```test.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
  - name: test-pod
    image: nginx:alpine
    volumeMounts:
    - name: secrets
      mountPath: /mount
      readOnly: true
  volumes:
  - name: secrets
    secret:
      secretName: mysql-user-pass
```

containers.volumeMounts.name と volumes.name は一致している必要がある。
またvolumes.secret.secretNameは先ほど作成した Secretsのnameと一致している必要もある。


### Secretsを環境変数として渡す方法

以下のように記述する。

```test.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
  - name: test-pod
    image: nginx:alpine
    envFrom:
    - secretRef:
        name: mysql-user-pass
```

podの中に入って環境変数を確認する。


```bash

$ kubectl get pod
NAME                              READY   STATUS      RESTARTS   AGE
test                              1/1     Running     59s

$ kubectl exec -ti test /bin/sh
/# printenv
・
・
mysql-password=1f2d1e2e67df
mysql-username=hoge
・
・
```

podに渡される課程で複合された平文が読み取れればOK。


## 最後に

冒頭で取り上げないこととして以下を挙げた。

- Kustomizeを使った生成
- Secretsの値を取り扱う上でのベストプラクティス
- Secretsを適切に扱うためのツール


特にSecretsを取り扱う上で、
セキュアさと現実の運用のつらさの間でどのようなバランスを取るかは難しい。そしてそれを楽にする手段は幾つかある。

ここらへんを書くと内容がてんこ盛りになってしまうので、それはまた別の機会に。