次世代のBIG-IP SPKとK8s コンテナの外部アクセス制御

 

This post is also available in English

  • k8sクラスタの外部への通信制御の課題
  • F5の役割
  • 技術概要

本記事では、F5 の 次世代のBIG-IP SPKに実装された新機能をご紹介します。

K8sクラスター内へのトラフィックの負荷分散は簡単ですが、クラスターから外部に出ていく通信の制御は難しく、制限を加えずパケットをそのまま通すことが多いです。つまり、従来のネットワークで使用されている一貫したポリシーの適用ができないため、クラスターがセキュリティ リスクにさらされます。

BIG-IP SPKを使って通信を制御する方法について詳しく説明します。

k8sクラスタの外部への通信制御の課題

デフォルトでは、calico CNI を使用してデプロイされたpodはデフォルト ルートに従ってクラスターから抜け出し、パケットは管理インターフェイス上のワーカー ホストの外部 IP アドレスを使用します。

Kubernetes NetworkPolicies は使用できますが、クラスターが成長するにつれて、すべてのnamespaceにわたる数百または数千のポリシーのライフサイクルを管理するのは困難になります。

Telco アプリケーションでよく見られるように、multus インターフェイスを備えたpodをデプロイすると、そのトラフィックはクラスター内に適用されているNetworkPolicy をバイパスします。

F5の役割

Service Proxy for Kubernetes (SPK) は、通信サービス プロバイダー (CoSP) の 5G ネットワークとコンテナ ワークロード向けに設計された、クラウドネイティブのアプリケーション トラフィック管理ソリューションです。

SPK の egress ゲートウェイ機能を使用すると、podのデフォルトの calico ネットワーク インターフェイスと multus インターフェイスの管理が簡単になります。

カーネル ルートは自動的に構成され、podのトラフィックが常に SPK ポッド経由でルーティングされるため、namespaceを認識したネットワーク ポリシー、ソース NAT 変換、およびその他の制御を適用できます。 

技術概要

このセクションでは、上記のシナリオの構成方法の概要を説明します。

ホストの前提条件

ホスト上で、2 つの macvlan ブリッジ shimを作成します。  1 つはアプリケーション ポッドの calico トラフィック用で、もう 1 つは macvlan トラフィック用です。これらの shim は、パケットを SPK に転送するために使用されます。これらのインターフェイスにより、それぞれ SPK の「internal」インターフェイスと「external2」インターフェイスへの接続が可能になります。

ip link add spk-shim link ens224 type macvlan mode bridge
ip addr add 10.1.30.244/24 dev shim1
ip link set shim1 up
 
 
ip link add spk-shim2 link ens256 type macvlan mode bridge
ip addr add 10.1.10.244/24 dev shim2
ip link set shim2 up
アプリの前提条件と設定

SPK コントローラーの value.yaml ファイルで、watchNamespace ブロックにアプリケーション ワークロードのnamespaceを定義します。

watchNamespace:
  - "spk-apps"
  - "spk-apps-2"

SPK はソース NAT をpodのegressトラフィックに適用するため、natOutcoming を false に設定して IPPool を作成します。この IPPool はアプリケーションによって使用されます。

apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
  name: app-ip-pool
spec:
  cidr: 10.124.0.0/16
  ipipMode: Always
  natOutgoing: false

IPPool を使用するには、以下のアノテーションをアプリケーションnamespaceに適用します。

kubectl annotate namespace spk-apps "cni.projectcalico.org/ipv4pools"=[\"app-ip-pool\"]
kubectl annotate namespace spk-apps-2 "cni.projectcalico.org/ipv4pools"=[\"app-ip-pool\"]

アプリケーションをデプロイします。アプリケーションのデプロイメント マニフェストの例については、以下を参照してください。デフォルトの calico インターフェイスに加えて、セカンダリ macvlan インターフェイスを追加しています。 IP アドレスは、対応する NetworkAttachmentDefinition での定義に従って自動的に構成されます。 SPK で使用される特定のラベルに注目してください。これにより、アプリケーションごとに SPK へのトラフィック ルーティングを有効にすることができます。

 

さらに、SPK は、enableSecureSPK=true ラベルを確認し、podのセカンダリ macvlan インターフェイスからのトラフィックを処理するリスナーを作成します。 (これらのリスナーは後で確認します)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  annotations:
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      annotations:
        k8s.v1.cni.cncf.io/networks: '[
          { "name": "macvlan-conf-ens256-myapp1" } ]'
      labels:
        app: nginx
        enableSecureSPK: "true"
        enablePseudoCNI: "true"
        secureSPKPort: "8050"
        secureSPKCNFPodIfName: "net1"
        secondaryCNINodeIfName: "spk-shim2"
        primaryCNINodeIfName: "spk-shim"
        secureSPKNetAttachDefName: "macvlan-conf-ens256"
        secureSPKEgressVlanName: "external"
SPKの設定

以下のカスタム リソースは、以下の役割を持つリスナーを構成します。

  • 内部 VLAN、またはアプリケーション podの calico インターフェイスからのトラフィックを処理します。
  • 送信元 IP が SPK の IP アドレスになるようにトラフィックを SNAT します。
apiVersion: "k8s.f5net.com/v1"
kind: F5SPKEgress
metadata:
  name: egress-crd
  namespace: ns-f5-spk
spec:
  #leave commented out for snat automap
  #egressSnatpool: "snatpool-1"
  dualStackEnabled: false
  maxTmmReplicas: 1
  vlans:
    vlanList: [internal]
    disableListedVlans: false

次に、カーネル ルールとルートを動的に作成する CSRC Daemonset をデプロイします。 daemonsetMode を「pseudoCNI」に設定しています。これは、プライマリ (calico) インターフェイスとセカンダリ インターフェイスのトラフィックの両方を SPK にルーティングすることを意味します。

values-csrc.yaml
image:
  repository: gitlab.tky.lab:5050/registry/spk/200
 
# daemonset mode, regular, secureSPK, or pseudoCNI
#daemonsetMode: "regular"
daemonsetMode: "pseudoCNI"
ipFamily: "ipv4"
 
imageCredentials:
  name: f5-common-pull-creds
 
config:
  iptableid: 200
  interfacename: "spk-shim"
    #tmmpodinterfacename: "internal"
json:
  ipPoolCidrInfo:
   cidrList:
   - name: cluster-cidr0
     value: "172.21.107.192/26"
   - name: cluster-cidr1
     value: "172.21.66.0/26"
   - name: cluster-cidr2
     value: "10.124.18.192/26"
   - name: node-cidr0
     value: "10.1.11.0/24"
   - name: node-cidr1
     value: "10.1.10.0/24"
  ipPoolList:
   - name: default-ipv4-ippool
     value: "172.21.64.0/18"
   - name: spk-app1-pool
     value: "10.124.0.0/16"

 

動作確認

その後、アプリケーションをホストしているワーカー ノードにログオンし、ルートとルールが作成されたことを確認できます。基本的に、このルールは、デフォルト ルートとして設定された SPK を持つカスタム ルート テーブルを calico インターフェイスに使用させることです。

# ip rule
0:  from all lookup local
32254:  from all to 172.21.107.192/26 lookup main
32254:  from all to 172.21.66.0/26 lookup main
32254:  from all to 10.124.18.192/26 lookup main
32254:  from all to 172.28.15.0/24 lookup main
32254:  from all to 10.1.10.0/24 lookup main
32257:  from 10.124.18.207 lookup ns-f5-spkshim1ipv4257 <--match on app pod1 calico IP!!!
32257:  from 10.124.18.211 lookup ns-f5-spkshim1ipv4257 <--match on app pod2 calico IP!!!
32258:  from 10.1.10.171 lookup ns-f5-spkshim2ipv4258 <--match on app pod1 macvlan IP!!!
32258:  from 10.1.10.170 lookup ns-f5-spkshim2ipv4258 <--match on app pod2 macvlan IP!!!
32766:  from all lookup main
32767:  from all lookup default

# ip route show table ns-f5-spkshim1ipv4257
default via 10.1.30.242 dev shim1
10.1.30.242 via 10.1.30.242 dev shim1

# ip route show table ns-f5-spkshim2ipv4258
default via 10.1.10.160 dev shim2
10.1.10.160 via 10.1.10.160 dev shim2

SPK 経由でアクセス可能なネットワーク セグメントに存在するサーバーに対してcurl コマンドを実行します。アプリケーション podは、CSRC で設定された IP ルールにヒットし、新しいデフォルト ゲートウェイ (SPK) に転送されます。

SPK ではソース NAT が有効になっているため、サーバーの観点からの「クライアント IP」は SPK の自己 IP になります。  アプリケーションのワークロードにファイアウォール ポリシーを簡単に適用できるほか、そのトラフィックを可視化できるようになりました。


k exec -it nginx-7d7699f86c-hsx48 -n my-app1 -- curl 10.1.70.30
================================================
 ___ ___   ___                    _
| __| __| |   \ ___ _ __  ___    /_\  _ __ _ __
| _||__ \ | |) / -_) '  \/ _ \  / _ \| '_ \ '_ \
|_| |___/ |___/\___|_|_|_\___/ /_/ \_\ .__/ .__/
                                      |_|  |_|
================================================
 
      Node Name: F5 Docker vLab
     Short Name: server.tky.f5se.com
 
      Server IP: 10.1.70.30
    Server Port: 80
 
      Client IP: 10.1.30.242
    Client Port: 59248
 
Client Protocol: HTTP
 Request Method: GET
    Request URI: /
 
    host_header: 10.1.70.30
     user-agent: curl/7.88.1

SPK のデバッグ コンテナ内で実行される tcpdump コマンドは、podのカリコ インターフェイス IP (10.124.18.192) が SPK 上の受信トラフィックの送信元 IP であることを確認します。送信パケットは、サーバーに接続するときに送信元 IP として SPK のセルフ IP (10.1.30.242) を使用します。

/tcpdump -nni 0.0 tcp port 80
----snip----
12:34:51.964200 IP 10.124.18.192.48194 > 10.1.70.30.80: Flags [P.], seq 1:75, ack 1, win 225, options [nop,nop,TS val 4077628853 ecr 777672368], length 74: HTTP: GET / HTTP/1.1 in slot1/tmm0 lis=egress-ipv4 port=1.1 trunk=
----snip----
12:34:51.964233 IP 10.1.30.242.48194 > 10.1.70.30.80: Flags [P.], seq 1:75, ack 1, win 225, options [nop,nop,TS val 4077628853 ecr 777672368], length 74: HTTP: GET / HTTP/1.1 out slot1/tmm0 lis=egress-ipv4 port=1.1 trunk=

セカンダリ macvlan インターフェイスを使用している出力アプリケーション トラフィックを見てみましょう。この場合、ソース NAT を設定していないため、SPK は元のpod  IP を保持したままトラフィックを転送します。

k exec -it nginx-7d7699f86c-g4hpv -n my-app1 -- curl 10.1.80.30
================================================
 ___ ___   ___                    _
| __| __| |   \ ___ _ __  ___    /_\  _ __ _ __
| _||__ \ | |) / -_) '  \/ _ \  / _ \| '_ \ '_ \
|_| |___/ |___/\___|_|_|_\___/ /_/ \_\ .__/ .__/
                                      |_|  |_|
================================================
 
      Node Name: F5 Docker vLab
     Short Name: ue-client3
 
      Server IP: 10.1.80.30
    Server Port: 80
 
      Client IP: 10.1.10.170
    Client Port: 56436
 
Client Protocol: HTTP
 Request Method: GET
    Request URI: /
 
    host_header: 10.1.80.30
     user-agent: curl/7.88.1

SPK のデバッグ コンテナ内で tcpdump コマンドを実行すると、この場合、上記の GET リクエストを受信し、ソース NAT を使用せずに送信していることがわかります。

/tcpdump -nni 0.0 tcp port 80
----snip----
13:54:40.389281 IP 10.1.10.170.56436 > 10.1.80.30.80: Flags [P.], seq 1:75, ack 1, win 229, options [nop,nop,TS val 4087715696 ecr 61040149], length 74: HTTP: GET / HTTP/1.1 in slot1/tmm0 lis=secure-egress-ipv4-virtual-server port=1.2 trunk=
----snip----
13:54:40.389305 IP 10.1.10.170.56436 > 10.1.80.30.80: Flags [P.], seq 1:75, ack 1, win 229, options [nop,nop,TS val 4087715696 ecr 61040149], length 74: HTTP: GET / HTTP/1.1 out slot1/tmm0 lis=secure-egress-ipv4-virtual-server port=1.2 trunk=

SPK のデバッグ コンテナ内で tmctl コマンドを使用すると、podのプライマリ (egress-ipv4) インターフェイスとセカンダリ (secure-egress-ipv4-virtual-server) インターフェイスの送信トラフィックを処理する両方のリスナーの統計を確認できます。

/tmctl -f /var/tmstat/blade/tmm0 virtual_server_stat -s name,clientside.bytes_in,clientside.bytes_out,no_staged_acl_match_accept -w 200
name                                           clientside.bytes_in clientside.bytes_out no_staged_acl_match_accept
---------------------------------------------- ------------------- -------------------- --------------------------
secure-egress-ipv4-virtual-server                              394                  996                          1
egress-ipv4                                                    394                 1011                          1

 

これで、外部向けのトラフィックが SPK データ プレーン pod にルーティングされるようになったので、F5 の custom resource definition (CRD) を使用して、セキュリティ要件を満たす詳細なアクセス コントロール リスト (ACL) を適用できます。ファイアウォール構成はコード (YAML マニフェスト) として定義されるため、Kubernetes とネイティブに統合されます。

F5BigContextGlobal: デフォルトのグローバル ファイアウォールの動作を定義し、ファイアウォール ポリシーを参照するための CRD。

F5BigFwPolicy: ファイアウォール ルールを定義するための CRD。

 

上の図と構成例は、動的 Kubernetes クラスターのセキュリティと制御を犠牲にする必要がないように、SPK がすべてのegressトラフィックを動的にキャプチャする方法を説明しています。

Published Aug 31, 2024
Version 1.0
No CommentsBe the first to comment