はじめに
この記事で取り上げること
- k8sのSecrets機能について
- 最低限の使い方
取り上げないこと
- Kustomizeを使った生成
- Secretsの値を取り扱う上でのベストプラクティス
- Secretsを適切に扱うためのツール
2020/3/10時点の公式ドキュメントを参考にしてます。
概要
k8sにはSecretsというConfigMapに似た機能がある。
Podやコンテナに対して機密情報を比較的安全に渡すための機能である。ConfigMapは設定のための環境依存の変数を、SecretsはDBのパスワードやCredentialを取り扱うのが主な用途である。
先日、「kubernetesのConfigMapを理解する」という記事を書いたが、Secretsも生成方法と渡し方はConfigMapと大きく変わらない。
早速まとめていく。
Secretsを作成する方法
- ファイルやディレクトリを指定して作成する方法
- マニフェストファイルから作成する方法
Podやコンテナが読み取る方法
- Secretsをボリュームマウントする方法
- Secretsを環境変数として渡す方法
Secretsを作成する方法
ファイルやディレクトリを指定して作成する方法
まずはテストとしての機密ファイルを用意する。すでにある場合はスキップしてOK。
$ echo -n 'hoge' > ./mysql-username.txt
$ echo -n '1f2d1e2e67df' > ./mysql-password.txt
$ kubectl create secret generic mysql-user-pass --from-file=./mysql-username.txt --from-file=./mysql-password.txt
secret/mysql-user-pass created
作成したSecretsを確認する。
$ 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
で作成するかで対応が異なる。
# --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が確認できる。
$ 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を使って暗号化された文字列にして格納もできる。
# base64で暗号化し、それをそのままリダイレクトする
$ echo -n 'hoge' | base64 > ./mysql-username.txt
$ echo -n '1f2d1e2e67df' | base64 > ./mysql-password.txt
格納した暗号文も複号できる。
$ echo -n 'MWYyZDFlMmU2N2Rm' | base64
マニフェストファイルから作成する方法
マニフェストファイルを作成する。
apiVersion: v1
kind: Secret
metadata:
name: mysql-user-pass
type: Opaque
data:
mysql-username: aG9nZQ==
mysql-password: MWYyZDFlMmU2N2Rm
$ 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をボリュームマウントする方法
このような感じでマニフェストファイルを作成。
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を環境変数として渡す方法
以下のように記述する。
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- name: test-pod
image: nginx:alpine
envFrom:
- secretRef:
name: mysql-user-pass
podの中に入って環境変数を確認する。
$ 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を取り扱う上で、
セキュアさと現実の運用のつらさの間でどのようなバランスを取るかは難しい。そしてそれを楽にする手段は幾つかある。
ここらへんを書くと内容がてんこ盛りになってしまうので、それはまた別の機会に。