当前位置:网站首页>Customizing security groups using BPF

Customizing security groups using BPF

2022-06-24 17:04:00 ritchiechen

Last one article A lot of reading , It seems that the theme of the Internet is more popular . In this article, we continue to explore BPF Application in network field : Use BPF To implement security groups .

According to Tencent cloud's file , The concept of a security group is as follows :

A security group is a kind of virtual firewall , With stateful packet filtering function , Used to set up the cloud server 、 Load balancing 、 Cloud database and other instances of network access control , Control instance level traffic flow , It is an important means of network security isolation .

In this paper , We will implement the following rules :

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

That is, all source ports are allowed to be 10216 Of TCP Traffic access server 12160 port , Traffic accessed through other ports is discarded ( This article only discusses the filtering of inbound traffic , Of course , It is also feasible to limit the outbound flow ).

XDP And BPF

XDP yes eXpress Data Path Abbreviation , stay Linux In the kernel is BPF Provides a framework , For high performance programmable packet processing . It is at the beginning of the entire software stack , That is, when the network driver receives the Ethernet frame, it runs BPF Program .

Back to the topic of this article , We translate the security group rules into BPF Program , utilize XDP Mount to the network card device to execute , To achieve the goal .

To achieve the above functions BPF The procedure is as follows :

#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";

The function of the program is , For every data received by the network card , Skip the legal Ethernet frame header in turn ,IP The head of the datagram , Check out TCP Whether the destination port of the message header is 12160, if , Further judge whether the source port is 10216, This determines whether inbound traffic is allowed . In the whole process , It is necessary to judge the pointer boundary , If missing , This will cause the program to fail through the kernel BPF Verification of verifier .( In the program , We don't know each other / Incomplete data should be spared ) Finally, it is compiled into binary file sg.bpf.o.

In addition, we also need a simple server program to verify the function :

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)
	}
}

The function of the program is very simple , Monitor in 12160 port , For clients on each connection , echo Client input . Finally compiled as an executable testserver.

experiment

In the load BPF Before the program , Let's run the server program for testing first ,

$ ./testserver

Then connect to the service from another host , At two different terminals , Execute the following commands respectively :

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

You can see , Both clients can normally access the server , Now? , We load the above BPF Program :

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

Namely the BPF Program loaded to eth0 NIC ( Here, the operation mode is selected xdpgeneric, Because the virtual machine of the experimental environment does not support xdpdrv/xdpoffload ).

Now? , Try sending data to the server at two terminals again :

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

Performance in line with expectations . The source port is 10216 The client can still send data to the server and receive the response , Other clients are always retransmitting , Until the server resets the connection .

In this paper, the Code You can find it here .

Conclusion

This paper discusses the use of XDP and BPF Implement custom security groups , The access control of inbound traffic is realized by programmable method .

原网站

版权声明
本文为[ritchiechen]所创,转载请带上原文链接,感谢
https://yzsam.com/2021/04/20210401225454111E.html