istio-cni详解
概述
Istio 在网格中部署的Pod中注入initContainer,istio-init。该istio-init容器设置荚的网络流量重定向到/从Istio三轮代理。这就要求将用户或服务帐户部署到网格上的Pod具有足够的Kubernetes RBAC权限才能部署具有NET_ADMIN和NET_RAW功能的容器。对于某些组织的安全合规性,要求Istio用户具有提升的Kubernetes RBAC权限是有问题的。Istio CNI插件是istio-init执行相同网络功能但不要求Istio用户启用提升的Kubernetes RBAC权限的容器的替代。
Istio CNI插件在Kubernetes Pod生命周期的网络设置阶段执行Istio Mesh Pod流量重定向,从而消除了 将Pod部署到Istio Mesh中的用户的需求NET_ADMIN和NET_RAW功能。Istio CNI插件取代了istio-init容器提供的功能。
配置istio启用istio-cni
apiVersion: install.istio.io/v1alpha1kind: IstioOperatorspec: components: cni: enabled: true values: cni: excludeNamespaces: - istio-system - kube-system logLevel: infoistioctl install -f istio-cni.yaml
此时查看启用自动注入的pod会发现init container的名称(istio-validation)有别于不启用istio-cni的名称(istio-init),
且执行命令多了以下参数,
- --run-validation # 校验流量是否劫持- --skip-rule-apply # 不执行iptables策略
install-cni
install-cni程序会根据获取配置通过模板对原始cni配置文件进行修改,配置模板存储在istio-cni-config configmap中,渲染后添加的配置如下
{ "kubernetes": { "cni_bin_dir": "/opt/cni/bin", "exclude_namespaces": [ "istio-system", "kube-system" ], "kubeconfig": "/etc/cni/net.d/ZZZ-istio-cni-kubeconfig" }, "log_level": "info", "name": "istio-cni", "type": "istio-cni" }
分别执行以下操作:
•拷贝所需要的相关二进制命令,包含istio-cni/istio-cni-repair/istio-iptables三个命令•创建所需要的kubeconfig,默认为/etc/cni/net.d/ZZZ-istio-cni-kubeconfig•生成cniconfig,即添加了一个kubernetes cni plugin,•永久等待
istio-cni
当kubelet调用cni时会执行上述插件, 通过cni传过来的参数获取相关配置,如果pod包含istio-init则不进行相关操作
•包含sidecar.istio.io/inject=true 注释•包含istio-init initcontainer•命名空间在Exclude列表中•包含 sidecar.istio.io/status 注释
通过NewRedirect生成Redirect信息
redirect结构如下,保存着后续iptables所需的信息
type Redirect struct { targetPort string redirectMode string noRedirectUID string includeIPCidrs string includePorts string excludeIPCidrs string excludeInboundPorts string excludeOutboundPorts string kubevirtInterfaces string}
然后调用istio.Program方法进入命名空间执行istio-iptables命令
func (ipt *iptables) Program(netns string, rdrct *Redirect) error { netnsArg := fmt.Sprintf("--net=%s", netns) nsSetupExecutable := fmt.Sprintf("%s/%s", nsSetupBinDir, nsSetupProg) nsenterArgs := []string{ netnsArg, //指定网络命名空间 nsSetupExecutable, //istio-iptables命令路径 "-p", rdrct.targetPort, //这些实际上就是istio-iptables的参数 "-u", rdrct.noRedirectUID, "-m", rdrct.redirectMode, "-i", rdrct.includeIPCidrs, "-b", rdrct.includePorts, "-d", rdrct.excludeInboundPorts, "-o", rdrct.excludeOutboundPorts, "-x", rdrct.excludeIPCidrs, "-k", rdrct.kubevirtInterfaces, } log.Info("nsenter args", zap.Reflect("nsenterArgs", nsenterArgs)) out, err := exec.Command("nsenter", nsenterArgs...).CombinedOutput() //执行命令 if err != nil { log.Error("nsenter failed", zap.String("out", string(out)), zap.Error(err)) log.Infof("nsenter out: %s", out) } else { log.Infof("nsenter done: %s", out) } return err}
进入对应的网络命名空间执行istio-iptables命令进行端口劫持相关操作,实现了和istio-init init container相同的功能,同样执行的是istio-iptables命令
istio-cni-repair
该程序相当于一个故障处理程序,主要解决以下两个问题
•如果应用程序容器在istio-cni安装完成之前启动,则Kubelet不了解Istio CNI插件。结果是出现了没有Istio iptables规则的应用程序容器。该Pod可以访问网络,而其他Pod可以访问该网络,从而有效地绕过了所有Istio策略。这是一个安全问题,因为它无提示地绕过所有策略检查。•当kubernetes响应负载的突然增加时,应用程序pod和Istio CNI安装程序之间的竞争更有可能发生。节点启动后,Kubernetes将有许多可调度的Pod分配给该节点,这些Pod都与CNI安装程序竞争。如果某个节点突然终止,例如在GKE可抢占节点的情况下,则可能出现这种情况。
上述问题解决思路
•通过init容器检测到iptables在pod中未正确配置,退出并返回错误(推荐方式)•物理安装,不通过daemonset安装•使用netd安装CNI
istio-cni-repair执行逻辑
istio-cni-repair的目的就是为了检测返回错误的pod,主要执行以下逻辑.
•初始化client-set•生成RepairController,根据标签检测istio-validation容器崩溃的pod•搜索istio-validation容器崩溃的pod•根据label-pods/delete-pods参数决定是否删除pod
总结
虽然istio-cni可以减少pod的授权,但是也带来了其他问题,增加了复杂性,如果对容器权限不敏感的情况下,不推荐使用istio-cni。
参考:
缓解istio cni race设计草案[1]
istio源码[2]
istio官方文档[3]
引用链接
[1]
缓解istio cni竞争设计草案: https://docs.google.com/document/d/1SQzrFxtcn3o_79OtJYbSHMPuqZNhR3EaEhbkpBVMXAw/edit#heading=h.7zgnj8bwqfld
[2]
istio源码: https://github.com/istio/istio
[3]
istio官方文档: https://istio.io/latest/docs/setup/additional-setup/cni/
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 面试进阶-数据库中的锁
- (译)针对 Kubernetes 工作负载的策略工具
- 又被逼着优化代码,这次我干掉了出入参 Log日志
- 想去看机会?这10道最高频的手撕代码题都会了吗?
- 你知道Python中的4种变量作用域是哪些吗?
- 图解 Python 浅拷贝与深拷贝
- 打卡群刷题总结0716——不同路径
- 原理 + 代码|手把手教你用Python实现智能推荐算法
- 机器学习必刷题-基础概念篇(1):为什么用AUC做评价指标?
- 机器学习必刷题-手撕推导篇(3):FM与softmax
- Python面试必刷题系列(4)
- SQL面试必刷题(1) Case When
- 张量的数学运算
- 数据结构高频面试题-图
- nn.functional和nn.Module