TT Lab
はじめる
学ぶ 学習パス コース

Istio 実測ラボ

Kubernetes の外のマシンに同じ ID を与える

TT Labで続きを見る

目標

同じVMの中のネットワークネームスペースで作った「メッシュ外のマシン」にVM用のサイドカーを起動し、WorkloadGroup・WorkloadEntryでメッシュに入れて、両方向のmTLSと、STRICT・認可ポリシーがPodとまったく同じようにかかることを確認します。

なぜ重要なのか

コンテナに移せなかったマシンは、メッシュの例外になりやすく、例外はPERMISSIVEや広い許可ルールにつながって、セキュリティの穴になります。そのマシンに同じサイドカーと同じ身元を与えれば、例外なく同じポリシーをかけられます。手順が長く、名前解決・トークン・リダイレクト規則でつまずきやすいので、一度最後までやってみるのが最も早い勉強です。

ステップ

  1. kubectl apply -f /opt/fixtures/istlab/vmworkload-app.yamlで材料を載せ、Podの準備ができるまで待ってください。このVMにはネットワークネームスペースlegacy(192.168.77.2)があり、その中でアプリが8080で動いています。/root/istlab-vm/01-outside.txtに3行を書いてください。clientからhttp://192.168.77.2:8080/のステータスコードfrom_client=、メッシュ外のprobeから同じリクエストのステータスコードfrom_outside=、istioctl proxy-statusにlegacyが見えればyes、見えなければnoのin_mesh=です。
  2. /root/istlab-vm/vmns.yamlに4つのリソースを書いて適用してください。ネームスペースvmns(注入ラベル付き)、サービスアカウントlegacy-sa、WorkloadGroupのlegacy(メタデータラベルapp: legacy、テンプレートのサービスアカウントlegacy-sa、ポートhttp: 8080)、そしてセレクターapp: legacyで8080を公開するServiceのlegacy(ポート名http)です。
  3. kubectl -n vmns get workloadgroup legacy -o yamlを/root/istlab-vm/wg.yamlに保存し、istioctl x workload entry configure -f /root/istlab-vm/wg.yaml -o /root/istlab-vm/files --clusterID Kubernetes --ingressIP <istiod 서비스의 ClusterIP> --internalIP 192.168.77.2でVM用のファイルを作ってください(プレースホルダーはistiodのServiceのClusterIPです)。/root/istlab-vm/filesにcluster.env・istio-token・mesh.yaml・root-cert.pem・hostsができている必要があります。
  4. ステップ3のファイルを公式ドキュメントの場所に置き(root-cert.pem→/etc/certs/root-cert.pem、istio-token→/var/run/secrets/tokens/istio-token、cluster.env→/var/lib/istio/envoy/cluster.env、mesh.yaml→/etc/istio/config/mesh、hostsの行はネームスペース用の/etc/netns/legacy/hostsに追記)、所有者をistio-proxyに変えた後、ネームスペースの中で/usr/local/bin/istio-start.shを起動してください(作業ディレクトリ/、POD_NAME=legacy-vm)。istioctl proxy-statusにlegacy-vm.vmnsが見える必要があり、/root/istlab-vm/04-iptables.txtに、netns_backend=(ネームスペースの中でISTIO規則が入っている側。legacyまたはnft)とhost_istio_rules=(ホストのnatテーブル、nftとlegacyの両方にあるISTIO規則の数)の2行を書いてください。
  5. /root/istlab-vm/workloadentry.yamlにWorkloadEntryのlegacy-vm(ネームスペースvmns、アドレス192.168.77.2、ラベルapp: legacy、サービスアカウントlegacy-sa)を書いて適用してください。適用後、clientからhttp://legacy.vmns:8080/が、本文legacy-vmと200を返す必要があります。
  6. ネームスペースの中からx-request-idを付けてhttp://web.shop/を呼び、そのリクエストを受けたwebのサイドカーのアクセスログの1行を、そのまま/root/istlab-vm/06-from-vm.logに保存してください。
  7. /root/istlab-vm/secure.yamlに2つのリソースを書いて適用してください。vmnsのPeerAuthenticationであるstrict(STRICT)と、AuthorizationPolicyのlegacy-only-client(セレクターapp: legacy、ALLOW、プリンシパルcluster.local/ns/shop/sa/client)です。適用後、clientは200、otherのintruderは403、メッシュ外のprobeが192.168.77.2:8080を平文で呼ぶと、接続が切れる必要があります。
  8. /root/istlab-vm/08-report.mdに5行を書き、その下に学んだことを4行以上書いてください。5行は、vm_identity=(VMのサイドカーが受け取った証明書のSPIFFE URI)、netns_iptables_backend=・host_istio_rules=(ステップ4)、plain_from_outside=(現在、probeの平文リクエストが通ればallowed、切れればrejected)、intruder=(現在のintruderのステータスコード)です。

参考

メッシュ外のマシンは、今どう見えるか

kubectl apply -f /opt/fixtures/istlab/vmworkload-app.yamlで材料を載せ、Podの準備ができるまで待ってください。このVMにはネットワークネームスペースlegacy(192.168.77.2)があり、その中でアプリが8080で動いています。/root/istlab-vm/01-outside.txtに3行を書いてください。clientからhttp://192.168.77.2:8080/のステータスコードfrom_client=、メッシュ外のprobeから同じリクエストのステータスコードfrom_outside=、istioctl proxy-statusにlegacyが見えればyes、見えなければnoのin_mesh=です。

ネットワークネームスペースは、カーネルが別に与えるネットワークスタックです。アドレス・ルーティングテーブル・iptablesがすべて別なので、同じVMの中にあっても「別のマシン」のように振る舞います。ホストとは、vethのペア(192.168.77.1 ↔ 192.168.77.2)でつながっていて、Podからそのアドレスへ向かうトラフィックは、ホストがルーティングします。中を見るときは、ip netns exec legacy <명령>を使います(プレースホルダーはコマンドです)。今このマシンは、メッシュが知らないアドレスなので、clientのサイドカーはそのまま素通りさせます。

VMのためのテンプレート: WorkloadGroupとService

/root/istlab-vm/vmns.yamlに4つのリソースを書いて適用してください。ネームスペースvmns(注入ラベル付き)、サービスアカウントlegacy-sa、WorkloadGroupのlegacy(メタデータラベルapp: legacy、テンプレートのサービスアカウントlegacy-sa、ポートhttp: 8080)、そしてセレクターapp: legacyで8080を公開するServiceのlegacy(ポート名http)です。

WorkloadGroupはVMのためのPodテンプレートです。ラベル・サービスアカウント・ポートのように、「こういうマシンたちが、こういう身元でこのポートを開く」を書きます。Kubernetesのサービスのセレクターは、PodだけでなくIstioのWorkloadEntryも選ぶので、VMを既存のサービス名の後ろに置けます。まだVM 1台(WorkloadEntry)はないので、サービスのエンドポイントは空です。

VMに渡すファイルを作る

kubectl -n vmns get workloadgroup legacy -o yamlを/root/istlab-vm/wg.yamlに保存し、istioctl x workload entry configure -f /root/istlab-vm/wg.yaml -o /root/istlab-vm/files --clusterID Kubernetes --ingressIP <istiod 서비스의 ClusterIP> --internalIP 192.168.77.2でVM用のファイルを作ってください(プレースホルダーはistiodのServiceのClusterIPです)。/root/istlab-vm/filesにcluster.env・istio-token・mesh.yaml・root-cert.pem・hostsができている必要があります。

VMのサイドカーが知る必要があるのは5つです。自分が誰か(cluster.envのネームスペース・サービスアカウント・ラベル)、最初に証明書を受け取るときに差し出すトークン(istio-token、サービスアカウントトークン)、メッシュの設定(mesh.yaml)、信頼するルート(root-cert.pem)、istiodを探すアドレス(hosts)です。複数のネットワークにまたがるインストールならEast-Westゲートウェイのアドレスを渡しますが、このVMでは、ネットワークネームスペースがホストのkube-proxyの規則を通って、istiodのClusterIPに直接届きます。istiodのアドレスはkubectl -n istio-system get svc istiod -o jsonpath='{.spec.clusterIP}'です。

メッシュ外のマシンでサイドカーを起動する

ステップ3のファイルを公式ドキュメントの場所に置き(root-cert.pem→/etc/certs/root-cert.pem、istio-token→/var/run/secrets/tokens/istio-token、cluster.env→/var/lib/istio/envoy/cluster.env、mesh.yaml→/etc/istio/config/mesh、hostsの行はネームスペース用の/etc/netns/legacy/hostsに追記)、所有者をistio-proxyに変えた後、ネームスペースの中で/usr/local/bin/istio-start.shを起動してください(作業ディレクトリ/、POD_NAME=legacy-vm)。istioctl proxy-statusにlegacy-vm.vmnsが見える必要があり、/root/istlab-vm/04-iptables.txtに、netns_backend=(ネームスペースの中でISTIO規則が入っている側。legacyまたはnft)とhost_istio_rules=(ホストのnatテーブル、nftとlegacyの両方にあるISTIO規則の数)の2行を書いてください。

istio-sidecar.deb(ラボの準備手順がすでにインストール済み)は、pilot-agent・envoy・istio-start.shを入れます。istio-start.shは、iptablesでリダイレクト規則を仕込み、istio-proxyユーザーでpilot-agentを起動します。これをip netns exec legacy setsid --fork nohup env POD_NAME=legacy-vm /usr/local/bin/istio-start.sh > 로그 2>&1 </dev/null(プレースホルダーはログファイルです)でネームスペースの中で動かすと、規則がそのネームスペースのiptablesにだけ入ります。スクリプトが./var/lib/istioのような相対パスを使うので、cd /の後に起動してください。規則は、ip netns exec legacy iptables-legacy -t nat -Sとiptables-nftで、ホスト側はネームスペースなしで見ます。ログは/var/log/istio/istio.logです。

VM 1台をサービスの後ろに入れる: WorkloadEntry

/root/istlab-vm/workloadentry.yamlにWorkloadEntryのlegacy-vm(ネームスペースvmns、アドレス192.168.77.2、ラベルapp: legacy、サービスアカウントlegacy-sa)を書いて適用してください。適用後、clientからhttp://legacy.vmns:8080/が、本文legacy-vmと200を返す必要があります。

WorkloadEntryは、VM 1台をPodのように表すリソースです。ラベルがサービスlegacyのセレクターに合うので、istiodはこのアドレスを、そのサービスのエンドポイントとして、サイドカーに送ります。clientのサイドカーはVMのサイドカーとmTLSで通信し、入る側は、ネームスペースの中のiptablesが8080をVMのサイドカーに回します。istioctl proxy-config endpoints client.shop --cluster 'outbound|8080||legacy.vmns.svc.cluster.local'に、192.168.77.2が見える必要があります。自動登録はこのインストールではオフなので、手で作ります。

VMからメッシュ内のサービスを名前で呼ぶ

ネームスペースの中からx-request-idを付けてhttp://web.shop/を呼び、そのリクエストを受けたwebのサイドカーのアクセスログの1行を、そのまま/root/istlab-vm/06-from-vm.logに保存してください。

VMのサイドカーもDNSプロキシを有効にしてあるので(cluster.envのISTIO_META_DNS_CAPTURE)、クラスターのサービス名を解決し、出て行くリクエストはサイドカーが受けて、mTLSで送ります。web側のログで、そのリクエストの送信元が192.168.77.2で、SNIがoutbound_.80_._.web.shop.svc.cluster.localかを見てください。平文ではなく、メッシュ内の接続だという証拠です。呼ぶときはip netns exec legacy curl …です。

VMの前にもmTLSと認可をかける

/root/istlab-vm/secure.yamlに2つのリソースを書いて適用してください。vmnsのPeerAuthenticationであるstrict(STRICT)と、AuthorizationPolicyのlegacy-only-client(セレクターapp: legacy、ALLOW、プリンシパルcluster.local/ns/shop/sa/client)です。適用後、clientは200、otherのintruderは403、メッシュ外のprobeが192.168.77.2:8080を平文で呼ぶと、接続が切れる必要があります。

PeerAuthenticationとAuthorizationPolicyは、PodでもVMでも、ワークロードのラベルで選びます。VMのサイドカーがcluster.envのラベル(app: legacy)を持ってistiodにつながったので、同じポリシーがVMのサイドカーに送られます。メッシュの外から平文で入ってくるリクエストは、ネームスペースの中のiptablesがサイドカーに回し、STRICTのサイドカーがその接続を切ります。ポリシーが行き渡るまで数秒かかります。

メッシュ外のマシンがメッシュに入った道筋を書く

/root/istlab-vm/08-report.mdに5行を書き、その下に学んだことを4行以上書いてください。5行は、vm_identity=(VMのサイドカーが受け取った証明書のSPIFFE URI)、netns_iptables_backend=・host_istio_rules=(ステップ4)、plain_from_outside=(現在、probeの平文リクエストが通ればallowed、切れればrejected)、intruder=(現在のintruderのステータスコード)です。

VMのサイドカーの証明書は、ネームスペースの中で、Envoyの管理ポートで見られます。ip netns exec legacy curl -s 'localhost:15000/config_dump?resource=dynamic_active_secrets'のdefault証明書をbase64でデコードして、openssl x509 -noout -ext subjectAltNameで読みます(Podではないので、istioctl proxy-configはこのプロキシにつなげません)。