先日Istio 1.21.0がリリースされ ambient meshにinpod redirectionが実装されました。(ambient meshはまだalphaなので本番環境では非推奨です) inpod redirectionが今までのambient meshとどう違うのか簡単にまとめて動作を見てみました。
ambient meshとは
sidecarを使用しない新しいデータプレーンモードです。
こちらの概要がまとまっていて分かりやすいです。ハンズオンあり。
以下に引用して説明すると
従来のサイドカーモードのIstioは多くの本番運用実績がありますが、データプレーンとアプリケーションの分離ができず、結果下記のような課題があげられています。
- データプレーンはサイドカーとしてアプリケーションpodに注入されるため、Istioデータプレーンのインストール、アップグレード時はpodの再起動が必要になり、アプリケーションワークロードを阻害してしまう
- データプレーンが提供する機能の選択ができないため、一部の機能(mTLS実装のみ等)しか使用しないワークロードにとっては不要なリソースをpodに確保する必要があり、全体のリソースを効率的に使用できなくなる
- HTTP準拠でないアプリケーション実装をしている場合、解析エラーや、誤ったL7プロトコルの解釈を引き起こす可能性がある
このようにsidecarではいくつかの課題が存在しました。
L4、L7機能の全てを管理しているサイドカーモードにおけるデータプレーンと異なり、Istio ambientモードではデータプレーンの機能を2つの層に分けて管理をします。
ambient meshではアーキテクチャの変更と機能の分割によって、これらの課題の解決を行いまし。また、一部機能だけを必要とするユーザーにとって運用コスト削減に繋がったり、段階的に機能を拡張することが可能になっています。
ambient meshの主要コンポーネント
Istio CNI
Podをambient meshに追加するNodeのagentです。クラスタ内にすでに存在するプライマリCNI の実装を拡張して、Workload Podとztunnel Pod間のトラフィックのリダイレクトを構成します。install時にDaemonSetとしてデプロイされます。また、sidecarモードではinit containerの代わりに利用できるようです。
ztunnel
メッシュ内のWorkload同士のセキュアな接続を行うコンポーネントです。HBONE(HTTP Based Overlay Network Encapsulation)を使用してWorkload間にTCP トラフィックを渡すトンネルを確立します。sidecarとは異なりデータプレーンとアプリケーションが分離されるため、アプリケーションへの影響なしでデータプレーンの有効化・無効化・拡張・アップグレードが可能になります。mTLS、L4認可、認証、TCPメトリクスとログ収集といったL3,L4の処理を行い、HTTPトラフィックに対するL7の処理は行いません。主にセキュリティに対する機能を担うのでzero-trust tunnel(= ztunnel)みたいです。install時にDaemonSetとしてデプロイされます。
今までのambient meshの実装
メッシュにWorkload Podが追加されるとistio-cniがNodeのnetwork namespaceでiptablesルールを設定します。これにより、Podに出入りするトラフィックがNode内の ztunnel Pod にリダイレクトされます。
また、iptablesではなくeBPFを利用したリダイレクトも可能なようです。
https://istio.io/latest/blog/2023/ambient-ebpf-redirection/
今までのambient meshの課題
1つ目の問題はNodeのnetwork namespaceでトラフィックのリダイレクトを行う(iptablesルールを書き換える)とistio-cniとプライマリCNI(Calicoなど)が同じnetwork namespaceでトラフィックを管理するため、不整合が生じる可能性があることです。
もう一つはNetworkPolicyが正常に機能しない可能性があることです。(NetworkPolicyはCNIにより実装されるため)
そこで、ztunnelへのトラフィックのリダイレクトをsidecarと同じようにPod内部から行うinpod redirectionが提案されました。
inpod redirection アーキテクチャ
メッシュへのWorkload Pod追加
※ 画像の番号とは対応していません
- istio-cni Podがistio.io/dataplane-mode=ambientラベルを持つnamespaceでWorkload Podを検出する
- istio-cni PodはWorkload Podのnetwork namespace内でiptablesのリダイレクトルールを設定する。Workload Podに出入りするトラフィックがインターセプトされ、Node内のztunnel Podに透過的にリダイレクトされるようにする。
- istio-cniは同一Node内のztunnelに、Podのnetwork namespace内でプロキシのリスニングポートの確立を依頼して、Podのnetwork namespace情報をztunnelに提供する。
- Nodeのztunnel Podは内部的にWorkload Pod専用の新しいプロキシインスタンスとリスニングポートを起動して、Workload Pod のnetwork namespace内にリスニング ソケットを作成する。
Maturing Istio Ambient: Compatibility Across Various Kubernetes Providers and CNIs
トラフィックの流れ
アウトバウンド
- Workload Podからリクエストが送信されるとWorkload Podのnetwork namespaceのiptablesによってトラフィックがztunnel Pod(port 15001)にリダイレクトされる
- トラフィックはztunnelによりHBONEカプセル化&暗号化がされてWorkload Podのnetwork namespaceから宛先に送信される
インバウンド
- トラフィックはWorkload Podのnetwork namespaceのiptablesによってztunnel Pod(port 15008)にリダイレクトされる
- トラフィックはztunnelによりカプセル化解除&復号化されWorkload Podのアプリケーションに送信される
リダイレクトはすべてPod側で行われ、sidecarが行うことと同様の効果があるとのことです。
Maturing Istio Ambient: Compatibility Across Various Kubernetes Providers and CNIs
試してみる
セットアップ
inpod redirectionを試してみます。 セットアップはこちらを参考にして、アプリケーションはistioのサンプルを使用します。Layer 4 Networking & mTLS with Ztunnel , Ztunnel traffic redirection こちらも参考にしています。
使用したツールのバージョン
- kind 0.20.0
- istioctl 1.21.0
Helmを使用したセットアップも可能です。base(CRD, ClusterRole)・cni(Istio CNI)・istiod(コントロールプレーン)・ztunnel・gatewayそれぞれインストールが必要です。
$ kind create cluster --config=- <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: ambient
nodes:
- role: control-plane
- role: worker
- role: worker
EOF
$ kubectl get crd gateways.gateway.networking.k8s.io &> /dev/null || \
{ kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd/experimental?ref=444631bfe06f3bcca5d0eadf1857eac1d369421d" | kubectl apply -f -; }
$ istioctl install --set profile=ambient --set "components.ingressGateways[0].enabled=true" --set "components.ingressGateways[0].name=istio-ingressgateway" --skip-confirmation
✔ Istio core installed
✔ Istiod installed
✔ Ingress gateways installed
✔ CNI installed
✔ Ztunnel installed
✔ Installation complete
Made this installation the default for injection and validation.
istio-cniやztunnelなど必要なコンポーネントが作成されます。
ambient-worker, ambient-worker2 Nodeのistio-cniをISTIO_CNI_WORKER, ISTIO_CNI_WORKER2、ztunnelをZTUNNEL_WORKER, ZTUNNEL_WORKER2としておきます。
$ kubectl get pod -n istio-system -l k8s-app=istio-cni-node -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
istio-cni-node-2kjs5 1/1 Running 0 69m 192.168.228.2 ambient-worker2 <none> <none>
istio-cni-node-dn5jb 1/1 Running 0 69m 192.168.228.3 ambient-control-plane <none> <none>
istio-cni-node-xvjkv 1/1 Running 0 69m 192.168.228.4 ambient-worker <none> <none>
$ kubectl get pod -n istio-system -l app=ztunnel -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ztunnel-59c7g 1/1 Running 0 75m 10.244.2.3 ambient-worker2 <none> <none>
ztunnel-lkvj6 1/1 Running 0 75m 10.244.1.3 ambient-worker <none> <none>
ztunnel-rx7nk 1/1 Running 0 75m 10.244.0.5 ambient-control-plane <none> <none>
$ ISTIO_CNI_WORKER=$(kubectl get pod -n istio-system -l k8s-app=istio-cni-node --field-selector=spec.nodeName=ambient-worker -o=jsonpath='{.items[0].metadata.name}')
$ ISTIO_CNI_WORKER2=$(kubectl get pod -n istio-system -l k8s-app=istio-cni-node --field-selector=spec.nodeName=ambient-worker2 -o=jsonpath='{.items[0].metadata.name}')
$ ZTUNNEL_WORKER=$(kubectl get pod -n istio-system -l app=ztunnel --field-selector=spec.nodeName=ambient-worker -o=jsonpath='{.items[0].metadata.name}')
$ ZTUNNEL_WORKER2=$(kubectl get pod -n istio-system -l app=ztunnel --field-selector=spec.nodeName=ambient-worker2 -o=jsonpath='{.items[0].metadata.name}')
メッシュの動作確認
サンプルを使ってWorkloadを作成していきます。
まずはhttpbinサービスを作成します。
$ kubectl create ns ambient-demo
$ kubectl apply -f samples/httpbin/httpbin.yaml -n ambient-demo
$ kubectl get pod -n ambient-demo -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
httpbin-5c779fc757-9dxfm 1/1 Running 0 26s 10.244.2.6 ambient-worker2 <none> <none>
Podのiptablesルールとソケットの情報を確認します。
iptablesルールは変更されていないようです。また、port 8080でlistenしていることが分かります。
$ kubectl logs $ISTIO_CNI_WORKER2 -n istio-system --tail 9
2024-03-17T12:07:08.827667Z info cni excluded because it does not have istio-proxy container (have [httpbin])
$ kubectl debug $(kubectl get pod -l app=httpbin -n ambient-demo -o jsonpath='{.items[0].metadata.name}') -it -n ambient-demo --image gcr.io/istio-release/base --profile=netadmin -- iptables-save
Defaulting debug container name to debugger-hwg7t.
$ kubectl debug $(kubectl get pod -l app=httpbin -n ambient-demo -o jsonpath='{.items[0].metadata.name}') -it -n ambient-demo --image nicolaka/netshoot -- ss -ntlp
Defaulting debug container name to debugger-n78p6.
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 2048 0.0.0.0:8080 0.0.0.0:*
ambient-demo namespaceでメッシュを有効にします。
$ kubectl label namespace ambient-demo istio.io/dataplane-mode=ambient
Podのiptablesルールを再度確認してみます。iptablesルールが変更されています。
$ kubectl debug $(kubectl get pod -l app=httpbin -n ambient-demo -o jsonpath='{.items[0].metadata.name}') -it -n ambient-demo --image gcr.io/istio-release/base --profile=netadmin -- iptables-save
Defaulting debug container name to debugger-qk974.
# Generated by iptables-save v1.8.7 on Sun Mar 17 12:20:18 2024
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:ISTIO_OUTPUT - [0:0]
:ISTIO_PRERT - [0:0]
-A PREROUTING -j ISTIO_PRERT
-A OUTPUT -j ISTIO_OUTPUT
-A ISTIO_OUTPUT -m connmark --mark 0x111/0xfff -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff
-A ISTIO_PRERT -m mark --mark 0x539/0xfff -j CONNMARK --set-xmark 0x111/0xfff
-A ISTIO_PRERT -s 169.254.7.127/32 -p tcp -m tcp -j ACCEPT
-A ISTIO_PRERT ! -d 127.0.0.1/32 -i lo -p tcp -j ACCEPT
-A ISTIO_PRERT -p tcp -m tcp --dport 15008 -m mark ! --mark 0x539/0xfff -j TPROXY --on-port 15008 --on-ip 0.0.0.0 --tproxy-mark 0x111/0xfff
-A ISTIO_PRERT -p tcp -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ISTIO_PRERT ! -d 127.0.0.1/32 -p tcp -m mark ! --mark 0x539/0xfff -j TPROXY --on-port 15006 --on-ip 0.0.0.0 --tproxy-mark 0x111/0xfff
COMMIT
# Completed on Sun Mar 17 12:20:18 2024
# Generated by iptables-save v1.8.7 on Sun Mar 17 12:20:18 2024
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:ISTIO_OUTPUT - [0:0]
-A OUTPUT -j ISTIO_OUTPUT
-A ISTIO_OUTPUT -d 169.254.7.127/32 -p tcp -m tcp -j ACCEPT
-A ISTIO_OUTPUT -p tcp -m mark --mark 0x111/0xfff -j ACCEPT
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -j ACCEPT
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -p tcp -m mark ! --mark 0x539/0xfff -j REDIRECT --to-ports 15001
COMMIT
# Completed on Sun Mar 17 12:20:18 2024
iptablesルールについて詳しいことは不明だったのですが、パケットのnetfilterマーク値(0x111/0xfff)を元にトラフィックのインターセプトをしているようです。
$ kubectl debug $(kubectl get pod -l app=httpbin -n ambient-demo -o jsonpath='{.items[0].metadata.name}') -it -n ambient-demo --image gcr.io/istio-release/base --profile=netadmin -- ip rule
0: from all lookup local
32764: from all fwmark 0x111/0xfff lookup 100
32766: from all lookup main
32767: from all lookup default
$ kubectl debug $(kubectl get pod -l app=httpbin -n ambient-demo -o jsonpath='{.items[0].metadata.name}') -it -n ambient-demo --image gcr.io/istio-release/base --profile=netadmin -- ip route show table 100
local default dev lo scope host
Podのソケットの情報も再度確認してみます。listenしているportが増えていることが分かります。
15001: アウトバウンドのトラフィックのリスニングポート
15006: インバウンドのplaintextトラフィックのリスニングポート
15008: インバウンドのHBONEトラフィックのリスニングポート
$ kubectl debug $(kubectl get pod -l app=httpbin -n ambient-demo -o jsonpath='{.items[0].metadata.name}') -it -n ambient-demo --image nicolaka/netshoot -- ss -ntlp
Defaulting debug container name to debugger-z4btp.
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 128 127.0.0.1:15080 0.0.0.0:*
LISTEN 0 2048 0.0.0.0:8080 0.0.0.0:*
LISTEN 0 128 *:15006 *:*
LISTEN 0 128 *:15001 *:*
LISTEN 0 128 *:15008 *:*
istio-cniとztunnelのlogを確認してみます。
istio-cniによってiptablesルールが設定され、ztunnelがプロキシを開始したことが分かります。
$ kubectl logs $ISTIO_CNI_WORKER2 -n istio-system
.
.
.
2024-03-17T12:17:13.766443Z info ambient Namespace ambient-demo is enabled in ambient mesh
2024-03-17T12:17:13.767425Z info ambient in pod mode - adding pod ambient-demo/httpbin-5c779fc757-9dxfm to ztunnel
2024-03-17T12:17:13.787691Z info iptables Running iptables-restore with the following input:
* nat
-N ISTIO_OUTPUT
-A OUTPUT -j ISTIO_OUTPUT
-A ISTIO_OUTPUT -d 169.254.7.127 -p tcp -m tcp -j ACCEPT
-A ISTIO_OUTPUT -p tcp -m mark --mark 0x111/0xfff -j ACCEPT
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -j ACCEPT
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -p tcp -m mark ! --mark 0x539/0xfff -j REDIRECT --to-ports 15001
COMMIT
* mangle
-N ISTIO_PRERT
-N ISTIO_OUTPUT
-A PREROUTING -j ISTIO_PRERT
-A OUTPUT -j ISTIO_OUTPUT
-A ISTIO_PRERT -m mark --mark 0x539/0xfff -j CONNMARK --set-xmark 0x111/0xfff
-A ISTIO_PRERT -s 169.254.7.127 -p tcp -m tcp -j ACCEPT
-A ISTIO_PRERT ! -d 127.0.0.1/32 -p tcp -i lo -j ACCEPT
-A ISTIO_PRERT -p tcp -m tcp --dport 15008 -m mark ! --mark 0x539/0xfff -j TPROXY --on-port 15008 --tproxy-mark 0x111/0xfff
-A ISTIO_PRERT -p tcp -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ISTIO_PRERT ! -d 127.0.0.1/32 -p tcp -m mark ! --mark 0x539/0xfff -j TPROXY --on-port 15006 --tproxy-mark 0x111/0xfff
-A ISTIO_OUTPUT -m connmark --mark 0x111/0xfff -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff
COMMIT
2024-03-17T12:17:13.787959Z info Running command (without lock): iptables-restore --noflush -v
2024-03-17T12:17:13.804091Z info ambient in pod mode - adding pod ambient-demo/httpbin-5c779fc757-9dxfm to ztunnel
2024-03-17T12:17:13.804889Z info iptables Running iptables-restore with the following input:
* nat
-N ISTIO_OUTPUT
-A OUTPUT -j ISTIO_OUTPUT
-A ISTIO_OUTPUT -d 169.254.7.127 -p tcp -m tcp -j ACCEPT
-A ISTIO_OUTPUT -p tcp -m mark --mark 0x111/0xfff -j ACCEPT
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -j ACCEPT
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -p tcp -m mark ! --mark 0x539/0xfff -j REDIRECT --to-ports 15001
COMMIT
* mangle
-N ISTIO_PRERT
-N ISTIO_OUTPUT
-A PREROUTING -j ISTIO_PRERT
-A OUTPUT -j ISTIO_OUTPUT
-A ISTIO_PRERT -m mark --mark 0x539/0xfff -j CONNMARK --set-xmark 0x111/0xfff
-A ISTIO_PRERT -s 169.254.7.127 -p tcp -m tcp -j ACCEPT
-A ISTIO_PRERT ! -d 127.0.0.1/32 -p tcp -i lo -j ACCEPT
-A ISTIO_PRERT -p tcp -m tcp --dport 15008 -m mark ! --mark 0x539/0xfff -j TPROXY --on-port 15008 --tproxy-mark 0x111/0xfff
-A ISTIO_PRERT -p tcp -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ISTIO_PRERT ! -d 127.0.0.1/32 -p tcp -m mark ! --mark 0x539/0xfff -j TPROXY --on-port 15006 --tproxy-mark 0x111/0xfff
-A ISTIO_OUTPUT -m connmark --mark 0x111/0xfff -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff
COMMIT
2024-03-17T12:17:13.805031Z info Running command (without lock): iptables-restore --noflush -v
2024-03-17T12:17:13.806840Z error Command error output: xtables other problem: line 2 failed
2024-03-17T12:17:13.806946Z error iptables failed to restore iptables rules: exit status 1
2024-03-17T12:17:13.807008Z error ambient failed to update POD inpod: ambient-demo/httpbin-5c779fc757-9dxfm exit status 1
2024-03-17T12:17:13.807055Z error ambient failed to add pod to ztunnel: exit status 1
$ kubectl logs $ZTUNNEL_WORKER2 -n istio-system
.
.
.
2024-03-17T12:17:13.794180Z INFO ztunnel::inpod::statemanager: pod WorkloadUid("9fbd4772-7d01-4f78-9e03-ea84e56165bd") received netns, starting proxy
2024-03-17T12:17:13.794745Z INFO ztunnel::proxy::inbound: listener established address=[::]:15008 component="inbound" transparent=true
2024-03-17T12:17:13.794815Z INFO ztunnel::proxy::inbound_passthrough: listener established address=[::]:15006 component="inbound plaintext" transparent=true
2024-03-17T12:17:13.794841Z INFO ztunnel::proxy::outbound: listener established address=[::]:15001 component="outbound" transparent=true
2024-03-17T12:17:13.794882Z INFO ztunnel::proxy::socks5: listener established address=127.0.0.1:15080 component="socks5"
2024-03-17T12:17:13.910075Z INFO xds{id=3}: ztunnel::xds::client: received response type_url="type.googleapis.com/istio.workload.Address" size=1 removes=0
続いて、sleepサービスを作成します。
$ kubectl apply -f samples/sleep/sleep.yaml -n ambient-demo
serviceaccount/sleep created
service/sleep created
deployment.apps/sleep created
$ kubectl get pod -n ambient-demo -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
httpbin-5c779fc757-9dxfm 1/1 Running 0 25m 10.244.2.6 ambient-worker2 <none> <none>
sleep-9454cc476-ntctf 1/1 Running 0 6s 10.244.2.8 ambient-worker2 <none> <none>
Podのiptablesルールとソケットの情報を確認すると、Podがメッシュに追加されていることが分かります。
$ kubectl debug $(kubectl get pod -l app=httpbin -n ambient-demo -o jsonpath='{.items[0].metadata.name}') -it -n ambient-demo --image gcr.io/istio-release/base --profile=netadmin -- iptables-save
Defaulting debug container name to debugger-wdn7d.
If you don't see a command prompt, try pressing enter.
warning: couldn't attach to pod/httpbin-5c779fc757-9dxfm, falling back to streaming logs: unable to upgrade connection: container debugger-wdn7d not found in pod httpbin-5c779fc757-9dxfm_ambient-demo
# Generated by iptables-save v1.8.7 on Sun Mar 17 12:36:16 2024
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:ISTIO_OUTPUT - [0:0]
:ISTIO_PRERT - [0:0]
-A PREROUTING -j ISTIO_PRERT
-A OUTPUT -j ISTIO_OUTPUT
-A ISTIO_OUTPUT -m connmark --mark 0x111/0xfff -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff
-A ISTIO_PRERT -m mark --mark 0x539/0xfff -j CONNMARK --set-xmark 0x111/0xfff
-A ISTIO_PRERT -s 169.254.7.127/32 -p tcp -m tcp -j ACCEPT
-A ISTIO_PRERT ! -d 127.0.0.1/32 -i lo -p tcp -j ACCEPT
-A ISTIO_PRERT -p tcp -m tcp --dport 15008 -m mark ! --mark 0x539/0xfff -j TPROXY --on-port 15008 --on-ip 0.0.0.0 --tproxy-mark 0x111/0xfff
-A ISTIO_PRERT -p tcp -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ISTIO_PRERT ! -d 127.0.0.1/32 -p tcp -m mark ! --mark 0x539/0xfff -j TPROXY --on-port 15006 --on-ip 0.0.0.0 --tproxy-mark 0x111/0xfff
COMMIT
# Completed on Sun Mar 17 12:36:16 2024
# Generated by iptables-save v1.8.7 on Sun Mar 17 12:36:16 2024
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:ISTIO_OUTPUT - [0:0]
-A OUTPUT -j ISTIO_OUTPUT
-A ISTIO_OUTPUT -d 169.254.7.127/32 -p tcp -m tcp -j ACCEPT
-A ISTIO_OUTPUT -p tcp -m mark --mark 0x111/0xfff -j ACCEPT
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -j ACCEPT
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -p tcp -m mark ! --mark 0x539/0xfff -j REDIRECT --to-ports 15001
COMMIT
# Completed on Sun Mar 17 12:36:16 2024
$ kubectl debug $(kubectl get pod -l app=httpbin -n ambient-demo -o jsonpath='{.items[0].metadata.name}') -it -n ambient-demo --image nicolaka/netshoot -- ss -ntlp
Defaulting debug container name to debugger-6775f.
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 128 127.0.0.1:15080 0.0.0.0:*
LISTEN 0 2048 0.0.0.0:8080 0.0.0.0:*
LISTEN 0 128 *:15006 *:*
LISTEN 0 128 *:15001 *:*
LISTEN 0 128 *:15008 *:*
また、istio-cniによってiptablesルールが設定され、ztunnelがプロキシを開始したことが分かります。
Podが後からnamespaceに追加された場合も同じようにメッシュに追加されています。
$ kubectl logs $ISTIO_CNI_WORKER2 -n istio-system
.
.
.
2024-03-17T12:32:32.184304Z info ambient in pod mode - deleting pod from ztunnel ns=ambient-demo name=sleep-9454cc476-stnlv
2024-03-17T12:32:32.184989Z info ambient in pod mode - deleting pod from ztunnel ns=ambient-demo name=sleep-9454cc476-stnlv
2024-03-17T12:32:47.267436Z warn ambient pod sleep-9454cc476-ntctf does not appear to have any assigned IPs, not capturing
2024-03-17T12:32:47.617849Z info ambient in pod mode - adding pod ambient-demo/sleep-9454cc476-ntctf to ztunnel
2024-03-17T12:32:47.618663Z info iptables Running iptables-restore with the following input:
* nat
-N ISTIO_OUTPUT
-A OUTPUT -j ISTIO_OUTPUT
-A ISTIO_OUTPUT -d 169.254.7.127 -p tcp -m tcp -j ACCEPT
-A ISTIO_OUTPUT -p tcp -m mark --mark 0x111/0xfff -j ACCEPT
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -j ACCEPT
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -p tcp -m mark ! --mark 0x539/0xfff -j REDIRECT --to-ports 15001
COMMIT
* mangle
-N ISTIO_PRERT
-N ISTIO_OUTPUT
-A PREROUTING -j ISTIO_PRERT
-A OUTPUT -j ISTIO_OUTPUT
-A ISTIO_PRERT -m mark --mark 0x539/0xfff -j CONNMARK --set-xmark 0x111/0xfff
-A ISTIO_PRERT -s 169.254.7.127 -p tcp -m tcp -j ACCEPT
-A ISTIO_PRERT ! -d 127.0.0.1/32 -p tcp -i lo -j ACCEPT
-A ISTIO_PRERT -p tcp -m tcp --dport 15008 -m mark ! --mark 0x539/0xfff -j TPROXY --on-port 15008 --tproxy-mark 0x111/0xfff
-A ISTIO_PRERT -p tcp -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A ISTIO_PRERT ! -d 127.0.0.1/32 -p tcp -m mark ! --mark 0x539/0xfff -j TPROXY --on-port 15006 --tproxy-mark 0x111/0xfff
-A ISTIO_OUTPUT -m connmark --mark 0x111/0xfff -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff
COMMIT
2024-03-17T12:32:47.618893Z info Running command (without lock): iptables-restore --noflush -v
2024-03-17T12:32:47.629705Z info ambient Pod sleep-9454cc476-ntctf update event skipped, added/labeled by CNI plugin
$ kubectl logs $ZTUNNEL_WORKER2 -n istio-system
.
.
.
2024-03-17T12:32:47.622080Z INFO ztunnel::inpod::statemanager: pod WorkloadUid("3cf803f5-c434-4357-854c-b197231ccb17") received netns, starting proxy
2024-03-17T12:32:47.622315Z INFO ztunnel::proxy::inbound: listener established address=[::]:15008 component="inbound" transparent=true
2024-03-17T12:32:47.622380Z INFO ztunnel::proxy::inbound_passthrough: listener established address=[::]:15006 component="inbound plaintext" transparent=true
2024-03-17T12:32:47.622396Z INFO ztunnel::proxy::outbound: listener established address=[::]:15001 component="outbound" transparent=true
2024-03-17T12:32:47.622418Z INFO ztunnel::proxy::socks5: listener established address=127.0.0.1:15080 component="socks5"
2024-03-17T12:32:48.386949Z INFO xds{id=3}: ztunnel::xds::client: received response type_url="type.googleapis.com/istio.workload.Address" size=1 removes=0
最後にnotsleepサービスを作成します。
httpbinとsleepがambient-worker Node、notsleepがambient-worker2 Nodeで動いています。
# podAntiAffinityでhttpbinと別Nodeにスケジュールしておきました
$ kubectl apply -f samples/sleep/notsleep.yaml -n ambient-demo
$ kubectl get pods -n ambient-demo -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
httpbin-5c779fc757-9dxfm 1/1 Running 0 34m 10.244.2.6 ambient-worker2 <none> <none>
notsleep-5995fd497d-9mzr8 1/1 Running 0 50s 10.244.1.4 ambient-worker <none> <none>
sleep-9454cc476-ntctf 1/1 Running 0 9m16s 10.244.2.8 ambient-worker2 <none> <none>
メッシュ内での通信確認
リクエストを送ってPod間の通信が問題なく行われるか確認します。
sleep→httpbin
$ kubectl exec deploy/sleep -n ambient-demo -- curl httpbin:8000 -s | grep title -m 1
<title>httpbin.org</title>
$ k logs $ZTUNNEL_WORKER2 -n istio-system
.
.
.
2024-03-17T12:59:50.445402Z INFO proxy{uid=3cf803f5-c434-4357-854c-b197231ccb17}:outbound{id=d5167a64b373047da72c5a4718e1b282}: ztunnel::proxy::outbound: proxy to 10.244.2.6:8080 using HBONE via 10.244.2.6:15008 type Direct
2024-03-17T12:59:50.447446Z INFO inbound{id=d5167a64b373047da72c5a4718e1b282 peer_ip=10.244.2.8 peer_id=spiffe://cluster.local/ns/ambient-demo/sa/sleep}: ztunnel::proxy::inbound: got CONNECT request to 10.244.2.6:8080
2024-03-17T12:59:50.456315Z INFO proxy{uid=3cf803f5-c434-4357-854c-b197231ccb17}:outbound{id=d5167a64b373047da72c5a4718e1b282}: ztunnel::proxy::outbound: complete dur=11.349553ms
notsleep→httpbin
$ kubectl exec deploy/notsleep -n ambient-demo -- curl httpbin:8000 -s | grep title -m 1
<title>httpbin.org</title>
$ k logs $ZTUNNEL_WORKER -n istio-system
.
.
.
2024-03-17T13:02:58.681807Z INFO proxy{uid=8d90dd69-98f8-44d3-a40e-dd65c1fd41bc}:outbound{id=de528326f325d587811838a35325087b}: ztunnel::proxy::outbound: proxy to 10.244.2.6:8080 using HBONE via 10.244.2.6:15008 type Direct
2024-03-17T13:02:58.697138Z INFO proxy{uid=8d90dd69-98f8-44d3-a40e-dd65c1fd41bc}:outbound{id=de528326f325d587811838a35325087b}: ztunnel::proxy::outbound: complete dur=16.335738ms
$ k logs $ZTUNNEL_WORKER2 -n istio-system
.
.
.
2024-03-17T13:02:58.686438Z INFO inbound{id=de528326f325d587811838a35325087b peer_ip=10.244.1.4 peer_id=spiffe://cluster.local/ns/ambient-demo/sa/notsleep}: ztunnel::proxy::inbound: got CONNECT request to 10.244.2.6:8080
通信が同一Node内の場合でも別Node間の場合でも、ztunnelによってトラフィックがリダイレクトされHBONE経由で通信が行われたことが確認できます。
L4認可ポリシー
L4認可ポリシー(AuthorizationPolicy)を適用してみます。
httpbinサービスにsleepサービスからのリクエストのみを許可するAuthorizationPolicyを作成します。
$ kubectl apply -n ambient-demo -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-sleep-to-httpbin
spec:
selector:
matchLabels:
app: httpbin
action: ALLOW
rules:
- from:
- source:
principals:
- cluster.local/ns/ambient-demo/sa/sleep
EOF
リクエストを送ってPod間の通信を確認します。
sleep→httpbin
$ kubectl exec deploy/sleep -n ambient-demo -- curl httpbin:8000 -s | grep title -m 1
<title>httpbin.org</title>
$ k logs $ZTUNNEL_WORKER2 -n istio-system --tail 5
2024-03-17T13:06:24.717884Z INFO proxy{uid=3cf803f5-c434-4357-854c-b197231ccb17}:outbound{id=053b02f8359f7f12161e01051b4891ff}: ztunnel::proxy::outbound: proxy to 10.244.2.6:8080 using HBONE via 10.244.2.6:15008 type Direct
2024-03-17T13:06:24.720993Z INFO inbound{id=053b02f8359f7f12161e01051b4891ff peer_ip=10.244.2.8 peer_id=spiffe://cluster.local/ns/ambient-demo/sa/sleep}: ztunnel::proxy::inbound: got CONNECT request to 10.244.2.6:8080
2024-03-17T13:06:24.730868Z INFO proxy{uid=3cf803f5-c434-4357-854c-b197231ccb17}:outbound{id=053b02f8359f7f12161e01051b4891ff}: ztunnel::proxy::outbound: complete dur=13.482913ms
notsleep→httpbin
$ kubectl exec deploy/notsleep -n ambient-demo -- curl httpbin:8000 -s | grep title -m 1
command terminated with exit code 56
$ k logs $ZTUNNEL_WORKER -n istio-system --tail 5
2024-03-17T13:07:18.567772Z INFO proxy{uid=8d90dd69-98f8-44d3-a40e-dd65c1fd41bc}:outbound{id=f5e38eeb84bb6a9987469dbe6845cf83}: ztunnel::proxy::outbound: proxy to 10.244.2.6:8080 using HBONE via 10.244.2.6:15008 type Direct
2024-03-17T13:07:18.570998Z WARN proxy{uid=8d90dd69-98f8-44d3-a40e-dd65c1fd41bc}:outbound{id=f5e38eeb84bb6a9987469dbe6845cf83}: ztunnel::proxy::outbound: failed dur=3.747878ms err=http status: 401 Unauthorized
$ k logs $ZTUNNEL_WORKER2 -n istio-system --tail 5
2024-03-17T13:07:18.570589Z INFO inbound{id=f5e38eeb84bb6a9987469dbe6845cf83 peer_ip=10.244.1.4 peer_id=spiffe://cluster.local/ns/ambient-demo/sa/notsleep}: ztunnel::proxy::inbound: got CONNECT request to 10.244.2.6:8080
2024-03-17T13:07:18.570659Z INFO inbound{id=f5e38eeb84bb6a9987469dbe6845cf83 peer_ip=10.244.1.4 peer_id=spiffe://cluster.local/ns/ambient-demo/sa/notsleep}: ztunnel::proxy::inbound: RBAC rejected conn=10.244.1.4(spiffe://cluster.local/ns/ambient-demo/sa/notsleep)->10.244.2.6:8080
sleep→httpbinへのリクエストは成功しましたが、notsleep→httpbinへのリクエストが拒否されました。ztunnelによってL4の処理が正常に行われていることが分かりました。
最後に
inpod redirectionにより任意のCNIでambient meshを動かすことが可能になりました。
今回はsidecarを使用するWorkloadとambientで動作するWorkloadがメッシュ内に共存した場合やメッシュ外との通信などについては触れませんでしたが、この辺りについても学習しておきたいです。
また、1.22で ambient meshはbetaになる予定です。ztunnelでkube-proxyとkube-dnsを置き換えている人がいたりしたので今後どうなっていくのか気になります。(Ciliumみたいな感じでIstioがService Mesh & CNIとして機能する?)