为 Kubernetes 之外的机器赋予同样的身份
目标
在用同一台 VM 内的网络命名空间构造的“网格之外的机器”上启动 VM 用的 sidecar,用 WorkloadGroup 和 WorkloadEntry 把它纳入网格,并确认双向 mTLS 以及 STRICT、授权策略会与 Pod 完全一样地生效。
为什么重要
没能迁移到容器中的机器,很容易成为网格的例外,而例外会导致 PERMISSIVE 或宽泛的放行规则,变成安全漏洞。如果给这台机器同样的 sidecar 和同样的身份,就可以无例外地施加同样的策略。这个流程很长,在名称解析、令牌和拦截规则上很容易卡住,所以完整做一遍是最快的学习方式。
步骤
- 用
kubectl apply -f /opt/fixtures/istlab/vmworkload-app.yaml部署材料,等待 Pod 就绪。这台 VM 中有网络命名空间legacy(192.168.77.2),应用在其中监听 8080。在/root/istlab-vm/01-outside.txt中写入三行——在 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中写入并应用四个资源——命名空间vmns(带注入标签)、服务账号legacy-sa、WorkloadGrouplegacy(元数据标签app: legacy,模板中的服务账号legacy-sa,端口http: 8080),以及用选择器app: legacy暴露 8080 的Servicelegacy(端口名称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(占位符为 istiod 服务的 ClusterIP)生成 VM 用的文件。/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 规则数量)。 - 在
/root/istlab-vm/workloadentry.yaml中写入并应用WorkloadEntrylegacy-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 sidecar 的访问日志中的一行原样保存到/root/istlab-vm/06-from-vm.log。 - 在
/root/istlab-vm/secure.yaml中写入并应用两个资源——vmns的PeerAuthenticationstrict(STRICT)和AuthorizationPolicylegacy-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中写入五行——vm_identity=(VM sidecar 所获得证书的 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 用的 sidecar 软件包(istio-sidecar.deb 1.31.0)已由配方预先准备好。 - 命名空间内的命令用
ip netns exec legacy <명령>(占位符为命令)执行。其中的/etc/hosts和/etc/resolv.conf是/etc/netns/legacy/下的文件。 - sidecar 日志在
/var/log/istio/istio.log,启动日志在/var/log/istio/start.log。重新启动时,要先关闭命名空间内的 pilot-agent(用ip netns pids legacy查找)。 - 这台 VM 在会话超过一小时后,istio-token 会过期。请在一小时内完成到第 4 步。
- 常见错误——把
/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 中写入三行——在 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 的 sidecar 只会直接放行。
为 VM 准备的模板——WorkloadGroup 和服务
在 /root/istlab-vm/vmns.yaml 中写入并应用四个资源——命名空间 vmns(带注入标签)、服务账号 legacy-sa、WorkloadGroup legacy(元数据标签 app: legacy,模板中的服务账号 legacy-sa,端口 http: 8080),以及用选择器 app: legacy 暴露 8080 的 Service legacy(端口名称 http)。
WorkloadGroup 是为 VM 准备的 Pod 模板——像标签、服务账号、端口这样,写明“这样的机器以这样的身份打开这个端口”。Kubernetes Service 的选择器不仅会选中 Pod,也会选中 Istio 的 WorkloadEntry,所以可以把 VM 放在已有的服务名称后面。目前还没有一台 VM(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(占位符为 istiod 服务的 ClusterIP)生成 VM 用的文件。/root/istlab-vm/files 中应生成 cluster.env、istio-token、mesh.yaml、root-cert.pem、hosts。
VM 的 sidecar 需要知道五件事——自己是谁(cluster.env 中的命名空间、服务账号、标签)、首次获取证书时出示的令牌(istio-token,服务账号令牌)、网格配置(mesh.yaml)、要信任的根(root-cert.pem),以及用来查找 istiod 的地址(hosts)。如果是跨多个网络的安装,要给出东西向网关的地址,但在这台 VM 上,网络命名空间会借助主机的 kube-proxy 规则直接到达 istiod 的 ClusterIP。istiod 的地址用 kubectl -n istio-system get svc istiod -o jsonpath='{.spec.clusterIP}' 获取。
在网格之外的机器上启动 sidecar
把第 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 规则数量)。
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 放到服务后面——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 表示为类似 Pod 的资源。标签与服务 legacy 的选择器匹配,所以 istiod 会把这个地址作为该服务的端点下发给各个 sidecar。client 的 sidecar 与 VM 的 sidecar 之间用 mTLS 通信,在进入的一侧,由命名空间内的 iptables 把 8080 转给 VM 的 sidecar。在 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 sidecar 的访问日志中的一行原样保存到 /root/istlab-vm/06-from-vm.log。
VM 的 sidecar 同样开启了 DNS 代理(cluster.env 中的 ISTIO_META_DNS_CAPTURE),所以能解析集群服务名称,发出的请求由 sidecar 接收后通过 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 中写入并应用两个资源——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 时连接应被断开。
无论是 Pod 还是 VM,PeerAuthentication 和 AuthorizationPolicy 都通过工作负载标签来选择。VM 的 sidecar 带着 cluster.env 中的标签(app: legacy)连上了 istiod,所以同样的策略会下发给 VM 的 sidecar。从网格之外以明文进入的请求,由命名空间内的 iptables 转给 sidecar,而处于 STRICT 的 sidecar 会断开这个连接。策略扩散需要几秒钟。
写下网格之外的机器进入网格的路径
在 /root/istlab-vm/08-report.md 中写入五行——vm_identity=(VM sidecar 所获得证书的 SPIFFE URI)、netns_iptables_backend=、host_istio_rules=(第 4 步)、plain_from_outside=(现在 probe 的明文请求能通则为 allowed,被断开则为 rejected)、intruder=(现在 intruder 的状态码)——并在其下写出学到的内容,至少四行。
VM sidecar 的证书可以在命名空间内通过 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 连不上这个代理)。