Skip to content

Commit b509871

Browse files
refactor
1 parent c985076 commit b509871

File tree

1 file changed

+125
-117
lines changed

1 file changed

+125
-117
lines changed

jail/linux.go

Lines changed: 125 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -13,121 +13,6 @@ import (
1313
"golang.org/x/sys/unix"
1414
)
1515

16-
type command struct {
17-
description string
18-
cmd *exec.Cmd
19-
ambientCaps []uintptr
20-
}
21-
22-
type commandRunner struct {
23-
commands []*command
24-
}
25-
26-
func newCommandRunner(commands []*command) *commandRunner {
27-
return &commandRunner{
28-
commands: commands,
29-
}
30-
}
31-
32-
func (r *commandRunner) run() error {
33-
for _, command := range r.commands {
34-
command.cmd.SysProcAttr = &syscall.SysProcAttr{
35-
AmbientCaps: command.ambientCaps,
36-
}
37-
38-
output, err := command.cmd.CombinedOutput()
39-
if err != nil {
40-
return fmt.Errorf("failed to %s: %v, output: %s", command.description, err, output)
41-
}
42-
}
43-
44-
return nil
45-
}
46-
47-
func (l *LinuxJail) configureParentNetworkingStep1() error {
48-
// Create veth pair with short names (Linux interface names limited to 15 chars)
49-
// Generate unique ID to avoid conflicts
50-
uniqueID := fmt.Sprintf("%d", time.Now().UnixNano()%10000000) // 7 digits max
51-
vethHostName := fmt.Sprintf("veth_h_%s", uniqueID) // veth_h_1234567 = 14 chars
52-
vethJailName := fmt.Sprintf("veth_n_%s", uniqueID) // veth_n_1234567 = 14 chars
53-
54-
// Store veth interface name for iptables rules
55-
l.vethHostName = vethHostName
56-
l.vethJailName = vethJailName
57-
58-
runner := newCommandRunner([]*command{
59-
{
60-
"create veth pair",
61-
exec.Command("ip", "link", "add", vethHostName, "type", "veth", "peer", "name", vethJailName),
62-
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
63-
},
64-
{
65-
"configure host veth",
66-
exec.Command("ip", "addr", "add", "192.168.100.1/24", "dev", vethHostName),
67-
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
68-
},
69-
{
70-
"bring up host veth",
71-
exec.Command("ip", "link", "set", vethHostName, "up"),
72-
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
73-
},
74-
})
75-
if err := runner.run(); err != nil {
76-
return err
77-
}
78-
79-
return nil
80-
}
81-
82-
// setupNetworking configures networking within the namespace
83-
func (l *LinuxJail) configureParentNetworkingStep2(pidInt int) error {
84-
PID := fmt.Sprintf("%v", pidInt)
85-
86-
runner := newCommandRunner([]*command{
87-
{
88-
"move veth to namespace",
89-
exec.Command("ip", "link", "set", l.vethJailName, "netns", PID),
90-
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
91-
},
92-
})
93-
if err := runner.run(); err != nil {
94-
return err
95-
}
96-
97-
return nil
98-
}
99-
100-
// setupChildNetworking configures networking within the namespace
101-
func SetupChildNetworking(vethNetJail string) error {
102-
runner := newCommandRunner([]*command{
103-
{
104-
"configure namespace veth",
105-
exec.Command("ip", "addr", "add", "192.168.100.2/24", "dev", vethNetJail),
106-
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
107-
},
108-
{
109-
"bring up namespace veth",
110-
exec.Command("ip", "link", "set", vethNetJail, "up"),
111-
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
112-
},
113-
{
114-
"bring up loopback",
115-
exec.Command("ip", "link", "set", "lo", "up"),
116-
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
117-
},
118-
{
119-
"set default route in namespace",
120-
exec.Command("ip", "route", "add", "default", "via", "192.168.100.1"),
121-
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
122-
},
123-
})
124-
if err := runner.run(); err != nil {
125-
return err
126-
}
127-
128-
return nil
129-
}
130-
13116
// LinuxJail implements Jailer using Linux network namespaces
13217
type LinuxJail struct {
13318
logger *slog.Logger
@@ -167,7 +52,7 @@ func (l *LinuxJail) ConfigureBeforeCommandExecution() error {
16752

16853
l.commandEnv = getEnvs(l.configDir, l.caCertPath)
16954

170-
if err := l.configureParentNetworkingStep1(); err != nil {
55+
if err := l.configureHostNetworkBeforeCmdExec(); err != nil {
17156
return err
17257
}
17358
if err := l.configureIptables(); err != nil {
@@ -202,7 +87,7 @@ func (l *LinuxJail) Command(command []string) *exec.Cmd {
20287
// With the child PID known, it moves the jail-side veth into the child’s network
20388
// namespace.
20489
func (l *LinuxJail) ConfigureAfterCommandExecution(pidInt int) error {
205-
err := l.configureParentNetworkingStep2(pidInt)
90+
err := l.configureHostNetworkAfterCmdExec(pidInt)
20691
if err != nil {
20792
return fmt.Errorf("failed to configure parent networking: %v", err)
20893
}
@@ -354,3 +239,126 @@ func (l *LinuxJail) removeNamespace() error {
354239
}
355240
return nil
356241
}
242+
243+
type command struct {
244+
description string
245+
cmd *exec.Cmd
246+
ambientCaps []uintptr
247+
}
248+
249+
type commandRunner struct {
250+
commands []*command
251+
}
252+
253+
func newCommandRunner(commands []*command) *commandRunner {
254+
return &commandRunner{
255+
commands: commands,
256+
}
257+
}
258+
259+
func (r *commandRunner) run() error {
260+
for _, command := range r.commands {
261+
command.cmd.SysProcAttr = &syscall.SysProcAttr{
262+
AmbientCaps: command.ambientCaps,
263+
}
264+
265+
output, err := command.cmd.CombinedOutput()
266+
if err != nil {
267+
return fmt.Errorf("failed to %s: %v, output: %s", command.description, err, output)
268+
}
269+
}
270+
271+
return nil
272+
}
273+
274+
// configureHostNetworkBeforeCmdExec prepares host-side networking before the target
275+
// process is started. At this point the target process is not running, so its PID and network
276+
// namespace ID are not yet known.
277+
func (l *LinuxJail) configureHostNetworkBeforeCmdExec() error {
278+
// Create veth pair with short names (Linux interface names limited to 15 chars)
279+
// Generate unique ID to avoid conflicts
280+
uniqueID := fmt.Sprintf("%d", time.Now().UnixNano()%10000000) // 7 digits max
281+
vethHostName := fmt.Sprintf("veth_h_%s", uniqueID) // veth_h_1234567 = 14 chars
282+
vethJailName := fmt.Sprintf("veth_n_%s", uniqueID) // veth_n_1234567 = 14 chars
283+
284+
// Store veth interface name for iptables rules
285+
l.vethHostName = vethHostName
286+
l.vethJailName = vethJailName
287+
288+
runner := newCommandRunner([]*command{
289+
{
290+
"create veth pair",
291+
exec.Command("ip", "link", "add", vethHostName, "type", "veth", "peer", "name", vethJailName),
292+
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
293+
},
294+
{
295+
"configure host veth",
296+
exec.Command("ip", "addr", "add", "192.168.100.1/24", "dev", vethHostName),
297+
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
298+
},
299+
{
300+
"bring up host veth",
301+
exec.Command("ip", "link", "set", vethHostName, "up"),
302+
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
303+
},
304+
})
305+
if err := runner.run(); err != nil {
306+
return err
307+
}
308+
309+
return nil
310+
}
311+
312+
// configureHostNetworkAfterCmdExec finalizes host-side networking after the target
313+
// process has started. It moves the jail-side veth into the target process's network
314+
// namespace using the provided PID. This requires the process to be running so
315+
// its PID (and thus its netns) are available.
316+
func (l *LinuxJail) configureHostNetworkAfterCmdExec(pidInt int) error {
317+
PID := fmt.Sprintf("%v", pidInt)
318+
319+
runner := newCommandRunner([]*command{
320+
{
321+
"move veth to namespace",
322+
exec.Command("ip", "link", "set", l.vethJailName, "netns", PID),
323+
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
324+
},
325+
})
326+
if err := runner.run(); err != nil {
327+
return err
328+
}
329+
330+
return nil
331+
}
332+
333+
// SetupChildNetworking configures networking within the target process's network
334+
// namespace. This runs inside the child process after it has been
335+
// created and moved to its own network namespace.
336+
func SetupChildNetworking(vethNetJail string) error {
337+
runner := newCommandRunner([]*command{
338+
{
339+
"configure namespace veth",
340+
exec.Command("ip", "addr", "add", "192.168.100.2/24", "dev", vethNetJail),
341+
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
342+
},
343+
{
344+
"bring up namespace veth",
345+
exec.Command("ip", "link", "set", vethNetJail, "up"),
346+
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
347+
},
348+
{
349+
"bring up loopback",
350+
exec.Command("ip", "link", "set", "lo", "up"),
351+
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
352+
},
353+
{
354+
"set default route in namespace",
355+
exec.Command("ip", "route", "add", "default", "via", "192.168.100.1"),
356+
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
357+
},
358+
})
359+
if err := runner.run(); err != nil {
360+
return err
361+
}
362+
363+
return nil
364+
}

0 commit comments

Comments
 (0)