GKEネットワークポリシーでのPod間通信の制御について

このブログシリーズ 「クラウドセキュリティ 実践集」 では、一般的なセキュリティ課題を取り上げ、「なぜ危険なのか?」 というリスクの解説から、 「どうやって直すのか?」 という具体的な修復手順(コンソール、gcloud CLI、Terraformなど)まで、分かりやすく解説します。
この記事では、GKEクラスタでネットワークポリシーが無効化されている場合のリスクと対策を解説します。

ポリシーの説明
Kubernetesのネットワークポリシーは、Pod間の通信を制御するためのセキュリティ機能です。デフォルトでは、Kubernetes内の全てのPodは相互に通信可能ですが、ネットワークポリシーを使用することで、必要な通信のみを許可し、不要な通信を遮断できます。GKEではCalicoを使用し、Dataplane V2ではCiliumを使用してネットワークポリシーを実装できます。
修復手順
⚠️ 重要な注意事項 ネットワークポリシーを有効化する前に、既存の通信パターンを分析し、必要なポリシーを事前に準備することを推奨します。
コンソールでの修復手順(Dataplane v2以前の手順)
Google Cloud コンソールを使用して、GKEクラスタでネットワークポリシーを有効化します。
- Google Cloud コンソールにアクセス
- GKEクラスタ一覧にアクセスします
- 対象のクラスタ名をクリックして詳細画面を開きます
- 既存クラスタでネットワークポリシーを有効化
- クラスタの詳細ページで「編集」ボタンをクリックします
- 「ネットワーキング」セクションまでスクロールします
- 「ネットワークポリシーを有効にする」のチェックボックスをオンにします
- ネットワークポリシープロバイダーとして「Calico」を選択(推奨)
- 「保存」をクリックして変更を適用します
- Cloud Shellを起動してクラスタに接続
gcloud container clusters get-credentials [CLUSTER_NAME] --zone [ZONE] --project [PROJECT_ID]
- ネットワークポリシーが有効になっていることを確認
# ネットワークポリシーのAPIが有効か確認 kubectl api-resources | grep networkpolicies # Calicoのポッドが実行されているか確認 kubectl get pods -n kube-system | grep calico
- デフォルト拒否ポリシーの作成
まず、全ての通信をデフォルトで拒否するポリシーを作成します:
# default-deny-all.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-all namespace: default spec: podSelector: {} policyTypes: - Ingress - Egress
kubectl apply -f default-deny-all.yaml
- 必要な通信のみを許可するポリシーを作成
例:フロントエンドからバックエンドへの通信のみを許可
# allow-frontend-to-backend.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-frontend-to-backend namespace: default spec: podSelector: matchLabels: app: backend policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: frontend ports: - protocol: TCP port: 8080
kubectl apply -f allow-frontend-to-backend.yaml
- DNSアクセスを許可(必須)
# allow-dns-access.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-dns-access namespace: default spec: podSelector: {} policyTypes: - Egress egress: - to: - namespaceSelector: matchLabels: name: kube-system ports: - protocol: UDP port: 53
kubectl apply -f allow-dns-access.yaml
gcloud CLIでの修復手順
gcloud CLIを使用してネットワークポリシーを有効化する完全な手順:
# 1. 現在のクラスタ設定を確認
gcloud container clusters describe CLUSTER_NAME \
--zone=ZONE \
--project=PROJECT_ID \
--format="value(networkPolicy.enabled)"
# 2. 既存クラスタでネットワークポリシーを有効化
gcloud container clusters update CLUSTER_NAME \
--zone=ZONE \
--project=PROJECT_ID \
--enable-network-policy
# 3. アップデートの進行状況を確認
gcloud container operations list \
--zone=ZONE \
--project=PROJECT_ID \
--filter="TYPE:UPDATE_CLUSTER AND targetLink~CLUSTER_NAME" \
--limit=1
# 4. 新規クラスタ作成時にネットワークポリシーを有効化
gcloud container clusters create secure-cluster \
--zone=ZONE \
--project=PROJECT_ID \
--enable-network-policy \
--enable-ip-alias \
--cluster-secondary-range-name=pods \
--services-secondary-range-name=services \
--enable-private-nodes \
--master-ipv4-cidr=10.0.0.0/28 \
--enable-autorepair \
--enable-autoupgrade
# 5. Dataplane V2 (Cilium) を使用する場合
gcloud container clusters create advanced-cluster \
--zone=ZONE \
--project=PROJECT_ID \
--enable-dataplane-v2 \
--enable-ip-alias \
--cluster-secondary-range-name=pods \
--services-secondary-range-name=services
# 6. ネットワークポリシーのテストツールをインストール
kubectl apply -f <https://raw.githubusercontent.com/ahmetb/kubernetes-network-policy-recipes/master/01-deny-all-traffic-to-an-application.yaml>
# 7. 接続性テスト用のPodを作成
kubectl run test-source --image=busybox --rm -it -- /bin/sh
# 別のターミナルで
kubectl run test-target --image=nginx --labels="app=target"
# 8. ポリシー適用前後の接続性を確認
# test-source Pod内から
wget -qO- test-target:80 # ポリシー適用前は成功
# ポリシー適用後は失敗することを確認
# 9. 監視用のログクエリを設定
gcloud logging read '
resource.type="k8s_cluster"
AND protoPayload.methodName="io.k8s.networking.v1.networkpolicies"' \
--limit=50 \
--format=json \
--project=PROJECT_ID
Terraformでの修復手順
ネットワークポリシーを有効化したGKEクラスタと、セキュアなネットワークポリシーのTerraformコードを説明します。
# ネットワークポリシーを有効化したGKEクラスタ
resource "google_container_cluster" "primary" {
name = "secure-gke-cluster"
location = var.region
# 初期ノードプールを削除(後で適切なものを作成)
remove_default_node_pool = true
initial_node_count = 1
# ネットワークポリシーの有効化(重要)
network_policy {
enabled = true
provider = "CALICO" # CALICOを使用(高度な機能をサポート)
}
# Dataplaneの設定(注意:ADVANCED_DATAPATHを使用する場合、network_policyは自動的に有効になります)
# datapath_provider = "ADVANCED_DATAPATH" # Ciliumを使用する場合はコメントアウトを解除
# IPエイリアスの有効化(ネットワークポリシーに必要)
ip_allocation_policy {
cluster_secondary_range_name = "pods"
services_secondary_range_name = "services"
}
# プライベートクラスタ設定(推奨)
private_cluster_config {
enable_private_nodes = true
enable_private_endpoint = false
master_ipv4_cidr_block = "10.0.0.0/28"
}
# ワークロードアイデンティティ
workload_identity_config {
workload_pool = "${var.project_id}.svc.id.goog"
}
}
# ノードプール
resource "google_container_node_pool" "primary" {
name = "primary-node-pool"
location = var.region
cluster = google_container_cluster.primary.name
node_count = var.node_count
node_config {
machine_type = var.machine_type
# セキュリティ設定
shielded_instance_config {
enable_secure_boot = true
enable_integrity_monitoring = true
}
metadata = {
disable-legacy-endpoints = "true"
}
oauth_scopes = [
"<https://www.googleapis.com/auth/logging.write>",
"<https://www.googleapis.com/auth/monitoring>",
]
}
}
# Kubernetesプロバイダーの設定
provider "kubernetes" {
host = "<https://$>{google_container_cluster.primary.endpoint}"
token = data.google_client_config.default.access_token
cluster_ca_certificate = base64decode(google_container_cluster.primary.master_auth[0].cluster_ca_certificate)
}
# ネームスペースの作成
resource "kubernetes_namespace" "app_namespaces" {
for_each = toset(["frontend", "backend", "database"])
metadata {
name = each.key
labels = {
name = each.key
}
}
}
# デフォルト拒否ポリシー(各ネームスペースに適用)
resource "kubernetes_network_policy" "default_deny_all" {
for_each = kubernetes_namespace.app_namespaces
metadata {
name = "default-deny-all"
namespace = each.value.metadata[0].name
}
spec {
pod_selector {}
policy_types = ["Ingress", "Egress"]
}
}
# DNS解決を許可(全ネームスペース)
resource "kubernetes_network_policy" "allow_dns" {
for_each = kubernetes_namespace.app_namespaces
metadata {
name = "allow-dns-access"
namespace = each.value.metadata[0].name
}
spec {
pod_selector {}
policy_types = ["Egress"]
egress {
to {
namespace_selector {
match_labels = {
"name" = "kube-system"
}
}
}
ports {
port = "53"
protocol = "UDP"
}
ports {
port = "53"
protocol = "TCP"
}
}
}
}
# フロントエンドからバックエンドへの通信を許可
resource "kubernetes_network_policy" "frontend_to_backend" {
metadata {
name = "allow-frontend-to-backend"
namespace = "backend"
}
spec {
pod_selector {
match_labels = {
app = "backend"
}
}
policy_types = ["Ingress"]
ingress {
from {
namespace_selector {
match_labels = {
name = "frontend"
}
}
pod_selector {
match_labels = {
app = "frontend"
}
}
}
ports {
port = "8080"
protocol = "TCP"
}
}
}
}
# バックエンドからデータベースへの通信を許可
resource "kubernetes_network_policy" "backend_to_database" {
metadata {
name = "allow-backend-to-database"
namespace = "database"
}
spec {
pod_selector {
match_labels = {
app = "database"
}
}
policy_types = ["Ingress"]
ingress {
from {
namespace_selector {
match_labels = {
name = "backend"
}
}
pod_selector {
match_labels = {
app = "backend"
}
}
}
ports {
port = "5432" # PostgreSQL
protocol = "TCP"
}
}
}
}
# インターネットからフロントエンドへのHTTPS通信を許可
resource "kubernetes_network_policy" "allow_internet_to_frontend" {
metadata {
name = "allow-internet-to-frontend"
namespace = "frontend"
}
spec {
pod_selector {
match_labels = {
app = "frontend"
}
}
policy_types = ["Ingress"]
ingress {
ports {
port = "443"
protocol = "TCP"
}
ports {
port = "80"
protocol = "TCP"
}
}
}
}
# バックエンドから外部APIへの通信を許可(必要に応じて)
resource "kubernetes_network_policy" "backend_egress_to_external" {
metadata {
name = "allow-backend-external-api"
namespace = "backend"
}
spec {
pod_selector {
match_labels = {
app = "backend"
}
}
policy_types = ["Egress"]
egress {
# DNSアクセス
to {
namespace_selector {
match_labels = {
name = "kube-system"
}
}
}
ports {
port = "53"
protocol = "UDP"
}
}
egress {
# 外部API(HTTPS)
ports {
port = "443"
protocol = "TCP"
}
}
}
}
# 変数定義
variable "project_id" {
description = "GCP Project ID"
type = string
}
variable "region" {
description = "GCP region"
type = string
default = "asia-northeast1"
}
variable "node_count" {
description = "Number of nodes"
type = number
default = 3
}
variable "machine_type" {
description = "Machine type for nodes"
type = string
default = "e2-standard-4"
}
# ネットワークポリシーの状態を出力
output "network_policy_status" {
value = {
enabled = google_container_cluster.primary.network_policy[0].enabled
provider = google_container_cluster.primary.network_policy[0].provider
}
}
最後に
この記事では、GKEクラスタでネットワークポリシーを有効化し、Pod間通信を制御する方法について、リスクと対策を解説しました。
ネットワークポリシーを適用することで、最小権限の原則に基づいた通信制御を実現し、横展開攻撃やデータ漏洩のリスクを大幅に軽減できます。デフォルト拒否ポリシーから始めて、必要な通信のみを明示的に許可することが重要です。
この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。 運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。 最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。