From 2093a68bbcdf99f4f127d6ca564f0cf216469466 Mon Sep 17 00:00:00 2001 From: YEVHENII SHCHERBINA Date: Mon, 8 Dec 2025 16:09:05 +0000 Subject: [PATCH 1/2] improve docs --- jail/networking_host.go | 60 ++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/jail/networking_host.go b/jail/networking_host.go index 42a2cfa..b4993c6 100644 --- a/jail/networking_host.go +++ b/jail/networking_host.go @@ -70,30 +70,70 @@ func (l *LinuxJail) configureHostNetworkAfterCmdExec(pidInt int) error { // setupIptables configures iptables rules for comprehensive TCP traffic interception func (l *LinuxJail) configureIptables() error { runner := newCommandRunner([]*command{ + // Enable IPv4 packet forwarding so the host can route packets between + // the jail's veth interface and the outside network. Without this, + // NAT and forwarding rules would have no effect because the kernel + // would drop transit packets. { "enable IP forwarding", exec.Command("sysctl", "-w", "net.ipv4.ip_forward=1"), []uintptr{}, }, + // Apply source NAT (MASQUERADE) for all traffic leaving the jail’s + // private subnet. This rewrites the source IP of packets originating + // from 192.168.100.0/24 to the host’s external interface IP. It enables: + // + // - outbound connectivity for jailed processes, + // - correct return routing from external endpoints, + // - avoidance of static IP assignment for the host interface. + // + // MASQUERADE is used instead of SNAT so it works even when the host IP + // changes dynamically. { "NAT rules for outgoing traffic (MASQUERADE for return traffic)", exec.Command("iptables", "-t", "nat", "-A", "POSTROUTING", "-s", "192.168.100.0/24", "-j", "MASQUERADE"), []uintptr{uintptr(unix.CAP_NET_ADMIN)}, }, + // Redirect *ALL TCP traffic* coming from the jail’s veth interface + // to the local HTTP/TLS-intercepting proxy. This causes *every* TCP + // connection (HTTP, HTTPS, plain TCP protocols) initiated by jailed + // processes to be transparently intercepted. + // + // The HTTP proxy will intelligently handle both HTTP and TLS traffic. + // + // PREROUTING is used so redirection happens before routing decisions. + // REDIRECT rewrites the destination IP to 127.0.0.1 and the destination + // port to the HTTP proxy's port, forcing traffic through the proxy without + // requiring any configuration inside the jail. { - // COMPREHENSIVE APPROACH: Route ALL TCP traffic to HTTP proxy - // The HTTP proxy will intelligently handle both HTTP and TLS traffic "Route ALL TCP traffic to HTTP proxy", exec.Command("iptables", "-t", "nat", "-A", "PREROUTING", "-i", l.vethHostName, "-p", "tcp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", l.httpProxyPort)), []uintptr{uintptr(unix.CAP_NET_ADMIN)}, }, + // Allow forwarding of non-TCP packets originating from the jail’s subnet. + // This rule is primarily needed for traffic that is *not* intercepted by + // the TCP REDIRECT rule — for example: + // + // - DNS queries (UDP/53) + // - ICMP (ping, errors) + // - Any other UDP or non-TCP protocols + // + // Redirected TCP flows never reach the FORWARD chain (they are locally + // redirected in PREROUTING), so this rule does not apply to TCP traffic. { - "iptables FORWARD -s", + "Allow outbound non-TCP traffic from jail subnet", exec.Command("iptables", "-A", "FORWARD", "-s", "192.168.100.0/24", "-j", "ACCEPT"), []uintptr{uintptr(unix.CAP_NET_ADMIN)}, }, + // Allow forwarding of return traffic destined for the jail’s subnet for + // non-TCP flows. This complements the previous FORWARD rule and ensures + // that responses to DNS (UDP) or ICMP packets can reach the jail. + // + // As with the previous rule, this has no effect on TCP traffic because + // all TCP connections from the jail are intercepted and redirected to + // the local proxy before reaching the forwarding path. { - "iptables FORWARD -d", + "Allow inbound return traffic to jail subnet (non-TCP)", exec.Command("iptables", "-A", "FORWARD", "-d", "192.168.100.0/24", "-j", "ACCEPT"), []uintptr{uintptr(unix.CAP_NET_ADMIN)}, }, @@ -127,22 +167,22 @@ func (l *LinuxJail) cleanupNetworking() error { func (l *LinuxJail) cleanupIptables() error { runner := newCommandRunner([]*command{ { - "Remove comprehensive TCP redirect rule", - exec.Command("iptables", "-t", "nat", "-D", "PREROUTING", "-i", l.vethHostName, "-p", "tcp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", l.httpProxyPort)), + "Remove: NAT rules for outgoing traffic (MASQUERADE for return traffic)", + exec.Command("iptables", "-t", "nat", "-D", "POSTROUTING", "-s", "192.168.100.0/24", "-j", "MASQUERADE"), []uintptr{uintptr(unix.CAP_NET_ADMIN)}, }, { - "Remove NAT rule", - exec.Command("iptables", "-t", "nat", "-D", "POSTROUTING", "-s", "192.168.100.0/24", "-j", "MASQUERADE"), + "Remove: Route ALL TCP traffic to HTTP proxy", + exec.Command("iptables", "-t", "nat", "-D", "PREROUTING", "-i", l.vethHostName, "-p", "tcp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", l.httpProxyPort)), []uintptr{uintptr(unix.CAP_NET_ADMIN)}, }, { - "Remove iptables FORWARD -s", + "Remove: Allow outbound non-TCP traffic from jail subnet", exec.Command("iptables", "-D", "FORWARD", "-s", "192.168.100.0/24", "-j", "ACCEPT"), []uintptr{uintptr(unix.CAP_NET_ADMIN)}, }, { - "Remove iptables FORWARD -d", + "Remove: Allow inbound return traffic to jail subnet (non-TCP)", exec.Command("iptables", "-D", "FORWARD", "-d", "192.168.100.0/24", "-j", "ACCEPT"), []uintptr{uintptr(unix.CAP_NET_ADMIN)}, }, From a7b6fdf29f129098d2813f7d19f29e46b89c9cc5 Mon Sep 17 00:00:00 2001 From: YEVHENII SHCHERBINA Date: Mon, 8 Dec 2025 16:24:20 +0000 Subject: [PATCH 2/2] improve docs --- jail/networking_host.go | 21 +++++++++++++++++---- jail/networking_ns.go | 23 +++++++++++++++++++---- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/jail/networking_host.go b/jail/networking_host.go index b4993c6..dbc7d32 100644 --- a/jail/networking_host.go +++ b/jail/networking_host.go @@ -23,18 +23,27 @@ func (l *LinuxJail) configureHostNetworkBeforeCmdExec() error { l.vethJailName = vethJailName runner := newCommandRunner([]*command{ + // Create a virtual Ethernet (veth) pair that forms a point-to-point link + // between the host and the jail namespace. One end stays on the host, + // the other will be moved into the jail. This provides a dedicated, + // isolated L2 network for the jail. { - "create veth pair", + "Create host–jail veth interface pair", exec.Command("ip", "link", "add", vethHostName, "type", "veth", "peer", "name", vethJailName), []uintptr{uintptr(unix.CAP_NET_ADMIN)}, }, + // Assign an IP address to the host side of the veth pair. The /24 mask + // implicitly defines the jail's entire subnet as 192.168.100.0/24. + // The host address (192.168.100.1) becomes the default gateway for + // processes inside the jail and is used by NAT and interception rules + // to route traffic out of the namespace. { - "configure host veth", + "Assign IP to host-side veth", exec.Command("ip", "addr", "add", "192.168.100.1/24", "dev", vethHostName), []uintptr{uintptr(unix.CAP_NET_ADMIN)}, }, { - "bring up host veth", + "Activate host-side veth interface", exec.Command("ip", "link", "set", vethHostName, "up"), []uintptr{uintptr(unix.CAP_NET_ADMIN)}, }, @@ -54,8 +63,12 @@ func (l *LinuxJail) configureHostNetworkAfterCmdExec(pidInt int) error { PID := fmt.Sprintf("%v", pidInt) runner := newCommandRunner([]*command{ + // Move the jail-side veth interface into the target network namespace. + // This isolates the interface so that it becomes visible only inside the + // jail's netns. From this point on, the jail will configure its end of + // the veth pair (IP address, routes, etc.) independently of the host. { - "move veth to namespace", + "Move jail-side veth into network namespace", exec.Command("ip", "link", "set", l.vethJailName, "netns", PID), []uintptr{uintptr(unix.CAP_NET_ADMIN)}, }, diff --git a/jail/networking_ns.go b/jail/networking_ns.go index 5a9d7c5..3d2e1bb 100644 --- a/jail/networking_ns.go +++ b/jail/networking_ns.go @@ -11,23 +11,38 @@ import ( // created and moved to its own network namespace. func SetupChildNetworking(vethNetJail string) error { runner := newCommandRunner([]*command{ + // Assign an IP address to the jail-side veth interface. The /24 mask + // matches the subnet defined on the host side (192.168.100.0/24), + // ensuring both interfaces appear on the same L2 network. This address + // (192.168.100.2) will serve as the jail's primary outbound source IP. { - "configure namespace veth", + "Assign IP to jail-side veth", exec.Command("ip", "addr", "add", "192.168.100.2/24", "dev", vethNetJail), []uintptr{uintptr(unix.CAP_NET_ADMIN)}, }, + // Bring the jail-side veth interface up. Until the interface is set UP, + // the jail cannot send or receive any packets on this link, even if the + // IP address and routes are configured correctly. { - "bring up namespace veth", + "Activate jail-side veth interface", exec.Command("ip", "link", "set", vethNetJail, "up"), []uintptr{uintptr(unix.CAP_NET_ADMIN)}, }, + // Bring the jail-side veth interface up. Until the interface is set UP, + // the jail cannot send or receive any packets on this link, even if the + // IP address and routes are configured correctly. { - "bring up loopback", + "Enable loopback interface in jail", exec.Command("ip", "link", "set", "lo", "up"), []uintptr{uintptr(unix.CAP_NET_ADMIN)}, }, + // Set the default route for all outbound traffic inside the jail. The + // gateway is the host-side veth address (192.168.100.1), which performs + // NAT and transparent TCP interception. This ensures that packets not + // destined for the jail subnet are routed to the host for processing. + { - "set default route in namespace", + "Configure default gateway for jail", exec.Command("ip", "route", "add", "default", "via", "192.168.100.1"), []uintptr{uintptr(unix.CAP_NET_ADMIN)}, },