专栏首页Serverless+使用 BPF 自定义安全组
原创

使用 BPF 自定义安全组

上一篇文章阅读量比较多,看起来网络的主题比较受欢迎。这一篇文章我们继续探索 BPF 在网络领域的应用:使用 BPF 来实现安全组。

按照腾讯云的文档,安全组的概念如下:

安全组是一种虚拟防火墙,具备有状态的数据包过滤功能,用于设置云服务器、负载均衡、云数据库等实例的网络访问控制,控制实例级别的出入流量,是重要的网络安全隔离手段。

在本文中,我们将实现如下的规则:

0.0.0.0/0:10216 ---> TCP:12160
0.0.0.0/0:*     -x-> TCP:12160

即允许所有源端口为 10216TCP 流量访问服务端 12160 端口,通过其他端口访问的流量都丢弃(本文仅讨论入站流量的过滤,当然,对出站流量的限制也是可行的)。

XDP 与 BPF

XDPeXpress Data Path 的缩写,在 Linux 内核中为 BPF 提供了一个框架,用于实现高性能的可编程的数据包处理。它在整个软件栈的起始点,即网络驱动程序收到以太网帧的时刻运行 BPF 程序。

回到本文的主题,我们通过把安全组规则翻译成 BPF 程序,利用 XDP 挂载至网卡设备上执行,即可达成目标。

实现上述功能的 BPF 程序如下:

#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>

#include "bpf_helpers.h"
#include "bpf_endian.h"

static int is_secure_source(void *data_begin, void *data_end)
{
	struct ethhdr *eth_header = data_begin;

	if ((void *)(eth_header + 1) > data_end) {
		return 1;
	}

	if (eth_header->h_proto != bpf_htons(ETH_P_IP)) {
		return 1;
	}

	struct iphdr *ip_header = (struct iphdr *)(eth_header + 1);

	if ((void *)(ip_header + 1) > data_end) {
		return 1;
	}

	if (ip_header->protocol != IPPROTO_TCP) {
		return 1;
	}

	struct tcphdr *tcp_header = (struct tcphdr *)(ip_header + 1);

	if ((void *)(tcp_header + 1) > data_end) {
		return 1;
	}

	if (tcp_header->dest == bpf_htons(12160)) {
		if (tcp_header->source != bpf_htons(10216)) {
			return 0;	// reject
		} else {
			return 1;	// accept
		}
	} else {
		return 1;
	}
}

SEC("xdp")
int xdp_secure_policy(struct xdp_md *ctx)
{
	void *data = (void *)(__u64)ctx->data;
	void *data_end = (void *)(__u64)ctx->data_end;
	if (is_secure_source(data, data_end)) {
		return XDP_PASS;
	} else {
		return XDP_DROP;
	}
}

char __license[] SEC("license") = "GPL";

程序的功能是,对于网卡收到的每一个数据,依次跳过合法的以太网帧首部,IP数据报首部,最后查看 TCP 报文首部的目的端口是否是 12160,若是,则进一步判断源端口是否是 10216,以此决定是否允许入站流量。在整个程序中,对于指针边界的判断是必需的,若缺失,会导致程序不能通过内核 BPF 验证器的校验。(在程序中,我们对不认识/不完整的数据都予以放过)最后编译为二进制文件 sg.bpf.o

另外我们还需要一个简单的服务端程序来验证功能:

package main

import (
	"io"
	"log"
	"net"
)

func serve(c net.Conn) {
	defer c.Close()

	log.Printf("client connected: %s\n", c.RemoteAddr().String())
	io.Copy(c, c)
	log.Printf("client closed: %s\n", c.RemoteAddr().String())
}

func main() {
	lis, err := net.Listen("tcp", ":12160")
	if err != nil {
		panic(err)
	}

	for {
		conn, err := lis.Accept()
		if err != nil {
			panic(err)
		}
		go serve(conn)
	}
}

程序的功能很简单,监听在 12160 端口,对于每一个连接上的客户端, echo 客户端的输入。最后编译为可执行程序 testserver

实验

在加载 BPF 程序之前,我们先运行测试用的服务端程序,

$ ./testserver

然后从另一台主机上连接到这个服务上,在两个不同的终端,分别执行如下命令:

$ nc $(SERVER_IP) 12160 -p 10216
hello
hello
$ nc $(SERVER_IP) 12160
hi
hi

可以看到,两个客户端都能正常访问服务端,现在,我们加载上述的 BPF 程序:

$ sudo ip link set dev eth0 xdpgeneric obj sg.bpf.o sec xdp verbose

即把 BPF 程序加载到 eth0 网卡上(这里操作模式选择了 xdpgeneric,因为实验环境的虚拟机不支持 xdpdrv/xdpoffload )。

现在,再次尝试在两个终端发送数据到服务端:

$ nc $(SERVER_IP) 12160 -p 10216
hello
hello
hey
hey
$ nc $(SERVER_IP) 12160
hi
hi
no reply

表现符合预期。源端口为 10216 的客户端仍然能将数据发送给服务端并接收响应,其他客户端则一直在重传,直至服务端重置连接。

本文的代码可以在这里找到。

结论

本文探讨了使用 XDP 和 BPF 实现自定义安全组,通过可编程的方式实现了对入站流量的访问控制。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 用BPF实现用户态tracing

    BPF是最近Linux内核领域热门的技术。传统的BPF指的是tcpdump命令用于过滤网络包的工具,现在BPF已经得到极大的扩展,不再是Berkeley Pac...

    王璞
  • 基于 eBPF 实现容器运行时安全

    BPF 全称是「Berkeley Packet Filter」,中文翻译为「伯克利包过滤器」。它源于 1992 年伯克利实验室,Steven McCanne 和...

    灵雀云
  • 【云原生技术研究】BPF使能软件定义内核

    BPF通过一种软件定义的方式,将内核的行为和数据暴露给用户空间,开发者可以通过在用户空间编写BPF程序,加载到内核空间执行,进而实现对内核行为的灵活管理和控制

    绿盟科技研究通讯
  • 高性能:3-为何性能分析工具需要BPF 【bpf performance tools读书笔记】

    性能工具之所以使用扩展的BPF,部分原因在于它的可编程性。BPF程序可以执行自定义等待时间计算和统计摘要。仅这些功能就可以构成一个有趣的工具,并且还有许多其他具...

    二狗不要跑
  • 一文入门eBPF

    bpf全称伯克利包过滤器(Berkeley Packet Filte),bpf技术诞生于1992年,早期主要用来提升对数据包的过滤性能,但是早期的bpf提供的指...

    fluyy
  • Linux超能力BPF技术介绍及学习分享(技术创作101训练营)

    近两年BPF技术跃然成为了一项热门技术,在刚刚结束的KubeCon 2020 Europe会议上有7个关于BPF的技术分享, 而在KubeCon 2020 Ch...

    nevermosby
  • eBPF技术简介

    “eBPF 是我见过的 Linux 中最神奇的技术,没有之一,已成为 Linux 内核中顶级子模块,从 tcpdump 中用作网络包过滤的经典 cbpf,到成为...

    CNCF
  • BPF的可移植性和CO-RE (Compile Once – Run Everywhere)

    在上一篇文章中介绍了提高socket性能的几个socket选项,其中给出了几个源于内核源码树中的例子,如果选择使用内核树中的Makefile进行编译的话,可能会...

    charlieroro
  • 使用Cilium增强Istio|通过Socket感知BPF程序

    8月1日凌晨,Istio 1.0发布,已生产就绪! Cilium社区感谢所有Istio贡献者为此付出的巨大努力。我们很幸运能够参与社区活动,为Istio做出贡献...

    CNCF
  • 使用组合自定义行为

    如果您的设计依赖于继承,则需要找到一种方法来更改对象的类型以更改其行为。对于组合,您只需要更改对象使用的策略

    公众号---人生代码
  • 自定义组件使用v-model

    当custom-input 的输入框的值变化的时候,可以看到父组件的inputvalue 的值跟着变化了。

    Daotin
  • BPF 可移植性和 CO-RE(一次编译,到处运行)

    本文翻译自 2020 年 Facebook 的一篇博客:BPF Portability and CO-RE[1], 作者 Andrii Nakryiko。

    米开朗基杨
  • BPF 之巅:洞悉 Linux 系统和应用性能

    BPF 是近年来Linux 系统技术领域一个巨大的创新。作为 Linux 内核的一个关键发展节点,其重要程度不亚于虚拟化、容器、SDN 等技术。

    博文视点Broadview
  • 首届eBPF.io Summit 2020见闻

    第一次在Thomas的Twitter上看到要办eBPF技术大会的时候,兴奋不已,作为一个已经学习BPF技术大半年并且还在持续学习中的爱好者,就像粉丝看到偶像要办...

    nevermosby
  • BPF CO-RE 示例代码解析

    在BPF的可移植性和CO-RE一文的末尾提到了一个名为runqslower的工具,该工具用于展示在CPU run队列中停留的时间大于某一值的任务。现在以该工具来...

    charlieroro
  • 为容器时代设计的高级 eBPF 内核特性(FOSDEM, 2021)

    本文翻译自 2021 年 Daniel Borkmann 在 FOSDEM 的一篇分享:Advanced eBPF kernel features for th...

    米开朗基杨
  • Libbpf-tools —— 让 Tracing 工具身轻如燕

    本篇文章概述了 BPF 的主要应用,重点描述了 libbpf-tools 解决了哪些 BCC 痛点以及在 PingCAP 内部的相关实践。

    PingCAP
  • 张亦鸣 : eBPF 简史 (下篇)

    数日之前,笔者参加某一技术会议之时,为人所安利了一款开源项目,演讲者对其性能颇为称道,称其乃基于近年在内核中炙手可热的 eBPF 技术。

    Linuxer
  • 大规模微服务利器:eBPF + Kubernetes 介绍

    本文翻译自 2020 年 Daniel Borkmann 在 KubeCon 的一篇分享: eBPF and Kubernetes: Little Helper...

    CNCF

扫码关注云+社区

领取腾讯云代金券