0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

KubernetesのSecretとは何か?

0
Posted at

はじめに

前回は、ConfigMapを使って、機密ではない設定値をコンテナイメージから分離する仕組みを学びました。

ConfigMap
├── RAILS_ENV=production
├── LOG_LEVEL=info
└── REDIS_HOST=redis-service
        ↓
Pod
        ↓
Container

しかし、アプリケーションが必要とする値は、公開して問題のない設定だけではありません。

たとえば、次のような値も必要です。

データベースのパスワード
外部APIのトークン
RailsのSECRET_KEY_BASE
TLSの秘密鍵

これらをConfigMapへ保存したり、Dockerイメージへ直接書いたりすると、機密情報が漏れる危険があります。

そこで登場するのが Secret(シークレット) です。

この記事では、次の内容を整理します。

  • Secretとは何か
  • ConfigMapとの違い
  • dataとstringDataの違い
  • Base64と暗号化の違い
  • Secretを環境変数として渡す方法
  • Secretをファイルとしてマウントする方法
  • Secretを更新したときの反映方法
  • SecretをGitへ保存してはいけない理由
  • RBACやNamespaceとの関係
  • EKSにおけるKMS暗号化
  • AWS Secrets Managerとの違い

結論

Secretとは、パスワード・トークン・秘密鍵などの機密情報を、コンテナイメージやPodの定義から分離して扱うKubernetesリソースです。

Secret
├── DATABASE_PASSWORD
├── SECRET_KEY_BASE
└── API_TOKEN
        ↓ 参照
DeploymentのPodテンプレート
        ↓
Pod
        ↓
Containerの環境変数・ファイル

ただし、Secretを作成しただけで、機密情報が完全に安全になるわけではありません。

Secret
= 機密情報をConfigMapやPod定義から分離する仕組み

RBAC
= 誰がSecretを読み取れるか制限する仕組み

保存時の暗号化
= Kubernetes APIの保存先でデータを保護する仕組み

Secrets Managerなど
= Secretの保管・取得・ローテーションを管理する外部サービス

Secretは、これらを組み合わせて安全に運用します。

なぜDockerイメージへ機密情報を入れないのか

たとえば、Dockerfileへパスワードを書いたとします。

ENV DATABASE_PASSWORD=example-password

この値は、作成したコンテナイメージやイメージ履歴から読み取られる可能性があります。

Dockerイメージ
└── DATABASE_PASSWORDが埋め込まれる
        ↓
イメージを取得できる人から見える可能性がある

Gitへコミットした後に行を削除しても、過去の履歴へ残る可能性があります。

Secretを使うと、アプリケーションのイメージと機密情報を分けられます。

Dockerイメージ
└── アプリケーション本体

Secret
└── 環境ごとの機密情報

同じDockerイメージを使いながら、開発・ステージング・本番で異なる機密情報を渡せます。

Secretは何を保存するのか

Secretは、主に次のような機密情報を保存します。

  • データベースのユーザー名・パスワード
  • 外部サービスのAPIトークン
  • RailsのSECRET_KEY_BASE
  • OAuthのクライアントシークレット
  • TLS証明書と秘密鍵
  • コンテナレジストリの認証情報
  • SSHの秘密鍵

機密性のない値はConfigMapへ保存します。

値 保存先の例
LOG_LEVEL=info ConfigMap
RAILS_ENV=production ConfigMap
REDIS_HOST=redis-service ConfigMap
DATABASE_PASSWORD=... Secret
SECRET_KEY_BASE=... Secret
API_TOKEN=... Secret

ConfigMapとSecretの違い

ConfigMapとSecretは、どちらも値をPodへ渡すために利用できます。

大きな違いは、保存する情報の性質です。

項目 ConfigMap Secret
主な用途 機密ではない設定 機密情報
値の例 ログレベル、ホスト名 パスワード、トークン
環境変数として渡す できる できる
ファイルとして渡す できる できる
主な参照方法 configMapRef secretRef
アクセス制御 RBACで制御 RBACでより厳格に制御すべき
ConfigMap
└── 見られても問題のない設定

Secret
└── 閲覧できる人を制限すべき機密情報

SecretはConfigMapより機密情報向けに設計されていますが、名前だけで自動的に万全な保護が行われるわけではありません。

Secret・Deployment・Podの関係

Secret自体がコンテナへ値を送り込むわけではありません。

DeploymentのPodテンプレートからSecretを参照します。

Secret
機密情報を保持する
        ↓ 名前で参照
Deployment
Podテンプレートへ参照方法を書く
        ↓ Podを作成
Pod
        ↓
Container
環境変数・ファイルとして利用する

ConfigMapと同様に、同じNamespace内の複数のPodから同じSecretを参照できます。

                    ┌→ Rails Deployment → Rails Pod
application-secret ┤
                    └→ Sidekiq Deployment → Sidekiq Pod

Secretを定義するYAML

Secretの基本的なYAMLは次のようになります。

apiVersion: v1
kind: Secret
metadata:
  name: application-secret
type: Opaque
data:
  DATABASE_PASSWORD: ZXhhbXBsZS1wYXNzd29yZA==
  SECRET_KEY_BASE: ZXhhbXBsZS1zZWNyZXQta2V5LWJhc2U=

dataへ記述する値は、Base64形式へ変換する必要があります。

なお、この記事の値は説明用のダミーです。実際の認証情報ではありません。

YAMLの各項目

apiVersionとkind

apiVersion: v1
kind: Secret
項目 意味
apiVersion: v1 Secretを扱うKubernetes APIのバージョン
kind: Secret 作成するリソースがSecretであることを示す

metadata.name

metadata:
  name: application-secret

Secretの名前です。

DeploymentやPodは、この名前を使ってSecretを参照します。

type

type: Opaque

Secretの用途を表します。

任意のキーと値を保存する一般的なSecretでは、Opaqueを使用します。

代表的なSecret Typeは次のとおりです。

Type 用途
Opaque 一般的な機密情報
kubernetes.io/tls TLS証明書と秘密鍵
kubernetes.io/dockerconfigjson コンテナレジストリの認証情報
kubernetes.io/basic-auth Basic認証のユーザー名・パスワード
kubernetes.io/ssh-auth SSH秘密鍵

用途に合ったTypeを使うことで、必要なキーが存在するかKubernetesが確認できる場合があります。

dataとstringDataの違い

Secretへ値を書く方法には、主にdataとstringDataがあります。

data

dataへ書く値は、Base64形式へ変換します。

data:
  DATABASE_PASSWORD: ZXhhbXBsZS1wYXNzd29yZA==

文字列をBase64へ変換する例です。

printf '%s' 'example-password' | base64

出力例です。

ZXhhbXBsZS1wYXNzd29yZA==

改行が値へ混ざらないように、echoではなくprintfを使うと分かりやすいです。

元の値へ戻すことも簡単です。

printf '%s' 'ZXhhbXBsZS1wYXNzd29yZA==' | base64 --decode

stringData

stringDataを使うと、Base64へ変換する前の文字列を記述できます。

apiVersion: v1
kind: Secret
metadata:
  name: application-secret
type: Opaque
stringData:
  DATABASE_PASSWORD: example-password
  SECRET_KEY_BASE: example-secret-key-base

Kubernetes APIが受け取った後、値はSecretのdataとして扱われます。

フィールド YAMLへ書く値
data Base64形式の値
stringData 通常の文字列

ただし、stringDataへ平文で書けば、YAMLファイル上ではそのまま読めます。

SecretのYAMLをGitへコミットしてよいという意味ではありません。

また、stringDataはServer-Side Applyと相性がよくないという注意点があります。

Base64は暗号化ではない

ここがSecretを理解するうえで最も重要です。

Base64は、データを文字列として表現するためのエンコードです。

暗号化ではありません。

example-password
        ↓ Base64エンコード
ZXhhbXBsZS1wYXNzd29yZA==
        ↓ Base64デコード
example-password

鍵を知らなくても、誰でも元に戻せます。

したがって、次の理解は間違いです。

× Base64に変換したから中身を読めない
× Secretに入れたからYAMLを公開してよい

正しくは次のとおりです。

Base64
= バイナリーデータを文字列として表現する形式

暗号化
= 鍵を持つ人だけが元の値を読めるようにする仕組み

Secretの安全性は、保存時の暗号化・通信の暗号化・RBAC・運用方法などによって決まります。

Secretを作成する

YAMLをsecret.yamlとして保存した場合、次のコマンドで作成・更新できます。

kubectl apply -f secret.yaml

一覧を確認します。

kubectl get secret

Secretの詳細を確認します。

kubectl describe secret application-secret

describeでは、通常、値そのものではなく各データのサイズが表示されます。

Data
====
DATABASE_PASSWORD:  16 bytes
SECRET_KEY_BASE:    23 bytes

一方、次のコマンドではBase64形式の値を取得できます。

kubectl get secret application-secret -o yaml

Secretを読み取る権限を持つ人は、Base64をデコードして元の値を確認できます。

そのため、kubectl get secretの権限自体を制限する必要があります。

コマンドからSecretを作成する

実際の値をYAMLへ直接書かず、コマンドからSecretを作成することもできます。

値を直接指定する

kubectl create secret generic application-secret \
  --from-literal=DATABASE_PASSWORD='example-password' \
  --from-literal=SECRET_KEY_BASE='example-secret-key-base'

ファイルから作成する

kubectl create secret generic tls-private-key \
  --from-file=private.key

この場合、通常はファイル名がキー、ファイルの中身が値になります。

ただし、シェルの履歴・CIのログ・プロセス情報などへ値が残らないように注意が必要です。

本番運用では、外部のSecret管理サービスや、安全なCI/CDのSecret機能から値を渡す方法も検討します。

コンテナへ渡す方法

Secretの代表的な渡し方は次の3つです。

方法 向いている用途
envFrom.secretRef 複数のキーをまとめて環境変数にする
env.valueFrom.secretKeyRef 必要なキーだけを環境変数にする
Volume 秘密鍵や証明書などをファイルとして渡す

それぞれ確認します。

方法1:すべての値を環境変数として渡す

envFromを使うと、Secretの値をまとめて環境変数へ渡せます。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: rails
spec:
  replicas: 3
  selector:
    matchLabels:
      app: rails
  template:
    metadata:
      labels:
        app: rails
    spec:
      containers:
        - name: rails
          image: example/rails-app:1.0
          envFrom:
            - secretRef:
                name: application-secret

Secretに次のキーがあるとします。

DATABASE_PASSWORD
SECRET_KEY_BASE

コンテナ内では、同じ名前の環境変数として利用できます。

DATABASE_PASSWORD=...
SECRET_KEY_BASE=...

Railsからは、たとえば次のように取得します。

ENV.fetch("SECRET_KEY_BASE")

方法2:必要な値だけを環境変数として渡す

secretKeyRefを使うと、Secretの特定のキーだけを参照できます。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: rails
spec:
  replicas: 3
  selector:
    matchLabels:
      app: rails
  template:
    metadata:
      labels:
        app: rails
    spec:
      containers:
        - name: rails
          image: example/rails-app:1.0
          env:
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: application-secret
                  key: DATABASE_PASSWORD

この設定では、SecretのDATABASE_PASSWORDを、コンテナ内のDB_PASSWORDとして渡します。

Secret
DATABASE_PASSWORD=...
        ↓
Container
DB_PASSWORD=...

必要な値だけを渡せるため、コンテナが参照できるSecretを最小限にできます。

方法3:ファイルとしてマウントする

Secretは、VolumeとしてPodへマウントできます。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          volumeMounts:
            - name: tls-secret-volume
              mountPath: /etc/nginx/tls
              readOnly: true
      volumes:
        - name: tls-secret-volume
          secret:
            secretName: tls-secret

Secretに次のキーがある場合を考えます。

tls.crt
tls.key

コンテナ内には、次のファイルとしてマウントされます。

/etc/nginx/tls/tls.crt
/etc/nginx/tls/tls.key

関係は次のとおりです。

Secretのキー
tls.key
        ↓ ファイル名になる
/etc/nginx/tls/tls.key
        ↑
Secretの値がファイル内容になる

秘密鍵や証明書など、アプリケーションがファイルとして読むデータに向いています。

環境変数とファイルのどちらを使うのか

観点 環境変数 Volumeによるファイル
向いている値 パスワード、短いトークン 証明書、秘密鍵、設定ファイル
アプリの読み方 ENVなど ファイルを開く
Secret更新 起動済みコンテナへ自動反映されない 最終的にマウントへ反映される
注意点 デバッグ出力や子プロセスへ漏らさない ファイル権限・読取対象を制限する

どちらを選んでも、アプリケーションのログやエラーメッセージへ値を出力しないことが重要です。

Secretを更新するとどうなるのか

Secretも、利用方法によって更新の反映方法が異なります。

環境変数として利用している場合

Secretを更新しても、すでに起動しているコンテナの環境変数は変わりません。

Secretを更新
        ↓
起動済みContainerの環境変数
        × そのまま

新しい値を使うには、Podを再作成します。

kubectl rollout restart deployment/rails

ローリングアップデートの状態を確認します。

kubectl rollout status deployment/rails

Volumeとして利用している場合

VolumeとしてマウントしたSecretは、Secretの更新後、時間を置いてPod内のファイルにも反映されます。

Secretを更新
        ↓
kubeletが変更を検知
        ↓
マウントされたファイルを更新

ただし、即時反映ではありません。

また、ファイルが変わっても、アプリケーションが自動で読み直すとは限りません。

アプリケーションが再読み込みに対応していなければ、Podの再作成が必要です。

subPathを使ってSecretの一部だけをマウントした場合、更新は自動反映されません。

Secretの変更でDeploymentは自動再起動しない

SecretとDeploymentは別のKubernetesリソースです。

Secretだけを更新しても、DeploymentのPodテンプレートは変わりません。

そのため、Deploymentは自動的に新しいPodを作りません。

Secretだけ変更
        ↓
DeploymentのPodテンプレートは同じ
        ↓
ローリングアップデートは始まらない

環境変数として利用している場合は、次のような運用を検討します。

  • kubectl rollout restartを実行する
  • Secret名へバージョンを付けて参照先を切り替える
  • Secret変更を検知してDeploymentを再起動する仕組みを導入する
  • 外部Secret管理サービスとローテーション方法を設計する

SecretとNamespace

SecretはNamespaceに所属するリソースです。

Podは、同じNamespaceにあるSecretを参照します。

Namespace: production
├── Secret: application-secret
└── Pod: rails-xxxxx
        ○ 参照できる

Namespace: staging
└── Pod: rails-yyyyy
        × productionのSecretは直接参照できない

環境ごとにNamespaceを分ける場合は、それぞれのNamespaceへSecretを用意します。

kubectl create secret generic application-secret \
  --from-literal=DATABASE_PASSWORD='example-password' \
  -n production

存在しないSecretを参照するとどうなるのか

Podが必須のSecretを参照しているのに、そのSecretが存在しない場合、Podは正常に起動できません。

Deployment
└── application-secretを参照

application-secretが存在しない
        ↓
Podの起動に失敗・待機

まずSecretを作成し、その後にDeploymentを作成するのが基本です。

意図的にSecretがなくても起動させたい場合は、optional: trueを指定できます。

envFrom:
  - secretRef:
      name: optional-secret
      optional: true

ただし、認証情報などの必須値をoptionalにすると、アプリケーション起動後に別のエラーが起きる可能性があります。

immutableなSecret

変更させたくないSecretには、immutable: trueを指定できます。

apiVersion: v1
kind: Secret
metadata:
  name: application-secret-v1
type: Opaque
immutable: true
stringData:
  DATABASE_PASSWORD: example-password

immutableなSecretは、作成後にdataやstringDataの内容を変更できません。

変更が必要な場合は、新しい名前のSecretを作成して参照先を切り替えます。

application-secret-v1
        ↓ 新しい値を作成
application-secret-v2

誤更新を防ぎたい場合や、Secretをバージョン単位で切り替えたい場合に利用できます。

SecretをGitへコミットしてはいけない

次のYAMLは、Base64になっていても機密情報を含んでいます。

data:
  DATABASE_PASSWORD: ZXhhbXBsZS1wYXNzd29yZA==

Base64は簡単に元へ戻せるため、このファイルをGitへコミットすると機密情報も共有されます。

Private Repositoryでも、次のようなリスクがあります。

  • 閲覧できるメンバーが増える
  • 誤ってRepositoryが公開される
  • CIのログへ表示される
  • ForkやCloneへ値が残る
  • 削除後もGit履歴へ残る

本番の認証情報を含むSecret YAMLを、平文やBase64のままGitへ保存しないようにします。

Gitで宣言的に管理したい場合は、暗号化されたSecretマニフェストや外部Secret管理サービスを利用する仕組みを検討します。

Secretを扱うときのRBAC

Secretを取得できるユーザーやServiceAccountは、値を読み取れます。

そのため、RBACでアクセス権を最小限にします。

開発者全員
└── すべてのSecretを取得できる
        × 権限が広すぎる

必要な運用担当者・ServiceAccount
└── 必要なNamespace・Secretだけ取得できる
        ○ 最小権限

特に、Secretに対する次の権限は慎重に付与します。

  • get
  • list
  • watch
  • create
  • update
  • patch
  • delete

Podを作成できる権限にも注意が必要です。

Secretを参照するPodを自由に作れる人は、そのPodを通してSecretの値を取得できる可能性があります。

複数コンテナがあるPodでの注意点

1つのPodに複数のコンテナがある場合でも、Secretが必要なコンテナだけへ渡します。

Pod
├── Rails Container
│   └── DATABASE_PASSWORDが必要
│
└── Metrics Container
    └── DATABASE_PASSWORDは不要

不要なコンテナへenvFromやvolumeMountsを設定しないことで、Secretへアクセスできる範囲を狭くできます。

Secretのサイズ上限

1つのSecretへ保存できるデータは1MiBまでです。

Secretは大きなファイルを保管するストレージではありません。

パスワード・トークン・鍵
→ Secretに向いている

動画・画像・大きなバックアップ
→ Secretに向いていない

大きなデータには、用途に合ったストレージを使用します。

Kubernetes Secretは保存時に暗号化されるのか

一般的なKubernetesでは、Secretを作成しただけで、保存先のetcdに必ず暗号化されるとは限りません。

Kubernetesの公式ドキュメントでは、Secretはデフォルトではetcdへ暗号化されずに保存されるため、保存時の暗号化を構成することが推奨されています。

kubectl
    ↓ TLSで通信
Kubernetes API Server
    ↓
etcd
Secretを保存

ここで分けて考える必要があります。

保護する場面 仕組みの例
通信中 TLS
Kubernetes APIの保存先 Encryption at Rest・Envelope Encryption
読み取れる主体 RBAC
Pod内での利用 環境変数・Volume・アプリの実装

Secretは、このすべてを自動的に解決するものではありません。

EKSではどう保存されるのか

Amazon EKSでは、Kubernetesのバージョンによって扱いが異なります。

AWSの現在のドキュメントでは、Kubernetes 1.28以降のEKSクラスターは、Kubernetes APIに保存されるすべてのデータをデフォルトでEnvelope Encryptionにより保護します。

Secretだけでなく、ConfigMapなどのAPIデータも対象です。

Kubernetes APIデータ
        ↓ データ暗号化キーで暗号化
暗号化されたデータ
        ↓
AWS KMSでキーを保護

これをEnvelope Encryptionと呼びます。

AWS所有のKMSキーが標準で使われ、要件に応じてカスタマーマネージドKMSキーを利用できます。

ただし、この暗号化はKubernetes APIへ保存されたデータを保護するものです。

Node上のデータやEBS Volume内のデータまで、同じ仕組みで自動的に保護するわけではありません。

利用中のEKSバージョンと暗号化設定を確認することが重要です。

Kubernetes SecretとAWS Secrets Managerの違い

EKSでは、Kubernetes Secretの代わりに、または組み合わせてAWS Secrets Managerを利用できます。

項目 Kubernetes Secret AWS Secrets Manager
管理場所 Kubernetes API AWSのマネージドサービス
Podからの利用 Secret参照 CSI Driverなどで取得
アクセス制御 Kubernetes RBAC AWS IAM
自動ローテーション 別途仕組みが必要 対応サービスでは設定可能
AWS監査との連携 Kubernetes側で設計 CloudTrailなどと連携しやすい

AWS Secrets Managerは、Secretの保管・アクセス制御・ローテーションをAWS側で管理したい場合に向いています。

EKS PodからAWS Secrets Managerを使う流れ

AWS Secrets and Configuration ProviderとSecrets Store CSI Driverを使うと、Secrets Managerの値をEKS Podへファイルとしてマウントできます。

AWS Secrets Manager
        ↓ IAMでアクセス許可
AWS Secrets and Configuration Provider
        ↓
Secrets Store CSI Driver
        ↓
EKS Pod内のVolume
        ↓
Containerがファイルを読む

Pod IdentityやIRSAを利用すると、Pod単位で必要なAWS権限を付与できます。

Rails Pod
└── application/production/* のSecretだけ取得できる

別のPod
└── 取得できない

これにより、Node全体へ広いIAM権限を与えずに、必要なPodだけがSecretへアクセスできます。

AWS Secrets Managerの自動ローテーションとCSI Driverのローテーション機能を組み合わせる方法もあります。

なお、AWS公式ドキュメントでは、AWS Secrets and Configuration Providerによるこの方式はEKSのFargate Nodeではサポートされていないとされています。

Kubernetes SecretとSecrets Managerのどちらを使うのか

一律にどちらかだけが正解というわけではありません。

状況 選択肢の例
Kubernetes内だけで完結する小規模な構成 Kubernetes Secret
AWSサービスと認証情報を共有する AWS Secrets Manager
自動ローテーションが必要 AWS Secrets Managerを検討
Kubernetes標準の可搬性を重視する Kubernetes Secret
IAMでPod単位のアクセスを管理したい Secrets Manager+Pod Identityなど

実際には、セキュリティ要件・運用負荷・既存の仕組み・コストを含めて決めます。

重要なのは、Secretをどこへ置くかだけでなく、次の流れを設計することです。

作成
↓
保存
↓
配布
↓
利用
↓
更新・ローテーション
↓
失効・削除

RailsとSidekiqで同じSecretを使う例

RailsとSidekiqを別Deploymentで動かす場合を考えます。

application-secret
├── DATABASE_PASSWORD
└── SECRET_KEY_BASE
        ↓
        ├── Rails Deployment
        │       └── Rails Pod
        │
        └── Sidekiq Deployment
                └── Sidekiq Pod

Rails Deploymentでは次のように参照します。

containers:
  - name: rails
    image: example/rails-app:1.0
    envFrom:
      - secretRef:
          name: application-secret

Sidekiq Deploymentでも同じSecretを参照します。

containers:
  - name: sidekiq
    image: example/rails-app:1.0
    command: ["bundle", "exec", "sidekiq"]
    envFrom:
      - secretRef:
          name: application-secret

同じDockerイメージとSecretを利用しながら、起動コマンドを変えられます。

Secretを環境変数として渡しているため、値を更新した場合はRailsとSidekiqの両方のPodを再作成します。

kubectl rollout restart deployment/rails
kubectl rollout restart deployment/sidekiq

ConfigMapとSecretを同時に使う例

実際のアプリケーションでは、ConfigMapとSecretを一緒に利用することが多くあります。

app-config
├── RAILS_ENV=production
├── LOG_LEVEL=info
└── REDIS_HOST=redis-service

application-secret
├── DATABASE_PASSWORD=...
└── SECRET_KEY_BASE=...

Deploymentでは、両方を参照できます。

containers:
  - name: rails
    image: example/rails-app:1.0
    envFrom:
      - configMapRef:
          name: app-config
      - secretRef:
          name: application-secret

コンテナから見ると、どちらも環境変数として利用できます。

ただし、Kubernetes上では公開設定と機密情報を別リソースとして管理できます。

Secretが渡されたか確認するときの注意

Pod名を確認します。

kubectl get pods

環境変数名が存在するかだけを確認する場合でも、値を画面やログへ出さないように注意します。

次のようなコマンドは値そのものを表示します。

kubectl exec rails-xxxxx -- printenv DATABASE_PASSWORD

共有画面・CIログ・シェル履歴などで実行すると、Secretが漏れる可能性があります。

トラブルシューティングでは、まず値を表示せずに次を確認します。

kubectl describe pod rails-xxxxx

確認する項目は次のとおりです。

  • Secretが存在するか
  • SecretとPodが同じNamespaceにあるか
  • Secret名が一致しているか
  • 参照しているキーが存在するか
  • PodのServiceAccountに必要な権限があるか
  • YAMLのインデントが正しいか

アプリケーション側でも、機密値をエラーメッセージへ含めないようにします。

よくある勘違い

Secretへ入れれば自動的に暗号化される

Base64は暗号化ではありません。

一般的なKubernetesでは、保存時の暗号化設定も確認する必要があります。

Base64だからGitへコミットしてよい

誰でも簡単にデコードできます。

機密情報を含むSecret YAMLを、そのままGitへ保存してはいけません。

Secretを作れば自動的に全Podへ渡される

自動では渡されません。

DeploymentやPodのYAMLから明示的に参照します。

Secretを更新すれば環境変数も変わる

起動済みコンテナの環境変数は変わりません。

新しい値を反映するにはPodの再作成が必要です。

Secretを更新すればDeploymentも再起動する

Secretの更新だけではDeploymentは自動再起動しません。

Secretを読める人は値を読めない

Secretを取得できる人は、Base64をデコードして元の値を読めます。

RBACで読み取り権限を制限します。

Secretを環境変数にすれば絶対に漏れない

アプリケーションのログ、エラー出力、デバッグ画面、子プロセスなどから漏れる可能性があります。

アプリケーション側の扱いも重要です。

Secretを安全に扱うためのチェックリスト

  • SecretのYAMLを平文・Base64のままGitへ保存しない
  • SecretをDockerfileやコンテナイメージへ埋め込まない
  • RBACは最小権限にする
  • 不要なコンテナへSecretを渡さない
  • Namespaceを使って環境を分離する
  • アプリケーションログへ機密値を出さない
  • CI/CDのログへ機密値を出さない
  • 保存時の暗号化設定を確認する
  • 定期的なローテーション方法を決める
  • 漏えい時に失効・再発行できるようにする
  • EKSではSecrets ManagerやPod Identityも検討する

これまでの流れへSecretを加える

ここまで学んだリソースを並べると、次のようになります。

Node
└── Podを動かすサーバー
        ↑
Deployment
└── Podの数と更新を管理する
        ↓
Pod
└── Containerを動かす単位
        ↑
Service
└── 変化するPod群へ安定した接続先を提供する
        ↑
Ingress
└── 外部のHTTP・HTTPS通信をServiceへ振り分ける

ConfigMap
└── 機密ではない設定値をContainerへ渡す

Secret
└── パスワードやトークンなどをContainerへ渡す

通信と設定を分けて考えると理解しやすくなります。

通信の流れ
Ingress → Service → Pod → Container

設定の流れ
ConfigMap → Pod → Container

機密情報の流れ
Secret → Pod → Container

まとめ

Secretは、パスワード・APIトークン・秘密鍵などの機密情報を、コンテナイメージやPod定義から分離して扱うKubernetesリソースです。

Secret
= 機密情報を保持するKubernetesリソース

Deployment・Pod
= Secretの参照方法を定義する

Container
= 環境変数やファイルとして値を利用する

重要な点は次のとおりです。

  • 機密ではない設定はConfigMap、機密情報はSecretへ分ける
  • dataの値はBase64形式で記述する
  • stringDataには通常の文字列を記述できる
  • Base64は暗号化ではない
  • secretRefで複数の値を環境変数として渡せる
  • secretKeyRefで特定の値だけを参照できる
  • Volumeを使うと秘密鍵などをファイルとして渡せる
  • 環境変数はSecret更新だけでは変わらない
  • Secretの更新だけではDeploymentは再起動しない
  • Secret YAMLをそのままGitへ保存しない
  • RBACで読み取れる人やPodを制限する
  • EKS 1.28以降ではKubernetes APIデータが標準でEnvelope Encryptionの対象になる
  • EKSではAWS Secrets ManagerとCSI Driverを利用する構成もある

一言でまとめると、次のようになります。

Secretは機密情報をアプリケーションから分離する仕組みですが、暗号化・権限・配布・ローテーションまで設計して初めて安全に運用できます。

次は、Podが再作成されてもデータを残すための Volume・PersistentVolume・PersistentVolumeClaim について整理します。

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?