Kubernetes ワークロードと VM 間のファイアウォールを強化
VMware はコンテナネットワーキング向けのソリューションとして、IaaS、CaaS からアプリケーション層に至る様々なレイヤの製品をご提供しています。
- Kubernetes を下支えする IaaS/vSphere 環境のネットワーク&セキュリティ製品である VMware NSX
- 仮想ロードバランサー製品である NSX-ALB (Advanced LoadBalancer) と、NSX-ALB を外部 LB として利用し Kuberbetes Service LoadBalancer、Ingress、Gateway 機能を実現する AKO (Avi Kubernetes Operator)
- Tanzu 製品群にも組み込まれているオープンソースの CNI である Antrea
- Kubernetes のクラスターネットワーキング機能として NSX を活用する NCP (NSX Container Plugin)
Antrea は NSX Manager と連携することで、マルチクラスタ環境で一貫した Kubernetes ネットワークポリシーの管理をすでに実現しています。詳細については以下のブログでご一読下さい。
さらに 2023 年 2 月に提供開始した NSX 4.1 では、Antrea CNI を備えた Kubernetes クラスタを NSX が得意とする VM のネットワーク管理に統合し、コンテナと VM にまたがったセキュリティポリシーをファイアウォールに設定できるようになりました。このブログでは、この Antrea〜VM 間連携が必要とされる背景や機能詳細をご紹介します。
すこし遡ると…
実は VMware がコンテナ技術に取り組み始めた当初から、VM とコンテナ間の相互運用におけるセキュリティの問題を取り上げていました。以下は2019年頃に vForum 等のイベントで用いていたスライドですが、すでに、一部のワークロードのコンテナ化が進んでも、VM やベアメタルに残るワークロードはあると想定し、クラスタとプラットフォームをまたがったセキュリティの課題を提起していました。
実際にコンテナ化やサーバーレス化が進行した今日、このような危惧は現実のものとなりつつあり、それに対処しうるネットワークとセキュリティの統合ソリューションが必要になると考えています。
本稿で取り上げる NSX と Antrea の連携ソリューションは、既存の仮想データセンター向けネットワーク仮想化ソリューションとして実績ある NSX と、様々な Kubernetes で利用できるオープン CNI である Antrea を連携させることで、異なるワークロード間のセキュリティ管理や相互運用性といった課題に対応することを目的としています。
アーキテクチャと設定
Antrea〜NSX 連携機能を使うと、NSX が展開された vSphere クラスタ上で稼働する Kubernetes クラスタと VM の間、あるいは異なる Kubernetes クラスタの間でファイアウォールを設定することができます。ファイアウォールを実際に適用するのは NSX 分散ファイアウォール (以下、DFW) もしくは NSX ゲートウェイファイアウォール (以下、GWFW) です。ファイアウォールは DFW/GWFW の通常のルールとして定義することができます。
以下の図では異なる vSphere クラスタにある Kubernetes クラスタや VM を NSX 論理ネットワークに接続し、VMware ソフトウェアだけを用いてコンテナと VM ワークロードに対してファイアウォールを適用しているイメージです。この例ではコンテナ〜VM 間で NSX ルーティング (Tier0/Tier1-GW) を使用していますが、VLAN ネットワーク構成で DFW だけを利用することも可能です。
DFW/GWFW のルールを作成する際、Kubernetes の入力・出力リソースオブジェクトを指定してファイアウォールルールの送信元や宛先として設定することが可能です。実際には、以下の Kubernetes 入力・出力リソースを組み合せて利用することができます。
- Kubernetes クラスタ
- Kubernetes ネームスペース
- Kubernetes ノード
- Kubernetes サービス
- Kubernetes Ingress (入力方向)
- Kubernetes ゲートウェイ
- Antrea Egress (出力方向)
- Antrea IP プール
これらの条件を組み合わせて NSX グループに登録し、それをルールの送信元や宛先に指定することで特定のコンテナと VM 間、あるいは異なるクラスタの特定のコンテナ間のファイアウォールを実現します。NSX はこれらの Kubernetes リソースを最終的には実 IP アドレスに変換しますが、そのために Antrea NSX Adapter コンテナを介して Kubernetes 側のインベントリ情報と同期しています。
動作環境と要件
Antrea〜NSX 連携機能は、2023 年 5 月現在、以下の組み合わせでご利用可能です。
- NSX 4.1
- VMware Container Networking with Antrea 1.6.0 (もしくは Antrea 1.9.0) 以降
- このバージョンの Antrea が動作する Kubernetes クラスタ
また、Antrea 連携を利用するには NSX に以下のライセンスのいずれかが必要です。
- NSX Advanced
- NSX Enterprise Plus
- Antrea Enterprise Standalone
本ブログの執筆にあたり使用したテスト環境では、NSX 4.1.0、Kubernetes v1.25.5 と Antrea v1.9.0 を利用しています。セキュリティポリシーの対象となる VM および Kubernetes クラスタノードの両方が、NSX 展開済みの vSphere クラスタ上で動作するように設定していますが、Kubernetes クラスタは vSphere 上に展開されていなくても適用可能です。
詳しい要件はこちらの NSX 管理者ガイドをご確認下さい。また Antrea-NSX 連携を使用するための設定方法を以下のブログでもご紹介していますので、こちらもご参照いただけると幸いです。
Kubernetes クラスタ〜VM 間トラフィックに DFW を適用
それでは、実際にいくつかのパターンで構成を組んで動作を見ていきましょう。今回は以下のような構成でテスト用のアプリケーションを構成しています。このアプリはフロントエンドの Web App コンテナとバックエンドのデータベース VM から成るシンプルなウェブアプリです。外部ロードバランサーとして NSX-ALB を使用しています。
ロードバランサーを介して Web App コンテナに HTTP リクエストを送ると(①、②)、データストアからデータ(フルーツの在庫情報)を読み出して(③)、以下のように表示します。その際に Web App コンテナの IP アドレス、ホスト名、データベース情報(アドレスとポート番号)を同時に表示します。
Kubernetes クラスタの状態は以下のようになっています。
❯ kubectl get node -o wide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME k8s-node Ready control-plane 8d v1.25.5 172.19.64.14 <none> Ubuntu 20.04.5 LTS 5.4.0-146-generic containerd://1.5.9 k8s-worker-1 Ready <none> 8d v1.25.5 172.19.64.1 <none> Ubuntu 20.04.5 LTS 5.4.0-146-generic containerd://1.5.9 k8s-worker-2 Ready <none> 8d v1.25.5 172.19.64.15 <none> Ubuntu 20.04.5 LTS 5.4.0-146-generic containerd://1.5.9 ❯ kubectl get pod -o wide -L app NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES APP fruits-app-bc6cc6b5f-6g8bv 1/1 Running 0 7h2m 172.19.101.38 k8s-worker-2 <none> <none> fruits-app-total fruits-app-bc6cc6b5f-dhr72 1/1 Running 0 7h2m 172.19.102.40 k8s-worker-1 <none> <none> fruits-app-total ❯ kubectl get services -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR fruits-app LoadBalancer 172.19.96.163 172.19.65.5 80:30396/TCP 2d13h app=fruits-app-total,service=fruits-app
シナリオ 1 : Kubernetes クラスタから VM へのアクセスをノード IP で制御
では、最初の例を見ていきます。ネットワーク管理者は増加するコンテナクラスタからデータベースへのアクセスを制限したいと考え、NSX DFW を用いてこのデータベース VM へのファイアウォールを設定します。ここではシンプルにクラスタからのアクセスを制限したいため、クラスタのノード IP を使って制御します。
DFW ポリシーを設定する前に、インベントリメニューでグループの定義をしていきます。まず Kubernetes クラスタを識別するためのグループ “kube-demo-a nodes” を作成します。以下のように、NSX グループ設定で Kubernetes クラスタ名が “kube-demo-a” に等しい場合に(表示が見切れていますが)、ノードの IP をメンバーとして認識します。
グループ作成後にメンバーを表示すると、”kube-demo-a” クラスタ内のノードと IP がメンバーとして自動的に選択されていることが分かります。
同様にデータベース VM のグループ “demo-vms” を作成します。ここでは NSX タグを利用して VM を識別するように設定しており、”app=demo” の場合に自動的にこのグループメンバーとして認識します。
対象の VM には事前に以下のように “app=demo” のタグを設定済みです。
“demo-vms” のメンバーを表示すると、VM 1 つとその IP アドレスが選択されています。
作成した 2 つのグループを使って、Kubernetes クラスタからデータベース VM へのアクセスを許可するポリシーを作成します。以下はこの接続のために作成したポリシーの全体です(小さくて見づらい場合はクリックいただくと拡大表示します)。ポリシー内にある最初のルールで、データベース VM への接続を許可しています。その際、サービスとして Raw ポートプロトコル設定で宛先ポートを “TCP 27017” に設定しています。2 行目のルールでは運用上の必要性から SSH アクセスを許可しています。3,4 行目がこの VM への送受信トラフィックをすべて拒否するルールとなります。つまり、明示的に許可されたトラフィック以外はすべて拒否されます。
この設定が有効な状態で Web App の LB VIP “172.19.65.5:80” にブラウザでアクセスすると、正常に応答します。
そこで、ポリシーを修正して1行目のルールを無効化し、MongoDB のポートへアクセスできないように変更します。1 行目のルール右端のボタンで無効化します(色がグレーに変わる)。
この状態で Web APP にアクセスすると以下のような表示に変わります。データベースへアクセスできなかったため “DB Read Error” を表示しています。
このように、Antrea〜NSX 連携機能を使えば Kubernetes クラスタ全体と外部 VM とのアクセス制御が可能です。
次のシナリオでは、アプリを識別した上でのアクセス制御を見ていきます。
シナリオ 2 : コンテナアプリから VM へのアクセスを Antrea Egress を使って制御
Antrea 連携機能では、送信側のコンテナアプリを識別する方法として、Antrea Egress (出力方向) と Antrea IP プールを利用できます。ここでは、Antrea Egress を使ってみます。
Antrea Egress とはクラスタ外部と通信する特定のコンテナ群に専用の Egress IP アドレスを指定し、通信制御を容易にするための機能です。シナリオ 1 ではクラスタ内のノードを対象に許可したので、すべてのコンテナからデータベース VM へのアクセスが許可されていましたが、Egress を利用すると指定したアプリからのアクセスのみを許可することが容易にできるようになります。
Egress は Antrea のカスタムリソースとして実装されており、下の図のように、特定の Pod のグループに対して出力方向の IP アドレスを固定的に割り当てます。これにより、Kubernetes クラスタ外部のネットワークコンポーネントが特定のコンテナアプリからのトラフィックを一意に識別できるようになります。割り当てる IP アドレスは、ExternalIPPool カスタムリソースを使用してレンジ指定して動的に割り当てる、もしくはレンジ内から指定して割り当てることが可能です。また Egress を実行するノードを指定することも可能です。Egress についてはこちらの記事もご参照いただければと思います。
それでは、実際に Egress を利用した動作例を見ていきたいと思います。ネットワーク管理者は Web App コンテナからデータベース VM へのアクセスを制限する際に、アプリを指定して NSX DFW ポリシーを設定することにしました。そのために、Antrea Egress を設定して DFW が一意に Web App コンテナからのトラフィックを識別できるようにします。
シナリオ 1 の設定に対して、Antrea Egress および ExternalIPPool の設定を追加します。ここでは、ExternalIPPool の設定状態は以下のようになっています。プール名は “external-ip-pool-1” です。ipRanges でノードと同じサブネットから IP アドレスの範囲を指定しています。また nodeSelector で “k8s-worker-1” を指定しています。ノードの指定をしないことも可能で、その場合はクラスタ内のノードから自動的に選択されます。
❯ kubectl get externalippool external-ip-pool-1 -o yaml apiVersion: crd.antrea.io/v1alpha2 kind: ExternalIPPool metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"crd.antrea.io/v1alpha2","kind":"ExternalIPPool","metadata":{"annotations":{},"name":"external-ip-pool-1"},"spec":{"ipRanges":[{"end":"172.19.64.110","start":"172.19.64.101"}],"nodeSelector":{"matchLabels":{"kubernetes.io/hostname":"k8s-worker-1"}}}} creationTimestamp: "2023-04-12T11:28:08Z" generation: 2 name: external-ip-pool-1 resourceVersion: "1641007" uid: 97e269fd-d24c-4b2c-9d6b-508950966f01 spec: ipRanges: - end: 172.19.64.110 start: 172.19.64.101 nodeSelector: matchLabels: kubernetes.io/hostname: k8s-worker-1 status: usage: total: 10 used: 1
Egress の状態は以下の通りです。Egress 名は “fruits-app-egress” です。namespaceSelector と podSelector を使って適用対象の Pod を指定しています。ここでは “fruits-app-total” のラベルが付いた “default” ネームスペース内の Pod を指定しています。シナリオ 1 で利用した fruits-app の Pod には “app=fruits-app-total” のラベルが付いていましたね。また externalIPPool にて “external-ip-pool-1” を指定し、egressIP にてそのプール範囲内の IP アドレスである “172.19.64.101” に Egress IP アドレスが固定されるよう設定しています。また egressNode として “external-ip-pool-1” で指定した “k8s-worker-1” が選択されていることが分かります。
❯ kubectl get egress fruits-app-egress -o yaml apiVersion: crd.antrea.io/v1alpha2 kind: Egress metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"crd.antrea.io/v1alpha2","kind":"Egress","metadata":{"annotations":{},"name":"fruits-app-egress"},"spec":{"appliedTo":{"namespaceSelector":{"matchLabels":{"kubernetes.io/metadata.name":"default"}},"podSelector":{"matchLabels":{"app":"fruits-app-total"}}},"egressIP":"172.19.64.101","externalIPPool":"external-ip-pool-1"}} creationTimestamp: "2023-04-20T08:44:38Z" generation: 1 name: fruits-app-egress resourceVersion: "1641008" uid: 57151a3c-b0d5-4e21-a815-610af67bd1cf spec: appliedTo: namespaceSelector: matchLabels: kubernetes.io/metadata.name: default podSelector: matchLabels: app: fruits-app-total egressIP: 172.19.64.101 externalIPPool: external-ip-pool-1 status: egressNode: k8s-worker-1
次に、この Egress をメンバーとする NSX グループを作成します。以下のように、Kubernetes クラスタ名が “kube-demo-a” に等しく、Antrea 出力方向(Egress)の名前が “fruits-app-egress” である場合にマッチするように設定します (UI 表示上はどちらの条件も見切れていますが)。
グループ作成後にメンバーを表示すると、”kube-demo-a” クラスタ内の Egress “fruits-app-egress” とその IP がメンバーとして選択されていることが分かります。
それ以外の設定はシナリオ 1 と同じです。
次に、DFW ポリシーを作成していきます。ポリシー内にある最初のルールで、Egress からデータベース VM への接続を許可しています。2 行目からのルールはシナリオ 1 と同じです。
この状態で Web App の LB VIP “172.19.65.5:80” にブラウザでアクセスすると、正常に応答します。
念のため データベース VM 側で 172.19.64.101 からアクセスされていることを確認します。(以下は netstat 出力結果の抜粋)
Active Internet connections (w/o servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 172.19.2.1:27017 172.19.64.101:34206 ESTABLISHED
ここで、同じ Kubernetes クラスター上に別のネームスペース “alt-ns” を作成し、同じ Web App を展開してみます。状態は以下のとおりです。
❯ kubectl get pod -o wide -L app -n alt-ns NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES APP fruits-app-9d85f967d-mthhn 1/1 Running 0 65s 172.19.101.39 k8s-worker-2 <none> <none> fruits-app-total ❯ ❯ kubectl get services -o wide -n alt-ns NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR fruits-app LoadBalancer 172.19.96.38 172.19.65.6 80:30955/TCP 78s app=fruits-app-total,service=fruits-app
この Web App の LB VIP は “172.19.65.6:80” なので、こちらにブラウザでアクセスすると、以下のように “DB Read Error” になりました。
この Web App も同じデータベース VM “172.19.2.1” にクエリーを行いますが、”alt-ns” ネームスペースに所属するため、作成済みの Egress “fruits-app-egress” の namespaceSelector と条件がマッチしません。そのため Egress IP を使わず通常のノード IP で通信を試みますが、その場合は適用済みの DFW 許可ルールの送信元の条件とマッチしないため、通信が拒否されたのです。
いかがでしたでしょうか。以上のように、Antrea〜NSX 連携機能を使えば Kubernetes クラスタ全体のアクセス制御に加えて、Antrea Egress のような特定のコンテナグループを識別してアクセス制御できることが、お分かりいただけたかと思います。
Antrea〜NSX 連携と AntreaPolicy の違い
以前、別のブログ “NSX の Antrea CNI 連携機能” では、 Antrea ベースの Kubernetes クラスタの Network Policy や Antrea Cluster Network Policy を NSX から管理する手法を取り上げました。本ブログで取り上げた新機能との違いを簡単にご説明すると、以下のようになります。
- Kubernetes クラスタ〜 VM 間のファイアウォール (本ブログでご紹介した機能)
- Kubernetes メンバータイプを利用して、VM に適用できる汎用グループを作成できます。グループは Kubernetes メンバー以外に VM、NSX セグメント、セグメントポート、VIF を包含可能です。
- グループ設定を評価して設定するのは NSX の役目です。
- セキュリティポリシーは NSX 分散ファイアウォール (DFW) もしくは ゲートウェイファイアウォール (GWFW) に適用できます。
- Antrea Policy: Kubernetes クラスタ内のトラフィックを制御
- “Antrea” タイプのグループを利用します。メンバータイプはポッド、ネームスペース、サービスです。評価されたグループは最終的にポッドに紐づきます。
- NSX は Kubernetes クラスタにグループ定義を配信し、Antrea Controller がグループを評価します。
- セキュリティポリシーは Kubernetes クラスタに適用されます。
NSX 4.1 からは両方とも同じ NSX Manager から設定できるようになりましたので、Kubernetes クラスタ内外のセキュリティポリシーをネットワーク管理者が一括で管理していくことも可能です。
終わりに
本ブログでは、NSX の新しい Antrea 連携機能を使えば、Kubernetes クラスタ〜VM 間のセキュリティポリシーを簡単に NSX UI で設定できることを動作確認を交えてご紹介しました。今回テストした構成では、コンテナから VM に向けて発信される出力側のコンテナ通信を主に取り上げましたが、VM からコンテナクラスタに向けて発信される入力側のコンテナ通信や、Kubernetes クラスタ同士の通信においても同じ仕組みを活用することが可能です。Kubernetes のオブジェクト名を使った直感的なファイアウォール運用の範囲が広がったのではないかと思います。今後さらに拡大する Kubernetes 環境のネットワーク統合管理をご検討されている方は、ぜひお試しいただければと思います。
〜お知らせ〜
※VMwareでは、各種製品をクラウド上でご評価いただける Hands-on Labs (HOL) を無償でご提供しています。
今回ご紹介した各種ソリューションへの入り口としてぜひご活用下さい。(Antrea連携機能のHOLはまだご提供しておりません)