Kubernetes Secrets の紹介 – データベースのパスワードやその他秘密情報をどこに保存するか?

なんてこったい(棒)

GitHub の Public リポジトリには、太っ腹な開発者によって大量の Credentials(外部サービスに接続するための秘密キーなど)が公開されており、賢い人たちが日夜クローラーを走らせてそれらを回収し、5万件もの Uber ドライバーの個人情報を頂戴するために利用したり高価な AWS のインスタンスを沢山立ち上げて、もの凄い勢いでビットコインを発掘したりしているらしい。

ウチのリポジトリはプライベートだから問題ないよねって思われる方もおられるかもしれないが、ほんの5分間違って公開しただけで流出したケースもあるらしいので、そもそもコードリポジトリに秘密情報を入れること自体が太っ腹行為の可能性を高めていることを理解する必要がある。

というわけで、Kubernetes でサービスを運用する場合、そういった秘密情報をどこに保存すれば良いかという要求に応えるのが Secrets という仕組みである。

kube-secrets

秘密情報を Secrets というデータベースで集中管理し、それぞれの情報はそれらを必要とする Pod/Container のみに送られる。Docker Image や Container を作るプロセスから秘密情報を切り離せるので、その過程で情報を漏洩させるリスクは少なくなる。Container に送られた秘密情報は tmpfs 上に置かれるので、ノード上のディスクに書き込まれることもない。

Kubernetes 自体がまだ若いプロジェクトなので、この Secrets にも注意しなければならない点がいくつかある。

  • Secrets のデータは etcd の中に平文で保存されているので、etcd には管理者ユーザーだけがアクセス出来るようにセットアップする必要がある。
    • etcdは kubernetes のあらゆるデータを保管しているデータストア。
  • 現在のところ、Secrets に対するユーザーごとのアクセスコントロールは出来ない(将来的にはサポート予定)。

以下は、社内向けに書いた Secrets の簡単なチュートリアル。


Secret のデータ構造

Secret の中身は単純な Key-Value ペアのリスト:

secret-structure

kubectl コマンドで登録されている Secret のリストを見る:

$ kubectl get secrets
NAME                  TYPE                                  DATA      AGE
default-token-pb7ls   kubernetes.io/service-account-token   3         21m
mysecret              Opaque                                2         38s

その中から一つの Secret を選んで中身を見てみる:

$ kubectl describe secret mysecret
Name:       mysecret
Namespace:  sandbox
Labels:     <none>
Annotations:    <none>

Type:   Opaque

Data
====
password:   12 bytes
username:   5 bytes

mysecret の内容を図に書くと以下のような感じ:

mysecret

Secret を登録する

Secret は、以下の二種類のファイルのいずれかを経由して登録できる。

  1. 中身が Value になっているファイル(便宜的に「Secret Value ファイル」と呼ぶ)
  2. YAML あるいは JSON 形式の Kubernetes Manifest ファイル

以下のような Secret を、

[Secret: test-secret] => [Key: password] => [Value: this-is-a-password]

それぞれのファイル形式で登録してみよう。

1. Secret Value ファイル経由

1) ファイルを作る

$ echo -n "this-is-a-password" > ./password

2) --from-file オプションを使って登録

$ kubectl create secret generic test-secret --from-file=./password
secret "test-secret" created

3) 中身を見てみる

$ kubectl describe secrets/test-secret
Name:       test-secret
Namespace:  sandbox
Labels:     <none>
Annotations:    <none>

Type:   Opaque

Data
====
password:   18 bytes

--from-file に指定したファイルの名前が Key になっていることが分かる。

2. Kubernetes Manifest ファイル経由

1) Value を base64 でエンコードする

$ echo -n "this-is-a-password" | base64
dGhpcy1pcy1hLXBhc3N3b3Jk

2) ファイルを作る

以下の内容を secret.yaml に保存:

apiVersion: v1
kind: Secret
metadata:
  name: test-secret
type: Opaque
data:
  password: dGhpcy1pcy1hLXBhc3N3b3Jk

3) -f オプションを使って登録

$ kubectl create -f ./secret.yaml

4) 中身を見てみる

$ kubectl describe secrets/test-secret
Name:       test-secret
Namespace:  sandbox
Labels:     <none>
Annotations:    <none>

Type:   Opaque

Data
====
password:   18 bytes

Secret Value ファイル経由のときと全く同じ Secret が出来ていることが分かる。

Secret の中身を取得する

$ kubectl get secret test-secret -o yaml
apiVersion: v1
data:
  password: dGhpcy1pcy1hLXBhc3N3b3Jk
kind: Secret
metadata:
  creationTimestamp: 2017-03-01T08:49:49Z
  name: test-secret
  namespace: sandbox
  resourceVersion: "12535581"
  selfLink: /api/v1/namespaces/sandbox/secrets/test-secret
  uid: 0747637f-fe5c-11e6-8f7a-0674330dcd09
type: Opaque

Value をデコードする:

$ echo "dGhpcy1pcy1hLXBhc3N3b3Jk" | base64 --decode
this-is-a-password

Secret を使う

Container から Secret を使うには、

  1. 環境変数
  2. Volume としてマウント

の二種類の経路がある。

1. 環境変数

Container の Manifest から以下のような感じで参照:

containers:
- name: http-debug-server
  image: cotoami/http-debug-server:latest
  ports:
  - containerPort: 3000
  env:
    - name: PASSWORD
      valueFrom:
        secretKeyRef:
          name: test-secret
          key: password

Container にログインして、環境変数を見てみる:

$ kubectl get pods
NAME                                 READY     STATUS    RESTARTS   AGE
http-debug-server-4073343574-ro8s8   1/1       Running   0          2m

$ kubectl exec -it http-debug-server-4073343574-ro8s8 /bin/sh

# echo $PASSWORD
this-is-a-password

2. Volume としてマウント

Container 内のディレクトリに、Key をファイル名、Value をファイルの中身としてマウントできる。

以下のように、volumes を定義しておいて、それを volumeMounts でマウントする:

containers:
- name: http-debug-server
  image: cotoami/http-debug-server:latest
  ports:
  - containerPort: 3000
  volumeMounts:
  - mountPath: /tmp
    name: test-secret
    readOnly: true
volumes:
- name: test-secret
  secret:
    secretName: test-secret

Container にログインして、マウントされたファイルを見てみる:

$ kubectl get pods
NAME                                 READY     STATUS    RESTARTS   AGE
http-debug-server-2197190929-q6lke   1/1       Running   0          1m

$ kubectl exec -it http-debug-server-2197190929-q6lke /bin/sh

# cat /tmp/password
this-is-a-password

マウントされた Secret を後から更新した場合は、Kubernetes が自動的に検出してリフレッシュしてくれる。

参考

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中