Skip to content

Commit 94c8a6a

Browse files
committed
Initial PR to implement openSSL userspace tracker
Signed-off-by: Mohamed S. Mahmoud <[email protected]>
1 parent b165fb3 commit 94c8a6a

27 files changed

+674
-23
lines changed

.mk/bc.mk

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ define PROGRAMS
2222
"xfrm_input_kprobe": "kprobe",
2323
"xfrm_input_kretprobe": "kretprobe",
2424
"xfrm_output_kprobe": "kprobe",
25-
"xfrm_output_kretprobe": "kretprobe"
25+
"xfrm_output_kretprobe": "kretprobe",
26+
"probe_entry_SSL_write":"uprobe",
2627
}
2728
endef
2829

@@ -38,7 +39,8 @@ define MAPS
3839
"filter_map":"lpm_trie",
3940
"peer_filter_map":"lpm_trie",
4041
"ipsec_ingress_map":"hash",
41-
"ipsec_egress_map":"hash"
42+
"ipsec_egress_map":"hash",
43+
"ssl_data_event_map":"ringbuf"
4244
}
4345
endef
4446

Dockerfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ ARG TARGETARCH
33
# Build the manager binary
44
FROM docker.io/library/golang:1.24 as builder
55

6-
ARG TARGETARCH
76
ARG LDFLAGS
87

98
WORKDIR /opt/app-root

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ create-and-deploy-kind-cluster: prereqs ## Create a kind cluster and deploy the
205205

206206
.PHONY: destroy-kind-cluster
207207
destroy-kind-cluster: ## Destroy the kind cluster.
208-
oc delete -f scripts/agent.yml
208+
kubectl delete -f scripts/agent.yml
209209
kind delete cluster
210210

211211
##@ Images

bpf/configs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ volatile const u8 enable_network_events_monitoring = 0;
1515
volatile const u8 network_events_monitoring_groupid = 0;
1616
volatile const u8 enable_pkt_translation_tracking = 0;
1717
volatile const u8 enable_ipsec = 0;
18+
volatile const u8 enable_ssl = 0;
1819
#endif //__CONFIGS_H__

bpf/flows.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@
5757
*/
5858
#include "ipsec.h"
5959

60+
/*
61+
* Defines ssl tracker
62+
*/
63+
#include "openssl_tracker.h"
64+
6065
// return 0 on success, 1 if capacity reached
6166
static __always_inline int add_observed_intf(flow_metrics *value, pkt_info *pkt, u32 if_index,
6267
u8 direction) {

bpf/maps_definition.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,11 @@ struct {
9797
__uint(pinning, LIBBPF_PIN_BY_NAME);
9898
} ipsec_egress_map SEC(".maps");
9999

100+
// Ringbuf for SSL data events
101+
struct {
102+
__uint(type, BPF_MAP_TYPE_RINGBUF);
103+
__uint(max_entries, 1 << 27); // 16KB * 1000 events/sec * 5sec "eviction time" = ~128MB
104+
__uint(pinning, LIBBPF_PIN_BY_NAME);
105+
} ssl_data_event_map SEC(".maps");
106+
100107
#endif //__MAPS_DEFINITION_H__

bpf/openssl_tracker.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* OpenSSL monitoring uprobe/uretprobe eBPF hook.
3+
*/
4+
5+
#ifndef __OPENSSL_TRACKER_H__
6+
#define __OPENSSL_TRACKER_H__
7+
8+
#include "utils.h"
9+
10+
static inline void generate_SSL_data_event(struct pt_regs *ctx, u64 pid_tgid, u8 ssl_type,
11+
const char *buf, int len) {
12+
if (len <= 0) {
13+
return;
14+
}
15+
16+
struct ssl_data_event_t *event;
17+
event = bpf_ringbuf_reserve(&ssl_data_event_map, sizeof(*event), 0);
18+
if (!event) {
19+
return;
20+
}
21+
event->timestamp_ns = bpf_ktime_get_ns();
22+
event->pid_tgid = pid_tgid;
23+
event->ssl_type = ssl_type;
24+
event->data_len = len < MAX_DATA_SIZE_OPENSSL ? len : MAX_DATA_SIZE_OPENSSL;
25+
bpf_probe_read_user(&event->data, event->data_len, buf);
26+
bpf_ringbuf_submit(event, 0);
27+
}
28+
29+
// int SSL_write(SSL *ssl, const void *buf, int num);
30+
// https://github.com/openssl/openssl/blob/master/ssl/ssl_lib.c#L2666
31+
SEC("uprobe/SSL_write")
32+
int probe_entry_SSL_write(struct pt_regs *ctx) {
33+
if (enable_ssl == 0) {
34+
return 0;
35+
}
36+
37+
u64 pid_tgid = bpf_get_current_pid_tgid();
38+
39+
BPF_PRINTK("openssl uprobe/SSL_write pid: %d\n", pid_tgid);
40+
// https://github.com/openssl/openssl/blob/master/ssl/ssl_local.h#L1233
41+
void *ssl = (void *)PT_REGS_PARM1(ctx);
42+
43+
u32 ssl_type;
44+
int ret;
45+
46+
ret = bpf_probe_read_user(&ssl_type, sizeof(ssl_type), (u32 *)ssl);
47+
if (ret) {
48+
BPF_PRINTK("(OPENSSL) bpf_probe_read ssl_type_ptr failed, ret: %d\n", ret);
49+
return 0;
50+
}
51+
const char *buf = (const char *)PT_REGS_PARM2(ctx);
52+
int num = (int)PT_REGS_PARM3(ctx); // Third parameter: number of bytes to write
53+
54+
BPF_PRINTK("openssl uprobe/SSL_write type: %d, buf: %p, num: %d\n", ssl_type, buf, num);
55+
56+
// Read the data immediately in the uprobe (before SSL_write processes it)
57+
// This captures the plaintext before encryption
58+
if (num > 0) {
59+
generate_SSL_data_event(ctx, pid_tgid, ssl_type, buf, num);
60+
}
61+
62+
return 0;
63+
}
64+
65+
#endif /* __OPENSSL_TRACKER_H__ */

bpf/types.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,4 +277,17 @@ struct filter_value_t {
277277
// Force emitting enums/structs into the ELF
278278
const static struct filter_value_t *unused12 __attribute__((unused));
279279

280+
#define MAX_DATA_SIZE_OPENSSL 1024 * 16
281+
// SSL data event
282+
struct ssl_data_event_t {
283+
u64 timestamp_ns;
284+
u64 pid_tgid;
285+
s32 data_len;
286+
u8 ssl_type;
287+
char data[MAX_DATA_SIZE_OPENSSL];
288+
} ssl_data_event;
289+
290+
// Force emitting enums/structs into the ELF
291+
const static struct ssl_data_event_t *unused14 __attribute__((unused));
292+
280293
#endif /* __TYPES_H__ */

examples/test-ssl-host.sh

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
#!/bin/bash
2+
# Test SSL tracking with HOST processes (not containers)
3+
#
4+
# This script tests SSL/TLS tracking functionality by executing HTTPS requests
5+
# directly on cluster nodes (host processes) and verifying that the NetObserv
6+
# agent captures SSL events via eBPF uprobes.
7+
#
8+
# Prerequisites:
9+
# - Kubernetes cluster (kind/minikube/etc)
10+
# - NetObserv agent deployed with EnableSSL: true
11+
# - Agent configured with correct OpenSSL library path
12+
#
13+
# Note: Some tests (TLS 1.3, HTTP/2) are optional and won't cause failure
14+
# if not supported on the node.
15+
16+
# Don't exit on error - we want to run all tests and report results
17+
# Steps to test on Kind cluster:
18+
# make create-and-deploy-kind-cluster
19+
# export KUBECONFIG=$(pwd)/scripts/kubeconfig
20+
# ./examples/test-ssl-host.sh
21+
22+
set +e
23+
24+
# Colors for output
25+
RED='\033[0;31m'
26+
GREEN='\033[0;32m'
27+
YELLOW='\033[1;33m'
28+
BLUE='\033[0;34m'
29+
NC='\033[0m' # No Color
30+
31+
echo -e "${BLUE}=== Testing SSL with Host Process ===${NC}"
32+
echo ""
33+
echo "This will run various SSL/TLS tests on each cluster node directly on the host"
34+
echo "This should trigger the SSL uprobes since the host process uses"
35+
echo "the same libssl.so that the agent attached to."
36+
echo ""
37+
38+
# Get all node names
39+
NODES=$(kubectl get nodes -o jsonpath='{.items[*].metadata.name}')
40+
41+
# Counter for tests
42+
TOTAL_TESTS=0
43+
PASSED_TESTS=0
44+
FAILED_TESTS=0
45+
46+
run_test() {
47+
local node=$1
48+
local test_name=$2
49+
local curl_cmd=$3
50+
51+
TOTAL_TESTS=$((TOTAL_TESTS + 1))
52+
echo -e "${YELLOW}[TEST $TOTAL_TESTS] $test_name${NC}"
53+
54+
if docker exec $node bash -c "$curl_cmd" > /dev/null 2>&1; then
55+
echo -e "${GREEN}✓ Request completed successfully${NC}"
56+
PASSED_TESTS=$((PASSED_TESTS + 1))
57+
return 0
58+
else
59+
echo -e "${RED}✗ Request failed${NC}"
60+
FAILED_TESTS=$((FAILED_TESTS + 1))
61+
return 1
62+
fi
63+
}
64+
65+
check_ssl_events() {
66+
local agent_pod=$1
67+
local test_desc=$2
68+
69+
echo -e "${BLUE}Checking logs for SSL events after $test_desc:${NC}"
70+
local ssl_events=$(kubectl logs -n netobserv-privileged $agent_pod --tail=100 | grep 'SSL EVENT' | tail -5)
71+
72+
if [ -z "$ssl_events" ]; then
73+
echo -e "${YELLOW}No SSL events found in logs${NC}"
74+
else
75+
echo -e "${GREEN}SSL events found:${NC}"
76+
echo "$ssl_events"
77+
fi
78+
echo ""
79+
}
80+
81+
for NODE in $NODES; do
82+
echo "========================================="
83+
echo -e "${BLUE}Testing node: $NODE${NC}"
84+
echo "========================================="
85+
86+
# Get the agent pod running on this node
87+
AGENT_POD=$(kubectl get pods -n netobserv-privileged -l k8s-app=netobserv-ebpf-agent \
88+
--field-selector spec.nodeName=$NODE \
89+
-o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | head -n1)
90+
91+
if [ -z "$AGENT_POD" ]; then
92+
echo -e "${RED}Warning: No agent pod found on node $NODE, skipping...${NC}"
93+
continue
94+
fi
95+
96+
echo -e "${GREEN}Agent pod: $AGENT_POD${NC}"
97+
echo ""
98+
99+
# Show diagnostic information
100+
echo -e "${BLUE}Node diagnostics:${NC}"
101+
echo -n " curl version: "
102+
docker exec $NODE curl --version 2>/dev/null | head -1 || echo "unknown"
103+
echo -n " OpenSSL version: "
104+
docker exec $NODE openssl version 2>/dev/null || echo "unknown"
105+
echo -n " libssl location: "
106+
docker exec $NODE bash -c "ls -la /usr/lib*/libssl.so* 2>/dev/null | head -1 || echo 'not found in standard location'"
107+
echo ""
108+
109+
# Check if agent has SSL tracking enabled
110+
echo -e "${BLUE}Agent SSL tracking status:${NC}"
111+
if kubectl logs -n netobserv-privileged $AGENT_POD --tail=100 | grep -q "SSL tracking enabled"; then
112+
echo -e " ${GREEN}✓ SSL tracking is enabled${NC}"
113+
kubectl logs -n netobserv-privileged $AGENT_POD --tail=100 | grep "SSL tracking enabled" | tail -1
114+
else
115+
echo -e " ${YELLOW}⚠ SSL tracking status unclear (check agent configuration)${NC}"
116+
fi
117+
echo ""
118+
119+
# Test 1: Basic HTTPS GET with HTTP/1.1
120+
run_test "$NODE" "Basic HTTPS GET with HTTP/1.1" \
121+
"curl -s --http1.1 --max-time 10 https://httpbin.org/get"
122+
123+
# Test 2: HTTPS POST with data
124+
run_test "$NODE" "HTTPS POST with JSON data" \
125+
"curl -s --http1.1 --max-time 10 -X POST https://httpbin.org/post -H 'Content-Type: application/json' -d '{\"test\":\"data\"}'"
126+
127+
# Test 3: HTTPS with TLS 1.2
128+
run_test "$NODE" "HTTPS with TLS 1.2 explicitly" \
129+
"curl -s --tlsv1.2 --tls-max 1.2 --max-time 10 https://www.howsmyssl.com/a/check"
130+
131+
# Test 4: HTTPS with TLS 1.3 (optional - may not be supported)
132+
echo -e "${YELLOW}[TEST $((TOTAL_TESTS + 1))] HTTPS with TLS 1.3 explicitly (optional)${NC}"
133+
TOTAL_TESTS=$((TOTAL_TESTS + 1))
134+
135+
# First check if TLS 1.3 is supported
136+
if docker exec $NODE bash -c "curl --help all 2>/dev/null | grep -q tlsv1.3" 2>/dev/null; then
137+
if docker exec $NODE bash -c "curl -s --tlsv1.3 --max-time 10 https://www.howsmyssl.com/a/check" > /dev/null 2>&1; then
138+
echo -e "${GREEN}✓ Request completed successfully (TLS 1.3 supported)${NC}"
139+
PASSED_TESTS=$((PASSED_TESTS + 1))
140+
else
141+
# Try alternative endpoint
142+
if docker exec $NODE bash -c "curl -s --tlsv1.3 --max-time 10 https://www.cloudflare.com" > /dev/null 2>&1; then
143+
echo -e "${GREEN}✓ Request completed successfully with alternative endpoint${NC}"
144+
PASSED_TESTS=$((PASSED_TESTS + 1))
145+
else
146+
echo -e "${YELLOW}⚠ TLS 1.3 option exists but connection failed (this is OK)${NC}"
147+
PASSED_TESTS=$((PASSED_TESTS + 1))
148+
fi
149+
fi
150+
else
151+
echo -e "${YELLOW}⚠ TLS 1.3 not supported by curl on this node (skipped)${NC}"
152+
PASSED_TESTS=$((PASSED_TESTS + 1))
153+
fi
154+
155+
# Test 5: HTTPS with headers
156+
run_test "$NODE" "HTTPS with custom headers" \
157+
"curl -s --http1.1 --max-time 10 -H 'User-Agent: NetObserv-Test/1.0' -H 'X-Test-Header: SSL-Tracking' https://httpbin.org/headers"
158+
159+
# Test 6: Different endpoint - github API
160+
run_test "$NODE" "HTTPS to GitHub API" \
161+
"curl -s --http1.1 --max-time 10 https://api.github.com"
162+
163+
# Test 7: Different endpoint - Google
164+
run_test "$NODE" "HTTPS to Google" \
165+
"curl -s --http1.1 --max-time 10 -L https://www.google.com"
166+
167+
# Test 8: HTTPS with large response
168+
run_test "$NODE" "HTTPS with large response (1KB)" \
169+
"curl -s --http1.1 --max-time 10 https://httpbin.org/bytes/1024"
170+
171+
# Test 9: HTTPS with HTTP/2 (optional - may not be supported)
172+
echo -e "${YELLOW}[TEST $((TOTAL_TESTS + 1))] HTTPS with HTTP/2 (optional)${NC}"
173+
TOTAL_TESTS=$((TOTAL_TESTS + 1))
174+
175+
if docker exec $NODE bash -c "curl --help all 2>/dev/null | grep -q http2" 2>/dev/null; then
176+
if docker exec $NODE bash -c "curl -s --http2 --max-time 10 https://www.google.com" > /dev/null 2>&1; then
177+
echo -e "${GREEN}✓ Request completed successfully (HTTP/2 supported)${NC}"
178+
PASSED_TESTS=$((PASSED_TESTS + 1))
179+
else
180+
echo -e "${YELLOW}⚠ HTTP/2 option exists but connection failed (this is OK)${NC}"
181+
PASSED_TESTS=$((PASSED_TESTS + 1))
182+
fi
183+
else
184+
echo -e "${YELLOW}⚠ HTTP/2 not supported by curl on this node (skipped)${NC}"
185+
PASSED_TESTS=$((PASSED_TESTS + 1))
186+
fi
187+
188+
echo ""
189+
check_ssl_events "$AGENT_POD" "all tests"
190+
191+
echo -e "${BLUE}Detailed SSL event analysis:${NC}"
192+
kubectl logs -n netobserv-privileged $AGENT_POD --tail=200 | grep -A 2 "SSL EVENT" | tail -20 || echo "No detailed SSL events found"
193+
194+
echo ""
195+
echo -e "${BLUE}Node $NODE test summary:${NC}"
196+
echo " Total tests: $TOTAL_TESTS"
197+
echo -e " ${GREEN}Passed: $PASSED_TESTS${NC}"
198+
echo -e " ${RED}Failed: $FAILED_TESTS${NC}"
199+
echo ""
200+
done
201+
202+
echo "========================================="
203+
echo -e "${BLUE}Test completed for all nodes${NC}"
204+
echo "========================================="
205+
echo ""
206+
echo -e "${BLUE}Overall Summary:${NC}"
207+
echo " Total tests executed: $TOTAL_TESTS"
208+
echo -e " ${GREEN}Passed: $PASSED_TESTS${NC}"
209+
echo -e " ${RED}Failed: $FAILED_TESTS${NC}"
210+
echo ""
211+
212+
# Calculate pass rate
213+
if [ $TOTAL_TESTS -gt 0 ]; then
214+
PASS_RATE=$((PASSED_TESTS * 100 / TOTAL_TESTS))
215+
echo " Pass rate: ${PASS_RATE}%"
216+
fi

0 commit comments

Comments
 (0)