Skip to content

Commit 1c23341

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

File tree

6 files changed

+133
-87
lines changed

6 files changed

+133
-87
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) ConfigureChildProcess(pid int) error {
82+
return b.jailer.ConfigureChildProcess(pid)
83+
}
84+
8185
func (b *Boundary) Close() error {
8286
// Stop proxy server
8387
if b.proxyServer != nil {

cli/cli.go

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -188,21 +188,34 @@ func Run(ctx context.Context, config Config, args []string) error {
188188
}
189189
}()
190190

191+
var pid int
191192
// Execute command in boundary
193+
cmd := boundaryInstance.Command(args)
194+
cmd.Stderr = os.Stderr
195+
cmd.Stdout = os.Stdout
196+
cmd.Stdin = os.Stdin
197+
198+
logger.Debug("Executing command in boundary", "command", strings.Join(args, " "))
199+
err = cmd.Start()
200+
if err != nil {
201+
logger.Error("Command execution failed", "error", err)
202+
return err
203+
}
204+
205+
pid = cmd.Process.Pid
192206
go func() {
193207
defer cancel()
194-
cmd := boundaryInstance.Command(args)
195-
cmd.Stderr = os.Stderr
196-
cmd.Stdout = os.Stdout
197-
cmd.Stdin = os.Stdin
198-
199-
logger.Debug("Executing command in boundary", "command", strings.Join(args, " "))
200-
err := cmd.Run()
201-
if err != nil {
208+
if err = cmd.Wait(); err != nil {
202209
logger.Error("Command execution failed", "error", err)
210+
return
203211
}
204212
}()
205213

214+
err = boundaryInstance.ConfigureChildProcess(pid)
215+
if err != nil {
216+
return err
217+
}
218+
206219
// Wait for signal or context cancellation
207220
select {
208221
case sig := <-sigChan:

jail/jail.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type Jailer interface {
1111
Start() error
1212
Command(command []string) *exec.Cmd
1313
Close() error
14+
ConfigureChildProcess(pid int) error
1415
}
1516

1617
type Config struct {

jail/linux.go

Lines changed: 99 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ import (
77
"log/slog"
88
"os"
99
"os/exec"
10+
"syscall"
1011
"time"
1112
)
1213

1314
// LinuxJail implements Jailer using Linux network namespaces
1415
type LinuxJail struct {
15-
logger *slog.Logger
16-
namespace string
16+
logger *slog.Logger
17+
//namespace string
1718
vethHost string // Host-side veth interface name for iptables rules
1819
commandEnv []string
1920
httpProxyPort int
@@ -27,8 +28,8 @@ type LinuxJail struct {
2728

2829
func NewLinuxJail(config Config) (*LinuxJail, error) {
2930
return &LinuxJail{
30-
logger: config.Logger,
31-
namespace: newNamespaceName(),
31+
logger: config.Logger,
32+
//namespace: newNamespaceName(),
3233
httpProxyPort: config.HttpProxyPort,
3334
configDir: config.ConfigDir,
3435
caCertPath: config.CACertPath,
@@ -46,21 +47,25 @@ func (l *LinuxJail) Start() error {
4647
e := getEnvs(l.configDir, l.caCertPath)
4748
l.commandEnv = mergeEnvs(e, map[string]string{})
4849

50+
return nil
51+
}
52+
53+
func (l *LinuxJail) ConfigureChildProcess(pid int) error {
4954
// Setup DNS configuration BEFORE creating namespace
5055
// 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-
}
56+
//err := l.setupDNS()
57+
//if err != nil {
58+
// return fmt.Errorf("failed to setup DNS: %v", err)
59+
//}
5560

5661
// Create namespace
57-
err = l.createNamespace()
58-
if err != nil {
59-
return fmt.Errorf("failed to create namespace: %v", err)
60-
}
62+
//err = l.createNamespace()
63+
//if err != nil {
64+
// return fmt.Errorf("failed to create namespace: %v", err)
65+
//}
6166

6267
// Setup networking within namespace
63-
err = l.setupNetworking()
68+
err := l.setupNetworking(pid)
6469
if err != nil {
6570
return fmt.Errorf("failed to setup networking: %v", err)
6671
}
@@ -76,12 +81,25 @@ func (l *LinuxJail) Start() error {
7681

7782
// Command returns an exec.Cmd configured to run within the network namespace
7883
func (l *LinuxJail) Command(command []string) *exec.Cmd {
79-
l.logger.Debug("Creating command with namespace", "namespace", l.namespace)
80-
81-
cmdArgs := []string{"netns", "exec", l.namespace}
82-
cmdArgs = append(cmdArgs, command...)
83-
84-
cmd := exec.Command("ip", cmdArgs...)
84+
l.logger.Debug("Creating command with namespace")
85+
//l.logger.Debug("Creating command with namespace", "namespace", l.namespace)
86+
87+
//cmdArgs := []string{"netns", "exec", l.namespace}
88+
//cmdArgs = append(cmdArgs, command...)
89+
//
90+
//cmd := exec.Command("ip", cmdArgs...)
91+
//cmd.Env = l.commandEnv
92+
93+
cmd := exec.Command(command[0], command[1:]...)
94+
cmd.SysProcAttr = &syscall.SysProcAttr{
95+
Cloneflags: syscall.CLONE_NEWUSER | syscall.CLONE_NEWNET,
96+
UidMappings: []syscall.SysProcIDMap{
97+
{ContainerID: 0, HostID: os.Getuid(), Size: 1},
98+
},
99+
GidMappings: []syscall.SysProcIDMap{
100+
{ContainerID: 0, HostID: os.Getgid(), Size: 1},
101+
},
102+
}
85103
cmd.Env = l.commandEnv
86104

87105
return cmd
@@ -106,34 +124,36 @@ func (l *LinuxJail) Close() error {
106124
}
107125

108126
// Clean up namespace-specific DNS config directory
109-
netnsEtc := fmt.Sprintf("/etc/netns/%s", l.namespace)
110-
err = os.RemoveAll(netnsEtc)
111-
if err != nil {
112-
l.logger.Warn("Failed to remove namespace DNS config", "dir", netnsEtc, "error", err)
113-
// Continue with other cleanup
114-
}
127+
//netnsEtc := fmt.Sprintf("/etc/netns/%s", l.namespace)
128+
//err = os.RemoveAll(netnsEtc)
129+
//if err != nil {
130+
// l.logger.Warn("Failed to remove namespace DNS config", "dir", netnsEtc, "error", err)
131+
// // Continue with other cleanup
132+
//}
115133

116134
// Remove network namespace
117-
err = l.removeNamespace()
118-
if err != nil {
119-
return fmt.Errorf("failed to remove namespace: %v", err)
120-
}
135+
//err = l.removeNamespace()
136+
//if err != nil {
137+
// return fmt.Errorf("failed to remove namespace: %v", err)
138+
//}
121139

122140
return nil
123141
}
124142

125143
// createNamespace creates a new network namespace
126-
func (l *LinuxJail) createNamespace() error {
127-
cmd := exec.Command("ip", "netns", "add", l.namespace)
128-
err := cmd.Run()
129-
if err != nil {
130-
return fmt.Errorf("failed to create namespace: %v", err)
131-
}
132-
return nil
133-
}
144+
//func (l *LinuxJail) createNamespace() error {
145+
// cmd := exec.Command("ip", "netns", "add", l.namespace)
146+
// err := cmd.Run()
147+
// if err != nil {
148+
// return fmt.Errorf("failed to create namespace: %v", err)
149+
// }
150+
// return nil
151+
//}
134152

135153
// setupNetworking configures networking within the namespace
136-
func (l *LinuxJail) setupNetworking() error {
154+
func (l *LinuxJail) setupNetworking(pidInt int) error {
155+
PID := fmt.Sprintf("%v", pidInt)
156+
137157
// Create veth pair with short names (Linux interface names limited to 15 chars)
138158
// Generate unique ID to avoid conflicts
139159
uniqueID := fmt.Sprintf("%d", time.Now().UnixNano()%10000000) // 7 digits max
@@ -148,18 +168,18 @@ func (l *LinuxJail) setupNetworking() error {
148168
command *exec.Cmd
149169
}{
150170
{"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)},
171+
{"move veth to namespace", exec.Command("ip", "link", "set", vethNetJail, "netns", PID)},
152172
{"configure host veth", exec.Command("ip", "addr", "add", "192.168.100.1/24", "dev", vethHost)},
153173
{"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")},
174+
{"configure namespace veth", exec.Command("nsenter", "-t", PID, "-n", "--", "ip", "addr", "add", "192.168.100.2/24", "dev", vethNetJail)},
175+
{"bring up namespace veth", exec.Command("nsenter", "-t", PID, "-n", "--", "ip", "link", "set", vethNetJail, "up")},
176+
{"bring up loopback", exec.Command("nsenter", "-t", PID, "-n", "--", "ip", "link", "set", "lo", "up")},
177+
{"set default route in namespace", exec.Command("nsenter", "-t", PID, "-n", "--", "ip", "route", "add", "default", "via", "192.168.100.1")},
158178
}
159179

160180
for _, command := range setupCmds {
161-
if err := command.command.Run(); err != nil {
162-
return fmt.Errorf("failed to %s: %v", command.description, err)
181+
if output, err := command.command.CombinedOutput(); err != nil {
182+
return fmt.Errorf("failed to %s: %v, output: %s, args: %v", command.description, err, output, command.command.Args)
163183
}
164184
}
165185

@@ -169,32 +189,32 @@ func (l *LinuxJail) setupNetworking() error {
169189
// setupDNS configures DNS resolution for the namespace
170190
// This ensures reliable DNS resolution by using public DNS servers
171191
// instead of relying on the host's potentially complex DNS configuration
172-
func (l *LinuxJail) setupDNS() error {
173-
// Always create namespace-specific resolv.conf with reliable public DNS servers
174-
// This avoids issues with systemd-resolved, Docker DNS, and other complex setups
175-
netnsEtc := fmt.Sprintf("/etc/netns/%s", l.namespace)
176-
err := os.MkdirAll(netnsEtc, 0755)
177-
if err != nil {
178-
return fmt.Errorf("failed to create /etc/netns directory: %v", err)
179-
}
180-
181-
// Write custom resolv.conf with multiple reliable public DNS servers
182-
resolvConfPath := fmt.Sprintf("%s/resolv.conf", netnsEtc)
183-
dnsConfig := `# Custom DNS for network namespace
184-
nameserver 8.8.8.8
185-
nameserver 8.8.4.4
186-
nameserver 1.1.1.1
187-
nameserver 9.9.9.9
188-
options timeout:2 attempts:2
189-
`
190-
err = os.WriteFile(resolvConfPath, []byte(dnsConfig), 0644)
191-
if err != nil {
192-
return fmt.Errorf("failed to write namespace-specific resolv.conf: %v", err)
193-
}
194-
195-
l.logger.Debug("DNS setup completed")
196-
return nil
197-
}
192+
//func (l *LinuxJail) setupDNS() error {
193+
// // Always create namespace-specific resolv.conf with reliable public DNS servers
194+
// // This avoids issues with systemd-resolved, Docker DNS, and other complex setups
195+
// netnsEtc := fmt.Sprintf("/etc/netns/%s", l.namespace)
196+
// err := os.MkdirAll(netnsEtc, 0755)
197+
// if err != nil {
198+
// return fmt.Errorf("failed to create /etc/netns directory: %v", err)
199+
// }
200+
//
201+
// // Write custom resolv.conf with multiple reliable public DNS servers
202+
// resolvConfPath := fmt.Sprintf("%s/resolv.conf", netnsEtc)
203+
// dnsConfig := `# Custom DNS for network namespace
204+
//nameserver 8.8.8.8
205+
//nameserver 8.8.4.4
206+
//nameserver 1.1.1.1
207+
//nameserver 9.9.9.9
208+
//options timeout:2 attempts:2
209+
//`
210+
// err = os.WriteFile(resolvConfPath, []byte(dnsConfig), 0644)
211+
// if err != nil {
212+
// return fmt.Errorf("failed to write namespace-specific resolv.conf: %v", err)
213+
// }
214+
//
215+
// l.logger.Debug("DNS setup completed")
216+
// return nil
217+
//}
198218

199219
// setupIptables configures iptables rules for comprehensive TCP traffic interception
200220
func (l *LinuxJail) setupIptables() error {
@@ -279,11 +299,11 @@ func (l *LinuxJail) cleanupNetworking() error {
279299
}
280300

281301
// removeNamespace removes the network namespace
282-
func (l *LinuxJail) removeNamespace() error {
283-
cmd := exec.Command("ip", "netns", "del", l.namespace)
284-
err := cmd.Run()
285-
if err != nil {
286-
return fmt.Errorf("failed to remove namespace: %v", err)
287-
}
288-
return nil
289-
}
302+
//func (l *LinuxJail) removeNamespace() error {
303+
// cmd := exec.Command("ip", "netns", "del", l.namespace)
304+
// err := cmd.Run()
305+
// if err != nil {
306+
// return fmt.Errorf("failed to remove namespace: %v", err)
307+
// }
308+
// return nil
309+
//}

jail/macos.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ func (n *MacOSJail) Start() error {
9999
return nil
100100
}
101101

102+
func (l *MacOSJail) ConfigureChildProcess(pid int) error {
103+
return nil
104+
}
105+
102106
// Command runs the command with the network boundary group membership
103107
func (n *MacOSJail) Command(command []string) *exec.Cmd {
104108
n.logger.Debug("Command called", "command", command)

jail/unprivileged.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ func (u *Unprivileged) Start() error {
4747
return nil
4848
}
4949

50+
func (l *Unprivileged) ConfigureChildProcess(pid int) error {
51+
return nil
52+
}
53+
5054
func (u *Unprivileged) Command(command []string) *exec.Cmd {
5155
u.logger.Debug("Creating unprivileged command", "command", command)
5256

0 commit comments

Comments
 (0)