Cilium 如何处理 L7 流量

news2024/12/24 10:03:21

还记得在 使用 Cilium 增强 Kubernetes 网络安全 示例中,我们通过设置网络策略限制钛战机 tiefighter 访问死星 deathstar/v1/exhaust-port 端点,但放行着陆请求 /v1/request-landing。在提起 Cilium 时,都说其是使用 eBPF 技术推动的用于提供、保护和观察容器工作负载之间的网络连接的开源软件。eBPF 可以处理 L3/4 的数据包,但是对复杂的 L7 的协议处理的成本比较高,并且无法应对 L7 协议策略的灵活性。Cilium 引入 Envoy Proxy(Cilium 定制的发行版)作为 L7 代理,来处理该场景。

那 Cilium 是如何处理 L7 流量的呢?今天就让我们一探究竟。

注,这篇的内容是基于目前最新的 Cilium 1.13.3 和 proxy 1.23.9,不同版本间会有差异。

在开始之前先搭建先前的“星球大战”环境,或者你也可以直接跳到 Debug 阶段。

环境搭建

集群

export INSTALL_K3S_VERSION=v1.27.1+k3s1
curl -sfL https://get.k3s.io | sh -s - --disable traefik --disable local-storage --disable metrics-server --disable servicelb --flannel-backend=none --write-kubeconfig-mode 644 --write-kubeconfig ~/.kube/config

安装 Cilium

CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/master/stable.txt)
CLI_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
sha256sum --check cilium-linux-${CLI_ARCH}.tar.gz.sha256sum
sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin
rm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
cilium install

安装示例应用

kubectl apply -n default -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: deathstar
  labels:
    app.kubernetes.io/name: deathstar
spec:
  type: ClusterIP
  ports:
  - port: 80
  selector:
    org: empire
    class: deathstar
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deathstar
  labels:
    app.kubernetes.io/name: deathstar
spec:
  replicas: 1
  selector:
    matchLabels:
      org: empire
      class: deathstar
  template:
    metadata:
      labels:
        org: empire
        class: deathstar
        app.kubernetes.io/name: deathstar
    spec:
      containers:
      - name: deathstar
        image: docker.io/cilium/starwars
---
apiVersion: v1
kind: Pod
metadata:
  name: tiefighter
  labels:
    org: empire
    class: tiefighter
    app.kubernetes.io/name: tiefighter
spec:
  containers:
  - name: spaceship
    image: docker.io/tgraf/netperf
---
apiVersion: v1
kind: Pod
metadata:
  name: xwing
  labels:
    app.kubernetes.io/name: xwing
    org: alliance
    class: xwing
spec:
  containers:
  - name: spaceship
    image: docker.io/tgraf/netperf
EOF

设置策略

kubectl apply -n default -f - <<EOF
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "rule1"
spec:
  description: "L7 policy to restrict access to specific HTTP call"
  endpointSelector:
    matchLabels:
      org: empire
      class: deathstar
  ingress:
  - fromEndpoints:
    - matchLabels:
        org: empire
    toPorts:
    - ports:
      - port: "80"
        protocol: TCP
      rules:
        http:
        - method: "POST"
          path: "/v1/request-landing"
EOF

测试

kubectl exec tiefighter -- curl -s -XPUT deathstar.default.svc.cluster.local/v1/exhaust-port
#Access denied
kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
#Ship landed

查看 pod 信息。

kubectl get po -o wide -n default
NAME                         READY   STATUS    RESTARTS   AGE     IP           NODE          NOMINATED NODE   READINESS GATES
deathstar-7848d6c4d5-58jc8   1/1     Running   0          6h57m   10.0.0.111   ubuntu-dev3   <none>           <none>
xwing                        1/1     Running   0          6h57m   10.0.0.209   ubuntu-dev3   <none>           <none>
tiefighter                   1/1     Running   0          6h57m   10.0.0.123   ubuntu-dev3   <none>           <none>

后面 debug 的操作我们会直接在 cilium 的 agent pod 进行。

agent=$(kubectl get po -l app.kubernetes.io/name=cilium-agent -n kube-system -o jsonpath='{.items[0].metadata.name}')

Debug

先贴上总结的图。

怎么下手呢?

在 深入探索 Cilium 的工作机制 时,我们对 Cilium 的网络策略处理机制一笔带过:

Cilium Agent 中运行着大量的 watcher,其中一个就是 CiliumNetworkPolicy watcher。当策略创建或者更新时,Agent 会对策略进行转换并将规则存储到 BPF Map 中。在网络通信时,BPF 程序会对网络流量进行检查并决定应当允许或者拒绝访问。

实际上这里的处理比较复杂,我们从 watcher 的初始化入手。

  • #enableK8sWatchers 开启一些列的 watcher
  • #ciliumNetworkPoliciesInit 开启 CiliumNetworkPolicy watcher
    • #addCiliumNetworkPolicyV2 添加 CiliumNetworkPolicy 的处理
      • #PolicyAdd 将规则写入 Daemon 的策略仓库中,实际发 PolicyAddEventrepository-change-queue 队列中。
      • #policyAdd 对规则进行预处理,并收集与规则相关的 endpoint(需要重新生成 endpoint 的数据,如加载 BPF 程序、更新 map 等),推送 PolicyReactionEvent 事件
        • PolicyReactionEvent.Handle 事件处理的过程,依次处理所有策略相关的 endpoint,最后有发出 EndpointRegenerationEvent 事件
          • EndpointRegenerationEvent#Handle 事件的处理过程
            • Endpoint.regenerate
              • Endpoint.regenerateBPF 重新加载 datapath BPF 程序,刷新 Map。

至此我们 apply 的网络策略被写入到 map 中。

接下来看下 ebpf 程序有任何使用该策略。

eBPF

还记得在 Kubernetes 网络学习之 Cilium 与 eBPF 中我们分析容器发出的数据包,被 LXC BPF Ingress 程序处理。这里不再赘述,处理流程可以看那篇文章。

我们先查看死星的 endpoint id 和 identity 分别为 8632033

kubectl get ciliumendpoint -n default
NAME                         ENDPOINT ID   IDENTITY ID   INGRESS ENFORCEMENT   EGRESS ENFORCEMENT   VISIBILITY POLICY   ENDPOINT STATE   IPV4         IPV6
tiefighter                   2216          29439         <status disabled>     <status disabled>    <status disabled>   ready            10.0.0.123
deathstar-7848d6c4d5-58jc8   863           2033          <status disabled>     <status disabled>    <status disabled>   ready            10.0.0.111
xwing                        775           5513          <status disabled>     <status disabled>    <status disabled>   ready            10.0.0.209

使用 endpoint id 通过通过命令查看为死星配置的网络策略,可以看到其中的两条 ingress 的策略,其代理端口 19313,这个端口就是 Cilium 中 L7 代理的监听端口。

kubectl exec $agent -n kube-system -c cilium-agent -- cilium bpf policy get 863
POLICY   DIRECTION   LABELS (source:key[=value])                                              PORT/PROTO   PROXY PORT   BYTES   PACKETS
Allow    Ingress     reserved:host                                                            ANY          NONE         0       0
                     reserved:kube-apiserver
Allow    Ingress     k8s:app.kubernetes.io/name=deathstar                                     80/TCP       19313        0       0
                     k8s:class=deathstar
                     k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=default
                     k8s:io.cilium.k8s.policy.cluster=default
                     k8s:io.cilium.k8s.policy.serviceaccount=default
                     k8s:io.kubernetes.pod.namespace=default
                     k8s:org=empire
Allow    Ingress     k8s:app.kubernetes.io/name=tiefighter                                    80/TCP       19313        0       0
                     k8s:class=tiefighter
                     k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=default
                     k8s:io.cilium.k8s.policy.cluster=default
                     k8s:io.cilium.k8s.policy.serviceaccount=default
                     k8s:io.kubernetes.pod.namespace=default
                     k8s:org=empire
Allow    Egress      reserved:unknown                                                         ANY          NONE         0       0

BPF 程序处理流量在检查策略时 bpf_lxc.c#L1842,检查配置的策略带有代理端口执行 POLICY_ACT_PROXY_REDIRECT 将流量重定向给代理(端口 19313,地址为主机地址)。

Cilium Proxy

Cilium agent 提供了 xds server 实现,通过 Unix Domain Socket /var/run/cilium/xds.sock 与 proxy 进行通信,下发配置。

我们参考 cilium-bugtool 的 dump 源码,dump 代理的配置。

kubectl exec $agent -n kube-system -c cilium-agent -- curl -s --unix-socket /var/run/cilium/envoy-admin.sock http://admin/config_dump?include_eds

从配置 config.json 中可以看到 Cilium 在 envoy proxy 中实现了如下三个不同类型的过滤器(Filter):

  • listener filter
  • filter
  • http filter

监听器过滤器

监听器过滤器(Listener Filter)cilium.BpfMetadata 会从几个数据源中准备元数据:策略、监听器设置、请求方的标识等。数据源包括 xds 配置、BPF map cilium_ipcachecilium_ct4_global(ct:connection tracking。当然还包括 ct6 相关的 map)。

从数据源中获取的数据保存在 socket option 中(proxy 源码 bpf_metadata.cc#L364),作为上下文元数据的在其他的过滤器中使用。

元数据数据源

xds filter 配置,这里提供了 bpf map 的根目录 /sys/fs/bpf,以及 is_ingress: true 表示当前 filter 是在入口监听器上(ingress listener):

{
 "name": "cilium.bpf_metadata",
 "typed_config": {
  "@type": "type.googleapis.com/cilium.BpfMetadata",
  "bpf_root": "/sys/fs/bpf",
  "is_ingress": true
 }

xds network policy 配置(截取了 proxy 的部分配置),从配置中可以找到 endpoint 的 IP 和 id,以及前面我们设置的 规则:

{
 "@type": "type.googleapis.com/cilium.NetworkPoliciesConfigDump",
 "networkpolicies": [
  {
   "endpoint_ips": [
    "10.0.0.111"
   ],
   "endpoint_id": "863",
   "ingress_per_port_policies": [
    {
     "port": 80,
     "rules": [
      {
       "http_rules": {
        "http_rules": [
         {
          "headers": [
           {
            "name": ":method",
            "safe_regex_match": {
             "google_re2": {},
             "regex": "POST"
            }
           },
           {
            "name": ":path",
            "safe_regex_match": {
             "google_re2": {},
             "regex": "/v1/request-landing"
            }
           }
          ]
         }
        ]
       }
      }
     ]
    }
   ],
   "egress_per_port_policies": [
    {}
   ],
   "conntrack_map_name": "global"
  },
  ...
}

Map cilium_ipcache,可以通过连接信息中的 IP 地址获取身份标识,如死星的 identity2033(见 proxy 源码 bpf_metadata.cc#L165):

kubectl exec $agent -n kube-system -c cilium-agent -- cilium bpf ipcache list
IP PREFIX/ADDRESS   IDENTITY
10.0.0.67/32        identity=1 encryptkey=0 tunnelendpoint=0.0.0.0 nodeid=0
10.0.0.111/32       identity=2033 encryptkey=0 tunnelendpoint=0.0.0.0 nodeid=0
10.0.0.123/32       identity=29439 encryptkey=0 tunnelendpoint=0.0.0.0 nodeid=0
10.0.0.243/32       identity=4 encryptkey=0 tunnelendpoint=0.0.0.0 nodeid=0
10.0.0.160/32       identity=19608 encryptkey=0 tunnelendpoint=0.0.0.0 nodeid=0
10.0.0.209/32       identity=5513 encryptkey=0 tunnelendpoint=0.0.0.0 nodeid=0
192.168.1.13/32     identity=1 encryptkey=0 tunnelendpoint=0.0.0.0 nodeid=0
0.0.0.0/0           identity=2 encryptkey=0 tunnelendpoint=0.0.0.0 nodeid=0

Map cilium_ct4_global,从连接跟踪(connection tracking)中获取请求方的 identity(SourceSecurityID 29439,钛战机的标识):

cilium bpf ct list global
TCP OUT 10.0.0.123:48954 -> 10.0.0.111:80 expires=58774 RxPackets=4 RxBytes=435 RxFlagsSeen=0x1b LastRxReport=58764 TxPackets=6 TxBytes=522 TxFlagsSeen=0x1b LastTxReport=58764 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=4 SourceSecurityID=29439 IfIndex=0
TCP IN 10.0.0.67:33988 -> 10.0.0.111:80 expires=58776 RxPackets=6 RxBytes=659 RxFlagsSeen=0x1b LastRxReport=58766 TxPackets=4 TxBytes=386 TxFlagsSeen=0x1b LastTxReport=58766 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=29439 IfIndex=0
TCP IN 10.0.0.123:48954 -> 10.0.0.111:80 expires=80364 RxPackets=6 RxBytes=522 RxFlagsSeen=0x1b LastRxReport=58764 TxPackets=0 TxBytes=0 TxFlagsSeen=0x00 LastTxReport=0 Flags=0x0051 [ RxClosing SeenNonSyn ProxyRedirect ] RevNAT=0 SourceSecurityID=29439 IfIndex=0

过滤器

过滤器(Filter)cilium.NetworkFilter 工作在 L4,用于处理已建立的链接,应用端口级的策略,即 L4 策略。

从上下文元数据中保存的 endpoint 相关的策略中查找与目标端口相关的策略,检查请求方证书中的 sni 和请求方的身份标识 identity 是否在白名单中,见 proxy 源码 network_filter.cc#L169。

假如策略上设置了 L7 的协议,会使用 Golang 编写的解析器对 L7 的数据进行解析。

在本示例中并未使用 L4 的策略。

HTTP 过滤器

HTTP 过滤器(HTTP Filter)cilium.L7Policy 是本文的重点,但相对其他两个过滤器来说逻辑就简单多了。

"http_filters": [
 {
  "name": "cilium.l7policy",
  "typed_config": {
   "@type": "type.googleapis.com/cilium.L7Policy",
   "access_log_path": "/var/run/cilium/access_log.sock"
  }
 }

在过滤器对 HTTP 请求头进行解码时(见 proxy 源码 l7policy.cc#L97),依然是从上下文元数据中获取策略等内容。拿到策略后,与请求方(对于这里 ingress 的场景检查请求方,如果是 egress 的场景,检查上游的标识)的标识、请求头的信息进行比对,决定放行还是拒绝请求。

总结

整篇看下来,Cilium 在处理 L7 流量上的实现还是比较复杂的,牵扯多个组件协同。eBPF 在 L3/L4 流量处理上有着优异的性能优势,但是对 L7 流量处理仍然无法脱离 sidecar 代理(不论 sidecar 是 per pod 还是 per node)。而 L7 流量处理也恰恰有着非常多的使用场景,不仅仅是 HTTP 协议。

关注"云原生指北"微信公众号 (转载本站文章请注明作者和出处乱世浮生,请勿用于任何商业用途)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

心法利器[85] | 算法技术和职业规划

心法利器 本栏目主要和大家一起讨论近期自己学习的心得和体会&#xff0c;与大家一起成长。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。 2022年新一版的文章合集已经发布&#xff0c;累计已经60w字了&#xff0c;获取方式看这里&…

花3万买的大学申请文书,竟和ChatGPT写的一样?

正值高考毕业季&#xff0c;留学出国又成热门话题。眼下&#xff0c;选学校、写申请书是不少学生头等大事。在AI如火如荼的今年&#xff0c;这个老行当却有了新变化。 当学生纷纷用AI写申请书&#xff0c;留学机构开始缩减业务&#xff0c;中介用AI写文书“糊弄”学生&#xf…

哈夫曼树和哈夫曼编码

一.哈夫曼树 1.哈夫曼树 哈夫曼树是一种用于编码的树形结构。它是通过将频率最低的字符反复组合形成的二叉树&#xff0c;使得出现频率高的字符具有较短的二进制编码&#xff0c;而出现频率低的字符具有较长的编码。 在哈夫曼树中&#xff0c;每个叶子节点都代表一个字符&am…

chatgpt赋能python:Python图形填充颜色教程

Python图形填充颜色教程 Python是一种简单易学、高效的编程语言&#xff0c;广泛应用于数据分析、机器学习、Web开发等领域。其中&#xff0c;图形处理是Python编程领域的一个重要方面。在很多情况下&#xff0c;我们需要填充图形颜色来增加图形的美观程度和可读性。本文将介绍…

使用OpenCV和MediaPipe实现姿态识别!

大家好&#xff0c;我是小F&#xff5e; MediaPipe是一款由Google开发并开源的数据流处理机器学习应用开发框架。 它是一个基于图的数据处理管线&#xff0c;用于构建使用了多种形式的数据源&#xff0c;如视频、音频、传感器数据以及任何时间序列数据。 MediaPipe通过将各个感…

表示学习(Representation Learning) Part1--Pretext Text

文章目录 Representation LearningInferring structure&#xff08;推断结构&#xff09; Transformation predictionRotation predictionRelative transformation prediction ReconstructionDenoising AutoencodersContext encodersColorizationSplit-brain encoders Instance…

屏幕录像视频录制编辑软件TechSmith Camtasia 2023 for Mac 简体中文版

TechSmith Camtasia for Mac 中文版 是一款专业的屏幕录像视频录制编辑软件&#xff0c;非常容易就可以获得精彩的截屏视频。创建引人注目的培训&#xff0c;演示和演示视频。Camtasia 屏幕录制软件简化&#xff0c;直观&#xff0c;让您看起来像专业人士。利用Camtasia&#x…

SpringMVC 学习总结

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 1. 什么是 Spring MVC 1.1 Spring、Spring MV…

Dockerfile创建镜像

一、Docker镜像的创建 创建镜像有三种方法&#xff0c;分别为【基于已有镜像创建】、【基于本地模板创建】以及【基于Dockerfile创建】。 1.1 基于现有镜像创建 &#xff08;1&#xff09;首先启动一个镜像&#xff0c;在容器里做修改docker run -it centos:7 /bin/bash …

旧手机卖掉之前我们需要做这几个操作

随着科技的不断进步&#xff0c;人们使用的电子产品也在不断地迭代更新。当我们不再使用旧手机时&#xff0c;卖掉它可以省下一笔开支&#xff0c;但也需要注意保护个人隐私数据。因此&#xff0c;在售卖二手手机之前&#xff0c;正确清除旧手机中的历史数据变得至关重要。 首先…

Java网络开发(Tomcat)——从同步到异步 从jsp 到 js + axios + vue 实现 数据分页显示 数据增删改查

目录 引出一些固定的东西1.固定的响应格式2.name 变成 v-model 进行双向绑定3.下拉框选中--:value"type.id"4.vue导包固定写法5.script固定写法6.axios的get请求7.axios的post请求8.前端美化&#xff1a; 数据分页显示1.后端改成resp响应2.前端的修改要点&#xff08…

揭秘报表新玩法!标配插件不再单调,如何用柱形图插件让你的报表瞬间高大上!

摘要&#xff1a;本文由葡萄城技术团队于CSDN原创并首发。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 前言 图表作为一款用于可视化数据的工具&#xff0c;可以帮助我们更好的分析和理解数…

flutter自定义系列之简单的K线图绘制

上篇文章讲了flutter自定义的相关流程&#xff0c; 今天继续练习下flutter的自定义K线&#xff1a; 我们可以通过自定义Painter来实现一个简单的K线图界面&#xff1a; 创建一个自定义的Painter&#xff0c;用于绘制K线图&#xff1a; import dart:ui;import package:flutte…

聊聊多线程

摘要 开发过程中&#xff0c;总会遇到一些并发安全问题。本文总结出常用的数据结构哪些是安全的&#xff0c;哪些是不安全的以及他们为什么是不安全。 java中sychronize锁的原理&#xff1a; 常见的数据结构 类型 数据结构是否安全ArrayList数组 不安全HashMap数…

Mocha Pro:AdjustTrack 模块

跟踪时由于缺乏细节或有障碍物阻挡&#xff0c;跟踪点发生了漂移&#xff0c;或者一个或多个跟踪点可能会离开画面&#xff0c;此时可考虑使用 AdjustTrack &#xff08;调整跟踪&#xff09;模块手动设置关键帧来获得更精准的跟踪数据。 尤其是当要利用表面 Surface区域进行插…

随机数组归并问题

1 问题 生成两个任意的随机数组&#xff0c;并将这两个数组按照数字大小按顺序归并到一个新数组中。 2 方法 思路&#xff1a;定义三个数组&#xff0c;两个数组自己输入值&#xff0c;第三个数组用来作归并后的数组&#xff0c;先将两个数组的值全部赋给第三个数组&#xff0c…

极简主义的远程文件浏览器Mikochi

什么是 Mikochi &#xff1f; Mikochi 是一个远程文件浏览器&#xff0c;用于自托管服务器 / NAS。它允许您浏览远程文件夹、上传文件、删除、重命名、下载和流式传输文件到 VLC/mpv。它带有一个由 JavaScript/Preact 提供支持的 Web 界面&#xff0c;以及一个内置于 Go/Gin 中…

ChatGPT 教我用 200 行代码写一个简版 Vue 框架 - OpenTiny

AI 是未来最好的老师 最近&#xff0c;我正在准备一份关于 Vue 基础的学习材料。期间我突发奇想&#xff1a;能否利用现在热门的 ChatGPT 帮我创建学习内容&#xff1f;其实 Vue 本身不难学&#xff0c;特别是基础用法&#xff0c;但是&#xff0c;如果你想深入掌握 Vue&#…

数据挖掘(7.1)--数据仓库

目录 引言 一、数据库 1.简介 2.数据库管理系统(DBMS) 二、数据仓库 数据仓库特征 数据仓库作用 数据仓库和DBMS对比 分离数据仓库和数据库 引言 数据仓库的历史可以追溯到20世纪60年代&#xff0c;当时计算机领域的主要工作是创建运行在主文件上的单个应用&#xff0…

LaravelPHP笔记-响应头去掉(隐藏)X-Powered-By

最近想搞个小项目&#xff0c;后端先用PHP&#xff0c;框架是Laravel但http响应头如下&#xff1a; 头带有X-Powered-By: PHP/7.3.33&#xff0c;这样很不安全&#xff0c;应该要隐藏&#xff0c;查了下百度。都是一个抄一个。 在代码中添加&#xff1a; header_remove(x-pow…