次世代の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トラフィックを動的にキャプチャする方法を説明しています。