# Using eBPF to enhance security, a simple use case

In this blog we are going to explore a POC that I did with eBPF . I have a basic express server which has a route /env which exposes a secret key, I restricted access to that route with the help of ePBF

### Environment setup

Used VirtualBox to run a Debina image (12.5.0) and setup a lab.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1711444615907/5b44c254-eb72-4b42-bb50-814e343c6b13.png align="center")

Now once the virtual OS was running, installed the below dependencies for eBPF development.

```bash
sudo apt-get update 

sudo apt-get install linux-headers-$(uname -r)

sudo apt-get install llvm clang

sudo apt-get install bpfcc-tools libbpfcc-dev libbpf-dev

sudo aptitude -y install bpftool
```

to install node

```bash
sudo apt-get install node npm
```

### Application server

now that the environment is setup it's time to write some code let's start with the express server.

```javascript

const express = require('express')
const app = express()
app.use(express.json())

app.all('/',(req,res)=>{
	res.status(200).json({'message' : "hello there !"})
})

app.get('/env',(req,res)=>{
	return res.status(200).json({'key':'secret'});
})
 
app.listen(3001,()=>{
	console.log( --- server started on 3001 ----)
})
```

the server has two routes one is the base route the other one is `/env` which exposes a secret key.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1711446502865/621ba209-c4a2-42da-93df-b5e5b5e48a8c.png align="center")

now with the help of eBPF we will try to block request to the `/env` path on our machine.

### injecting eBPF code

In the eBPF code logic what I am going to do is check every http packet on the xdp data path, if the request path contains /env drop the packet else let it through.

```javascript
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <bpf/bpf_helpers.h>

// Define IPPROTO_TCP if not already defined
#ifndef IPPROTO_TCP
#define IPPROTO_TCP 6
#endif

static int strlen(const char *str) {
    int len = 0;
    while (*str != '\0') {
        len++;
        str++;
    }
    return len;
}

static int strncmp(const char *s1, const char *s2, __u64 len) {
	for (__u64 i = 0; i < len; i++) {
    	if (s1[i] != s2[i])
        	return s1[i] - s2[i];
    	if (s1[i] == '\0')
        	return 0;
	}
	return 0;
}

SEC("xdp")
int hello(struct xdp_md *ctx) {
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;

 
    struct ethhdr *eth = data;

    if (data + sizeof(*eth) > data_end)
        return XDP_PASS;

    if (eth->h_proto != __constant_htons(ETH_P_IP))
        return XDP_PASS;

    struct iphdr *ip = data + sizeof(struct ethhdr);

    if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end)
        return XDP_PASS;

    if (ip->protocol != IPPROTO_TCP)
        return XDP_PASS;

    struct tcphdr *tcp = data + sizeof(struct ethhdr) + (ip->ihl << 2);

    if (data + sizeof(struct ethhdr) + (ip->ihl << 2) + sizeof(struct tcphdr) > data_end)
        return XDP_PASS;

    // Pointer to the start of TCP payload
    void *tcp_payload = data + sizeof(struct ethhdr) + (ip->ihl << 2) + (tcp->doff << 2);
    void *tcp_payload_end = data_end;

    // HTTP request line for the specific path
    char *request_line = "GET /env HTTP/1.1";
    int request_line_len = strlen(request_line);
    bpf_printk("%s",tcp_payload);
    // Check if the HTTP request line matches the specific path
    if (tcp_payload + request_line_len <= tcp_payload_end &&
        strncmp(tcp_payload, request_line, request_line_len) == 0) {
        // Match found, drop the packet
        bpf_printk("Match found, dropping packet\n");
        return XDP_DROP;
    }
    bpf_printk("Match now found, allow packet\n");
    // No match found, allow the packet to pass through
    return XDP_PASS;
}

char _license[] SEC("license") = "GPL";
```

now to compile the code with the makefile below

```bash
TARGETS = hello

all: $(TARGETS)
.PHONY: all

$(TARGETS): %: %.bpf.o 

%.bpf.o: %.bpf.c
	clang \
	    -target bpf \
		-I/usr/include/$(shell uname -m)-linux-gnu \
		-g \
	    -O2 -o $@ -c $<

clean: 
	- rm *.bpf.o
	- rm -f /sys/fs/bpf/hello
```

after running the makefile ( `make hello` ) , there will be a object file named , in this case its `hello.bpf.c`

now attach the eBPF program to the local loopback network interface with the below command

```bash
ip link set dev lo xdp obj hello.bpf.o sec xdp
```

we can see the attached program with `bpftool net list`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1711469500531/a5da83ac-fb5e-4a00-88d3-92c8297fa43c.png align="center")

and to monitor the logs so that we can see the inner working of the eBPF program

```bash
bpftool prog trace log
```

now if we hit the server on base url with we can see the logs on the terminal.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1711470292756/252f20fe-87c4-47ed-b255-65d419ebeb65.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1711469862847/be7fadf0-5798-4889-a92f-bf89c9f9cdf2.png align="center")

now if I hit the /env

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1711470013666/83ce211b-b6a4-4cfd-8cf3-798c12afc174.png align="center")

we can see that the packets are blocked.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1711470339751/eaa7dffa-d8c6-459f-abd3-98a546f520ad.png align="center")

to detach the program

```bash
bpftool net detach xdp dev lo
```

It was fun experiment .

and I know ....

![David Brandt, the Ohio farmer behind the 'honest work' meme ...](https://media.npr.org/assets/img/2023/05/26/honest-work-meme-cb0f0fb2227fb84b77b3c9a851ac09b095ab74d8-s1100-c50.jpg align="left")

until next time 🙋🏽‍♂️.
