Skip to content

Commit 2d6bc28

Browse files
docs: improve documentation for linux jail (#109)
* improve docs * improve docs
1 parent bbc174e commit 2d6bc28

File tree

2 files changed

+86
-18
lines changed

2 files changed

+86
-18
lines changed

jail/networking_host.go

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,27 @@ func (l *LinuxJail) configureHostNetworkBeforeCmdExec() error {
2323
l.vethJailName = vethJailName
2424

2525
runner := newCommandRunner([]*command{
26+
// Create a virtual Ethernet (veth) pair that forms a point-to-point link
27+
// between the host and the jail namespace. One end stays on the host,
28+
// the other will be moved into the jail. This provides a dedicated,
29+
// isolated L2 network for the jail.
2630
{
27-
"create veth pair",
31+
"Create host–jail veth interface pair",
2832
exec.Command("ip", "link", "add", vethHostName, "type", "veth", "peer", "name", vethJailName),
2933
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
3034
},
35+
// Assign an IP address to the host side of the veth pair. The /24 mask
36+
// implicitly defines the jail's entire subnet as 192.168.100.0/24.
37+
// The host address (192.168.100.1) becomes the default gateway for
38+
// processes inside the jail and is used by NAT and interception rules
39+
// to route traffic out of the namespace.
3140
{
32-
"configure host veth",
41+
"Assign IP to host-side veth",
3342
exec.Command("ip", "addr", "add", "192.168.100.1/24", "dev", vethHostName),
3443
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
3544
},
3645
{
37-
"bring up host veth",
46+
"Activate host-side veth interface",
3847
exec.Command("ip", "link", "set", vethHostName, "up"),
3948
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
4049
},
@@ -54,8 +63,12 @@ func (l *LinuxJail) configureHostNetworkAfterCmdExec(pidInt int) error {
5463
PID := fmt.Sprintf("%v", pidInt)
5564

5665
runner := newCommandRunner([]*command{
66+
// Move the jail-side veth interface into the target network namespace.
67+
// This isolates the interface so that it becomes visible only inside the
68+
// jail's netns. From this point on, the jail will configure its end of
69+
// the veth pair (IP address, routes, etc.) independently of the host.
5770
{
58-
"move veth to namespace",
71+
"Move jail-side veth into network namespace",
5972
exec.Command("ip", "link", "set", l.vethJailName, "netns", PID),
6073
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
6174
},
@@ -70,30 +83,70 @@ func (l *LinuxJail) configureHostNetworkAfterCmdExec(pidInt int) error {
7083
// setupIptables configures iptables rules for comprehensive TCP traffic interception
7184
func (l *LinuxJail) configureIptables() error {
7285
runner := newCommandRunner([]*command{
86+
// Enable IPv4 packet forwarding so the host can route packets between
87+
// the jail's veth interface and the outside network. Without this,
88+
// NAT and forwarding rules would have no effect because the kernel
89+
// would drop transit packets.
7390
{
7491
"enable IP forwarding",
7592
exec.Command("sysctl", "-w", "net.ipv4.ip_forward=1"),
7693
[]uintptr{},
7794
},
95+
// Apply source NAT (MASQUERADE) for all traffic leaving the jail’s
96+
// private subnet. This rewrites the source IP of packets originating
97+
// from 192.168.100.0/24 to the host’s external interface IP. It enables:
98+
//
99+
// - outbound connectivity for jailed processes,
100+
// - correct return routing from external endpoints,
101+
// - avoidance of static IP assignment for the host interface.
102+
//
103+
// MASQUERADE is used instead of SNAT so it works even when the host IP
104+
// changes dynamically.
78105
{
79106
"NAT rules for outgoing traffic (MASQUERADE for return traffic)",
80107
exec.Command("iptables", "-t", "nat", "-A", "POSTROUTING", "-s", "192.168.100.0/24", "-j", "MASQUERADE"),
81108
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
82109
},
110+
// Redirect *ALL TCP traffic* coming from the jail’s veth interface
111+
// to the local HTTP/TLS-intercepting proxy. This causes *every* TCP
112+
// connection (HTTP, HTTPS, plain TCP protocols) initiated by jailed
113+
// processes to be transparently intercepted.
114+
//
115+
// The HTTP proxy will intelligently handle both HTTP and TLS traffic.
116+
//
117+
// PREROUTING is used so redirection happens before routing decisions.
118+
// REDIRECT rewrites the destination IP to 127.0.0.1 and the destination
119+
// port to the HTTP proxy's port, forcing traffic through the proxy without
120+
// requiring any configuration inside the jail.
83121
{
84-
// COMPREHENSIVE APPROACH: Route ALL TCP traffic to HTTP proxy
85-
// The HTTP proxy will intelligently handle both HTTP and TLS traffic
86122
"Route ALL TCP traffic to HTTP proxy",
87123
exec.Command("iptables", "-t", "nat", "-A", "PREROUTING", "-i", l.vethHostName, "-p", "tcp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", l.httpProxyPort)),
88124
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
89125
},
126+
// Allow forwarding of non-TCP packets originating from the jail’s subnet.
127+
// This rule is primarily needed for traffic that is *not* intercepted by
128+
// the TCP REDIRECT rule — for example:
129+
//
130+
// - DNS queries (UDP/53)
131+
// - ICMP (ping, errors)
132+
// - Any other UDP or non-TCP protocols
133+
//
134+
// Redirected TCP flows never reach the FORWARD chain (they are locally
135+
// redirected in PREROUTING), so this rule does not apply to TCP traffic.
90136
{
91-
"iptables FORWARD -s",
137+
"Allow outbound non-TCP traffic from jail subnet",
92138
exec.Command("iptables", "-A", "FORWARD", "-s", "192.168.100.0/24", "-j", "ACCEPT"),
93139
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
94140
},
141+
// Allow forwarding of return traffic destined for the jail’s subnet for
142+
// non-TCP flows. This complements the previous FORWARD rule and ensures
143+
// that responses to DNS (UDP) or ICMP packets can reach the jail.
144+
//
145+
// As with the previous rule, this has no effect on TCP traffic because
146+
// all TCP connections from the jail are intercepted and redirected to
147+
// the local proxy before reaching the forwarding path.
95148
{
96-
"iptables FORWARD -d",
149+
"Allow inbound return traffic to jail subnet (non-TCP)",
97150
exec.Command("iptables", "-A", "FORWARD", "-d", "192.168.100.0/24", "-j", "ACCEPT"),
98151
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
99152
},
@@ -127,22 +180,22 @@ func (l *LinuxJail) cleanupNetworking() error {
127180
func (l *LinuxJail) cleanupIptables() error {
128181
runner := newCommandRunner([]*command{
129182
{
130-
"Remove comprehensive TCP redirect rule",
131-
exec.Command("iptables", "-t", "nat", "-D", "PREROUTING", "-i", l.vethHostName, "-p", "tcp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", l.httpProxyPort)),
183+
"Remove: NAT rules for outgoing traffic (MASQUERADE for return traffic)",
184+
exec.Command("iptables", "-t", "nat", "-D", "POSTROUTING", "-s", "192.168.100.0/24", "-j", "MASQUERADE"),
132185
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
133186
},
134187
{
135-
"Remove NAT rule",
136-
exec.Command("iptables", "-t", "nat", "-D", "POSTROUTING", "-s", "192.168.100.0/24", "-j", "MASQUERADE"),
188+
"Remove: Route ALL TCP traffic to HTTP proxy",
189+
exec.Command("iptables", "-t", "nat", "-D", "PREROUTING", "-i", l.vethHostName, "-p", "tcp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", l.httpProxyPort)),
137190
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
138191
},
139192
{
140-
"Remove iptables FORWARD -s",
193+
"Remove: Allow outbound non-TCP traffic from jail subnet",
141194
exec.Command("iptables", "-D", "FORWARD", "-s", "192.168.100.0/24", "-j", "ACCEPT"),
142195
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
143196
},
144197
{
145-
"Remove iptables FORWARD -d",
198+
"Remove: Allow inbound return traffic to jail subnet (non-TCP)",
146199
exec.Command("iptables", "-D", "FORWARD", "-d", "192.168.100.0/24", "-j", "ACCEPT"),
147200
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
148201
},

jail/networking_ns.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,38 @@ import (
1111
// created and moved to its own network namespace.
1212
func SetupChildNetworking(vethNetJail string) error {
1313
runner := newCommandRunner([]*command{
14+
// Assign an IP address to the jail-side veth interface. The /24 mask
15+
// matches the subnet defined on the host side (192.168.100.0/24),
16+
// ensuring both interfaces appear on the same L2 network. This address
17+
// (192.168.100.2) will serve as the jail's primary outbound source IP.
1418
{
15-
"configure namespace veth",
19+
"Assign IP to jail-side veth",
1620
exec.Command("ip", "addr", "add", "192.168.100.2/24", "dev", vethNetJail),
1721
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
1822
},
23+
// Bring the jail-side veth interface up. Until the interface is set UP,
24+
// the jail cannot send or receive any packets on this link, even if the
25+
// IP address and routes are configured correctly.
1926
{
20-
"bring up namespace veth",
27+
"Activate jail-side veth interface",
2128
exec.Command("ip", "link", "set", vethNetJail, "up"),
2229
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
2330
},
31+
// Bring the jail-side veth interface up. Until the interface is set UP,
32+
// the jail cannot send or receive any packets on this link, even if the
33+
// IP address and routes are configured correctly.
2434
{
25-
"bring up loopback",
35+
"Enable loopback interface in jail",
2636
exec.Command("ip", "link", "set", "lo", "up"),
2737
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
2838
},
39+
// Set the default route for all outbound traffic inside the jail. The
40+
// gateway is the host-side veth address (192.168.100.1), which performs
41+
// NAT and transparent TCP interception. This ensures that packets not
42+
// destined for the jail subnet are routed to the host for processing.
43+
2944
{
30-
"set default route in namespace",
45+
"Configure default gateway for jail",
3146
exec.Command("ip", "route", "add", "default", "via", "192.168.100.1"),
3247
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
3348
},

0 commit comments

Comments
 (0)