こんにちは! Kubernetesの世界へようこそ。
コンテナオーケストレーションの波に乗って、複数のマイクロサービスをデプロイし、「よし、動いた!」と胸をなでおおろしているそこのあなた。
ちょっと待ってください。そのクラスター、デフォルトでは「隣の家との壁がない状態」になっていませんか?
今回は、Kubernetesのセキュリティにおいて最も重要でありながら、多くのエンジニアがハマる落とし穴である「NetworkPolicy(ネットワークポリシー)」について、優しく、そして実務で即座に使えるレベルまで徹底的に解説していきます。
これをマスターすれば、あなたのクラスターの安全性は劇的に向上し、セキュリティ監査も怖くなくなりますよ。ぜひ最後までついてきてくださいね!
—
1. なぜKubernetesのネットワークは危険なのか?
まずは、Kubernetesの基本思想を知ることから始めましょう。
Kubernetesをインストールして最初に驚くことの一つが、「デフォルトでは、どのPodからどのPodへも通信し放題(All Allow)」という仕様です。
異なる名前空間(Namespace)にいるPod同士であっても、IPアドレスさえわかれば通信できてしまいます。
これは開発初期には非常に便利です。「あれがつながらない、これがつながらない」と悩む必要がないからです。しかし、本番環境やステージング環境において、これが何を意味するか分かりますか?
- 万が一、フロントエンドのWebアプリが脆弱性を突かれて乗っ取られたら?
- 攻撃者は、同じクラスター内にある機密情報を扱うバックエンドのデータベース(DB) Podへ、何の障害もなく到達できてしまいます。
「うちはクラウドのVPCで守られているから大丈夫」?
いいえ、クラウドのファイアウォールはクラスターの「外側」を守るもの。クラスターの「内側(東西トラフィック)」は、Kubernetes自身の機能で守る必要があるのです。
そこで登場するのが、NetworkPolicyです。
—
2. ツールの役割と「CNIプラグイン」という最大の落とし穴
NetworkPolicyは、KubernetesのAPIリソースの一つです。「どのPodからどのPodへの通信を許可・拒否するか」をルール(ファイアウォールの規則のようなもの)として定義します。
しかし、ここで絶対に知っておかなければならない最大の落とし穴があります。
> 「KubernetesのAPIでNetworkPolicyを作っても、それだけでは何の効果もない場合がある」
どういうことか?
実は、NetworkPolicyのルールを実際に解釈してパケットをドロップしたり通したりするのは、Kubernetes本体ではなく、下回りで動いているCNI(Container Network Interface)プラグインの仕事なのです。
- Calico / Cilium / Antrea など: NetworkPolicyを完全にサポートしています(おすすめ)。
- Kubernetes標準のKubenetや、一部のシンプルなCNI: NetworkPolicyをサポートしていません(マニフェストを書いても無視されます)。
もしあなたがAWSのEKSやGCPのGKE、あるいはオンプレミスでクラスターを構築しているなら、「自分のクラスターのCNIがNetworkPolicyをサポートしているか?」を真っ先に確認してください。これが最初の関門です。
—
3. 基礎のセットアップ:動く環境を作ろう
今回は、CNIとして広く使われており、NetworkPolicyを強力にサポートするCalico(または標準でサポートしている環境)を想定して話を進めます。
まずは、NetworkPolicyの動作を実感するための「HelloWorld」的な実験をしてみましょう。
ステップ1: 検証用の名前空間とPodを作る
通信の制御をテストするために、2つの名前空間(`app-A` と `app-B`)を作ります。
namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: app-a
—
apiVersion: v1
kind: Namespace
metadata:
name: app-b
それぞれの名前空間に、簡易的なNginxサーバーを立てておきます。
app-aにサーバーをデプロイ
kubectl run web-a –image=nginx -n app-a
app-bにサーバーをデプロイ
kubectl run web-b –image=nginx -n app-b
この状態(NetworkPolicy未設定)で、`app-a` のPodから `app-b` のNginxへ通信してみます。
app-aからapp-bのIPに向かって通信してみる(通ってしまうはず!)
kubectl exec -it web-a -n app-a — curl
はい、すんなりレスポンスが返ってきましたね。これが「全通し」の状態です。
—
4. 実戦! NetworkPolicyでトラフィックを絞り込む
ここからが本番です。実務で使える具体的なセグメンテーションのパターンを2つ紹介します。
パターン1:名前空間をまたいだ通信の厳格な制御(Ingress)
「`app-a` からの通信は絶対に受け付けないが、特定の監視ツールなどからは受け付けたい」といった要件のベースとなる設定です。
基本の考え方として、NetworkPolicyは「デフォルト・セキュア(何もかも禁止)」から始めるのが鉄則です。
まずは、「名前空間 `app-b` 内のすべての通信をデフォルトで完全に遮断する(Default Deny)」マニフェストを作りましょう。
01-default-deny.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: app-b
spec:
podSelector: {} # 名前空間内のすべてのPodが対象
policyTypes:
- Ingress
# ingressセクションが空なので、外からの通信はすべて拒否される
これを適用すると、先ほどまで成功していた `app-a` から `app-b` への `curl` はタイムアウトするようになります。おめでとうございます、これで最初の要塞ができました!
次に、「特定の条件を満たす通信だけを許可する」ルールを追加します。
例えば、「`app-a` 名前空間にいて、かつ `role: frontend` というラベルがついているPodからだけは、`app-b` へのアクセスを許可する」という場合です。
02-allow-specific.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-frontend-a
namespace: app-b
spec:
podSelector:
matchLabels:
app: web-b # app-bにあるこのラベルのPodに対して許可
policyTypes:
- Ingress
ingress:
- from:
# 名前空間を指定して絞り込む
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: app-a
# さらにPodのラベルで絞り込む
podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 80 # Nginxのポートのみ許可
この設定により、ただ同じクラスターにいるというだけで自由にアクセスできた空間に、「誰が、どこから、どのポートにアクセスして良いか」という厳格な身元確認が生まれました。
—
パターン2:踏み台にされても外に出さない(Egress制限)
初心者がやりがちなもう一つの落とし穴が、Egress(外向き)の通信放置です。
もしPodが乗っ取られた場合、攻撃者はクラスター内を荒らすだけでなく、外部の悪意あるサーバー(C2サーバーなど)と通信してデータを盗み出そうとします(データ Exfiltration)。これを防ぐのがEgress制限です。
以下のポリシーは、「このPodからの外部インターネットへの通信を一切禁止し、必要な内部サービス(例えば社内DB)への通信だけを許可する」設定です。
03-egress-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-egress
namespace: app-a
spec:
podSelector:
matchLabels:
app: web-a
policyTypes:
- Egress
egress:
# 1. 内部のDNS名前解決(CoreDNS)への通信は必須(これがないと名前解決できなくなるので注意!)
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
# 2. 許可する特定の外部宛先(例: 信頼されたAPIサーバーのIP)
- to:
- ipBlock:
cidr: 192.168.100.50/32
ports:
- protocol: TCP
port: 443
⚠️ ここで超重要な実務の知見:
Egressポリシーを適用するとき、DNS(Port 53)の許可を忘れると、アプリケーションが名前解決できずに一斉にクラッシュします。「EgressをかけるときはDNSをセットで通す」は、SREの鉄則として覚えておいてください。
—
5. まとめ:今日から始めるステップアップ
いかがだったでしょうか?
KubernetesのNetworkPolicyは、最初は難しく感じるかもしれませんが、要点を押さえれば怖くありません。
1. まずはCNIがNetworkPolicyをサポートしているか確認する
2. 「Default Deny(全部禁止)」をベースに設計する
3. Ingress(入ってくる通信)だけでなく、Egress(出ていく通信)も意識する
4. DNS(Port 53)の許可を忘れない
これをマスターすれば、あなたの管理するKubernetesクラスターのセキュリティレベルは、プロダクション環境にふさわしい堅牢なものに生まれ変わります。毎日の運用や障害対応に追われる日々から、一歩進んだ「攻めのインフラ設計」へ。
ぜひ、検証環境でマニフェストを書いて試してみてくださいね。あなたのKubernetesライフがより安全で快適なものになる応援をしています!