Skip to content

Commit bff1cd7

Browse files
Lowering perms to CAP_NET_ADMIN
1 parent 55a44f2 commit bff1cd7

File tree

6 files changed

+171
-45
lines changed

6 files changed

+171
-45
lines changed

boundary.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ func (b *Boundary) Command(command []string) *exec.Cmd {
7878
return b.jailer.Command(command)
7979
}
8080

81+
func (b *Boundary) ConfigureAfterRun(processPID int) {
82+
b.jailer.ConfigureAfterRun(processPID)
83+
}
84+
8185
func (b *Boundary) Close() error {
8286
// Stop proxy server
8387
if b.proxyServer != nil {

cli/cli.go

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cli
33
import (
44
"context"
55
"fmt"
6+
"log"
67
"log/slog"
78
"os"
89
"os/signal"
@@ -94,6 +95,36 @@ func BaseCommand() *serpent.Command {
9495

9596
// Run executes the boundary command with the given configuration and arguments
9697
func Run(ctx context.Context, config Config, args []string) error {
98+
isChild := os.Getenv("CHILD") == "true"
99+
if isChild {
100+
fmt.Printf("CHILD is started\n")
101+
fmt.Printf("%v ||| %v\n", args[0], args[1:])
102+
//fmt.Printf("%v\n", os.Environ())
103+
time.Sleep(time.Second * 3) // wait for parent to configure env
104+
105+
// TODO: uncomment
106+
vethNetJail := "veth_n_1111111"
107+
err := jail.SetupChildNetworking(vethNetJail)
108+
if err != nil {
109+
fmt.Fprintf(os.Stderr, "failed setupChildNetworking: %v\n", err)
110+
os.Exit(1)
111+
}
112+
113+
// Program to run
114+
bin := args[0]
115+
args = args[1:]
116+
env := os.Environ()
117+
// syscall.Exec replaces the current process image
118+
// with the new program, so nothing after this call runs.
119+
if err := syscall.Exec(bin, args, env); err != nil {
120+
log.Fatalf("exec failed: %v", err)
121+
}
122+
123+
// This line is never reached if Exec succeeds.
124+
log.Println("done")
125+
return nil
126+
}
127+
97128
ctx, cancel := context.WithCancel(ctx)
98129
defer cancel()
99130

@@ -191,13 +222,21 @@ func Run(ctx context.Context, config Config, args []string) error {
191222
// Execute command in boundary
192223
go func() {
193224
defer cancel()
194-
cmd := boundaryInstance.Command(args)
225+
cmd := boundaryInstance.Command(os.Args)
226+
cmd.Env = append(cmd.Env, "CHILD=true")
195227
cmd.Stderr = os.Stderr
196228
cmd.Stdout = os.Stdout
197229
cmd.Stdin = os.Stdin
198230

199-
logger.Debug("Executing command in boundary", "command", strings.Join(args, " "))
200-
err := cmd.Run()
231+
logger.Debug("Executing command in boundary", "command", strings.Join(os.Args, " "))
232+
err := cmd.Start()
233+
if err != nil {
234+
logger.Error("Command execution failed", "error", err)
235+
}
236+
237+
boundaryInstance.ConfigureAfterRun(cmd.Process.Pid)
238+
239+
err = cmd.Wait()
201240
if err != nil {
202241
logger.Error("Command execution failed", "error", err)
203242
}

jail/jail.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
type Jailer interface {
1111
Start() error
1212
Command(command []string) *exec.Cmd
13+
ConfigureAfterRun(processPID int)
1314
Close() error
1415
}
1516

jail/linux.go

Lines changed: 120 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import (
77
"log/slog"
88
"os"
99
"os/exec"
10+
"syscall"
1011
"time"
12+
13+
"golang.org/x/sys/unix"
1114
)
1215

1316
// LinuxJail implements Jailer using Linux network namespaces
@@ -46,47 +49,48 @@ func (l *LinuxJail) Start() error {
4649
e := getEnvs(l.configDir, l.caCertPath)
4750
l.commandEnv = mergeEnvs(e, map[string]string{})
4851

49-
// Setup DNS configuration BEFORE creating namespace
50-
// This ensures the namespace-specific resolv.conf is available when namespace is created
51-
err := l.setupDNS()
52-
if err != nil {
53-
return fmt.Errorf("failed to setup DNS: %v", err)
54-
}
55-
56-
// Create namespace
57-
err = l.createNamespace()
58-
if err != nil {
59-
return fmt.Errorf("failed to create namespace: %v", err)
60-
}
61-
62-
// Setup networking within namespace
63-
err = l.setupNetworking()
64-
if err != nil {
65-
return fmt.Errorf("failed to setup networking: %v", err)
66-
}
67-
68-
// Setup iptables rules on host
69-
err = l.setupIptables()
70-
if err != nil {
71-
return fmt.Errorf("failed to setup iptables: %v", err)
72-
}
73-
7452
return nil
7553
}
7654

7755
// Command returns an exec.Cmd configured to run within the network namespace
7856
func (l *LinuxJail) Command(command []string) *exec.Cmd {
7957
l.logger.Debug("Creating command with namespace", "namespace", l.namespace)
8058

81-
cmdArgs := []string{"netns", "exec", l.namespace}
82-
cmdArgs = append(cmdArgs, command...)
83-
84-
cmd := exec.Command("ip", cmdArgs...)
59+
//cmdArgs := []string{"netns", "exec", l.namespace}
60+
//cmdArgs = append(cmdArgs, command...)
61+
//
62+
//cmd := exec.Command("ip", cmdArgs...)
63+
cmd := exec.Command(command[0], command[1:]...)
8564
cmd.Env = l.commandEnv
65+
cmd.Env = append(cmd.Env, "CHILD=true")
66+
67+
cmd.SysProcAttr = &syscall.SysProcAttr{
68+
Cloneflags: syscall.CLONE_NEWUSER | syscall.CLONE_NEWNET,
69+
UidMappings: []syscall.SysProcIDMap{
70+
{ContainerID: 0, HostID: os.Getuid(), Size: 1},
71+
},
72+
GidMappings: []syscall.SysProcIDMap{
73+
{ContainerID: 0, HostID: os.Getgid(), Size: 1},
74+
},
75+
}
8676

8777
return cmd
8878
}
8979

80+
func (l *LinuxJail) ConfigureAfterRun(pidInt int) {
81+
err := l.setupParentNetworking(pidInt)
82+
if err != nil {
83+
fmt.Fprintf(os.Stderr, "failed setupParentNetworking: %v\n", err)
84+
os.Exit(1)
85+
}
86+
87+
err = l.setupIptables()
88+
if err != nil {
89+
fmt.Fprintf(os.Stderr, "can't setup iptables: %v\n", err)
90+
os.Exit(1)
91+
}
92+
}
93+
9094
// Close removes the network namespace and iptables rules
9195
func (l *LinuxJail) Close() error {
9296
l.logger.Debug("Close called")
@@ -133,31 +137,51 @@ func (l *LinuxJail) createNamespace() error {
133137
}
134138

135139
// setupNetworking configures networking within the namespace
136-
func (l *LinuxJail) setupNetworking() error {
140+
func (l *LinuxJail) setupParentNetworking(pidInt int) error {
141+
PID := fmt.Sprintf("%v", pidInt)
142+
137143
// Create veth pair with short names (Linux interface names limited to 15 chars)
138144
// Generate unique ID to avoid conflicts
139145
uniqueID := fmt.Sprintf("%d", time.Now().UnixNano()%10000000) // 7 digits max
140-
vethHost := fmt.Sprintf("veth_h_%s", uniqueID) // veth_h_1234567 = 14 chars
141-
vethNetJail := fmt.Sprintf("veth_n_%s", uniqueID) // veth_n_1234567 = 14 chars
146+
uniqueID = "1111111"
147+
vethHost := fmt.Sprintf("veth_h_%s", uniqueID) // veth_h_1234567 = 14 chars
148+
vethNetJail := fmt.Sprintf("veth_n_%s", uniqueID) // veth_n_1234567 = 14 chars
142149

143150
// Store veth interface name for iptables rules
144151
l.vethHost = vethHost
145152

146153
setupCmds := []struct {
147154
description string
148155
command *exec.Cmd
156+
ambientCaps []uintptr
149157
}{
150-
{"create veth pair", exec.Command("ip", "link", "add", vethHost, "type", "veth", "peer", "name", vethNetJail)},
151-
{"move veth to namespace", exec.Command("ip", "link", "set", vethNetJail, "netns", l.namespace)},
152-
{"configure host veth", exec.Command("ip", "addr", "add", "192.168.100.1/24", "dev", vethHost)},
153-
{"bring up host veth", exec.Command("ip", "link", "set", vethHost, "up")},
154-
{"configure namespace veth", exec.Command("ip", "netns", "exec", l.namespace, "ip", "addr", "add", "192.168.100.2/24", "dev", vethNetJail)},
155-
{"bring up namespace veth", exec.Command("ip", "netns", "exec", l.namespace, "ip", "link", "set", vethNetJail, "up")},
156-
{"bring up loopback", exec.Command("ip", "netns", "exec", l.namespace, "ip", "link", "set", "lo", "up")},
157-
{"set default route in namespace", exec.Command("ip", "netns", "exec", l.namespace, "ip", "route", "add", "default", "via", "192.168.100.1")},
158+
{
159+
"create veth pair",
160+
exec.Command("ip", "link", "add", vethHost, "type", "veth", "peer", "name", vethNetJail),
161+
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
162+
},
163+
{
164+
"move veth to namespace",
165+
exec.Command("ip", "link", "set", vethNetJail, "netns", PID),
166+
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
167+
},
168+
{
169+
"configure host veth",
170+
exec.Command("ip", "addr", "add", "192.168.100.1/24", "dev", vethHost),
171+
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
172+
},
173+
{
174+
"bring up host veth",
175+
exec.Command("ip", "link", "set", vethHost, "up"),
176+
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
177+
},
158178
}
159179

160180
for _, command := range setupCmds {
181+
command.command.SysProcAttr = &syscall.SysProcAttr{
182+
AmbientCaps: command.ambientCaps,
183+
}
184+
161185
if err := command.command.Run(); err != nil {
162186
return fmt.Errorf("failed to %s: %v", command.description, err)
163187
}
@@ -166,6 +190,48 @@ func (l *LinuxJail) setupNetworking() error {
166190
return nil
167191
}
168192

193+
// setupChildNetworking configures networking within the namespace
194+
func SetupChildNetworking(vethNetJail string) error {
195+
setupCmds := []struct {
196+
description string
197+
command *exec.Cmd
198+
ambientCaps []uintptr
199+
}{
200+
{
201+
"configure namespace veth",
202+
exec.Command("ip", "addr", "add", "192.168.100.2/24", "dev", vethNetJail),
203+
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
204+
},
205+
{
206+
"bring up namespace veth",
207+
exec.Command("ip", "link", "set", vethNetJail, "up"),
208+
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
209+
},
210+
{
211+
"bring up loopback",
212+
exec.Command("ip", "link", "set", "lo", "up"),
213+
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
214+
},
215+
{
216+
"set default route in namespace",
217+
exec.Command("ip", "route", "add", "default", "via", "192.168.100.1"),
218+
[]uintptr{uintptr(unix.CAP_NET_ADMIN)},
219+
},
220+
}
221+
222+
for _, command := range setupCmds {
223+
command.command.SysProcAttr = &syscall.SysProcAttr{
224+
AmbientCaps: command.ambientCaps,
225+
}
226+
227+
if output, err := command.command.CombinedOutput(); err != nil {
228+
return fmt.Errorf("failed to %s: %v, output: %s, args: %v", command.description, err, output, command.command.Args)
229+
}
230+
}
231+
232+
return nil
233+
}
234+
169235
// setupDNS configures DNS resolution for the namespace
170236
// This ensures reliable DNS resolution by using public DNS servers
171237
// instead of relying on the host's potentially complex DNS configuration
@@ -204,6 +270,9 @@ func (l *LinuxJail) setupIptables() error {
204270

205271
// NAT rules for outgoing traffic (MASQUERADE for return traffic)
206272
cmd = exec.Command("iptables", "-t", "nat", "-A", "POSTROUTING", "-s", "192.168.100.0/24", "-j", "MASQUERADE")
273+
cmd.SysProcAttr = &syscall.SysProcAttr{
274+
AmbientCaps: []uintptr{uintptr(unix.CAP_NET_ADMIN)},
275+
}
207276
err := cmd.Run()
208277
if err != nil {
209278
return fmt.Errorf("failed to add NAT rule: %v", err)
@@ -212,22 +281,31 @@ func (l *LinuxJail) setupIptables() error {
212281
// COMPREHENSIVE APPROACH: Route ALL TCP traffic to HTTP proxy
213282
// The HTTP proxy will intelligently handle both HTTP and TLS traffic
214283
cmd = exec.Command("iptables", "-t", "nat", "-A", "PREROUTING", "-i", l.vethHost, "-p", "tcp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", l.httpProxyPort))
284+
cmd.SysProcAttr = &syscall.SysProcAttr{
285+
AmbientCaps: []uintptr{uintptr(unix.CAP_NET_ADMIN)},
286+
}
215287
err = cmd.Run()
216288
if err != nil {
217289
return fmt.Errorf("failed to add comprehensive TCP redirect rule: %v", err)
218290
}
219291

220292
// TODO: clean up this rules
221293
cmd = exec.Command("iptables", "-A", "FORWARD", "-s", "192.168.100.0/24", "-j", "ACCEPT")
294+
cmd.SysProcAttr = &syscall.SysProcAttr{
295+
AmbientCaps: []uintptr{uintptr(unix.CAP_NET_ADMIN)},
296+
}
222297
err = cmd.Run()
223298
if err != nil {
224-
return err
299+
return fmt.Errorf("forward -s error: %v, output: %v")
225300
}
226301

227302
cmd = exec.Command("iptables", "-A", "FORWARD", "-d", "192.168.100.0/24", "-j", "ACCEPT")
303+
cmd.SysProcAttr = &syscall.SysProcAttr{
304+
AmbientCaps: []uintptr{uintptr(unix.CAP_NET_ADMIN)},
305+
}
228306
err = cmd.Run()
229307
if err != nil {
230-
return err
308+
return fmt.Errorf("forward -r error: %v", err)
231309
}
232310

233311
l.logger.Debug("Comprehensive TCP boundarying enabled", "interface", l.vethHost, "proxy_port", l.httpProxyPort)

jail/macos.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,3 +340,5 @@ func (n *MacOSJail) cleanupTempFiles() {
340340
}
341341
}
342342
}
343+
344+
func (u *MacOSJail) ConfigureAfterRun(processPID int) {}

jail/unprivileged.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,5 @@ func (u *Unprivileged) Close() error {
6060
u.logger.Debug("Closing unprivileged jail")
6161
return nil
6262
}
63+
64+
func (u *Unprivileged) ConfigureAfterRun(processPID int) {}

0 commit comments

Comments
 (0)