Kubernetes Secretとは?入門者向けに徹底解説
Kubernetesを使っていると、データベースのパスワード、APIキー、TLS証明書など、アプリケーションにとって非常に重要な「機密情報」を扱う必要が出てきます。これらの情報を安全に管理し、必要なPodにのみアクセスさせるための仕組みが、KubernetesにおけるSecretです。
「Secret」と聞くと、「暗号化されていて絶対に見られない!」と思うかもしれません。しかし、KubernetesのSecretは、私たちが想像するような強固なエンドツーエンド暗号化とは少し異なります。その実態と、どのように安全に扱うべきかを知ることは、Kubernetesでアプリケーションを運用する上で非常に重要です。
この記事では、Kubernetes初心者の方に向けて、Secretの基本的な概念から、その作成、利用方法、そして最も重要な「セキュリティ」について、約5000語で徹底的に解説します。これを読めば、Kubernetes Secretの仕組みを理解し、安全に機密情報を管理できるようになるでしょう。
さあ、Kubernetes Secretの世界へ踏み出しましょう!
1. はじめに:なぜSecretが必要なのか?
Kubernetesは、コンテナ化されたアプリケーションのデプロイ、スケーリング、管理を自動化するための強力なプラットフォームです。マイクロサービスやクラウドネイティブなアプリケーションでは、複数のコンテナが連携して一つのサービスを提供します。
このようなシステムでは、以下のような機密情報を扱うことがよくあります。
- データベースのユーザー名とパスワード
- APIキーやトークン
- プライベートなソースコードリポジトリやコンテナレジストリへの認証情報
- TLS/SSL証明書と秘密鍵
- 設定ファイルに含まれる秘密情報
これらの情報をどのように管理すれば安全でしょうか?
誤った機密情報の管理方法の例:
- コンテナイメージの中に埋め込む: ソースコードリポジトリやイメージ自体が漏洩した場合、機密情報も一緒に漏洩します。また、イメージの再ビルドなしに情報を更新できません。
- 設定ファイルに平文で記述し、ConfigMapとして扱う: ConfigMapは機密情報を扱うためのものではありません。ConfigMapに保存された情報は、デフォルトでは平文で保存され、容易に参照できます。
- 環境変数に平文で記述し、Pod定義ファイルに直書き: Pod定義ファイル(YAML)はバージョン管理システムにコミットされることが多く、機密情報が歴史として残ってしまいます。また、
kubectl describe pod
コマンドなどで容易に参照できてしまいます。
これらの方法は、情報漏洩のリスクが非常に高く、セキュリティの観点から推奨されません。機密情報は、それが本当に必要な場所、つまり実行中のPodからのみ安全にアクセスできる形で管理される必要があります。
そこで登場するのがKubernetes Secretです。Secretは、これらの機密情報をクラスター内に安全に保管し、許可されたPodが必要な形式で利用できるようにするための標準的なメカニズムを提供します。
この記事では、Secretの基本的な仕組みを理解し、YAMLファイルやkubectl
コマンドを使った操作方法、そしてPodからSecretを利用する方法を学びます。さらに、Secretのセキュリティ上の注意点と、より安全に扱うためのベストプラクティスについても詳しく解説します。
2. Secretの基本的な概念
では、Kubernetes Secretが具体的にどのようなものかを見ていきましょう。
2.1 Secretとは何か?
Kubernetes Secretは、パスワード、OAuthトークン、SSHキーなどの機密情報を格納するために設計されたオブジェクトです。ConfigMapがアプリケーションの設定情報(設定ファイルの内容、環境変数など)を扱うためのオブジェクトであるのに対し、Secretは機密性の高い情報を扱うためのオブジェクトとして区別されています。
Secretに格納されたデータは、デフォルトではKubernetesのデータストアであるEtcdにBase64エンコードされた形式で保存されます。Base64エンコードは暗号化ではありません。単にバイナリデータをテキスト形式に変換しているだけです。したがって、Secretの内容は、Etcdにアクセスできる人、またはKubernetes API経由でSecretを参照できる人にとっては、容易にデコードして内容を確認できます。
重要なポイント:
* Secretは機密情報を「格納」し、Podから「安全に利用可能にする」ための仕組みです。
* Secret自体は、デフォルトで「強力に暗号化されているわけではない」ことに注意が必要です。EtcdにBase64エンコードされて保存されます。
* Secretのセキュリティは、主にKubernetesクラスター全体のセキュリティ(特にEtcdのセキュリティ)と、アクセス制御(RBAC)に依存します。
2.2 ConfigMapとSecretの使い分け
ConfigMapとSecretはどちらも「設定情報」をPodに渡すために使われますが、その目的と性質が異なります。
- ConfigMap: 機密性のない設定情報(アプリケーションのログレベル、接続先のサービスURL、一般公開されているAPIキーなど)を扱います。データは平文で保存され、クラスター内の多くのPodから参照される可能性があります。
- Secret: 機密性の高い情報(データベースパスワード、プライベートAPIキー、TLS秘密鍵など)を扱います。データはBase64エンコードされて保存され、通常は特定のPodやユーザーにのみアクセスが許可されるようにアクセス制御(RBAC)を設定します。
原則として、機密性が少しでもある情報はSecretで管理すべきです。ConfigMapに機密情報を置くのは絶対に避けてください。
2.3 Secretの種類 (Type)
Kubernetesにはいくつかの組み込みSecretタイプがあり、それぞれ特定のユースケースに合わせて設計されています。Secretマニフェストのtype
フィールドで指定します。
Opaque
(デフォルト): 特定の構造を持たない、ユーザー定義のSecretデータに使用されます。最も一般的です。kubernetes.io/service-account-token
: Service Accountに関連付けられ、そのService AccountとしてKubernetes APIにアクセスするための認証情報を格納します。これはKubernetesがPod起動時に自動的に作成・マウントすることが多いタイプです。kubernetes.io/dockerconfigjson
: Dockerなどのコンテナレジストリに認証するための情報を格納します。プライベートレジストリからイメージをPullする際に使用されます。kubernetes.io/tls
: TLS証明書と秘密鍵を格納します。Ingressコントローラーや他のTLS終端を必要とするリソースで使用されます。kubernetes.io/basic-auth
: ベーシック認証のユーザー名とパスワードを格納します。kubernetes.io/ssh-auth
: SSH認証情報を格納します。
これらの組み込みタイプは、特定の目的のために設計されており、Kubernetesやアドオンがそれらを認識して特別な方法で扱うことがあります(例: kubernetes.io/dockerconfigjson
はPodのimagePullSecrets
で参照されると、kubeletがイメージプルに利用する)。Opaque
タイプは、上記以外のあらゆる汎用的な機密情報に使用できます。
2.4 Secretの構成要素
Secretオブジェクトは、他のKubernetesオブジェクトと同様に、YAML形式で定義できます。基本的な構成要素は以下の通りです。
yaml
apiVersion: v1
kind: Secret
metadata:
name: my-secret
namespace: default # または他のnamespace
type: Opaque # または他の組み込みタイプ
data:
# Base64エンコードされたキー-値ペア
username: YWRtaW4= # 'admin' をBase64エンコードしたもの
password: cGFzc3dvcmQxMjM= # 'password123' をBase64エンコードしたもの
stringData:
# Base64エンコードされていないプレーンテキストのキー-値ペア (省略可能)
api-key: some-very-secret-api-key
apiVersion
,kind
,metadata
: どのKubernetesオブジェクトでも共通の必須フィールドです。metadata.name
はSecretの名前を指定します。type
: 上記で説明したSecretのタイプを指定します。省略するとOpaque
になります。data
: Base64エンコードされたキー-値ペアを格納するためのフィールドです。キーは文字列、値はBase64エンコードされた文字列です。ここに指定するデータは、実際にSecretとして格納される際にBase64デコードされて利用されます。stringData
: Kubernetes 1.14で導入された便利なフィールドです。ここに記述する値はBase64エンコードする必要がなく、平文で記述できます。KubernetesがSecretを作成または更新する際に、stringData
フィールドの内容を自動的にBase64エンコードし、data
フィールドに格納します。stringData
は読み取り専用で、data
フィールドと重複するキーを指定した場合、stringData
の値が優先されます。このフィールドは、SecretのYAMLマニフェストを人間が扱いやすくするために提供されています。ただし、kubectl get secret <name> -o yaml
でSecretの内容を参照した場合、stringData
フィールドは表示されず、エンコードされた値がdata
フィールドに表示される点に注意が必要です。
3. Secretの作成方法
Secretを作成する方法はいくつかあります。主にkubectl
コマンドを使う方法と、YAMLマニフェストを定義して適用する方法があります。
3.1 kubectl
コマンドで作成する
簡単なSecretを作成する場合や、スクリプトでSecretを生成する場合に便利です。
方法1: リテラル値から作成 (--from-literal
)
単純なキー-値ペアを直接コマンドラインで指定してSecretを作成します。
bash
kubectl create secret generic my-app-secret \
--from-literal=username=admin \
--from-literal=password=password123
このコマンドは、my-app-secret
という名前のOpaque
タイプのSecretを作成します。username
とpassword
というキーで、指定した値がBase64エンコードされて格納されます。
作成されたSecretの内容を確認してみましょう(後述しますが、このコマンドはBase64エンコードされた値が表示されます)。
bash
kubectl get secret my-app-secret -o yaml
出力例:
yaml
apiVersion: v1
data:
password: cGFzc3dvcmQxMjM= # password123
username: YWRtaW4= # admin
kind: Secret
metadata:
creationTimestamp: "..."
name: my-app-secret
namespace: default
resourceVersion: "..."
uid: "..."
type: Opaque
data
フィールドにBase64エンコードされた値が格納されているのがわかります。
方法2: ファイルの内容から作成 (--from-file
)
設定ファイルや証明書ファイルなど、ファイルの内容全体をSecretの値として格納したい場合に便利です。
まず、格納したい内容を持つファイルを作成します。
bash
echo "this_is_a_very_sensitive_api_key_from_file" > ./api_key.txt
echo "db.example.com:5432" > ./db_host.txt
これらのファイルからSecretを作成します。
bash
kubectl create secret generic my-file-secret \
--from-file=api-key=./api_key.txt \
--from-file=db-host=./db_host.txt
--from-file=<key>=<path>
の形式で指定すると、ファイルの内容がBase64エンコードされ、指定した<key>
の名前でSecretに格納されます。--from-file=<path>
の形式で指定すると、ファイル名(拡張子なし)がキー名として使用されます。例えば--from-file=./api_key.txt
とした場合、api_key
というキーでファイルの内容が格納されます。
複数のファイルをまとめて一つのSecretに格納することも可能です。
bash
kubectl create secret generic my-multi-file-secret \
--from-file=./api_key.txt \
--from-file=./db_host.txt \
--from-literal=some-setting=value
このコマンドでは、api_key
とdb_host
というキー(ファイル名由来)と、some-setting
というキー(リテラル由来)を持つSecretが作成されます。
TLS Secretの作成:
Ingressなどで使用するTLS証明書(公開鍵)と秘密鍵からSecretを作成する際にもkubectl
コマンドが便利です。
bash
kubectl create secret tls my-tls-secret \
--cert=path/to/tls.crt \
--key=path/to/tls.key
このコマンドは、kubernetes.io/tls
タイプのSecretを作成し、指定された証明書ファイルと秘密鍵ファイルの内容をそれぞれtls.crt
とtls.key
というキーで格納します。
Docker Registry Secretの作成:
プライベートコンテナレジストリからイメージをPullするための認証情報Secretを作成します。
bash
kubectl create secret docker-registry my-docker-secret \
--docker-server=your-registry.example.com \
--docker-username=your-user \
--docker-password=your-password \
[email protected] # メールアドレスは必須ではありません
このコマンドは、kubernetes.io/dockerconfigjson
タイプのSecretを作成します。このタイプのSecretは、.dockerconfigjson
というキーで、Docker互換の認証情報JSONを格納します。PodのimagePullSecrets
でこのSecretを参照すると、kubeletがイメージPull時に利用します。
kubectl create secret
コマンドは手軽ですが、機密情報をコマンドラインの履歴に残してしまうリスクがあるため、特にパスワードなどのリテラル値を直接指定する際は注意が必要です(履歴に残さないようにするための工夫はありますが、それでもリスクはゼロではありません)。より安全な方法としては、YAMLマニフェストを使用することが推奨されます。
3.2 YAMLマニフェストで作成する
YAMLファイルでSecretを定義し、kubectl apply -f <file.yaml>
コマンドで適用する方法は、Secretの定義をバージョン管理できるため、より管理しやすく、自動化にも適しています。また、kubectl create secret
コマンドのように機密情報がコマンド履歴に残る心配もありません。
YAMLマニフェストの例:
yaml
apiVersion: v1
kind: Secret
metadata:
name: my-yaml-secret
namespace: default
type: Opaque
data:
# ここにBase64エンコードされた値を記述
db_username: YWRtaW4= # 'admin'
db_password: c3VwZXJzZWNyZXRwYXNzd29yZA== # 'supersecretpassword'
data
フィールドに記述する値は、必ずBase64エンコードされている必要があります。元の値(平文)をBase64エンコードするには、例えばLinuxやmacOSでは echo -n '元の値' | base64
コマンドを使います。WindowsのPowerShellでは [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('元の値'))
のように実行できます。
例:'admin'
をBase64エンコード
“`bash
echo -n ‘admin’ | base64
出力例: YWRtaW4=
“`
例:'supersecretpassword'
をBase64エンコード
“`bash
echo -n ‘supersecretpassword’ | base64
出力例: c3VwZXJzZWNyZXRwYXNzd29yZA==
“`
stringData
フィールドの利用:
YAMLマニフェストをより読みやすくするために、stringData
フィールドを利用できます。
yaml
apiVersion: v1
kind: Secret
metadata:
name: my-yaml-secret-with-stringdata
namespace: default
type: Opaque
stringData:
# ここにBase64エンコードされていない値を記述
db_username: admin
db_password: supersecretpassword
api_endpoint: http://internal-api.example.com/v1
data:
# Base64エンコードされた値も同時に記述可能だが、キーが重複するとstringDataが優先される
some_other_data: Zm9vYmFy # 'foobar'
このYAMLを適用すると、stringData
フィールドの値はKubernetesによって自動的にBase64エンコードされ、data
フィールドに格納されます。kubectl get secret my-yaml-secret-with-stringdata -o yaml
で確認すると、stringData
は表示されず、data
フィールドにエンコードされた値が表示されます。
YAMLマニフェストでSecretを作成する際も、YAMLファイルを安全に管理することが非常に重要です。特にstringData
を使用する場合、ファイルの中に平文の機密情報が含まれるため、そのファイル自体へのアクセスを厳しく制限する必要があります。バージョン管理システムにコミットする場合は、そのリポジトリのアクセス権を管理されたメンバーに限定するなどの対策が必要です。
どちらの方法で作成する場合でも、作成されたSecretオブジェクトはEtcdに格納されます。
4. Secretの使い方(Podからの利用)
作成したSecretは、アプリケーションが動作するPodから利用できます。主に以下の2つの方法があります。
- コンテナの環境変数として利用する
- ボリュームとしてPodにマウントし、ファイルとして利用する
これらの方法は、PodのYAMLマニフェストで設定します。
4.1 コンテナの環境変数として利用する
Secretの特定のキーの値を、コンテナの環境変数として渡すことができます。これは、アプリケーションが環境変数から設定を読み取るように設計されている場合に便利です。
PodのYAMLマニフェストのcontainers
セクション内で、env
フィールドまたはenvFrom
フィールドを使用します。
方法1: env
フィールドと valueFrom.secretKeyRef
Secretの特定のキーの値を、個別の環境変数として指定する場合に使用します。
yaml
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod-env
spec:
containers:
- name: my-app-container
image: my-app-image:latest
env:
- name: DB_USERNAME # コンテナ内の環境変数名
valueFrom:
secretKeyRef:
name: my-app-secret # 参照するSecretの名前
key: username # Secret内のキー
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-app-secret
key: password
# オプション: Secretまたはキーが存在しない場合でもエラーにしない
# optional: true
env
: コンテナに設定する環境変数のリスト。name
: コンテナ内で使用される環境変数名。valueFrom
: 環境変数の値をどこから取得するかを指定。secretKeyRef
: 値をSecretの特定のキーから取得することを示します。name
: 参照するSecretの名前。Podと同じNamespaceにある必要があります。key
: Secret内の、環境変数として設定したい値のキー名。optional: true
(Kubernetes 1.8以降): 指定したSecretまたはSecret内のキーが存在しない場合でも、Podの起動を失敗させずに、その環境変数を設定しないようにします。デフォルトはfalse
で、Secretまたはキーが存在しないとPodは起動できません。
この方法では、Secretの各キーを個別の環境変数にマッピングできます。環境変数名はSecretのキー名と異なっていても構いません。
方法2: envFrom
フィールドと secretRef
Secretに含まれる全てのキー-値ペアを、環境変数としてまとめてコンテナに渡す場合に使用します。Secretのキー名がそのまま環境変数名として使用されます。
yaml
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod-envfrom
spec:
containers:
- name: my-app-container
image: my-app-image:latest
envFrom:
- secretRef:
name: my-app-secret # 参照するSecretの名前
# オプション: Secretが存在しない場合でもエラーにしない
# optional: true
# envFromとenvは併用可能です
env:
- name: SOME_OTHER_VAR
value: "some_value"
envFrom
: SecretやConfigMapから複数の環境変数をまとめて設定するためのリスト。secretRef
: 参照するSecretを指定します。name
: 参照するSecretの名前。optional: true
: 指定したSecretが存在しない場合でもPodの起動を失敗させないようにします。
envFrom
を使うと、Secret内の全てのキーが自動的に環境変数として設定されるため、Secretの内容が多くてもPod定義がシンプルになります。ただし、Secret内の全ての情報が環境変数として渡されるため、必要な情報だけを含んだSecretを作成する必要があります。また、Secretのキー名がそのまま環境変数名になるため、命名規則に注意が必要です。Secret内にハイフン(-
)が含まれるキーがある場合、それは環境変数名としては無効な文字であるため、アンダースコア(_
)に変換されます。
環境変数利用の注意点:
- 環境変数として渡されたSecretの値は、そのコンテナのプロセスツリーで平文で見えてしまいます。これは、
ps aux | grep my-app-container
のようなコマンドを実行したり、/proc/<pid>/environ
を参照したりすることで、クラスターノードへのアクセス権を持つユーザーに見えてしまう可能性があることを意味します。 - したがって、環境変数で機密情報を渡す場合、ノードへのアクセス権限を厳しく管理することが重要です。また、機密情報がログに出力されないよう、アプリケーション側で十分注意する必要があります。
- Secretの値を環境変数として設定した場合、Secretの内容が更新されても、起動済みのPodの環境変数は自動的に更新されません。新しいSecretの値を反映するには、Podを再作成(またはローリングアップデート)する必要があります。
4.2 ボリュームとしてマウントしてファイルとして利用する
Secretの内容をファイルとしてPod内の特定のパスにマウントする方法です。アプリケーションが設定ファイルを特定のパスから読み込むように設計されている場合に便利です。
PodのYAMLマニフェストのspec.volumes
とspec.containers.volumeMounts
セクションを使用します。
yaml
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod-volume
spec:
containers:
- name: my-app-container
image: my-app-image:latest
volumeMounts:
- name: secret-volume # Podのvolumesで定義したボリューム名
mountPath: /etc/secrets # コンテナ内のマウントパス
readOnly: true # 必須ではありませんが、機密情報なので読み取り専用が推奨されます
volumes:
- name: secret-volume # ボリューム名
secret:
secretName: my-app-secret # 参照するSecretの名前
# items: # オプション: 特定のキーだけをマウントする場合
# - key: username
# path: username.txt # マウントパスの下でのファイル名
# - key: password
# path: db_password.txt # マウントパスの下でのファイル名
# defaultMode: 0444 # オプション: マウントされるファイルのパーミッション (デフォルトは0644)
# optional: true # オプション: Secretが存在しない場合でもエラーにしない (Kubernetes 1.10以降)
volumes
: Podにアタッチするボリュームのリスト。name
: ボリューム名。secret
: ボリュームのタイプがSecretであることを示します。secretName
: 参照するSecretの名前。Podと同じNamespaceにある必要があります。items
: (オプション) Secret内の特定のキーだけをファイルとしてマウントしたい場合に指定します。key
でSecret内のキーを指定し、path
でマウントパス以下のファイル名を指定します。このフィールドを省略すると、Secret内の全てのキーがそれぞれのキー名をファイル名としてマウントパス直下にファイルとして作成されます。defaultMode
: (オプション) マウントされるファイルのパーミッションを指定します。8進数で指定します(例:0444
は所有者、グループ、その他のユーザーに対して読み取り権限のみを与える)。デフォルトは0644
です。機密情報ファイルの場合、不要なユーザーからのアクセスを防ぐため、読み取り権限のみを与える0444
や、PodのServiceAccount(所有者)のみに読み取り権限を与える0400
などを設定することが推奨されます。optional: true
(Kubernetes 1.10以降): 指定したSecretが存在しない場合でもPodの起動を失敗させないようにします。デフォルトはfalse
です。volumeMounts
: コンテナ内でボリュームをマウントする設定のリスト。name
: マウントするボリューム名(volumes
で定義したものと同じ名前)。mountPath
: コンテナ内のファイルシステム上のマウントポイントパス。readOnly
: (推奨) マウントされたボリュームを読み取り専用にするか指定します。Secretは機密情報なので、通常はtrue
に設定します。
この方法でSecretをマウントすると、Secretの各キーの内容が、指定されたmountPath
以下のファイルとして利用可能になります。例えば、上記の例でitems
を省略した場合、/etc/secrets/username
と /etc/secrets/password
というファイルが作成され、それぞれのファイルの内容はSecretのusername
キーとpassword
キーの値(Base64デコード後)になります。
ボリュームマウント利用の利点:
- 環境変数として渡す方法と比較して、機密情報がプロセス環境に見えてしまうリスクを低減できます。ファイルシステム上のパーミッション制御も可能です。
- Secretの内容が更新された場合、ボリュームとしてマウントされたファイルの内容は自動的に更新されます(デフォルトで、更新は数秒から数分かかる場合があります)。アプリケーションが設定ファイルの変更を監視していれば、Podの再起動なしに新しいSecretの値を読み込むことができます。ただし、この自動更新は一部のファイルシステムタイプに依存する場合があり、確実な反映を求める場合はアプリケーション側でのファイル変更監視の実装が必要です。
ボリュームマウント利用の注意点:
- アプリケーションがファイルから設定を読み込むように設計されている必要があります。
- マウントパスやファイル名の命名規則に注意が必要です。
defaultMode
でファイルパーミッションを適切に設定することが重要です。
どちらの方法を選ぶかは、アプリケーションの設計や要件によります。環境変数の方が手軽な場合が多いですが、セキュリティやSecret更新の自動反映を考慮すると、ボリュームマウントの方が有利な場合があります。
5. Secretのセキュリティ
Secretは機密情報を扱うため、そのセキュリティは非常に重要です。しかし、KubernetesのSecretがデフォルトで提供するセキュリティレベルは限定的であり、運用者自身が追加の対策を講じる必要があります。
5.1 Secretは暗号化されているのか?
繰り返しになりますが、Secretに格納されたデータは、デフォルトではKubernetesのデータストアであるEtcdにBase64エンコードされて保存されます。Base64エンコードは暗号化ではありません。 Base64デコードは誰でも容易に行えるため、Etcdにアクセスできる人であれば、Secretの内容を容易に知ることができます。
つまり、デフォルトのSecretのセキュリティは、Etcdデータストア自体のセキュリティに強く依存します。 Etcdへのアクセスを適切に制限することが、Secretの内容を保護する上で最も基本的な対策となります。
5.2 セキュリティ上の課題
デフォルトのSecretの扱いに伴うセキュリティ上の課題を理解しましょう。
- Etcdのセキュリティ: Etcdクラスターが侵害されると、全てのSecretを含むクラスターの全データが漏洩するリスクがあります。Etcdへのネットワークアクセス制限、認証・認可設定が不可欠です。
- RBACによるアクセス制御: Secretオブジェクト自体へのアクセスは、KubernetesのRole-Based Access Control (RBAC) によって制御されます。しかし、デフォルトではService AccountにNamespace内のSecretに対する
get
,list
,watch
権限などが付与されている場合があります。不要なService AccountやユーザーにSecretへの参照権限を与えないように、RBAC設定を適切に行う必要があります。 - PodへのSecretマウント時のリスク:
- 環境変数: 上記で述べたように、コンテナプロセス環境から平文のSecret値が見えてしまう可能性があります。
- ボリュームマウント: マウントされたファイルは、コンテナ内でファイルシステムアクセス権を持つ誰か(例えば、コンテナにシェルで入れたユーザーや、コンテナ内で実行される悪意のあるプロセス)によって読み取られる可能性があります。適切なファイルパーミッション(
defaultMode
)の設定が重要です。
- ログへの機密情報漏洩: アプリケーションが誤って環境変数やファイルから読み取った機密情報をログに出力してしまうリスクがあります。アプリケーション開発時に、ログ出力内容に機密情報が含まれないよう十分に注意する必要があります。
- CI/CDパイプラインでの機密情報: Secretを作成するためのYAMLファイルや、
kubectl create secret
コマンドを実行するスクリプトなどがCI/CDパイプラインに含まれることがあります。これらのパイプラインが実行される環境や、パイプラインを構成するファイル自体の管理も重要です。特に、stringData
を含むYAMLファイルは平文で機密情報が含まれるため、厳重な管理が必要です。 - イメージへの機密情報の埋め込み: Secretを使わず、コンテナイメージのビルド時に機密情報をファイルとして追加したり、環境変数としてハードコードしたりすることは、イメージが漏洩した場合に機密情報も同時に漏洩するため、絶対に避けるべきです。
5.3 Secretの保護策
上記の課題を踏まえ、Secretをより安全に扱うための保護策を見ていきましょう。
基本的な対策:
- Etcdのセキュリティ強化:
- Etcdノードへのネットワークアクセスを厳しく制限します。
- Etcdクラスターへのクライアント認証(Mutual TLSなど)を有効にします。
- Etcdのバックアップを安全に管理します。
- RBACの適切な設定:
- Secretに対する
get
,list
権限などを必要最小限のユーザー、グループ、Service Accountにのみ付与します。 - 特に、Namespace内の全てのSecretを参照できるような広範な権限は避けるべきです。
- Secretに対する
- Podマウント時のパーミッション設定:
- ボリュームとしてSecretをマウントする場合、
defaultMode
でファイルパーミッションを0400
(所有者のみ読み取り可) や0444
(全ユーザー読み取り可) のように、必要最小限の権限に設定します。 readOnly: true
を設定し、コンテナからSecretの内容を誤って書き換えたり削除したりできないようにします。
- ボリュームとしてSecretをマウントする場合、
- アプリケーション開発時の注意:
- 機密情報がログや標準出力に出力されないよう、コードレビューやセキュリティテストを実施します。
- 環境変数で機密情報を渡す場合、プロセス環境からその情報が読み取られる可能性があることを開発者が理解しておく必要があります。
- YAMLファイルやスクリプトの管理:
stringData
を含むYAMLファイルや、kubectl create secret
コマンドを含むスクリプトは、アクセス権限を厳しく管理された安全な場所に保管します。バージョン管理システムを使用する場合は、そのリポジトリを保護します。
より高度な対策:
- Etcdの暗号化 (Encryption at Rest):
- KubernetesのAPIサーバー設定でEtcdのデータ暗号化を有効にできます。これにより、Etcdにデータが書き込まれる際に暗号化され、ディスク上では暗号化された状態で保存されます。Etcdデータが盗まれたとしても、暗号化キーがなければ内容を復元することは困難になります。これは、Secretのセキュリティを大幅に向上させる重要な機能です。ただし、この機能の設定方法や管理はクラスターの設定に依存し、やや高度な知識が必要です。
- 外部シークレット管理システムとの連携:
- HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, GCP Secret Managerなどの専門的なシークレット管理システムを導入し、Kubernetesクラスターの外で機密情報を一元管理します。
- Kubernetesクラスター内のPodは、これらの外部システムから動的に機密情報を取得するようにします。この方法の大きな利点は、機密情報がEtcdに格納されないため、Etcdのセキュリティリスクを回避できる点です。
- この連携を実現するための一般的な方法として、Secrets Store CSI Driver があります。これはContainer Storage Interface (CSI) を利用して、外部シークレットストアからSecretを取得し、Podのボリュームとしてマウントする仕組みです。これにより、アプリケーションはファイルシステム上のファイルとしてSecretにアクセスでき、Secretの値は外部ストアから動的に取得されるため、Kubernetes SecretオブジェクトとしてEtcdに永続的に格納する必要がなくなります。
これらの高度な対策は、運用コストや複雑さが増しますが、特に高いセキュリティ要件を持つ環境では検討すべきです。
6. Secretの管理と操作
Secretオブジェクトを日常的に管理するために使用するkubectl
コマンドを紹介します。
6.1 Secretの表示
Secretオブジェクトの情報を表示するには、kubectl get secret
や kubectl describe secret
コマンドを使用します。
Secret一覧の取得:
特定のNamespace内のSecret一覧を表示します。
bash
kubectl get secrets -n default
出力例:
NAME TYPE DATA AGE
default-token-xxxxx kubernetes.io/service-account-token 3 1d
my-app-secret Opaque 2 5h
my-file-secret Opaque 2 4h
my-yaml-secret-with-stringdata Opaque 3 3h
DATA
列は、Secretに格納されているキーの数を示します。
特定のSecretの詳細表示:
特定のSecretの詳細情報を表示します。このとき、data
フィールドの値はBase64エンコードされたまま表示されます。
bash
kubectl get secret my-app-secret -o yaml
または
bash
kubectl describe secret my-app-secret
describe
コマンドの出力例:
“`
Name: my-app-secret
Namespace: default
Labels:
Annotations:
Type: Opaque
Data
password: 13 bytes # Base64デコード後のバイトサイズ
username: 5 bytes # Base64デコード後のバイトサイズ
“`
describe
コマンドはdata
フィールドの値自体は表示しませんが、そのサイズを表示します。より詳細なYAML形式で表示する場合、get -o yaml
を使用します。
6.2 Secretの内容をBase64デコードして表示する
kubectl get secret <name> -o yaml
で取得したSecretのYAML出力から、data
フィールドの値を取り出してBase64デコードすることで、平文の内容を確認できます。
例: my-app-secret
の username
キーの値を確認する
“`bash
YAMLを取得し、jqコマンドで特定のキーの値を取り出し、base64コマンドでデコード
kubectl get secret my-app-secret -o yaml | \
jq -r ‘.data.username’ | base64 -d
“`
出力:
admin
jq -r '.data.<key_name>'
の部分は、YAML/JSONから特定のキーの値を抽出するためのコマンドです。jqコマンドがインストールされていない場合は、手動でYAML出力からBase64文字列をコピーし、echo 'Base64文字列' | base64 -d
コマンドでデコードすることもできます。
!!! 注意 !!!
このコマンドは、Secretの平文の機密情報を端末に出力します。これにより、シェル履歴に機密情報が残ってしまう可能性があります。本番環境や共有環境で安易に実行することは避けてください。 確認が必要な場合は、| base64 -d
の部分を削除してBase64エンコードされた値だけを確認するか、kubectl describe
を利用してバイトサイズだけを確認するなど、慎重に行ってください。
6.3 Secretの更新
既存のSecretの内容を更新するには、YAMLファイルを編集してkubectl apply -f <file.yaml>
コマンドを使用するか、kubectl edit secret <name>
コマンドを使用します。
YAMLファイルを編集して適用:
Secretを作成したYAMLファイル(例: my-yaml-secret.yaml
)を編集し、data
またはstringData
フィールドの値を変更します。
“`bash
my-yaml-secret.yaml をエディタで開いて修正
例: stringData: の password を変更
kubectl apply -f my-yaml-secret.yaml
“`
kubectl apply
コマンドは、差分を検出してSecretを更新します。
kubectl edit
コマンド:
エディタでSecretのYAML定義を開いて直接編集し、保存することでSecretを更新できます。
bash
kubectl edit secret my-app-secret
これにより、エディタでSecretのYAML定義が開きます。data
フィールドのBase64値を手動で編集するか、stringData
フィールドを追加・編集して保存します。
!!! 注意 !!!
kubectl edit secret
で表示されるYAMLは、デフォルトではdata
フィールドにBase64エンコードされた値が含まれています。stringData
フィールドは表示されません(元のYAMLにstringData
があったとしても)。Base64エンコードされた値を手動で編集するのは間違いやすいので、可能であれば元のYAMLファイル(stringData
を含むもの)を編集してkubectl apply
するのが推奨されます。もしstringData
フィールドを編集したい場合は、kubectl edit
でYAML定義を開き、手動でstringData
フィールドを追加して値を記述することも可能です。
Secretを更新しても、そのSecretを環境変数として利用しているPodは自動的に更新されません。 更新されたSecretを反映するには、Podを再起動(またはローリングアップデートなどのデプロイメント更新)する必要があります。
一方、ボリュームとしてSecretをマウントしているPodは、通常、ファイルの内容が自動的に更新されます(デフォルト設定の場合。Kubeletの同期間隔やファイルシステムの種類に依存します)。ただし、アプリケーション側でファイルの変更を検知して再読み込みする仕組みが必要です。
6.4 Secretの削除
Secretを削除するには、kubectl delete secret <name>
コマンドを使用します。
bash
kubectl delete secret my-app-secret
Secretが削除されると、そのSecretを参照しているPodは、新しいPodが起動できなくなったり、実行中のPodがSecretにアクセスできなくなったりする可能性があります(Pod定義でoptional: true
が設定されていない場合)。Secretを削除する際は、そのSecretに依存しているPodやDeploymentがないか確認し、必要に応じて事前にPodを停止または更新してください。
6.5 Secretのローテーション(更新頻度)
セキュリティのベストプラクティスとして、データベースパスワードやAPIキーなどの機密情報は定期的にローテーション(更新)することが推奨されます。Kubernetes Secretの場合も、定期的な更新が必要です。
手動でSecretを更新する方法は上記で説明した通りですが、多数のSecretを頻繁に更新するのは手間がかかります。自動化するためには、外部シークレット管理システムや、Kubernetesのカスタムコントローラーなどを利用することを検討します。
Secretのローテーションを行う際は、そのSecretを利用しているアプリケーションへの影響を考慮する必要があります。アプリケーションが新しいSecretの値を適切に読み込めるような設計になっているか確認が必要です。ボリュームマウントによるSecret利用は、多くのケースでアプリケーションの再起動なしに値を反映させやすいという利点があります。
7. Secretの高度なトピック
ここでは、特定のユースケースで使用されるSecretタイプや、関連する概念について少し踏み込んで解説します。
7.1 Service Account Tokens (kubernetes.io/service-account-token
)
Kubernetesでは、PodがKubernetes APIと通信する際に、認証・認可のためにService Accountを使用します。Service Accountには、対応するAPIトークンが含まれたSecretが自動的に作成されます。
このSecretは、Podが起動する際に自動的にPodにボリュームとしてマウントされます(デフォルト設定の場合)。通常、/var/run/secrets/kubernetes.io/serviceaccount/token
というファイルとしてマウントされます。Pod内で実行されるアプリケーションやツール(例: kubectl
コマンドをコンテナ内で実行する場合)は、このトークンを使用してKubernetes APIにアクセスし、Pod自身や他のリソースに関する操作を行います。
このService Account Token SecretはKubernetesによって自動的に管理されるため、通常、ユーザーが手動で作成、更新、削除する必要はありません。
セキュリティに関する注意点:
- このSecretは、PodがKubernetes APIにアクセスするための認証情報です。このトークンが悪意のあるユーザーの手に渡ると、Service Accountに付与された権限でクラスター内の操作が可能になってしまいます。
- Service Accountに不要な権限を与えないように、RBAC設定を適切に行うことが非常に重要です。
- Pod定義で
automountServiceAccountToken: false
を設定することで、Service Account Token Secretの自動マウントを無効にできます。PodがKubernetes APIと通信する必要がない場合は、この設定を検討すべきです。
7.2 Docker Registry Secrets (kubernetes.io/dockerconfigjson
)
プライベートなコンテナレジストリからイメージをPullする場合、認証情報が必要です。Kubernetesでは、この認証情報を格納するためにkubernetes.io/dockerconfigjson
タイプのSecretを使用します。
このSecretには、通常、ユーザー名、パスワード、レジストリサーバーなどの情報を含む.dockerconfigjson
形式のデータが格納されます。
Pod定義のspec.imagePullSecrets
フィールドで、このタイプのSecretを参照することで、kubeletはイメージをPullする際にそのSecretに含まれる認証情報を使用します。
yaml
apiVersion: v1
kind: Pod
metadata:
name: private-image-pod
spec:
containers:
- name: my-container
image: your-private-registry.example.com/my-private-image:latest
imagePullSecrets:
- name: my-docker-secret # kubernetes.io/dockerconfigjson タイプのSecret名
my-docker-secret
というSecretは、kubectl create secret docker-registry
コマンドやYAMLマニフェストで事前に作成しておく必要があります。
7.3 TLS Secrets (kubernetes.io/tls
)
TLS証明書と秘密鍵を格納するために使用されます。主に、IngressコントローラーやWebhookなどで、クライアントとの間でTLS接続を確立するために使用されます。
このタイプのSecretには、Base64エンコードされた証明書チェーンがtls.crt
というキーで、秘密鍵がtls.key
というキーで格納されている必要があります。
yaml
apiVersion: v1
kind: Secret
metadata:
name: my-tls-secret
namespace: default
type: kubernetes.io/tls
data:
tls.crt: |
-----BEGIN CERTIFICATE-----
... (Base64 encoded certificate) ...
-----END CERTIFICATE-----
tls.key: |
-----BEGIN PRIVATE KEY-----
... (Base64 encoded private key) ...
-----END PRIVATE KEY-----
(注: 上記YAML例のdata
フィールドの値は実際には1行のBase64文字列になります。|
はYAMLの複数行文字列の書き方ですが、Secretの値は通常1行で表現されます。)
Ingressリソースのspec.tls
セクションで、このタイプのSecretを参照することで、指定したホスト名に対してTLS終端を行うことができます。
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
tls:
- hosts:
- myapp.example.com
secretName: my-tls-secret # kubernetes.io/tls タイプのSecret名
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-service
port:
number: 80
TLS Secretの管理は、証明書の有効期限管理やローテーションが必要になるため、運用上の注意が必要です。
8. Secretに関するトラブルシューティング
Secret関連で発生しやすい問題と、その対処法をいくつか紹介します。
8.1 PodがSecretにアクセスできない/起動できない
- Secretが存在しない: Pod定義で参照しているSecretが、Podと同じNamespaceに存在するか確認してください。
kubectl get secrets -n <namespace>
で一覧を確認できます。 - Secret名が間違っている: Pod定義の
secretName
が、実際のSecretの名前と一致しているか確認してください。 - Namespaceが間違っている: PodとSecretが異なるNamespaceに存在する場合、通常は参照できません。同じNamespaceに作成するか、クロスNamespace参照を可能にする(Kubernetesの標準機能ではないため、Service Meshなどの追加コンポーネメントが必要になることが多い)方法を検討してください。
- キーが存在しない: 環境変数として
secretKeyRef
で参照しているキーや、ボリュームマウントのitems
で参照しているキーが、Secret内に存在するか確認してください。kubectl get secret <secret-name> -o yaml
でSecretの内容を確認できます。キー名は大文字・小文字を区別します。 - RBAC権限不足: Podが使用するService Accountが、参照しようとしているSecretに対する
get
権限を持っているか確認してください。特にPodが起動できない場合、kubeletがPodの代わりにSecretを取得する際に権限が必要になります。kubectl auth can-i get secret <secret-name> --as=system:serviceaccount:<namespace>:<serviceaccount-name>
のようなコマンドで権限を確認できます。 optional: false
の場合: Secretや参照しているキーが存在しない場合、optional: false
(デフォルト) ではPodは起動できません。イベントログ (kubectl describe pod <pod-name>
) を確認して、Secretが見つからないというエラーが出ていないか確認してください。
8.2 SecretがMountされない/ファイルが作成されない
- ボリューム定義が不足/誤り: Pod定義の
spec.volumes
セクションで、Secretタイプのボリュームが正しく定義されているか確認してください。secretName
が正しいか、タイプがsecret
になっているかなどを確認します。 - ボリュームマウント定義が不足/誤り:
spec.containers.volumeMounts
セクションで、定義したボリュームがコンテナに正しくマウントされているか確認してください。name
がvolumes
で定義したボリューム名と一致しているか、mountPath
が適切かを確認します。 items
の指定ミス:items
で特定のキーのみをマウントするように指定している場合、key
やpath
が正しいか確認してください。- kubeletの問題: 極まれに、kubeletがSecretの変更を検知してボリュームを更新するのに時間がかかったり、問題が発生したりすることがあります。Podを再作成することで解消される場合があります。
- Secretの内容が空: Secretに
data
またはstringData
フィールドが定義されていない(キーが一つもない)場合、ボリュームとしてマウントしてもファイルは作成されません。
8.3 SecretのBase64デコードエラー
- 手動でのBase64エンコード/デコードミス: YAMLファイルを手動で編集する際に、Base64エンコード/デコードを間違えている可能性があります。YAMLの
data
フィールドに直接編集する場合は、正確なBase64文字列を入力する必要があります。stringData
フィールドを使用すれば、このミスを防げます。 - 意図しない文字の混入: Base64文字列をコピー&ペーストする際に、改行やスペースなどの余分な文字が混入していると、正しくデコードできません。
8.4 権限不足でSecretを操作できない
- RBAC権限不足: Secretを
get
,list
,create
,update
,delete
しようとした際に権限エラーが発生する場合、その操作を行おうとしているユーザーまたはService Accountに、対象のNamespaceのSecretに対する適切なRBAC権限が付与されていない可能性があります。クラスター管理者やNamespace管理者に連絡して、権限設定を確認・修正してもらう必要があります。
9. まとめ:Secretを安全に使うために
Kubernetes Secretは、アプリケーションの機密情報を安全にクラスター内に格納し、Podから利用可能にするための標準的なメカニズムです。しかし、SecretのデータがデフォルトではEtcdにBase64エンコードされて保存されるだけであるという点は、入門者が誤解しやすいポイントです。
この記事を通じて、Secretの基本的な概念、作成・利用方法、そして最も重要なセキュリティ上の注意点と対策について理解を深めていただけたかと思います。
Secretを安全に使うためのベストプラクティスを改めてまとめます。
- 機密情報は必ずSecretで管理する: ConfigMapやPod定義ファイルに平文で機密情報を記述することは絶対に避けてください。
- SecretはBase64エンコードされることを理解する: Base64は暗号化ではありません。Secretの内容を保護するためには、追加の対策が必要です。
- Etcdのセキュリティを最優先する: SecretのセキュリティはEtcdのセキュリティに依存します。Etcdへのアクセス制限、認証、そして可能であればEtcdの暗号化 (Encryption at Rest) を設定します。
- RBACでSecretへのアクセスを厳しく制御する: 必要最小限のユーザー、グループ、Service AccountにのみSecretへの参照権限を付与します。
- PodでのSecret利用方法を選択し、セキュリティに配慮する:
- 環境変数として利用する場合、プロセス環境から機密情報が見えるリスクを理解し、ノードアクセス権限を厳しく管理します。ログ出力にも十分注意します。Secretの更新にはPodの再起動が必要です。
- ボリュームとしてマウントする場合、
defaultMode
でファイルパーミッションを適切に設定し、readOnly: true
を検討します。通常、Secret更新はファイルに自動反映されますが、アプリケーション側の対応が必要です。
- YAMLファイルやスクリプトを安全に管理する: 特に
stringData
を含むSecret定義ファイルは、平文で機密情報を含むため、厳重なアクセス管理が必要です。 - 機密情報は定期的にローテーションする: 定期的な更新はセキュリティリスクを低減します。
- 必要に応じて外部シークレット管理システムとの連携を検討する: 高いセキュリティ要件を持つ場合や、機密情報の一元管理が必要な場合は、Vaultやクラウドプロバイダーのシークレットマネージャーなどを利用し、Secrets Store CSI Driverなどで連携することを検討します。これにより、機密情報をEtcdから分離できます。
Kubernetes Secretは強力なツールですが、その仕組みを正しく理解し、適切なセキュリティ対策を講じることなく使用すると、重大な情報漏洩につながる可能性があります。この記事が、皆さんがKubernetes Secretをより安全に、そして効果的に活用するための一助となれば幸いです。
これで、Kubernetes Secretの基本的な使い方はもちろん、セキュリティ上の注意点やより安全な管理方法についても理解できたはずです。これらの知識を活かして、Kubernetes環境で機密情報を適切に扱っていきましょう。