mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
feat(ebpf-poc): redirect eligible connections to the proxy
Turns the recorded ACTION_REDIRECT decision into an actual rewrite of user_ip4 and user_port, so a connection that would have reached the registry directly lands on the configured target instead. The target address is copied out of the map as is. It is already held in network byte order, the same layout as ctx->user_ip4, so converting it again silently corrupts the destination: 127.0.0.1 becomes 1.0.0.127, which is routable, never answers, and shows up as a two minute hang rather than an error. Only the port is converted, since it is kept in host order for the userspace side. Verified against a dummy listener. curl to an external HTTPS host is logged as REDIRECT against its original destination, and the TLS ClientHello arrives on the local target.
This commit is contained in:
@@ -35,3 +35,7 @@ packages/*/bin/
|
||||
# Build artifacts in wrapper package
|
||||
packages/pmg/dist/
|
||||
packages/pmg/README.md
|
||||
|
||||
ebpf-poc/pmgwatch
|
||||
go.work.sum
|
||||
pmgwatch
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+16
-4
@@ -12,8 +12,8 @@ char __license[] SEC("license") = "Dual MIT/GPL";
|
||||
// steered into a proxy that cannot speak their protocol.
|
||||
#define REDIRECT_DPORT 443
|
||||
|
||||
// What the hook decided. Recorded on every event so the ladder can be checked
|
||||
// against real traffic before any rewrite is switched on.
|
||||
// What the hook decided. Recorded on every event so the ladder stays auditable:
|
||||
// a connection that was not redirected always reports which check stopped it.
|
||||
#define ACTION_REDIRECT 0
|
||||
#define ACTION_SKIP_PROTO 1
|
||||
#define ACTION_SKIP_LOOPBACK 2
|
||||
@@ -106,7 +106,19 @@ int connect4(struct bpf_sock_addr *ctx) {
|
||||
bpf_ringbuf_submit(e, 0);
|
||||
}
|
||||
|
||||
// Shadow mode. The decision is recorded and never acted on. Turning
|
||||
// ACTION_REDIRECT into a rewrite of user_ip4 and user_port is the next step.
|
||||
if (action == ACTION_REDIRECT) {
|
||||
__u32 key = 0;
|
||||
struct target *t = bpf_map_lookup_elem(&target_map, &key);
|
||||
if (!t || t->port == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// t->ip is already network byte order, the same layout as user_ip4, so it
|
||||
// is copied as is. Byte swapping here would corrupt the address. Only the
|
||||
// port is converted, since it is stored host order for the userspace side.
|
||||
ctx->user_ip4 = t->ip;
|
||||
ctx->user_port = bpf_htons(t->port);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ func run(proxyAddr, exemptUIDs string, tcpOnly bool) error {
|
||||
rd.Close()
|
||||
}()
|
||||
|
||||
fmt.Println("Shadow mode: decisions are recorded, nothing is redirected. Ctrl+C to exit.")
|
||||
fmt.Println("Attached to /sys/fs/cgroup. Ctrl+C to exit.")
|
||||
fmt.Printf("%-15s %-17s %-7s %-8s %-22s %s\n", "ACTION", "COMMAND", "UID", "PID", "DESTINATION", "PROTO")
|
||||
|
||||
return drain(rd, tcpOnly)
|
||||
|
||||
Reference in New Issue
Block a user