Kubernetes の外のマシンに同じ ID を与える
目標
同じVMの中のネットワークネームスペースで作った「メッシュ外のマシン」にVM用のサイドカーを起動し、WorkloadGroup・WorkloadEntryでメッシュに入れて、両方向のmTLSと、STRICT・認可ポリシーがPodとまったく同じようにかかることを確認します。
なぜ重要なのか
コンテナに移せなかったマシンは、メッシュの例外になりやすく、例外はPERMISSIVEや広い許可ルールにつながって、セキュリティの穴になります。そのマシンに同じサイドカーと同じ身元を与えれば、例外なく同じポリシーをかけられます。手順が長く、名前解決・トークン・リダイレクト規則でつまずきやすいので、一度最後までやってみるのが最も早い勉強です。
ステップ
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=です。/root/istlab-vm/vmns.yamlに4つのリソースを書いて適用してください。ネームスペースvmns(注入ラベル付き)、サービスアカウントlegacy-sa、WorkloadGroupのlegacy(メタデータラベルapp: legacy、テンプレートのサービスアカウントlegacy-sa、ポートhttp: 8080)、そしてセレクターapp: legacyで8080を公開するServiceのlegacy(ポート名http)です。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ができている必要があります。- ステップ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行を書いてください。 /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を返す必要があります。- ネームスペースの中から
x-request-idを付けてhttp://web.shop/を呼び、そのリクエストを受けたwebのサイドカーのアクセスログの1行を、そのまま/root/istlab-vm/06-from-vm.logに保存してください。 /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を平文で呼ぶと、接続が切れる必要があります。/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は準備に3–5分かかります。ネットワークネームスペース
legacy(192.168.77.2)とその中のアプリ(8080)、VM用のサイドカーパッケージ(istio-sidecar.deb 1.31.0)は、ラボの準備手順ですでに用意されています。 - ネームスペースの中のコマンドは、
ip netns exec legacy <명령>で打ちます(プレースホルダーはコマンドです)。その中の/etc/hostsと/etc/resolv.confは、/etc/netns/legacy/のファイルです。 - サイドカーのログは
/var/log/istio/istio.log、起動ログは/var/log/istio/start.logです。起動し直すときは、ネームスペースの中のpilot-agentを先に止めてください(ip netns pids legacyで探します)。 - このVMは、セッションが1時間を超えるとistio-tokenが期限切れになります。ステップ4までは、1時間以内に終えてください。
- よくある失敗:
/etc/netns/legacy/hostsを丸ごと上書きして、ホスト名の行を消すケースです。istio-start.shのsudoが名前解決を待って、数十秒止まります。
メッシュ外のマシンは、今どう見えるか
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はこのプロキシにつなげません)。