Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 67 additions & 14 deletions jail/networking_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)},
},
Expand All @@ -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)},
},
Expand All @@ -70,30 +83,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)},
},
Expand Down Expand Up @@ -127,22 +180,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)},
},
Expand Down
23 changes: 19 additions & 4 deletions jail/networking_ns.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)},
},
Expand Down
Loading