今回はKubernetes(以下k8s)のSecretについてまとめていきます。
もくじ
1.Secretとは ( 概要 / ConfigMapとの違い )
2.Secret生成方法( ファイル利用 / 通常マニフェスト利用 / マニフェスト利用特殊版(stringData) / SecretGenerator利用 )
4.Secret利用方法( 環境変数として利用 / Volumeとしてマウント )
■Secretとは
◎概要
機密情報を保存・管理できるリソース。
k8sの初期化によって、k8s APIにアクセスするための認証情報を含むSecretが自動的に生成され、
作られたSecretをpodが参照するようになっている。
k8s利用のためのSecret以外にも、例えばDB接続のためのユーザIDとパスワードを登録しておくためなどに、Secretを自作・利用することができる。
Secretは、以下の4タイプが存在するが、今回の記事ではGenericタイプに焦点を当てる。
・Generic(type: Opaque)
…最もよく使われる、一般的なSecret。
・TLS(type: kubernetes.io/tls)
…証明書として利用されるSecret。Ingerssリソース等から利用されることが多い。
・Dockerレジストリ(type: kubernetes.io/dockerconfig.json)
…プライベートリポジトリからのDockerイメージ取得で必要な、認証情報としてのSecret。
・Service Account(type: kubernetes.io/service-account-token)
…Service Accountのトークンや証明書をpodにマウントするためのSecret。
◎ConfigMapとの違い
SecretとConfigMapは、ともにk8sのConfig & Storageリソースの1つであり、
設定した内容が、環境変数やk8sのVolumeプラグインとして利用されることも、共通している。
※ConfigMapの詳細記事はこちら。
https://ameblo.jp/bakery-diary/entry-12615300336.html
Secretは、機密情報を安全に保存しておくことができる点で、ConfigMapと異なる。
Secret内の情報は、ふだんMaster Node内のetcdに保管され、そのSecretを利用するpodがある場合のみ、 over SSL/TLS通信でコンテナへ転送される。転送後、Worker Node上にデータが残らないよう、値は一時記憶領域に保持されるようになっている。
保管する情報が、機密情報(パスワード等)かどうかで、SecretとConfigMapを使い分けるとよい。
■Secret生成方法
◎ファイル利用
値だけをあらかじめ記述しておいたファイルから、Secretを生成する。
キーは、デフォルトではファイル名になる。
<基本のコマンド>
kubectl create secret (Secret名) --from-file=(ファイルパス)
<コマンド例>
kubectl create secret generic db-auth-secret --from-file=./username.txt --from-file=./password.txt
→この場合、「username.txt」というキーに対して、username.txtのファイルの中身が値、
かつ、「password.txt」というキーに対して、password.txtのファイルの中身が値として、
「db-auth-secret」という名のSercretリソースに登録される。
<キー名の指定>
キーをファイル名にしたくない場合、任意のキー名を指定することも可能。
以下のように、ファイルパスの前に、「=」でつないでキーを指定する。
kubectl create secret generic db-auth-secret --from-file=username=./username.txt
この場合、キー名「username」に対して、username.txtの中身が値となる。
◎通常マニフェスト利用
マニフェストファイルを利用して、Secretを生成することも可能。
Genericタイプ(type: Opaque)にすることと、dataの値をBASE64でエンコードすべきことが注意点。
<例> ※内容は公式よりそのまま転記させて頂きました
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data: #値をBASE64でエンコードする
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
マニフェストファイル作成後は、通常のリソースと同様、kubectl apply -f ~で、デプロイすればOK。
<BASE64のエンコード・デコード>※通常のLinuxコマンド利用
値のBASE64でのエンコードは、以下のLinuxコマンドを使って先に行う。
エンコードされた出力結果を、マニフェストファイルにコピペするとよい。
echo -n 'エンコードしたい値' | base64
例: echo -n 'P@ssw0rd' | base64
デコードしたい場合は以下コマンド。
echo 'デコードしたい値(base64でエンコードされた値)' | base64 --decode
◎マニフェスト利用特殊版(stringData)
Base64でエンコードされていない、平文の文字列を、マニフェストファイルに記述することもできる。
その場合は、通常のdataフィールドではなく、stringDataフィールドを用いる。
<例>
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
stringData:
username: testuser
password: P@ssw0rd
もとの文字列は、Secretの生成・更新タイミングで、自動的にBase64にエンコードされることになる。
<その他注意点>
同じsecretに、dataとstringDataでそれぞれ同じキーでの定義をした場合、
stringDataのほうが優先される。
※以下の例は公式に記載された例より抜粋
data:
username: YWRtaW4=
stringData: #優先される
username: administrator
◎SecretGenerator利用
ConfigMap同様、ジェネレーターを使ってSecretを生成することもできる。
まず、以下の例のようなyamlファイルを作成する。(例によって公式から転記しております)
このときyamlファイル名は、kustmization.yamlとしなければならない。
<例① リテラルから生成>
secretGenerator:
- name: db-user-pass
literals:
- username=testuser
- password=P@ssw0rd
secretGenerator[].literals[]を用いる時は、直接、キーとバリュー(平文)を指定する。
<例② ファイルから生成>
secretGenerator:
- name: db-user-pass
files:
- username.txt
- password.txt
secretGenerator[].files[]の際は、バリューをあらかじめファイルに記述したうえで、そのファイルを指定する。
<デプロイ方法>
例1、2ともに、kubectl apply時は、「-k」オプションを付ける必要がある。「-f」でないことに注意。
さらに、ファイル名は指定しなくて良い(Generatorとして勝手に認識してくれる)ことに気を付けたい。
上記を踏まえ、kustomization.yamlを配置したディレクトリ内で、以下のようにkubectl applyを実行すること。
kubectl apply -k .
■Secret編集方法
生成したSecretのデータ値は、あとから編集することが可能。
パスワードが変更になった等の時に便利。
<コマンド>
kubectl edit secrets mysecret
→エディタ(vimなど。環境による)が開かれ、編集できるようになる。
■Secret参照方法
◎環境変数として利用
ConfigMap内の特定のキー&バリューを個別指定し、その値をpod内で使えるようにする方法。
pod内特有の定数名で、該当値を利用することができる。
<pod作成例① 個別キー指定>
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: sample
env:
- name: USERNAME
valueFrom:
secretKeyRef:
name: test-secret
key: username
環境変数の設定は、マニフェストファイル内の、spec.containers[].envで行う。
spec.containers[].env.name(例の「USERNAME」の部分)は、このpod内で使う環境変数の名称。
spec.containers[].env.valueFrom以下に、どのSecretの、どのキーの値を取ってくるか、を記述する。
今回は、「test-secret」という名前のSecret内に定義された、「username」というキー名の値を取得している。
<pod作成例② Secretまるごと取得>
作成例①は、キーを個別に指定していたが、Secretの内容をまるごととってくることも可能。
その際は以下のような記述になる。
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: sample
envFrom:
- secretRef:
name: test-secret
個別キー指定時は、spec.containers[].env[]として設定していたところを、
spec.containers[].envFrom[]となっているところに注意。
◎Volumeとしてマウント
k8sのVolumeとして、Worker Node上にマウントし、Secretを参照することも可能。
以下のようにPodのマニフェストを作成する。
<pod作成例① 個別キー指定> ※公式より転記
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
secret:
secretName: mysecret
items:
- key: username
path: my-group/my-username
spec.volumes[].secretに、利用するSecretの情報を記述する。
例の場合は、「mysecret」という名のSecret内から、
キーを直接指定(items[].key)し、その値のマウント先を設定(items[].path)している。
マウント先は、volumeMounts.mountPathで指定したパスの配下となるため、
上記例ならば /etc/foo/my-group/my-username にマウントされることになる。
<pod作成例② Secretまるごと取得>
作成例①は、キーを個別に指定していたが、Secretの内容をまるごととってくることも可能。
その際は以下のような記述になる。
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
<Volumeマウント時の注意点>
SecretをVolumeとしてマウントする際の注意点として、
必ず「.spec.containers[].volumeMounts[].readOnly = true」を設定するようにする。
readOnllyはvolumeMountsのオプションだが、trueにすることで、
コンテナからは設定値を読み込むだけ(書き込み等不可)の設定となる。
Secretは機密情報を含むため、セキュリティ上、読み込みだけの設定にすべきである。
■参考文献
・公式
https://kubernetes.io/docs/concepts/configuration/secret/
・青山真也著『Kubernetes完全ガイド』