EKS の強化された Network Policy を利用して、DNS ベースでのネットワーク制御を行ってみる
背景
EKS Auto Mode の現時点での制約として Pod の Security Group が使えないという物があります。
EKS Auto Mode does not support:
Security Groups per Pod (SGPP).
https://docs.aws.amazon.com/eks/latest/userguide/auto-networking.html
そのため、AWS リソースとの通信制御が若干行い辛いデメリットがありました。
AWS リソースは IP が変更することを意識する必要があるケースが多いのに対して、通常の Network Policy では IP ブロックを指定しての通信制御しかできないからです。
この際、DNS ベースのフィルタリングを利用できれば AWS リソースとの通信制御もかなり行いやすくなります。

先日のアップデートで EKS で利用できる Network Policy が強化され、VPC CNI 単体で DNS ベースでのフィルタリングを行うことが可能になりました。
What's New
AWS Blog
これまでも Cilium など 3rd パーティのネットワークポリシーエンジンを利用すれば EKS Auto Mode で DNS ベースのフィルタリングを実施することができました。
とはいえ、VPC CNI が DNS ベースのフィルタリングに対応してくれるのであれば、セットアップが非常に楽になります。
また、構成がシンプルになって予期せぬ振る舞いも発生し辛くなると想定されます。
ということで、EKS Auto Mode で強化された Network Policy を利用して DNS ベースのフィルタリングを行ってみます。
やってみる
AWS インフラ作成
Auto Mode を有効化した EKS クラスターを Terraform で作成します。
AWS リソースとの疎通確認を行うため、マルチ AZ の RDS クラスターも作成します。
VPC
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 6.0.1"
name = "eks-vpc"
cidr = "10.0.0.0/16"
azs = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
public_subnets = ["10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24"]
private_subnets = ["10.0.100.0/24", "10.0.101.0/24", "10.0.102.0/24"]
enable_nat_gateway = true
single_nat_gateway = true
public_subnet_tags = {
"kubernetes.io/role/elb" = 1
}
private_subnet_tags = {
"kubernetes.io/role/internal-elb" = 1
}
}
EKS クラスター
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 21.0.4"
name = "test-cluster"
kubernetes_version = "1.34"
endpoint_public_access = true
endpoint_private_access = true
enable_irsa = false
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
authentication_mode = "API"
enable_cluster_creator_admin_permissions = true
compute_config = {
enabled = true
node_pools = ["general-purpose"]
}
}
RDS クラスター
resource "aws_db_subnet_group" "db_subnet" {
name = "sample-db-subnet-gp"
subnet_ids = module.vpc.private_subnets
}
resource "aws_security_group" "rds_sg" {
name = "sample-rds-sg"
description = "sample rds security group"
vpc_id = module.vpc.vpc_id
ingress {
description = "PostgreSQL"
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = [module.vpc.vpc_cidr_block]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_rds_cluster" "aurora_postgres_clsuster" {
cluster_identifier = "sample-aurora-postgres-cluster"
engine = "aurora-postgresql"
engine_version = "16.4"
database_name = "postgres"
master_username = "postgres"
master_password = "password"
db_subnet_group_name = aws_db_subnet_group.db_subnet.name
vpc_security_group_ids = [aws_security_group.rds_sg.id]
skip_final_snapshot = true
backup_retention_period = 7
preferred_backup_window = "07:00-09:00"
port = 5432
storage_encrypted = true
}
resource "aws_rds_cluster_instance" "aurora_postgres_instance" {
count = 2
cluster_identifier = aws_rds_cluster.aurora_postgres_clsuster.id
instance_class = "db.t3.medium"
engine = "aurora-postgresql"
engine_version = "16.4"
publicly_accessible = false
db_subnet_group_name = aws_db_subnet_group.db_subnet.name
auto_minor_version_upgrade = true
}
Network Policy Controller を有効化
Network Policy Controller を有効化します。
コントロールプレーン側に自動でインストールされるコンポーネントですが、Config Map を作成して有効化する必要があります。
apiVersion: v1
kind: ConfigMap
metadata:
name: amazon-vpc-cni
namespace: kube-system
data:
enable-network-policy-controller: "true"
続いて、利用する NodeClass で Network Policy が有効になっていることを確認します(Disabled になっていないことを確認)。
クラスター作成時に作成された default ノードクラスについても、最初から有効化されていました。
% kubectl get nodeclass default -o jsonpath='{.spec.networkPolicy}'
DefaultAllow
NetworkPolicy で通信制御してみる
名前空間を作成します。
% kubectl create ns app
namespace/app created
この状態でコンテナを作成します。
% kubectl run postgres -it --rm --image=postgres:latest -n app /bin/bash
DefaultAllow が効いているので、Aurora PostgreSQL に問題無く接続できます。
root@postgres:/# psql -h sample-aurora-postgres-cluster.cluster-cx4ayeauo8zn.ap-northeast-1.rds.amazonaws.com -U postgres
Password for user postgres:
psql (18.1 (Debian 18.1-1.pgdg13+2), server 16.4)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off, ALPN: none)
Type "help" for help.
postgres=> \q
root@postgres:/# exit
デフォルトで通信を拒否したいので、kubectl edit nodeclass default でノードクラスの設定を Default Deny に変更しました。
% kubectl get nodeclass default -o jsonpath='{.spec.networkPolicy}'
DefaultDeny%
改めて、コンテナを立てます。
kubectl run postgres -it --rm --image=postgres:latest -n app -- /bin/bash
この状態だと、名前解決もできなくなってます。
root@postgres:/# psql -h sample-aurora-postgres-cluster.cluster-cx4ayeauo8zn.ap-northeast-1.rds.amazonaws.com -U postgres
psql: error: could not translate host name "sample-aurora-postgres-cluster.cluster-cx4ayeauo8zn.ap-northeast-1.rds.amazonaws.com" to address: Temporary failure in name resolution
DNS 通信を通す NetworkPolicy を作成します。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-network-policy
namespace: app
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- ports:
- protocol: TCP
port: 53
- ports:
- protocol: UDP
port: 53
再度、コンテナを立てます。
kubectl run postgres -it --rm --image=postgres:latest -n app -- /bin/bash
名前解決はできますが、疎通できない状態まで来ました。
root@postgres:/# psql -h sample-aurora-postgres-cluster.cluster-cx4ayeauo8zn.ap-northeast-1.rds.amazonaws.com -U postgres
psql: error: connection to server at "sample-aurora-postgres-cluster.cluster-cx4ayeauo8zn.ap-northeast-1.rds.amazonaws.com" (10.0.101.113), port 5432 failed: Connection timed out
Is the server running on that host and accepting TCP/IP connections?
続いて IP を指定して、Aurora クラスターへの通信を許可する NetworkPolicy を作成します。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-network-policy
namespace: app
spec:
podSelector:
matchLabels:
role: backend
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: "10.0.101.113/32"
ports:
- protocol: TCP
port: 5432
これで、上手く通信が通るようになりました。
root@postgres:/# psql -h sample-aurora-postgres-cluster.cluster-cx4ayeauo8zn.ap-northeast-1.rds.amazonaws.com -U postgres
Password for user postgres:
psql (18.1 (Debian 18.1-1.pgdg13+2), server 16.4)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off, ALPN: none)
Type "help" for help.
postgres=>
リーダー側のエンドポイントを利用すると、IP が違うのでちゃんと拒否されます。
root@postgres:/# psql -h sample-aurora-postgres-cluster.cluster-ro-cx4
ayeauo8zn.ap-northeast-1.rds.amazonaws.com -U postgres
psql: error: connection to server at "sample-aurora-postgres-cluster.cluster-ro-cx4ayeauo8zn.ap-northeast-1.rds.amazonaws.com" (10.0.102.105), port 5432 failed: Connection timed out
Is the server running on that host and accepting TCP/IP connections?
続いて、NetworkPolicy で domainNames を指定してみます。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-network-policy
namespace: app
spec:
podSelector:
matchLabels:
role: backend
policyTypes:
- Egress
egress:
- to:
- domainNames:
- "sample-aurora-postgres-cluster.cluster-cx4ayeauo8zn.ap-northeast-1.rds.amazonaws.com"
ports:
- protocol: TCP
port: 5432
そういった属性は使えないので当然失敗します。
% k apply -f backend-policy.yaml
The request is invalid: patch: Invalid value: "map[metadata:map[annotations:map[kubectl.kubernetes.io/last-applied-configuration:{\"apiVersion\":\"networking.k8s.io/v1\",\"kind\":\"NetworkPolicy\",\"metadata\":{\"annotations\":{},\"name\":\"backend-network-policy\",\"namespace\":\"app\"},\"spec\":{\"egress\":[{\"ports\":[{\"port\":5432,\"protocol\":\"TCP\"}],\"to\":[{\"domainNames\":[\"sample-aurora-postgres-cluster.cluster-cx4ayeauo8zn.ap-northeast-1.rds.amazonaws.com\"]}]}],\"podSelector\":{\"matchLabels\":{\"role\":\"backend\"}},\"policyTypes\":[\"Egress\"]}}\n]] spec:map[egress:[map[ports:[map[port:5432 protocol:TCP]] to:[map[domainNames:[sample-aurora-postgres-cluster.cluster-cx4ayeauo8zn.ap-northeast-1.rds.amazonaws.com]]]]]]]": strict decoding error: unknown field "spec.egress[0].to[0].domainNames"
Application Network Policy で通信制御してみる
ということで Application Network Policy の登場です。
先程の Network Policy を削除して、下記 Application Network Policy を作成します。
apiVersion と kind が異なりますが、その他の書き方は同じです。
apiVersion: networking.k8s.aws/v1alpha1
kind: ApplicationNetworkPolicy
metadata:
name: backend-network-policy
namespace: app
spec:
podSelector:
matchLabels:
role: backend
policyTypes:
- Egress
egress:
- to:
- domainNames:
- "sample-aurora-postgres-cluster.cluster-cx4ayeauo8zn.ap-northeast-1.rds.amazonaws.com"
ports:
- protocol: TCP
port: 5432
改めてコンテナを作成します。
kubectl run postgres -it --rm --image=postgres:latest --labels="role=backend" -n app -- /bin/bash
無事、通信も通りました。
root@postgres:/# psql -h sample-aurora-postgres-cluster.cluster-cx4ayeauo8zn.ap-northeast-1.rds.amazonaws.com -U postgres
Password for user postgres:
psql (18.1 (Debian 18.1-1.pgdg13+2), server 16.4)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off, ALPN: none)
Type "help" for help.
postgres=>
ドメインが異なるリーダーエンドポイントは通信が通らないことも確認できます。
root@postgres:/# psql -h sample-aurora-postgres-cluster.cluster-ro-cx4
ayeauo8zn.ap-northeast-1.rds.amazonaws.com -U postgres
psql: error: connection to server at "sample-aurora-postgres-cluster.cluster-ro-cx4ayeauo8zn.ap-northeast-1.rds.amazonaws.com" (10.0.102.105), port 5432 failed: Connection timed out
Is the server running on that host and accepting TCP/IP connections?
ここは Application Network Policy でも Network Policy でも一緒ですが、Pod のラベルを変えると対象では無くなり、DefaultDeny によって拒否されると思われます。
kubectl run postgres -it --rm --image=postgres:latest --labels="role=frontend" -n app -- /bin/bash
frontend ラベルがついたコンテナからだと、ちゃんと接続できないことを確認できました。
root@postgres:/# psql -h sample-aurora-postgres-cluster.cluster-cx4ayeauo8zn.ap-northeast-1.rds.amazonaws.com -U postgres
psql: error: connection to server at "sample-aurora-postgres-cluster.cluster-ro-cx4ayeauo8zn.ap-northeast-1.rds.amazonaws.com" (10.0.102.105), port 5432 failed: Connection timed out
Is the server running on that host and accepting TCP/IP connections?
注意点
ApplicationNetworkPolicy は EKS Auto Mode でのみ利用できます。
EKS Auto Mode を有効化していても、マネージドインスタンス上のワークロードにしか適用されません。
DNS based rules defined using the ApplicationNetworkPolicy are only applicable to workloads running in EKS Auto Mode-launched EC2 instances. If you are running a mixed mode cluster (consisting of both EKS Auto and non EKS Auto worker nodes), your DNS-based rules are only effective in the EKS Auto mode worker nodes (EC2 managed instances).
https://docs.aws.amazon.com/eks/latest/userguide/auto-net-pol.html#_considerations
最後に
EKS Auto Mode で強化された Network Policy を利用して、DNS ベースのフィルタリングを試してみました。
今回は AWS リソースとの接続制御に利用しましたが、外部 SaaS やオンプレミスにあるリソースとの通信制御にも有用です。
是非試してみて下さい!










