Kubernetesにおけるシークレットの最小権限・自動ローテーション・監査可能性を両立するなら、VaultとGitOpsの組み合わせが有力です。Vault Secrets Operator (VSO) はCRDを通してVaultの値をKubernetes Secretとして調達し、Gitで意図を宣言できます。
本稿では、VSOのCRD設計、Argo CD/Fluxによる同期順序、最小権限・ローテーション設計、障害時のふるまいまでを、Ops実務と資格対策の両面から解説します。
VSOは、Vaultへの接続・認証・シークレット取得をKubernetesのCRDとして宣言し、最終的にKubernetes Secretを生成します。GitOpsでは、これらCRDマニフェストをGitに保存し、Argo CDやFluxが同期してオペレーターの調和処理を呼び出します。結果として、秘匿情報の配布は「意図の宣言=Gitの変更」に集約され、変更理由やレビュー履歴を監査できます。
典型フローは次の通りです。1) GitにVaultConnection/VaultAuth/VaultStaticSecret/VaultDynamicSecretを定義 2) Argo CD/Fluxが同期 3) VSOがVaultに認証しシークレットを取得・更新 4) アプリPodはKubernetes Secretを参照。
GitOps×VSO×Vaultのデータフロー
最小セットのCRD例(Gitに置く宣言)
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultConnection
metadata:
name: vc-default
namespace: app
spec:
address: https://vault.example.com:8200
# Enterprise利用時はnamespace等を指定可。TLS/CABundleもここで指定。
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultAuth
metadata:
name: va-k8s
namespace: app
spec:
vaultConnectionRef: vc-default
method: kubernetes
mount: kubernetes
kubernetes:
role: app-role
serviceAccount: app-sa
audiences:
- vault
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: app-kv
namespace: app
spec:
vaultAuthRef: va-k8s
mount: kv
type: kv-v2
path: app/config
refreshAfter: 1m
destination:
create: true
name: app-config
# 必要なキーのみ抽出
data:
- secretKey: DB_USER
remoteRef:
key: username
- secretKey: DB_PASS
remoteRef:
key: passwordVSOの主なCRDは、接続情報を定義するVaultConnection、認証方式と紐付くVaultAuth、静的値(KVなど)を取得するVaultStaticSecret、動的値(DBやクラウド一時認証など)を扱うVaultDynamicSecretです。これらをNamespace単位で分けると、チーム境界や責務分離が明確になります。
Vault側のポリシーは「必要なパスだけ許可」が基本です。VaultAuthが指すロール(Kubernetes AuthのroleやAppRole)は、対象パスのread/issue/renewに限定します。Kubernetes側ではVSOのServiceAccountに対して、対象Namespace内のSecret作成・更新の最小権限のみを与えます。
| 観点 | Vault Secrets Operator (VSO) | Vault CSI/Agent Injector | External Secrets Operator (ESO) |
|---|---|---|---|
| 配布単位 | CRD(K8s Secretを生成) | Pod注入/CSIボリューム(Secretを生成しない構成も多い) | CRD(K8s Secretを生成) |
| 動的シークレット | 対応(TTL更新・再発行) | 基本はランタイム注入(再読込/再起動で更新) | 対応(プロバイダ経由で取得) |
| テンプレート化 | 一部サポート(キー抽出/変換) | 限定的(注入中心) | サポート(テンプレート/マッピング) |
| GitOps適合 | 高い(CRDで意図を宣言) | 中(注入設定はPod側アノテーション中心) | 高い(CRDで意図を宣言) |
| Vault認証 | Kubernetes/AuthRole/JWT等をVaultAuthで宣言 | Agent側で設定(Kubernetes/AuthRole等) | プロバイダ設定(Vault AppRole/K8s等) |
VaultポリシーとKubernetes Authロールの最小例(概念)
# Vaultポリシー(HCL): app-policy
path "kv/data/app/config" {
capabilities = ["read"]
}
# Kubernetes Authロール作成(概念的なCLI例)
# 実際のパラメータ名は環境に合わせて調整してください
vault write auth/kubernetes/role/app-role \
bound_service_account_names=app-sa \
bound_service_account_namespaces=app \
policies=app-policy \
ttl=1h同期順序は重要です。Argo CDならsync-waveアノテーション、FluxならKustomizationのdependsOnで、VaultConnection/VaultAuth → VaultStatic/DynamicSecret → Workloadの順に整列させます。これにより、アプリ適用時にはすでにSecretが存在します。
リポジトリ構成は、baseにVSO CRDと共通ポリシー、overlayに環境固有(パスやロール名、宛先Secret名)を置く形がわかりやすいです。マルチクラスターではクラスター毎のKustomization/Argo Applicationで分離します。
Argo CDでの同期順序(例)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: app-secrets
annotations:
argocd.argoproj.io/sync-wave: "0"
spec:
source:
repoURL: https://git.example.com/org/app-config.git
path: k8s/overlays/prod/secrets
targetRevision: main
destination:
server: https://kubernetes.default.svc
namespace: app
syncPolicy:
automated:
prune: true
selfHeal: true
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: app-workload
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
source:
repoURL: https://git.example.com/org/app-config.git
path: k8s/overlays/prod/workload
targetRevision: main
destination:
server: https://kubernetes.default.svc
namespace: appGitにはVaultパスや宛先Secret名などのメタデータだけを保存し、値はVaultだけに保持します。VSOのServiceAccountにはSecretのcreate/update権限のみを付与し、list/getを不要にしないか検討します(監査ポリシーに応じて決定)。
ネットワーク的には、VSO PodからVaultへのEgressを許容するNetworkPolicyを設定し、VaultにはmTLS/適切なCAを設定します。バックアップ面では、Kubernetes Secretのバックアップはあくまでキャッシュと捉え、復元はVaultから再調達する設計に寄せます。
RBAC最小例(VSOがSecretを作るための権限)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: vso-secrets-writer
namespace: app
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: vso-secrets-writer-binding
namespace: app
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: vso-secrets-writer
subjects:
- kind: ServiceAccount
name: vault-secrets-operator
namespace: vso-system静的シークレットはrefreshAfter等の周期やSpec変更で再取得されます。動的シークレットはTTLに合わせて更新/再発行され、最大TTL到達で再発行へ移行します。いずれも、更新されたKubernetes Secretをアプリがどう取り込むか(ローリング再起動、SIGHUP、ファイル監視など)は別途設計が必要です。
Vault到達不能時や認証失敗時は、VSOがステータス条件とイベントで失敗を通知し、以前に生成済みのKubernetes Secretは即座に削除されないのが一般的です。復旧後に再調和されます。挙動はエンジンやバージョン設定に依存するため、本番適用前に意図通りかを検証してください。
運用時のコマンド例
# Argo CDで強制再同期
aio argocd app sync app-secrets
# Secret更新に伴うDeploymentのローリング再起動
kubectl -n app rollout restart deploy/myapp
# 失敗イベントの確認
kubectl -n app describe vaultstaticsecret app-kv
kubectl -n vso-system logs deploy/vault-secrets-operator出題では、どのリソースをGitで管理し、Vault側で何を定義するか、依存順序をどう保証するか、という運用設計が問われがちです。VSOのCRD役割、Kubernetes AuthロールとVaultポリシーの最小化、Argo CD/Fluxの同期制御は押さえておきましょう。
障害時のふるまい(Vault不達や認証失敗時の挙動、Secretの残存、動的シークレットのTTL更新ロジック)も理解しておくと、実務と試験の両方で有利です。
Kustomizeレイアウト(概念)
# ディレクトリ構成(例)
# k8s/
# ├─ base/
# │ ├─ vso/ # VSO CRDインスタンス(Connection/Auth/Static/Dynamic)
# │ └─ rbac/ # 最小RBAC
# └─ overlays/
# ├─ dev/
# │ ├─ kustomization.yaml
# │ └─ patches/ # Vaultパスや宛先Secret名の調整
# └─ prod/
# ├─ kustomization.yaml
# └─ patches/
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base/vso
- ../../base/rbac
namespace: app
namePrefix: prod-
# 必要に応じてpatchesでVaultパスやロール名を変えるOps
問題 1
GitOpsでVSOを用いてアプリにシークレットを供給する。起動失敗を避け、最小権限を満たす構成として最も適切なのはどれか。
正解: A
Secret→Workloadの順序制御(sync-waveで0→1→2)と、Vaultポリシーの必要最小権限(対象パスのみread)が原則。Bは順序が逆でポリシーが過剰、Cは順序未定義と過剰権限、Dは競合と重大なセキュリティ違反がある。
VSOとVault Agent Injector/CSIはどちらを選べばよいですか?
GitOpsで「Kubernetes Secretを成果物として残す」運用ならVSOが適します。Podへの直接注入やファイルとしてのエフェメラル提供を重視し、Secretリソースを作りたくない場合はAgent Injector/CSIが適します。両者の混在は可能ですが、パターンは明確に分けて運用してください。
Secretが更新されたらPodは自動で再起動されますか?
Kubernetes Secretを環境変数で参照している場合は自動再起動されません。ボリュームでマウントしている場合はファイルは更新されますが、アプリ側の再読込が必要です。ローリング再起動の自動化(例: Argo CDのhooksや専用コントローラの利用)を検討してください。
マルチテナント環境ではどう分割すべきですか?
NamespaceごとにVaultConnection/VaultAuthを分離し、VaultのKubernetes AuthロールもNamespaceとServiceAccountでバインドします。Argo CD/FluxのスコープもNamespaceまたはアプリ単位で分け、RBACはSecretのcreate/updateのみを付与する最小権限で運用します。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
Vault のコア概念を最短距離で理解する:Secret / Auth / Policy / Token
HashiCorp Vault Associate レベルで押さえるべきコア概念(Secret Engine、Auth ...
Vault Operations Professional: 上位資格としての範囲を実務目線で押さえる
HashiCorp Vault Operations Professional(Ops Pro)の出題範囲を、Assoc...
Vaultにおけるパスベースのルーティング: マウントとAPI構造を読み解く
HashiCorp Vaultのパスベースのルーティングを、マウント設計とAPIパスの観点から整理。Associateレ...
Vault Tokens の基礎: 認証の起点となる概念
HashiCorp Vault におけるトークンの役割、種類、ライフサイクル、ポリシー連携、設計パターンをAssocia...
Vault のトークン種類を正しく使い分ける: service / batch 実践
HashiCorp Vault Associate 向けの試験対策と実務運用を両立させた、service トークンと b...