Kubernetesの心臓を止めるな:etcdを「ただのKVストア」で終わらせないための防御術
エンジニアの諸君、お疲れ様。今日もどこかで誰かが「Kubernetesの設定はデフォルトで安全」という甘い幻想を抱き、泣きを見る準備をしている。
Kubernetesにおける etcd は、クラスタの全状態(State)を握る心臓部だ。もし君が「クラウドプロバイダーのマネージドサービス(EKSやGKE)を使っているから大丈夫」と考えているなら、それは半分正解で半分危険だ。今回は、オンプレミスかクラウドかを問わず、必ず押さえておくべき「etcdの暗号化」と「TLS通信」という、防御の最前線について語ろう。
なぜ攻撃者はetcdを狙うのか?
もし攻撃者がAPI Serverを経由せずに、直接 etcd にアクセス(あるいはスナップショットを取得)できたらどうなるか?
- Secretsの平文抽出:
etcdに格納されたSecretは、デフォルトではBase64エンコードされているだけだ。これは暗号化ではない。攻撃者はスナップショットを吸い出すだけで、DBのパスワード、APIトークン、SSH鍵をすべて手に入れる。 - 権限昇格: 攻撃者が
etcdのデータを直接改ざんできれば、新しいClusterRoleを作成し、自身のPodに管理者権限を付与することも容易だ。
これは理論上の話ではない。KubernetesのAPI認証をバイパスして直接バックエンドを突くのは、標的型攻撃における「王道」なんだ。
—
1. etcdの暗号化(Encryption at Rest)の実装
Kubernetesの EncryptionConfiguration を使って、ディスク上のデータを保護する。これをしていない状態で「セキュリティを担保している」とは言わせない。
以下のマニフェストファイル encryption-config.yaml を作成して、API Serverの起動引数に --encryption-provider-config として渡すのが鉄則だ。
# encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets # Secretリソースのみを暗号化対象にするのが鉄則
providers:
- aescbc: # AES-CBCモードで暗号化。鍵はBase64でエンコードして設定
keys:
- name: key1
secret: <ここに32バイトのランダムな文字列をBase64エンコードして記載>
- identity: {} # 復号失敗時のフォールバック。基本はこれ
ポイント: 鍵管理は外部のKMS(AWS KMSやGCP KMS)と連携させるのがベストだ。自前で管理すると鍵のローテーションで詰む。現場では「鍵をファイルに書くな、IAMロールで引け」と叩き込んでいる。
—
2. 通信のTLS化:mTLSを徹底する
API Serverと etcd 間の通信を暗号化するのは当然として、さらに「相互認証(mTLS)」を強制せよ。証明書を持っていない怪しいPodや攻撃者が、etcd ポート(デフォルト 2379)へ不正に接続することを防ぐ。
etcd 側の設定ファイルで、以下のようにTLSパラメータを絞り込む。
# etcd.conf の抜粋
--client-cert-auth=true \ # クライアント証明書による認証を強制
--trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt \
--cert-file=/etc/kubernetes/pki/etcd/server.crt \
--key-file=/etc/kubernetes/pki/etcd/server.key \
--peer-client-cert-auth=true \ # ピア(メンバ間)通信も暗号化・認証
--peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
もし、君たちがPythonで etcd に直接クエリを投げるようなツールを作っているなら、以下のように接続時に証明書を必ず指定すること。
# python-etcd3 を使ったセキュアな接続例
import etcd3
# 証明書を指定して信頼を担保する
etcd = etcd3.client(
host='etcd-cluster-endpoint',
port=2379,
ca_cert='/path/to/ca.crt',
cert_cert='/path/to/client.crt',
cert_key='/path/to/client.key'
)
# これで中間者攻撃を防ぎつつ、許可されたノードだけがアクセス可能になる
print(etcd.get('/registry/secrets/default/my-secret'))
—
実戦的なアドバイス:セキュリティは「設定」で終わらない
どれだけ強固な暗号化を施しても、etcd にアクセスできる「ネットワーク経路」が野放しであれば意味がない。
1. ネットワーク分離: etcd を動かしているノードは、プライベートサブネットに隔離し、セキュリティグループ(またはNetworkPolicy)で「API ServerのIPアドレス以外からの2379ポートへの接続」を拒否すること。
2. 監査ログの有効化: 誰が etcd に触ろうとしたか、API Serverの監査ログ(Audit Log)を必ず監視すること。不審なアクセスがあれば即座にアラートを飛ばす。
まとめ
セキュリティとは「城壁を高くする」ことではなく、「侵入された時に致命傷を与えない」ための多層防御だ。etcd の保護をサボることは、家の鍵をかけずに金庫を置くのと同じことだと心に刻んでおいてほしい。
明日、出社したらまずは自分のクラスタの EncryptionConfiguration が有効か、etcd へのアクセスがmTLSで保護されているかを確認すること。それが、エンジニアとしての最初の矜持だ。
コメント