【実務・中級編】 クラウドネイティブ環境におけるシークレット管理(K8s Secrets vs Vault) – インフラ・ネットワーク & クラウドセキュリティ防御ガイド

現場で数々の修羅場をくぐり抜けてきた経験から断言しよう。君たちが何気なく設定している「環境変数」への機密情報のベタ書きは、攻撃者からすれば「どうぞここから盗んでください」という招待状に等しい。

「KubernetesのSecretsがあるから大丈夫」? 甘い。それは単なるBase64エンコードに過ぎない。今回は、なぜその手法が危険なのか、そしてHashiCorp Vaultを用いた「脱・環境変数」の真実を語ろう。

—

なぜ「環境変数」は攻撃者の宝庫なのか

攻撃者がWebアプリの脆弱性(例えばLFIやRCE)を突いたとき、最初に行うアクションの一つが env コマンドや /proc/self/environ の覗き見だ。ここにデータベースのパスワードやAPIキーが平文で転がっている。

特にK8s環境では、Pod内の環境変数は kubectl describe pod ができる権限さえあれば誰でも閲覧できる。さらに、ログ収集基盤(Fluentd等)が不適切に設定されていれば、コンテナの標準出力に紛れ込んだ機密情報がログサーバーにまで複製される。これは「セキュリティの連鎖的汚染」だ。

HashiCorp Vaultによる動的シークレット注入

Vaultの真骨頂は、静的な情報を保存するだけでなく、必要に応じて動的に認証情報を生成(Dynamic Secrets)できる点にある。例えば、DBのパスワードを一定時間で自動無効化・再生成すれば、万が一キーが流出しても影響範囲は数分で終わる。

実践:PythonからVaultへ安全にアクセスする

環境変数にキーを置くのではなく、PodのServiceAccountトークンを使ってVaultから直接シークレットを取得する手法が、現在クラウドネイティブ環境におけるゴールドスタンダードだ。

以下は、Pythonで hvac ライブラリを使用してシークレットを取得する実装例だ。

import hvac
import os

def get_secret_from_vault(secret_path):
    # K8sのServiceAccountトークンを使用してVaultへ認証
    # トークンは通常 /var/run/secrets/kubernetes.io/serviceaccount/token にある
    with open('/var/run/secrets/kubernetes.io/serviceaccount/token', 'r') as f:
        jwt = f.read()

    client = hvac.Client(url='https://vault.internal.example.com:8200')
    
    # K8s認証メソッドでログイン
    client.auth.kubernetes.login(
        role='my-app-role',
        jwt=jwt
    )

    # シークレットの読み出し
    read_response = client.secrets.kv.v2.read_secret_version(path=secret_path)
    return read_response['data']['data']['db_password']

# 利用例:環境変数には何も入れない
db_pass = get_secret_from_vault('prod/database/config')
print("安全にシークレットを取得しました")

—

クラウドネイティブな構成:Vault Agent Sidecar Injection

アプリ側に上記のようなロジックを書くのが面倒だというチームも多いだろう。その場合は、Vault Agent Sidecarを導入すべきだ。K8sのPod内にVault用のサイドカーコンテナを自動注入し、シークレットを共有メモリ(Shared Volume)にファイルとして書き出す手法だ。

これにより、アプリは「ローカルの特定ファイルを読む」だけで済む。

サンプル:K8s Deploymentのアノテーション

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-secure-app
spec:
  template:
    metadata:
      annotations:
        # Vault Agentのサイドカーを有効化する設定
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "my-app-role"
        # シークレットを /vault/secrets/config に書き出す設定
        vault.hashicorp.com/agent-inject-secret-config: "secret/data/myapp/db"
        vault.hashicorp.com/agent-inject-template-config: |
          {{- with secret "secret/data/myapp/db" -}}
          export DB_PASSWORD="{{ .Data.data.password }}"
          {{- end -}}
    spec:
      containers:
      - name: app
        image: my-secure-app:latest
        # 環境変数に機密を置かないことを徹底する

—

現場のエンジニアへ送る、最後の忠告

セキュリティに「魔法の杖」はない。しかし、リスクを管理可能なレイヤーまで引き下げることはできる。

1. 環境変数を疑え: printenv を打って機密情報が出るなら、それは即時修正案件だ。
2. 最小権限の原則: Vaultのパスに対して、各マイクロサービスが必要な権限しか持たないようポリシーを細分化せよ。
3. 監査ログの徹底: 「誰が」「いつ」シークレットにアクセスしたか。このログがない環境は、インシデント発生時に手足をもがれた状態になる。

君たちが書くコードの一行一行が、システムの堅牢性を左右する。面倒な作業こそ、自動化と標準化で強固な壁に変えていく。それがプロの仕事だ。

何か技術的な壁にぶつかったら、また聞きに来い。現場からは以上だ。

コメント

タイトルとURLをコピーしました