現場で数々の修羅場をくぐり抜けてきた経験から断言しよう。君たちが何気なく設定している「環境変数」への機密情報のベタ書きは、攻撃者からすれば「どうぞここから盗んでください」という招待状に等しい。
「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. 監査ログの徹底: 「誰が」「いつ」シークレットにアクセスしたか。このログがない環境は、インシデント発生時に手足をもがれた状態になる。
君たちが書くコードの一行一行が、システムの堅牢性を左右する。面倒な作業こそ、自動化と標準化で強固な壁に変えていく。それがプロの仕事だ。
何か技術的な壁にぶつかったら、また聞きに来い。現場からは以上だ。
コメント