istio流量劫持总结

举报
fuziye 发表于 2020/02/29 17:40:35 2020/02/29
【摘要】 § envoy 启动流程数据面组件启动流程initContainer执行初始化脚本,为Pod添加iptables规则Pilot-agent根据启动参数和K8S API Server中的配置信息生成Envoy的初始配置文件envoy-rev0.json,该文件告诉Envoy从xDS server中获取动态配置信息,并配置了xDS server的地址信息,即控制面的Pilot。Pilot-age...

§ envoy 启动流程

数据面组件启动流程

  1. initContainer执行初始化脚本,为Pod添加iptables规则

  2. Pilot-agent根据启动参数和K8S API Server中的配置信息生成Envoy的初始配置文件envoy-rev0.json,该文件告诉Envoy从xDS server中获取动态配置信息,并配置了xDS server的地址信息,即控制面的Pilot。

  3. Pilot-agent使用envoy-rev0.json启动Envoy进程。

  4. Envoy根据初始配置获得Pilot地址,采用xDS接口从Pilot获取到Listener,Cluster,Route等d动态配置信息。

  5. Envoy根据获取到的动态配置启动Listener,并根据Listener的配置,结合Route和Cluster对拦截到的流量进行处理。

§ istio劫持流量总流程


image.png


进流量:
downstream -> iptables -> envoy Inbound -> app
出流量:
app -> iptables -> envoy Outbound -> upstream


§ 1. IPTABLES 劫持转发流量的过程

  1. 启动流程中有讲到第一步是envoy执行初始化脚本,添加iptables规则
    参考文章
    [istio sidecar中创建iptables流程
    ](https://www.jianshu.com/p/50af80c5baba)

§ 2. envoy Inbound 和 Outbound 流程

§ istio 的监听端口

$ nsenter -t 20533 -n netstat -nl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:15090           0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:15000         0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:15001           0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:15006           0.0.0.0:*               LISTEN
tcp6       0      0 :::15020                :::*                    LISTEN
tcp6       0      0 :::8889                 :::*                    LISTEN

由上图可以看到envoy有多个端口在监听
15000 端口: 管理端口 
15001 端口:Outbound流量重定向端口 
15006 端口:Inbound流量重定向端口 
15020 端口:健康检查端口 
15090 端口: 普罗米修斯端口

应用端口:8899

§ envoy Inbound处理流量过程

Inbound 流程是将 iptables 拦截到的 downstream 的流量转交给 localhost,与 Pod 内的应用程序容器建立连接。

下面以进入calculator的流量为例子,来分析一下Inbound流程

§ calculator Inbound流程图



(https://upload-images.jianshu.io/upload_images/17125059-73f4b57e4755557f.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240)

§ Inbound配置

  1. listener

  • virtual listener

{
	"version_info": "2020-02-11T01:52:11Z/5",
	"listener": {
		"name": "virtualInbound",
		"address": {
			"socket_address": {
				"address": "0.0.0.0",
				"port_value": 15006
			}
		},
		"filter_chains": [{
			"filter_chain_match": {
				"prefix_ranges": [{
					"address_prefix": "::0",
					"prefix_len": 0
				}]
			},
			"filters": [{
				"name": "envoy.tcp_proxy",
				"typed_config": {
					"@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy",
					"stat_prefix": "InboundPassthroughClusterIpv6",
					"cluster": "InboundPassthroughClusterIpv6"
				}
			}],
			"filter_chain_match": {
				"prefix_ranges": [{
					"address_prefix": "192.144.99.163",
					"prefix_len": 32
				}],
				"destination_port": 8888
			},
			"filters": [{
				"name": "envoy.http_connection_manager",
				"typed_config": {
					"@type": "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager",
					"stat_prefix": "inbound_192.144.99.163_8888",
					"http_filters": [{
							"name": "istio_authn"
						},
						{
							"name": "mixer"
						},
						{
							"name": "envoy.router"
						}
					],
					"route_config": {
						"name": "inbound|8888|grpc|calculator.vm.svc.cluster.local",
						"virtual_hosts": [{
							"name": "inbound|http|8888",
							"domains": [
								"*"
							],
							"routes": [{
								"match": {
									"prefix": "/"
								},
								"decorator": {
									"operation": "calculator.vm.svc.cluster.local:8888/*"
								},
								"name": "default",
								"route": {
									"timeout": "0s",
									"max_grpc_timeout": "0s",
									"cluster": "inbound|8888|grpc|calculator.vm.svc.cluster.local"
								}
							}]
						}],
						"validate_clusters": false
					}
				}
			}]
		}]
	}
}
  • 该Listener中第三个filterchain用于处理Calculator服务的入向请求。

  • 该filterchain的匹配条件为Calculator服务的 IP和8888端口,配置了一个http_connection_manager filter

  • http_connection_manager 中又嵌入了istio_auth,Mixer,envoy.router等http filter,经过这些filter进行处理后,请求最终将被转发给"“cluster”: “inbound|8888|grpc|calculator.vm.svc.cluster.local”"

接下来我们看下inbound cluster的配置

  1. cluster

  • inbound cluster

    {
     "version_info": "2020-02-10T08:16:25Z/3",
     "cluster": {
      "name": "inbound|8888|grpc|calculator.vm.svc.cluster.local",
      "type": "STATIC",
      "connect_timeout": "10s",
      "circuit_breakers": {
       "thresholds": [
        {
         "max_connections": 4294967295,
         "max_pending_requests": 4294967295,
         "max_requests": 4294967295,
         "max_retries": 4294967295
        }
       ]
      },
      "http2_protocol_options": {
       "max_concurrent_streams": 1073741824
      },
      "load_assignment": {
       "cluster_name": "inbound|8888|grpc|calculator.vm.svc.cluster.local",
       "endpoints": [
        {
         "lb_endpoints": [
          {
           "endpoint": {
            "address": {
             "socket_address": {
              "address": "127.0.0.1",
              "port_value": 8888
             }
            }
           }
          }
         ]
        }
       ]
      }
     }
    }
  • 这个Inbound Cluster,由于该Inbound Cluster中配置的Upstream为127.0.0.1:8888(?如何查看其upstream)

  • 由于iptable设置中127.0.0.1不会被拦截,该请求将发送到Calculator服务的8888端口上进行业务处理。

§ Inbound流程代码

//todo

§ envoy Outbound处理流量过程

§ webapp outbound流程图


§ outbound配置

  • virtual outbound istener

{
	"listener": {
		"name": "virtualOutbound",
		"address": {
			"socket_address": {
				"address": "0.0.0.0",
				"port_value": 15001
			}
		},
		"filter_chains": [{
				"filter_chain_match": {
					"prefix_ranges": [{
						"address_prefix": "10.244.2.73",
						"prefix_len": 32
					}]
				},
				"filters": [{
					"name": "envoy.tcp_proxy",
					"typed_config": {
						"@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy",
						"stat_prefix": "BlackHoleCluster",
						"cluster": "BlackHoleCluster"
					}
				}]
			},
			{
				"filters": [{
						"name": "mixer"
					},
					{
						"name": "envoy.tcp_proxy",
						"typed_config": {
							"@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy",
							"stat_prefix": "PassthroughCluster",
							"cluster": "PassthroughCluster"
						}
					}
				]
			}
		],
		"use_original_dst": true
	}
}

改listener的最后一行有个配置 “use_original_dst”: true,表示用原有地址,不进行路由,这样就会转发到0.0.0.0_8888这个监听器上

下面看一下0.0.0.0_8888这个监听器的配置

  • outbound listener

{
	"listener": {
		"name": "0.0.0.0_8888",
		"address": {
			"socket_address": {
				"address": "0.0.0.0",
				"port_value": 8888
			}
		},
		"filter_chains": [{
				"filter_chain_match": {
					"prefix_ranges": [{
						"address_prefix": "10.244.2.73",
						"prefix_len": 32
					}]
				},
				"filters": [{
					"name": "envoy.tcp_proxy",
					"typed_config": {
						"@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy",
						"stat_prefix": "BlackHoleCluster",
						"cluster": "BlackHoleCluster"
					}
				}]
			},
			{
				"filters": [{
					"name": "envoy.http_connection_manager",
					"typed_config": {
						"@type": "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager",
						"stat_prefix": "outbound_0.0.0.0_8888",
						"http_filters": [{
								"name": "mixer"
							},
							{
								"name": "envoy.cors"
							},
							{
								"name": "envoy.fault"
							},
							{
								"name": "envoy.router"
							}
						],
						"use_remote_address": false,
						"generate_request_id": true,
						"upgrade_configs": [{
							"upgrade_type": "websocket"
						}],
						"stream_idle_timeout": "0s",
						"normalize_path": true,
						"rds": {
							"config_source": {
								"ads": {}
							},
							"route_config_name": "8888"
						}
					}
				}]
			}
		],
		"deprecated_v1": {
			"bind_to_port": false
		},
		"listener_filters_timeout": "0.100s",
		"traffic_direction": "OUTBOUND",
		"continue_on_listener_filters_timeout": true
	}
}

同理最后有一个envoy.router filter,这个filter中route_config_name:“8888”,所以我们可以找到名为"8888"的Route配置

  • outbound Route

{
	"route_config": {
		"name": "8888",
		"virtual_hosts": [{
				"name": "calculator.vm.svc.cluster.local:8888",
				"domains": [
					"calculator.vm.svc.cluster.local",
					"calculator.vm.svc.cluster.local:8888",
					"calculator.vm",
					"calculator.vm:8888",
					"calculator.vm.svc.cluster",
					"calculator.vm.svc.cluster:8888",
					"calculator.vm.svc",
					"calculator.vm.svc:8888",
					"10.106.45.142",
					"10.106.45.142:8888"
				],
				"routes": [{
					"match": {
						"prefix": "/"
					},
					"route": {
						"weighted_clusters": {
							"clusters": [{
									"name": "outbound|8888|v1|calculator.vm.svc.cluster.local",
									"weight": 50
								},
								{
									"name": "outbound|8888|v2|calculator.vm.svc.cluster.local",
									"weight": 50
								}
							]
						}
					},
					"decorator": {
						"operation": "calculator:8888/*"
					}
				}]
			},
			{
				"name": "allow_any",
				"domains": [
					"*"
				],
				"routes": [{
					"match": {
						"prefix": "/"
					},
					"route": {
						"cluster": "PassthroughCluster"
					}
				}]
			}
		],
		"validate_clusters": false
	}
}

根据Route 8888的配置可以找到outbound cluster

  • outbound cluster

    {
     "version_info": "2020-02-07T10:38:17Z/1847",
     "cluster": {
      "name": "outbound|8888|v2|calculator.vm.svc.cluster.local",
      "type": "EDS",
      "eds_cluster_config": {
       "eds_config": {
        "ads": {}
       },
       "service_name": "outbound|8888|v2|calculator.vm.svc.cluster.local"
      },
      "connect_timeout": "10s",
      "circuit_breakers": {
       "thresholds": [
        {
         "max_connections": 4294967295,
         "max_pending_requests": 4294967295,
         "max_requests": 4294967295,
         "max_retries": 4294967295
        }
       ]
      },
      "http2_protocol_options": {
       "max_concurrent_streams": 1073741824
      },
      "metadata": {
       "filter_metadata": {
        "istio": {
         "config": "/apis/networking/v1alpha3/namespaces/vm/destination-rule/calculator"
        }
       }
      }
     },
     "last_updated": "2020-02-07T10:38:17.893Z"
    }

我们会发现这个cluster没有直接的endpoint,因为这是动态资源,
可以通过Pilot的调试接口获取该Cluster的endpoint:

$ curl http://xxx.xxx.xxx.xxx.108:15014/debug/edsz > pilot_eds_dump

最后发现endpoint为192.xxx.xxx.163:8888
然后就把流量转发到calculator服务了

  • BlackHoleCluster
    这是一个特殊的Cluster,并没有配置后端处理请求的Host。如其名字所暗示的一样,请求进入后将被直接丢弃掉。如果一个请求没有找到其对的目的服务,则被发到cluste。

{
    "cluster": {
        "name": "BlackHoleCluster",
        "type": "STATIC",
        "connect_timeout": "1s"
    }
}

*PassthroughCluster
和BlackHoleCluter相反,发向PassthroughCluster的请求会被直接发送到其请求中要求的原始目地的,Envoy不会对请求进行重新路由。

{
    "cluster": {
        "name": "PassthroughCluster",
        "type": "ORIGINAL_DST",
        "connect_timeout": "1s",
        "lb_policy": "CLUSTER_PROVIDED",
        "circuit_breakers": {
            "thresholds": [
                {
                    "max_connections": 4294967295,
                    "max_pending_requests": 4294967295,
                    "max_requests": 4294967295,
                    "max_retries": 4294967295
                }
            ]
        }
    }
}

§ outbound流程代码

//todo


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。