istio流量拦截机制
流量拦截机制
pod运行环境要求
service association(服务关联)
pod必须属于某个service,哪怕pod没有暴露端口
pod同时属于多个svc时,这些svc不在使用同一个端口时,指定的协议必须一致
应用运行uid
uid 1137预留给sidecar使用,业务pod不能使用这个uid运行
capabilities授权
- 强制启用PSP(pod安全策略)的k8s环境中,必须允许在网格内的pod上使用net_admin(允许生成网络命名空间中的iptables规则)、net_raw(允许使用裸tcp、套接字)2个
- 若未启用PSP,或使用的istio自己的CNI插件,也可以不用(对应部署istio时,是否需要加载相关内核模块)
capabilities:
原先内核对权限分root和普通用户,后来对普通用户授权特殊权限比方便,加了capabilities机制,可用于对某种特殊权限赋予普通用户
pod标签
每个pod必须使用:app、version两个标签指明应用名和版本
app标签用于分布式追踪生成context,version用于显示应用的版本
svc端口命名
每个svc端口都要指定名称、协议,方便istio识别协议做流量调度
k8s 1.18后,可直接在svc中appProtocol项定义协议,就可省去下面的命名格式:
协议-后缀 http-80
协议选择
支持任何类型tcp流量,但不代理udp
- http
- https
- grpc
- tcp(原始raw tcp)
- tls
- grpc-web
- mysql
- redis
- mongo。
注:可自动检测识别http和http2,未检测出的协议全是视为普通tcp,或者手动指定协议
sidecar envoy代理方式
sidecar envoy和应用容器,在pod中共享网络、uts、ipc等命名空间,因此也使用共同的网络协议栈
envoy sidecar基于init容器设置iptables规则进行流量拦截,iptables将拦截后的流量转给envoy,envoy根据配置完成代理,出站流量也使用iptables做拦截转发envoy
拦截模式
- redirect重定向模式
- tproxy透明代理模式

劫持过程
- init:pod启动时,注入的init特权容器开启流量劫持,并设置流量劫持规则,规则分为Inbound规则和Outbound规则
- Inbound:pod外部请求进来时,被traffic intercetion劫持,它根据Inbound规则将请求转到sidecar,再转给业务应用
- Outbound:业务应用向pod请求时被traffic interception劫持,它根据outbound规则转给sidecar,再发往外部

注入的sidecar容器
istio基于k8s admission controller webhook完成sidecar自动注入,为每个pod注入2个容器
端口
特殊端口
15000/tcp 管理端口
15001/tcp 出站端口
15004/http debug端口
15006/tcp 入站端口
15008/h2 hbone mtls隧道端口
15009/h2c hbone安全网络端口
15020/http 内置合并指标数据的端口
15021/http 健康检测
15053/dns dns端口,默认未启用
15090/http 内置普罗米修斯端口
控制平面端口
443/https
15010/grpc xds、ca服务
15012/grpc xds、ca
15014/http 控制平面监控
15017/https 转发443
istio-init容器
属于init-containers,负责在pod中生成iptables规则(k8s-1.29版本信息中有说明开始在未来弃用iptables改nfttables,我猜以后istio可能也会改用nfttables吧)
使用istio/proxyv2镜像启动,所有流量拦截后发给2个端口:15006、15001
规则生成工具:
使用istio-iptables程序生成拦截规则,新版本使用此
istio-iptables [选项]
-z 15006 #入站劫持端口,只用于重定向模式
-p 15001 #出站劫持端口
-m 拦截模式 #支持REDIRECT、TCPPROXY
-b 端口 #流量拦截目标端口列表
-d 端口 #排除的目标端口列表
规则生成脚本:
旧版本使用脚本生成iptables规则
脚本:https://github.com/istio/cni/blob/master/tools/packaging/common/istio-iptables.sh
规则查看
方法1:使用nsenter
nsenter命令可以在宿主机,直接进入容器的网络命名空间,运行iptables命令
注:查看文档,显示nsenter方法很多,但rhel8中查看不到规则信息(可能需要用cni方式部署istio)
yum install -y util-linux
nsenter -t 2479332 -n iptables -t nat -S
yum install -y util-linux jq
C_CID=`crictl ps |grep admin |awk 'NR==2{print $1}'`
C_PID=`crictl inspect -o json $C_CID |jq .info.pid`
#rhel8中使用没有规则输出,不知道什么问题
nsenter -t $C_PID -n iptables -t nat -S
#rhel8系列使用,但我显示为空,不知道什么问题
nsenter -t $C_PID -n nft list table nat

方法2:日志查看
kubectl logs admin istio-init
规则解读:

入站流量:
prerouting --> istio_inbound
- PREROUTING、INPUT、POSTROUTING、OUTPUT链策略全部为允许
- 新建自定义链
- 将所有入站PREROUTING的tcp数据交给ISTIO_INBOUND处理
- 在ISTIO_INBOUND链中对tcp端口:15008、15020、15021、15090,直接返回(返回意味回到主链,而主链是允许所有且无规则,就代表直接不拦截),其他所有tcp流量交给ISTIO_IN_REDIRECT链处理
- ISTIO_REDIRECT链对所有tcp流量重定向给端口15006
出站流量:
uid和gid 1337为envoy程序
output --> istio_output
- 将所有出站OUTPUT的tcp数据交给ISTIO_OUTPUT处理
- 对源地址是127.0.0.6/32、或者是此源地址且发送网卡是lo的,直接返回主链,对于非这个源地址且网卡、uid和gid不是1337的流量交给ISTIO_IN_REDIRECT链
- 对于lo网卡,且uid和gid不是1337的流量直接返回主链,对于uid和gid是1337的流量也返回主链
- 其他所有流量交给ISTIO_REDIRECT链处理
- ISTIO_REDIRECT对所有tcp流量重定向到15001端口
istio-proxy容器
2个进行组成:
- pilot-agent:基于k8s的api-server为envoy初始化bootstrap配置并启动envoy,监控并管理envoy,如出错时重启,提供xds、配置重载等
- envoy:由pilot生成bootstrap配置后启动,通过xds从pilot中获取动态配置,代理出入站流量

pilot
命令行工具
pilot-agent [选项] 命令
request #可用于替代没有curl命令时,向envoy管理端口发请求
GET /listeners #请求envoy的管理api中侦听器
配置转换
pilot基于k8s的svc,发现svc的所有端点,然后将转换后的实际配置分发出去。所有svc都被配置为egress,只有属于svc的pod,才会定义1个igress对应该svc
虚拟侦听器
“虚拟”意为,实际上envoy是匹配iptables的入站、出站拦截后转发的端口,配置了一个总的侦听器,将数据再次划分匹配给其他对应的侦听器
请求到达时,经过iptables拦截后转给监听15006端口的虚拟侦听器,此虚拟侦听器会匹配数据报文特征,把数据转给实际侦听器,如再转给80侦听器
查看方法:
将配置导出后,找个在线json解析查看,内容非常多,几w行
kubectl exec -it admin -c istio-proxy -- pilot-agent request GET /config_dump > cfg.json
virtual outbound listener
流量 -->15001侦听器(匹配目标ip和端口是否有对应的egress侦听器)-->egress侦听器(实际处理流量)

通过15001端口接收所有出站流量,此侦听器配置:use_origin_dest: true,实现将接收的请求交给真正接收请求的egress侦听器
若不存在能接收转发报文的侦听器,则envoy将根据istio的全局配置选项:outboundTrafficPolicy的值来做处理:
- ALLOW_ANY:允许所有,直接由tco_proxy过滤器做passthrough cluster进行透传外部,默认配置
- REGISTRY_ONLY:只允许外发请求到注册在pilot的服务。由侦听器上的tcp_proxy过滤器指向的BlackHoleCluster将流量直接丢弃
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
...
spec:
meshConfig:
outboundTrafficPolicy:
mode: REGISTRY_ONLY
内置出站侦听器端口
- 9080 处理发往details、reviews、rating等服务流量
- 8411 发往zipkin
- 3000 发往grafana
- 9090 发往普罗米修斯
virtual inbound listener
通过15006端口创建侦听器,此侦听器下有多个filterChain过滤链,每个链中有多个filterChainMath匹配,每个filterChainMath都匹配目标端口、协议,匹配到1个就不继续匹配,然后交给过滤器处理,路由给对应集群

查看:
istioctl pc cluster demoapp-10-5d667f96d4-zg7zm --port 15006
istioctl pc cluster demoapp-10-5d667f96d4-zg7zm --fqdn demoapp
- 匹配15006端口,不做任何处理
- tls、裸tcp直接透传
- 匹配80端口,转给真实集群


虚拟侦听器和透传的直接原格式转发

集群类型
静态集群由envoy-rev0.json配置static_resources,初始化普罗米修斯指标、xds服务、zipkin服务等
动态集群由xds api从pilot获取
- inbound cluster:sidecar envoy反向代理
- outbound cluster:网格中所有服务,包含自身正向代理的svc和其他直接代理的svc
- passthrough cluster和inbound passthrough cluster IPv4:透传,发往此类集群的请求直接透传给原始目标地址,envoy不做处理
- black hole cluster:特殊集群,没有端点,发往此处的请求直接丢弃,相当于黑洞作用,一般没有匹配到目标服务的请求发给此处

sidecar资源
默认istio会为每个sidecar envoy,生成所有配置,其中包含不需要访问的资源,如果想要做到,只生成对应关联配置,则需要使用sidecar crd
为每个sidecar envoy单独配置,避免为所有sidecar都生成侦听器(看起来混乱且不易排查问题)
注:较少使用此资源,一把用不到
生效机制
- sidecar资源通过workload selector字段选择同一个命名空间中1到多个workload(pod);
- 对于没有提供workload selector字段的sidecar资源,配置会应用到命名空间中所有workload应用
- 命名空间中同时存在有workload selector字段、没有workload selector字段的sidecar资源时,workload实例将优先应用带有此字段的sidecar对象
- 每个命名空间中,只应该提供1个没有带有workload selector字段的sidecar资源,否则配置结果会难以确定。此外,每个workload也应该只应用1个带有workload selector字段的sidecar资源,否则也难以明确结果
配置:
kubectl explain sidecars

apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
spec:
egress:
- bind: str
captureMode: str #流量拦截机制
#DEFAULT,默认方法拦截
#IPTABLES,iptables拦截
#NONE,不拦截
hosts: [str] #为指定命名空间中的服务创建侦听器,其他服务不创建侦听器。以:命名空间/dns域名 格式配置,此处可以是svc,也可以是serviceEntry或、vs中配置的svc。dns名称为FQDN的主机名,支持*通配,ns也支持*通配,"."为当前命名空间。
port: #生成filter_chains中的链的匹配的端口条件
name: str #访问的目标svc名称
number: int #访问的目标端口
targetPort: int
protocol: 协议
ingress:
- bind: str
captureMode: str
defaultEndpoint: str
port:
name: str
number: int
targetPort: int
protocol: 协议
tls:
caCertificates: str
cipherSuites: [str]
credentialName: str
httpsRedirect: boolean
maxProtocolVersion: str
minProtocolVersion: str
mode: str
privateKey: str
serverCertificate: str
subjectAltNames: [str]
verifyCertificateHash: [str]
verifyCertificateSpki: [str]
outboundTrafficPolicy: #用于定义未匹配egress侦听器的流量的出站规则
egressProxy:
host: str
port:
number: int
subset: str
mode: str #模式
#ALLOW_ANY,允许出去
#REGISTRY_ONLY,不允许,丢给黑洞
workloadSelector: #标签选择器,生效的pod范围
labels: {}
案例:
例1:为proxy限定只访问当前命名空间的服务
1)配置Sidecar资源
cat <<EOF |kubectl apply -f -
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
name: proxy-sidecar
spec:
workloadSelector:
labels:
app: proxy
egress:
- hosts:
- './*'
#- 'istio-system/*'
EOF
只剩当前命名空间下的所有egress侦听器配置

2)再修改Sidecar资源
cat <<EOF |kubectl apply -f -
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
name: proxy-sidecar
spec:
workloadSelector:
labels:
app: proxy
egress:
- hosts:
- './*'
#- 'istio-system/*'
EOF

浙公网安备 33010602011771号