网络隔离场景下访问 Pod 网络

接着上文 VPC网络架构下的网络上数据采集

介绍

        考虑一个监控系统,它的数据采集 Agent 是以 daemonset 形式运行在物理机上的,它需要采集 Pod 的各种监控信息。现在很流行的一个监控信息是通过 Prometheus 提供指标信息。

        一般来说,daemonset Agent 会使用物理机网络(就算有自己的网络也没关系),它需要通过 ICMP、TCP、HTTP 等协议访问目标 Pod。这在一般的 k8s 场景下是没问题的,因为此时 daemonset Agent 与 目标 Pod 之间的网络是通的,没有隔离。

        而在一些公有云或多租户环境下, 一个物理机上实际分给了好几个租户,此时网络安全管理员往往会限制物理机网络与 Pod 网络是隔离的。这么一搞我们原本的整套链路都不工作了。当然,网络安全管理员这么做是师出有名,并且确实是必要的。但这种方案确实没有给监控场景留一条活路,我们也很难受。

        本文分享几种方案从不同角度突破了这种隔离,当然它们并不一定通用,更多的是为监控场景服务。

可能涉及到的技术或概念

  • Input / Output Stream
  • command exec
  • Linux namespace 、nsenter
  • docker exec / docker SDK
  • unix domain socket / abstract unix domain socket, 以下简称 uds 或 abstract uds
  • socat

基于 Linux namespace 的方案

        如果你使用的是标准的 docker/containerd/runc 或 containerd/runc 的组合,并且对 docker、runc 的实现原理有一些了解,那么你应该听说过 Linux namespace。本文不再介绍 Linux namespace 与 docker、runc 的关系。

        Agent 想与 pod:ip 建立 TCP 连接,只需要将当前线程 network namespace 切换到目标 pod 的 network namespace,这样对于当前线程来说它就进入了目标 pod 的网络,此时你再执行 `net.Dial` 那就能连接上了.

        可以参考下面链接的代码.

https://github.com/traas-stack/holoinsight-agent/blob/main/pkg/cri/cricore/nsenter_linux.go#L52

        linux 有个命令叫 nsenter 本质它就是直接切换的 namespace 然后执行命令, 感兴趣的可以看一下.        

基于 docker sdk exec 的方案

        这里的 docker sdk exec 指的是通过 docker sdk 执行 exec 命令, 而非命令行里的 docker exec. 这样做效率更高一些.

       基于 Linux namespace 的方案只能适用于 runc 容器运行时. 遇到其他容器运行时就不工作了.

但无论怎么变,底层的 CRI 实现总是会提供类似 docker exec、containerd exec 的功能。

        我们可以想办法往目标容器里放一个辅助流量转发的 binary,这个 bianry 从 stdin 读数据,然后写到目标 pod:port, 从目标 pod:port 读数据, 然后写到 stdout. 通过 docker exec 执行该 binary 让其转发流量.

        可以参考如下链接的代码

https://github.com/traas-stack/holoinsight-agent/blob/main/pkg/cri/criutils/tcpproxy.go#L31

        如果直接用 docker sdk exec 方案的话,它对代码的修改比较大。需要配合基于 HTTP 代理 或 socks5 代理才能减少对代码的侵入性。

        docker sdk exec 提供了一种通用的方式用于请求转发, 在我遇到的所有场景里, 它都可以工作. 当然由于它是基于命令行执行的, 因此执行效率会低一些, 使用的时候要考虑这一点.

        考虑到 k8s 提出了自己的 CRI 规范, 而不再强依赖 docker/containerd 的实现API. 因此我们之后应该直接面向 CRI 编程.

        但是 CRI 也有一些缺点:

  • CRI 的初始化步骤比较麻烦, 不像 docker 只需要指定其 unix domain sock 地址就行了.
  • CRI 的 exec 无法指定 user/workdir/env

某些特定容器运行时有建立 proxy 的功能

        在实践中,某些以安全为特点的容器运行时也考虑到了网络隔离问题,因此它们往往具备建立 Proxy 的能力。

        以某个基于 VM 的容器运行时为例。

        它会有 2个 netns: VM 有一个,Pod 有一个。

        VM 的 netns 我们可以从 docker inspect 的信息中看到,而 Pod 的 netns 我们无法看到(也无法访问,不在一个 Linux 内核里)。

        该容器运行时提供了一个命令,它可以连接 VM netns 和 Pod netns 里的 2 个 abstract uds。甚至可以做到连接 2 个 netns 里的 ip:port (其实就是做一层转发).

        Agent 可以通过 setns 的方式进入 VM 的 netns,再利用上述命令即可打通 Agent 到 目标 Pod 的 ip:port。

        创建 proxy 时需要选择方向:

  • VM 主动建连 Pod,需要在 Pod 上 Listen
  • Pod 主动建连 VM,需要在 VM 上 Listen

基于 HTTP 代理的方案

        假设 Agent 访问 Pod 用的是 HTTP 协议。

        我们在 Agent 上实现一个 HTTP 代理服务器,Agent 访问 Pod 时配置 HTTP proxy,这样流量就会发到 HTTP 代理服务器上。在该代理服务器的实现里,我们读取请求序列化为 []byte,通过 docker exec 将该请求序列化的 []byte 作为 stdin 传入调用容器内部的辅助程序。辅助程序启动后从 stdin 读数据并反序列化出 HTTP 请求,辅助程序在容器内部访问 http://pod:port 是一定可以通的。辅助程序将 http://pod:port 的返回结果写到 stdout 里。HTTP 代理服务器实现读 docker exec 的 output 转成对应的 http.Response 即可。

        可以参考下面链接的代码.

https://github.com/traas-stack/holoinsight-agent/blob/main/pkg/cri/impl/netproxy/proxy_http.go#L118

基于 socks5 代理的方案

        比 HTTP 更通用的场景是 TCP 级的代理,常见的是 socks5 代理。

比如你想要通过 mysql 命令连到隔离的目标容器里,mysql 讲的显然是它自己的协议不是 HTTP,此时 TCP 级别的代理就派上用场了。我们只需要在 mysql 建连的地方设置 socks5 proxy 就行。

        刚好有一个开源软件 socat 可以从 标准流 双向拷贝到一个 TCP 流, 所以我上面的图用的是 socat. 但实际情况中, 目标容器里并不一定有 socat. 所以实际情况中我们会复制一个辅助 binary 到容器内部, 该 binary 实现了类似的功能.

        可以参考下面链接的代码.https://github.com/traas-stack/holoinsight-agent/blob/main/pkg/cri/impl/netproxy/proxy_socks5.go

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/443118.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据中心IP代理是什么?有何优缺点?海外代理IP全解

海外代理IP中,数据中心代理IP是很热门的选择。这些代理服务器为用户分配不属于 ISP(互联网服务提供商)且来自第三方云服务提供商的 IP 地址,是分配给位于数据中心的服务器的 IP 地址,通常由托管和云公司拥有。 这些 I…

Kali Linux初识

Kali Linux(以前称为 BackTrack Linux)是一个开源的、基于 Debian 的 Linux 发行版,旨在进行高级渗透测试和安全审计。它通过提供通用工具、配置和自动化来做到这一点,使用户能够专注于需要完成的任务。 包括 600 多种渗透测试工…

麒麟系统—— openKylin 安装 Nginx

麒麟系统—— openKylin 安装 Nginx 一、准备工作1. 确保麒麟系统 openKylin 已经安装完毕。 二、下载 nginx三、解压与运行解压检查与编译安装编译运行 Nginx 是一款高性能的 HTTP 和反向代理服务器,广泛应用于 Web 服务器领域。本文将分享如何在麒麟系统&#xf…

嵌入式学习第十五天

内存管理: 1.malloc void *malloc(size_t size); 功能: 申请堆区空间 参数: size:申请堆区空间的大小 返回值: 返回获得的空间的首地址 失败返回NULL 2.free void free(void *ptr); 功能: 释放堆区空间 注…

2024.1.30

快速排序降序 #include<stdio.h> #include<string.h> #include<stdlib.h> int quick_sort(int arr[],int low,int high) {//基准值int keyarr[low];int low1low,high1high;if(low>high) return 0;while(low<high) {//high开始比较while(low1<high1…

直方图均衡化原理与代码实现

1. 简介 直方图均衡化是一种用于增强图像对比度的图像处理技术。通过调整图像的灰度级别分布&#xff0c;直方图均衡化能够使图像中的像素值更加均匀分布&#xff0c;从而增强图像的细节和对比度。 2. 原理 直方图均衡化的原理是通过调整图像的累积分布函数&#xff08;CDF&…

Vue中嵌入原生HTML页面

Vue中嵌入html页面并相互通信 需求&#xff1a;b2b支付需要从后获取到数据放到form表单提交跳转&#xff0c;如下&#xff1a; 但是vue目前暂时没找到有类似功能相关文档&#xff0c;所以我采用iframe嵌套的方式 1. Vue中嵌入Html <iframe src"/static/gateway.htm…

C++类和对象(中)六个默认成员函数

&#x1f308;类的六个默认成员函数 任何一个类&#xff0c;不管是否为空&#xff0c;都会在生成的时候默认调用六个成员函数&#xff0c;这些成员函数可以自动生成&#xff0c;也可以由程序员写出。这六个默认成员函数分别是&#xff1a; 最主要的是前四个&#xff1a; 初始…

小红书论文刷新 SOTA:人体动作预测再升级,能精准到指尖

想象一下&#xff0c;你在玩一款 VR 游戏&#xff0c;准备伸手拿起一个虚拟杯子喝水。‍​​‌​‌​‎‎ 在传统的交互系统中&#xff0c;这通常需要你按下控制器上的特定按钮。但如果游戏集成了 EAI 框架&#xff0c;这一过程将变得无比自然。当你的手缓缓接近虚拟杯子时&…

初识C语言·文件操作

目录 1 关于文件 i)文件的基本知识 ii)数据文件的分类 2 文件打开和关闭 i)流和标准流 ii)文件指针 iii)文件打开和关闭 3 文件的顺序读写 i) fgetc fputc ii) fgets fputs iii) fscanf fprintf iv) fwrite fread 4 对比一组函数 scanf/fscanf/sscanf/printf/fpri…

Django视图函数技巧,从入门到实战

文章目录 Django视图函数1.request对象的方法2.视图函数的常用的返回对象&#xff08;1&#xff09;response对象&#xff08;2&#xff09;JsonResponse对象&#xff08;3&#xff09;redirect() &#xff1a;给浏览器了一个30x的状态码 3.设置响应头和状态码&#xff08;1&am…

springboot-前后端分离——第二篇

本篇主要介绍一个发送请求的工具—postman&#xff0c;然后对请求中的参数进行介绍&#xff0c;例如简单参数、实体参数、数组参数、集合参数、日期类型参数以及json类型参数&#xff0c;对这些参数接收进行总结。最后对响应数据进行介绍&#xff0c;使用统一响应结果返回浏览器…