Kubernetesの「裸の王様」を晒すな:匿名認証とRBAC不備が招くクラスタ崩壊の現実
やあ、現場のエンジニア諸君。今日はKubernetes(K8s)における「最も見栄えは良いが、最も危険な設定ミス」について話をしよう。
君たちがせっせとコンテナをデプロイし、マニフェストを書き換えている裏側で、APIサーバーの公開設定が「鍵の掛かっていない玄関」になっていることは少なくない。特に system:anonymous ユーザーに過剰な権限を与えていたり、ClusterRoleBinding で system:unauthenticated グループを野放しにしているケースは、我々攻撃者からすれば「どうぞクラスタを乗っ取ってください」という招待状に等しいんだ。
今回は、この「認証の盲点」がどう悪用されるのか、そしてそれをどう鉄壁の防御に変えるのかを、現場の知見を交えて解説する。
—
1. 攻撃の構図:なぜ「匿名アクセス」が致命的なのか
KubernetesのAPIサーバーは、認証に失敗したリクエストに対して system:anonymous というユーザーを割り当てる仕様になっている。もし、君たちが「利便性」のためにこのユーザーに対して view 権限や、最悪の場合 edit 権限を付与してしまったら、そこが全ての終わりの始まりだ。
攻撃者はまず、ターゲットのAPIサーバーに対して curl を投げ、応答が返ってくるかを確認する。
# 匿名でAPIサーバーのバージョンを確認する(これが通るなら入り口は全開だ)
curl -k https://<k8s-api-server>:6443/version
ここからが本番だ。もし system:anonymous が system:discovery 以外の権限を持っている場合、kubectl を通さずとも、APIを直接叩くスクリプト一つでクラスタ内の情報を根こそぎ抽出できる。
攻撃のPoC(Pythonによる偵察スクリプト)
以下のコードは、匿名アクセスが許可されている場合にクラスタ内の全Podの情報を抜き出す簡便なサンプルだ。
import requests
import json
# ターゲットのAPIサーバーURL
API_SERVER = "https://<target-k8s-api>:6443"
def extract_pod_info():
# トークンなし(匿名)でPodリストを要求
url = f"{API_SERVER}/api/v1/pods"
try:
response = requests.get(url, verify=False) # 検証用のためverify=False
if response.status_code == 200:
pods = response.json()
for pod in pods.get('items', []):
print(f"発見されたPod: {pod['metadata']['name']}")
else:
print(f"アクセス拒否: {response.status_code}")
except Exception as e:
print(f"接続エラー: {e}")
if __name__ == "__main__":
extract_pod_info()
このスクリプトが動いてしまう時点で、君のクラスタは「誰でも内部を覗ける状態」にある。これがもし ClusterRole で secrets の読み取り権限まで持っていたら? データベースのパスワードや外部サービスのAPIキーは即座に流出だ。
—
2. 防御の要:RBAC監査と厳格な権限管理
この脆弱性を封じ込めるには、二段構えの防衛策が必要だ。
① 「匿名」を徹底的に排除する
まず、system:anonymous には、最小限の system:discovery 権限以外は一切与えないのが鉄則だ。ClusterRoleBinding を再確認し、もし心当たりがないなら即座に削除しろ。
② 監査ログで「誰が何をしたか」を可視化する
Kubernetesの監査ログ(Audit Log)を有効にすることで、匿名ユーザーによる不審なアクセスを検知できる。これはインシデントハンドリングにおける最後の砦だ。
以下の設定をAPIサーバーの起動引数に追加(またはKubeadmの設定ファイルに記述)しろ。
# audit-policy.yamlの例
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# system:anonymous によるアクセスを全て記録する
- level: RequestResponse
users: ["system:anonymous"]
verbs: ["get", "list", "watch"]
—
3. 明日から使えるセキュアな実装ルール
最後に、現場で今日から適用すべき「セキュアなクラスタ設計」のチェックリストを置いておく。
1. デフォルトの制限: system:anonymous に対して、system:discovery 以外の ClusterRole がバインドされていないか定期的に監査する。
2. APIサーバーの露出: APIサーバーは決してインターネットに直接公開するな。VPN経由、あるいは Cloud IAP(Identity-Aware Proxy)を介したアクセスに限定すること。
3. RBACの最小権限: ClusterRole ではなく、必要な名前空間に限定した Role と RoleBinding を優先的に使用する。
Nginxリバースプロキシによる防御(参考)
APIサーバーの手前にNginxを置く場合、特定のIP以外からのアクセスを遮断し、ヘッダーを検証する設定を必ず入れること。
# Nginxの設定例
server {
listen 443 ssl;
server_name k8s-api.your-domain.com;
# 許可するIP帯域のみに制限
allow 10.0.0.0/24;
deny all;
location / {
proxy_pass https://<internal-k8s-api>:6443;
# 不正なヘッダーのフィルタリングなど
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
最後に:セキュリティは「性悪説」で設計せよ
Kubernetesは強力なツールだが、デフォルト設定は「利便性」を優先しすぎている傾向がある。君たちが書いたコードがクラウド上で動くとき、その裏でAPIサーバーがどう守られているか、今一度確認してほしい。
「誰もそんなところ突いてこないだろう」という慢心こそが、我々攻撃者が最も好む餌だ。設定ファイルを見直すなら、今すぐやるんだ。明日では遅すぎるかもしれないのだから。
何か不明な点があればいつでも聞いてくれ。君たちがセキュアな設計を追求する限り、私はいつでもバックアップする。
コメント