Istio Ambient Mesh の inpod redirection 試してみた

Kaita Nakamura

2024.4.23

先日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追加

※ 画像の番号とは対応していません

  1. istio-cni Podがistio.io/dataplane-mode=ambientラベルを持つnamespaceでWorkload Podを検出する
  2. istio-cni PodはWorkload Podのnetwork namespace内でiptablesのリダイレクトルールを設定する。Workload Podに出入りするトラフィックがインターセプトされ、Node内のztunnel Podに透過的にリダイレクトされるようにする。
  3. istio-cniは同一Node内のztunnelに、Podのnetwork namespace内でプロキシのリスニングポートの確立を依頼して、Podのnetwork namespace情報をztunnelに提供する。
  4. Nodeのztunnel Podは内部的にWorkload Pod専用の新しいプロキシインスタンスとリスニングポートを起動して、Workload Pod のnetwork namespace内にリスニング ソケットを作成する。

Maturing Istio Ambient: Compatibility Across Various Kubernetes Providers and CNIs

トラフィックの流れ

アウトバウンド

  1. Workload Podからリクエストが送信されるとWorkload Podのnetwork namespaceのiptablesによってトラフィックがztunnel Pod(port 15001)にリダイレクトされる
  2. トラフィックはztunnelによりHBONEカプセル化&暗号化がされてWorkload Podのnetwork namespaceから宛先に送信される

インバウンド

  1. トラフィックはWorkload Podのnetwork namespaceのiptablesによってztunnel Pod(port 15008)にリダイレクトされる
  2. トラフィックは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として機能する?)

https://istio.slack.com/

ブログ一覧へ戻る

お気軽にお問い合わせください

SREの設計・技術支援から、
SRE運用内で使用する
ツールの導入など、
SRE全般についてご支援しています。

資料請求・お問い合わせ