この記事はkintone生成AIチームで連載中のkintone AI リレーブログ 2026 の8本目の記事です。 リレーブログでは生成AIチームのメンバーがAIトピックに限らず、さまざまなことについて発信していきます。
こんにちは! kintone の生成 AI チームでソフトウェアエンジニアをやっている福田です。
以前の記事で生成 AI チームで Kubernetes の基盤を構築してアプリケーションを運用していることをご紹介しました。
アプリケーションを運用する上で、セキュリティは欠かせない要件の 1 つです。 セキュリティと一口に言ってもさまざまな側面での対策が考えられますが、アプリケーションによる通信を無条件で許可するのではなく、必要な経路に絞って許可することは最も基本的な対策のひとつなのではないでしょうか。
Kubernetes ではこのような要件に対応するための仕組みとして NetworkPolicy という仕組みが用意されています。
この記事では EKS Auto Mode 環境で NetworkPolicy をどのように適用しているか、そして、実際に遭遇したハマりどころと解決方法を共有します。
NetworkPolicy の基本
Kubernetes の NetworkPolicy は Pod の通信を IP アドレスとポート単位で制御できる仕組みです。 NetworkPolicy は Namespace 単位のリソースとなっており、1 つの NetworkPolicy リソースを複数の Namespace の Pod に適用することはできません。(ある Namespace の Pod から他の Namespace の Pod への通信を許可することはできます。)
指定方法としては ある Pod が通信できる相手を Pod の selector または CIDR 範囲で指定します。 それぞれの用途としては、クラスタ内の Pod を指定したい場合は Pod の selector、通信元や通信先としてクラスタ外部を指定したい場合は CIDR 範囲で指定することになります。 NetworkPolicy はステートフルに通信を制御するので、通信先の egress が許可されていれば戻りの通信も許可されます。 ただし、通信先の Pod に NetworkPolicy が適用されている場合、その Pod の ingress として通信元の Pod を指定しないと通信ができないので注意が必要です。
Pod に NetworkPolicy が適用されると、その Pod は ingress/egress いずれかが「隔離された」状態になり、許可された通信以外は拒否されるようになります。 例えば、ある Pod に ingress の通信を制御するポリシーが 1 つでも存在すれば、その Pod は許可された経路以外の通信は受け入れなくなります。 egress の場合も同様に、Pod に割り当てられたポリシーが 1 つでも存在すれば、その Pod は許可された経路以外に通信できなくなります。
以下は app-1 namespace の app=hoge というラベルを持った Pod から app-2 namespace の app=fuga というラベルを持った Pod への通信を許可するような NetworkPolicy の例です。
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-hoge-egress namespace: app-1 spec: egress: - ports: - port: 80 protocol: TCP to: - podSelector: matchLabels: app: fuga podSelector: matchLabels: app: hoge policyTypes: - Egress
このポリシーだけを適用した場合、app-2 の app=fuga に ingress ルールが 1 つも適用されていない場合は通信できます。
適用されている場合は以下のルールも追加で適用する必要があります。
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-fuga-ingress namespace: app-2 spec: ingress: - ports: - port: 80 protocol: TCP from: - podSelector: matchLabels: app: hoge podSelector: matchLabels: app: fuga policyTypes: - Ingress
EKS Auto Mode での NetworkPolicy の有効化
Amazon EKS Auto Mode では Amazon VPC CNI(以下 VPC CNI)を使用してネットワークの設定を行う構成になっています。 VPC CNI はデフォルトでは NetworkPolicy が使用できませんが、ConfigMap を作成して NodeClass で有効化することで簡単に有効化できます。 デフォルトで作成される NodeClass では既に有効化されているので、実質的には ConfigMap を 1 つ作成するだけで有効化できます。
apiVersion: v1 kind: ConfigMap metadata: name: amazon-vpc-cni namespace: kube-system data: enable-network-policy-controller: "true"
このような ConfigMap を作成することで、NetworkPolicy がデフォルト allow の状態で有効化されます。 デフォルト deny にするには NodeClass の設定を変更する必要があります。デフォルトで作成される NodeClass の設定を変更することはできないようなので NodeClass を自分で定義する必要があり、やや複雑な設定が必要になります。
NetworkPolicy の適用方針
私たちの環境では、デフォルトで全ての ingress と egress の通信を拒否しておき、必要な経路のみで通信を許可する方針で NetworkPolicy を適用しています。 このようにすることで、通信経路を明示的に許可する必要があるため、意図しない通信を防止できます。 一方で、必要な通信経路を全て許可するための NetworkPolicy を定義する必要があるため、運用の手間が増えるのはデメリットです。 このデメリットを軽減するための工夫については後述します。
ハマったところ
DNS 解決のために Node 上のプロセスとの通信許可が必要

ある Pod からクラスタ上の他のサービスの名前解決を行うためには、クラスタの CoreDNS と通信する必要があります。EKS Auto Mode では CoreDNS はクラスタ上の Pod としてではなく、ノード上のプロセスとして起動するため、Pod の selector は使用できず、ipBlock を使用して IP アドレス単位のアクセス許可を行う必要があります。
Kubernetes 上で Pod が CoreDNS と通信可能にするためには、kube-system namespace 内の k8s-app: kube-dns というラベルを持った Pod に対してアクセス許可を設定すればよいことが多いです。
しかし、EKS Auto Mode では CoreDNS は Pod としてではなくノード上のプロセスとして起動するため、Pod の selector を使用してアクセス許可を設定することができません。
代わりに、EKS Auto Mode では、CoreDNS はクラスタ CIDR の .10 で終わる IP アドレスに割り当てられる1ため、以下のような ipBlock を指定するルールを適用する必要がありました。
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-egress-to-coredns namespace: your-namespace spec: egress: - ports: - port: 53 protocol: UDP - port: 53 protocol: TCP to: - ipBlock: cidr: <cluster-cidr>.10/32 podSelector: matchLabels: app: your-app policyTypes: - Egress
Webhook のためにコントロールプレーンからの通信許可が必要

Kubernetes API を呼び出すためには、Pod はコントロールプレーンのコンポーネントである kube-apiserver との疎通性を確保する必要があります。これは default Namespace 内の kubernetes という Service の ClusterIP と疎通できていればよいです。
しかし、MutatingWebhookConfiguration や ValidatingWebhookConfiguration を使用している場合、通信の向きが逆になります。Webhook は「Pod が API サーバーを呼ぶ」のではなく「API サーバーが Pod を呼ぶ」という構造であるため、コントロールプレーンから Pod への Ingress を許可しなければなりません。
この通信は Service の ClusterIP とは別の、コントロールプレーンノード自体の IP アドレスから届きます。そのため Pod の podSelector や namespaceSelector では対象を絞れず、ipBlock で対応する必要があります。さらに、Kubernetes のバージョンアップ等によるコントロールプレーンの再起動で IP アドレスが変わることがあるため、個別の IP を指定するのではなくコントロールプレーンが立つサブネット全体を許可しておく必要があります。
ingress: - from: - ipBlock: cidr: 10.0.0.0/24 # コントロールプレーンが属するサブネットの CIDR ports: - protocol: TCP port: 8000 # Webhook サーバーが待ち受けているポート
運用の工夫
適用したい NetworkPolicy は共通していることが多いため、毎回定義していると非常に非効率です。 さらに、デフォルト拒否で必要な経路に絞って許可する方針で NetworkPolicy を適用しているため、ボイラープレートコードが多くなりがちです。
私たちは cdk8s を使って Kubernetes マニフェストを生成している2ため、特定用途のポリシーを cdk8s の Construct を用意することで、このような冗長さを軽減しています。
例として EKS の Pod Identity を使用する場合を考えます。 Pod Identity を使用するには Pod から Pod Identity エージェントへの通信を許可する必要があります3。 このようなルールは以下のような Construct で再利用可能な形で定義することができます:
export type AllowAWSEKSPodIdentityAgentProps = { namespace: string; // 適用対象の Pod セレクター selector: IPodSelector; }; export class AllowAWSEKSPodIdentityAgentRule extends Construct { constructor(scope: Construct, id: string, props: AllowAWSEKSPodIdentityAgentProps) { super(scope, id); const ports = [NetworkPolicyPort.tcp(80), NetworkPolicyPort.tcp(2703)]; new NetworkPolicy(this, 'allow-egress-to-eks-pod-identity-agent', { metadata: { namespace: props.namespace, name: 'allow-egress-to-eks-pod-identity-agent', }, selector: props.selector, egress: { rules: [ { ports, peer: NetworkPolicyIpBlock.ipv4(this, 'pod-identity-agent-ipv4', '169.254.170.23/32') }, { ports, peer: NetworkPolicyIpBlock.ipv6(this, 'pod-identity-agent-ipv6', 'fd00:ec2::23/128') } ] } }); } }
Construct としてまとめておくことで、そのルールで実現できることに名前がつくので、コード上でもポリシーの意図を読み取りやすくなる効果もあると感じています。
トラブルシューティングの Tips
NetworkPolicy の問題は闇雲に設定をいじっていてもなかなか解決しないことが多いです。 問題を切り分けるためのヒントをいくつか共有します。
kubectl np-viewer で NetworkPolicy を可視化する
NetworkPolicy の YAML はそのままでは読み解きにくいですが、kubectl np-viewer(np-viewer プラグイン)を使うと Pod ごとや ingress/egress ごとに適用されているルールを可視化できます。
例えば、以下のコマンドで、特定の Pod に適用されている NetworkPolicy を一覧できます。
kubectl np-viewer -n your-namespace -p pod-name
netstat で実際の通信元を特定する
NetworkPolicy は正しいはずなのに通信が通らない(あるいは通ってほしくないのに通ってしまう)という状況では、実際にどこから通信が来ているのかを確認することが有効です。
通信先の Pod にシェルで入り、netstat することで確認できます。
kubectl exec -it <pod-name> -n your-namespace -- netstat -taepn
これにより、プロセス情報も含めた全 TCP コネクションが一覧できます。 確認できたアドレスが具体的に何なのかが分からなくとも、レンジが ClusterIP なのか、AWS のサブネットなのか、あるいは別のレンジなのかを把握することで、どのようなルールを追加すればよいのか見当がつきやすくなります。
今後の展望
VPC CNI の Clusterwide な NetworkPolicy の利用
NetworkPolicy の構成時点では発表されていませんでしたが、NetworkPolicy を cluster-wide で適用できる機能が VPC CNI で使用できるようになりました。 こちらを使用すると共通部分をシンプルに書ける可能性があるので、今後の運用で使用していくことを検討しています。
We are hiring!
kintone 生成 AI チームでは、一緒に働くメンバーを募集中です!
生成 AI を使って一緒に kintone をもっと便利にしませんか? ご応募お待ちしております!
- kubeadm などの一部の Kubernetes インストーラーでは DNS を Cluster CIDR の 10 番目のアドレスに割り当てており、EKS もその慣習に従っているようです。↩
- ccdk8s を使ってみた! - TypeScript で Kubernetes マニフェストを管理する - Cybozu Inside Out | サイボウズエンジニアのブログ↩
- EKS Pod Identity が Pod に AWS サービスへのアクセス権を付与する仕組みを学ぶ↩