Repository navigation
Linux 4.0.0 - #253
Merged
Merged
Linux 4.0.0#253
Conversation
Selected processes' connect()/sendmsg() are redirected by cgroup sock_addr hooks to a local relay instead of queueing every packet to userspace. The data path is untouched, there is no per-packet work, and the kernel removes the programs by itself when the process exits or crashes. - eBPF: connect4/6, sendmsg4/6, recvmsg4/6, getpeername4/6 and sockops. Processes are identified by executable (dev, inode) and mapped to a class of compiled rules; anything the kernel cannot decide goes to the relay once and the result is cached in the kernel. Sockets of other network namespaces are ignored. - Rule engine: validated globs, IPv4/IPv6, CIDR, ranges, octet patterns, port lists/ranges, protocol, first-match-wins, and domain names. - Multiple proxies, each rule can pick its own (SOCKS5, SOCKS5 with auth, HTTP CONNECT). A PROXY rule whose proxy is missing blocks. - Domain rules via a DNS tap: DNS answers (UDP and TCP) seen by the relay build a bounded IP -> name map, armed in the kernel before the answer is delivered; also loads /etc/hosts. - epoll relay with splice(), bounded memory (idle connections cost ~100 bytes), SOCKS5 UDP ASSOCIATE, direct forwarding, idle timeouts, connection cap and hostname proxy resolution with a deadline and cache. - Public API gains proxy management, per-rule proxy ids, rule validation, reordering, and keeps rules across Stop/Start.
- repeatable --proxy [name=]type://[user:pass@]host:port (IPv6 hosts in []) - --rule process:hosts:ports:protocol:action[=proxy] - hosts accept domain names (github.com, *.github.com) - validation errors from the library are printed - no work in signal handlers; --cleanup is now a documented no-op
- proxy list manager with per-proxy test, default proxy and validation - profiles (new, duplicate, rename, delete, switch without restarting the engine), each with its own proxies, rules and default proxy; old configs migrate to a profile named "Default" - rule editor: proxy selection, inline validation, move up/down, JSON import/export with a validation summary - domain names accepted in the hosts field; logs show the domain - PROXYBRIDGE_CONFIG_DIR overrides the config directory - signal handling and callback memory safety fixes - config/profile unit test
Run against the real kernel datapath (root): e2e.sh, e2e_rules.sh (every rule form and proxy type), e2e_domains.sh (real dig + curl), api_live.sh (live rule/proxy edits), e2e_openssh.sh (interop with a real third-party SOCKS5 server), gui_headless.sh (real GUI under the GTK Broadway backend), scale.sh (10,000 concurrent connections), plus hookbench/thrbench/dnsbench. Includes fake SOCKS5/HTTP/DNS servers.
README: new architecture, domain rules and their limits, multiple proxies, requirements (kernel >= 5.8 with BTF and cgroup v2) and testing. setup/deploy: depend on libbpf instead of netfilter/iptables. .gitignore: Linux build artifacts and generated skeletons; keep test scripts.
build.sh now compiles in a temporary directory and copies just libproxybridge.so, ProxyBridge and ProxyBridgeGUI into output/. The source tree no longer collects .o files, the generated eBPF skeleton or libraries, even when a build fails. The Makefiles take BUILD_DIR/LIBDIR for this; a plain `make` still builds in place for development. - check for gcc, make, clang, bpftool and libbpf with install hints - OUTPUT_DIR can be overridden; a clear error if output/ cannot be removed - fix the GUI rpath: $ORIGIN was expanded to an empty string by the shell - test scripts use the built binaries from output/ (override with PB_DIR)
The Linux workflows still installed libnetfilter-queue and libnfnetlink. The eBPF build needs clang (BPF object), bpftool (skeleton) and libbpf-dev. The build workflow now also runs the rule engine, DNS parser and relay unit tests, which need no root.
- run on push and pull requests for Linux-ebpf as well as master and dev - the GUI is now required to build (GTK3 is installed on the runner) - smoke test: CLI help, library linkage, GUI $ORIGIN runpath, exported public API, and no libnetfilter dependency - fail if the build leaves untracked or ignored files outside Linux/output - best-effort end-to-end job (e2e, e2e_rules, e2e_domains, api_live) against the real kernel datapath; reported but non-blocking since it needs a BTF kernel with cgroup v2 and root
On Ubuntu 24.04 "bpftool" is a virtual package, so `apt install bpftool` fails. Install linux-tools-common/linux-tools-generic and locate the real binary under /usr/lib/linux-tools, exporting it as BPFTOOL. build.sh and the end-to-end scripts honour $BPFTOOL, so a bpftool that is not on PATH works; the error message now suggests the right package per distro.
The relay test harness called unshare(CLONE_NEWUSER|CLONE_NEWNET) in its main process. On GitHub's Ubuntu 24.04 runners the namespace is created but loopback cannot be configured, which stranded the whole run in a network namespace without loopback and made every later test fail or hang. - run the port-53 DNS tests in a forked child; the main process never changes namespaces; if the child cannot isolate itself the tests are skipped - global watchdog (PB_TEST_WATCHDOG, default 300s), line-buffered output and bounded client timeouts so a broken environment fails fast - start-up loopback self-check with a clear FATAL message - workflows: job and step timeouts so a hung step cannot hold a runner
- fake decision state is now accessed under a mutex (it was a plain struct shared with relay threads) - fd-leak checks settle and assert end <= base instead of equality - wait budgets for closes, UDP replies and slot reuse are generous; hangs are still bounded by the watchdog - expect_closed prints fd, poll, errno and recv details when it fails Verified with 12 runs on 2 shared CPUs under 4 busy loops, plus plain, ASan+UBSan and the simulated no-loopback mode.
The remaining intermittent CI failure ("TCP DECIDE -> BLOCK") happens before
expect_closed is reached, i.e. relay_connect() returned -1. Print the failing
call, the source port and errno so the next occurrence is diagnosable.
For flows the relay aborts (BLOCK decisions, connection cap) it closes with a RST right after accept. On a fast loopback the RST can arrive before the client's connect() has returned, so connect() itself fails with ECONNRESET. That is a valid way for such a flow to end, but the tests only accepted a reset after connect(); GitHub runners hit the race in ~2/3 of the runs. closed_by_relay() treats ECONNRESET from connect() or EOF/RST afterwards as closed, and is used by every "the relay must close this" assertion.
…ding, TCP and UDP) Runs ProxyBridge in front of two Nexus instances (open and username/password) with real origin servers and a real DNS server, instead of fakes: rules by IP, port, port range, process, CIDR and domain; DNS itself carried by the proxy (UDP and TCP); 200 MB download and 100 MB upload with checksums; 100 parallel and 1,500 sequential connections; one UDP socket talking to two proxies, connected UDP, 59 KB datagrams, 300 concurrent UDP sockets, a sustained stream; proxy restart (TCP fails closed, UDP re-associates). Stock Nexus cannot do UDP ASSOCIATE on Linux (IPv6 socket bound to 0.0.0.0, IPv4 destinations not sent as ::ffff:, mapped replies not matched); tests/nexus-linux-udp.patch fixes it and the UDP checks are skipped without it. fake_dns.py gains a PB_DNS_ZONE override.
The eBPF program calls bpf_get_current_task_btf(), added in Linux 5.11, to read the calling executable. Correct the README, the public header comment and the load-failure message.
…al proxies, slim the datapath Security - The kernel now keys a pending TCP flow by the client's source address AND port; the relay passes both when it claims the flow. A process connecting from another loopback address (e.g. 127.0.0.2) can no longer take another process's destination mapping. Behaviour - Processes that listen on a configured local proxy address are exempted automatically (per process, refreshed every 2 s), so a wildcard-process rule can no longer send a proxy's own connections back through itself. - A UDP rule that points at an HTTP proxy (which cannot carry UDP) is logged with the rule id and proxy name instead of silently going direct. - /etc/hosts is reloaded when it changes. Robustness - Shutdown detaches the hooks first and frees the BPF maps only after the relay has stopped; relay threads could previously touch freed maps. - Executable paths are stored with their full length (a fixed 240-byte buffer could truncate and misclassify a long path). - The skeleton header is generated atomically so a failed bpftool run cannot leave an empty file behind. Leaner code - Config lives in BPF global data (no map lookup per field), the never-read stats map is gone, IPv4/IPv6 hooks share their helpers, cached rule views replace per-decision allocation, the event reader sleeps instead of polling, the library exports only the 23 ProxyBridge_* functions, unused helpers and interface parameters are removed, comments are cut to the non-obvious ones. Compiled datapath is ~7% smaller. - Makefile accepts EXTRA_CFLAGS/EXTRA_LDFLAGS for sanitizer builds.
… leaner code - claim a pending TCP flow with the accepted peer's address and port - every error and teardown path audited for fd, memory, thread and session leaks; a churn test (100,000 TCP connections across nine flow types, 20,000 UDP sessions) asserts that descriptors return to baseline and RSS stays flat - the UDP flow lookup (a BPF map syscall per datagram) is cached per client for one second; SOCKS5 headers are sent by scatter-gather; accepted sockets inherit their options from the listener - banner and narration comments, benchmark-only hooks and dead helpers removed - tests: hijack attempts from other loopback addresses, stress-tolerant assertions, a PB_TEST_ONLY filter, smaller sizes under the sanitizers
…he version macro --proxy specs are wiped from argv after parsing so `ps` no longer shows user:pass; the banner prints PROXYBRIDGE_VERSION; narration comments removed.
… JSON parser out - profiles, proxies and rules are freed at exit (the only leak found) - gui_json.c holds the rules import/export parser: it finds the end of an object properly (a '}' inside a string no longer breaks an import) and is unit-tested with hostile input (unterminated quotes, wrong types, a 20 MB string, 1M open braces, a 100k-rule round trip) - duplicated dialog, combo and idle-callback code merged into small helpers; unused includes, structs and exported functions removed; comments cut to under 2% - config tests run clean under ASan/LeakSanitizer and include a 20,000-cycle profile create/rename/duplicate/delete + save/load stress
…ty model - e2e_security.sh plants a pending flow in the real kernel map and checks that a connection from 127.0.0.2 is refused, the owner still works, and an entry cannot be replayed - churn.sh / churn_test.c: thousands of add/edit/move/disable/delete operations on rules and proxies plus restarts under live traffic; in-use heap, descriptors and threads must stay flat - e2e_nexus.sh no longer needs a DIRECT rule for the proxy's own process; a second node process that is not the proxy must still be proxied - e2e_rules.sh checks the UDP-over-HTTP-proxy warning; api_live.sh and the CI end-to-end job gain the new tests - README: security model, automatic proxy exemption, UDP over HTTP behaviour
…ixes Connection events go through a bounded queue drained on the GTK main loop, with unit tests including a TSan run.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request
Required Pre-Submission Checklist
devbranch (NOTmaster)masterbranch is ONLY updated when a new release is createdmasterwill be REJECTED⛔ Critical Information
DO NOT submit a pull request directly to the
masterbranch. The master branch is only updated when a new release is created. No direct commits or merges are accepted to master - neither from the InterceptSuite team nor from contributors.All changes MUST be submitted to the
devbranch, which contains the latest code and commits.You MUST create a GitHub issue first before submitting a pull request. This allows the InterceptSuite team to:
Pull requests without an associated issue will be rejected.
Pull Request Details
Type of Change
Related GitHub Issue
Issue Link:
Platform(s) Affected
macOS Application Changes
Does this pull request include changes to the macOS application?
If YES, you MUST answer the following:
macOS Code Signing & Verification
Description of Changes
What does this pull request do?
How has this been tested?
Compilation & Dependencies
New dependencies (if any):
Screenshots/Logs (if applicable)
Additional Context
Final Checklist
devbranch