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
21 changes: 14 additions & 7 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ func Run(config Config, args []string) error {

// Write CA certificate to a temporary file for tools that need a file path
caCertPath := filepath.Join(configDir, "ca-cert.pem")
if err := os.WriteFile(caCertPath, caCertPEM, 0644); err != nil {
err = os.WriteFile(caCertPath, caCertPEM, 0644)
if err != nil {
logger.Error("Failed to write CA certificate file", "error", err)
return fmt.Errorf("failed to write CA certificate file: %v", err)
}
Expand Down Expand Up @@ -205,7 +206,8 @@ func Run(config Config, args []string) error {
go func() {
sig := <-sigChan
logger.Info("Received signal during setup, cleaning up...", "signal", sig)
if err := networkInstance.Cleanup(); err != nil {
err := networkInstance.Cleanup()
if err != nil {
logger.Error("Emergency cleanup failed", "error", err)
}
os.Exit(1)
Expand All @@ -214,15 +216,17 @@ func Run(config Config, args []string) error {
// Ensure cleanup happens no matter what
defer func() {
logger.Debug("Starting cleanup process")
if err := networkInstance.Cleanup(); err != nil {
err := networkInstance.Cleanup()
if err != nil {
logger.Error("Failed to cleanup network jail", "error", err)
} else {
logger.Debug("Cleanup completed successfully")
}
}()

// Setup network jail
if err := networkInstance.Setup(networkConfig.HTTPPort, networkConfig.HTTPSPort); err != nil {
err = networkInstance.Setup(networkConfig.HTTPPort, networkConfig.HTTPSPort)
if err != nil {
logger.Error("Failed to setup network jail", "error", err)
return fmt.Errorf("failed to setup network jail: %v", err)
}
Expand All @@ -248,7 +252,8 @@ func Run(config Config, args []string) error {

// Start proxy server in background
go func() {
if err := proxyServer.Start(ctx); err != nil {
err := proxyServer.Start(ctx)
if err != nil {
logger.Error("Proxy server error", "error", err)
}
}()
Expand All @@ -259,7 +264,8 @@ func Run(config Config, args []string) error {
// Execute command in network jail
go func() {
defer cancel()
if err := networkInstance.Execute(args, extraEnv); err != nil {
err := networkInstance.Execute(args, extraEnv)
if err != nil {
logger.Error("Command execution failed", "error", err)
}
}()
Expand All @@ -274,7 +280,8 @@ func Run(config Config, args []string) error {
}

// Stop proxy server
if err := proxyServer.Stop(); err != nil {
err = proxyServer.Stop()
if err != nil {
logger.Error("Failed to stop proxy server", "error", err)
}

Expand Down
68 changes: 45 additions & 23 deletions network/linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,28 +45,32 @@ func (l *LinuxJail) Setup(httpPort, httpsPort int) error {
// Setup DNS configuration BEFORE creating namespace
// This ensures the namespace-specific resolv.conf is available when namespace is created
l.logger.Debug("Setting up DNS configuration")
if err := l.setupDNS(); err != nil {
err := l.setupDNS()
if err != nil {
return fmt.Errorf("failed to setup DNS: %v", err)
}
l.logger.Debug("DNS setup completed")

// Create network namespace
l.logger.Debug("Creating network namespace", "namespace", l.namespace)
if err := l.createNamespace(); err != nil {
err = l.createNamespace()
if err != nil {
return fmt.Errorf("failed to create namespace: %v", err)
}
l.logger.Debug("Network namespace created")

// Setup network interface in namespace
l.logger.Debug("Setting up networking")
if err := l.setupNetworking(); err != nil {
err = l.setupNetworking()
if err != nil {
return fmt.Errorf("failed to setup networking: %v", err)
}
l.logger.Debug("Networking setup completed")

// Setup iptables rules
l.logger.Debug("Setting up iptables rules")
if err := l.setupIptables(); err != nil {
err = l.setupIptables()
if err != nil {
return fmt.Errorf("failed to setup iptables: %v", err)
}
l.logger.Debug("Iptables setup completed")
Expand Down Expand Up @@ -175,21 +179,24 @@ func (l *LinuxJail) Cleanup() error {
}

// Remove iptables rules
if err := l.removeIptables(); err != nil {
err := l.removeIptables()
if err != nil {
return fmt.Errorf("failed to remove iptables rules: %v", err)
}

// Clean up namespace-specific DNS config directory
netnsEtc := fmt.Sprintf("/etc/netns/%s", l.namespace)
if _, err := os.Stat(netnsEtc); err == nil {
if err := os.RemoveAll(netnsEtc); err != nil {
err := os.RemoveAll(netnsEtc)
if err != nil {
// Don't fail cleanup for this, just log
fmt.Printf("Warning: failed to remove DNS config directory %s: %v\n", netnsEtc, err)
}
}

// Remove network namespace
if err := l.removeNamespace(); err != nil {
err = l.removeNamespace()
if err != nil {
return fmt.Errorf("failed to remove namespace: %v", err)
}

Expand All @@ -199,7 +206,8 @@ func (l *LinuxJail) Cleanup() error {
// createNamespace creates a new network namespace
func (l *LinuxJail) createNamespace() error {
cmd := exec.Command("ip", "netns", "add", l.namespace)
if err := cmd.Run(); err != nil {
err := cmd.Run()
if err != nil {
return fmt.Errorf("failed to create namespace: %v", err)
}
return nil
Expand All @@ -214,46 +222,54 @@ func (l *LinuxJail) setupNetworking() error {
vethNetJail := fmt.Sprintf("veth_n_%s", uniqueID) // veth_n_1234567 = 14 chars

cmd := exec.Command("ip", "link", "add", vethHost, "type", "veth", "peer", "name", vethNetJail)
if err := cmd.Run(); err != nil {
err := cmd.Run()
if err != nil {
return fmt.Errorf("failed to create veth pair: %v", err)
}

// Move netjail end to namespace
cmd = exec.Command("ip", "link", "set", vethNetJail, "netns", l.namespace)
if err := cmd.Run(); err != nil {
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to move veth to namespace: %v", err)
}

// Configure host side of veth pair
cmd = exec.Command("ip", "addr", "add", "192.168.100.1/24", "dev", vethHost)
if err := cmd.Run(); err != nil {
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to configure host veth: %v", err)
}

cmd = exec.Command("ip", "link", "set", vethHost, "up")
if err := cmd.Run(); err != nil {
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to bring up host veth: %v", err)
}

// Configure namespace side of veth pair
cmd = exec.Command("ip", "netns", "exec", l.namespace, "ip", "addr", "add", "192.168.100.2/24", "dev", vethNetJail)
if err := cmd.Run(); err != nil {
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to configure namespace veth: %v", err)
}

cmd = exec.Command("ip", "netns", "exec", l.namespace, "ip", "link", "set", vethNetJail, "up")
if err := cmd.Run(); err != nil {
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to bring up namespace veth: %v", err)
}

cmd = exec.Command("ip", "netns", "exec", l.namespace, "ip", "link", "set", "lo", "up")
if err := cmd.Run(); err != nil {
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to bring up loopback: %v", err)
}

// Set default route in namespace
cmd = exec.Command("ip", "netns", "exec", l.namespace, "ip", "route", "add", "default", "via", "192.168.100.1")
if err := cmd.Run(); err != nil {
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to set default route: %v", err)
}

Expand All @@ -267,7 +283,8 @@ func (l *LinuxJail) setupDNS() error {
// Always create namespace-specific resolv.conf with reliable public DNS servers
// This avoids issues with systemd-resolved, Docker DNS, and other complex setups
netnsEtc := fmt.Sprintf("/etc/netns/%s", l.namespace)
if err := os.MkdirAll(netnsEtc, 0755); err != nil {
err := os.MkdirAll(netnsEtc, 0755)
if err != nil {
return fmt.Errorf("failed to create /etc/netns directory: %v", err)
}

Expand All @@ -280,7 +297,8 @@ nameserver 1.1.1.1
nameserver 9.9.9.9
options timeout:2 attempts:2
`
if err := os.WriteFile(resolvConfPath, []byte(dnsConfig), 0644); err != nil {
err = os.WriteFile(resolvConfPath, []byte(dnsConfig), 0644)
if err != nil {
return fmt.Errorf("failed to write namespace-specific resolv.conf: %v", err)
}

Expand All @@ -296,21 +314,24 @@ func (l *LinuxJail) setupIptables() error {

// NAT rules for outgoing traffic
cmd = exec.Command("iptables", "-t", "nat", "-A", "POSTROUTING", "-s", "192.168.100.0/24", "-j", "MASQUERADE")
if err := cmd.Run(); err != nil {
err := cmd.Run()
if err != nil {
return fmt.Errorf("failed to add NAT rule: %v", err)
}

// Redirect HTTP traffic to proxy
cmd = exec.Command("ip", "netns", "exec", l.namespace, "iptables", "-t", "nat", "-A", "OUTPUT",
"-p", "tcp", "--dport", "80", "-j", "DNAT", "--to-destination", fmt.Sprintf("192.168.100.1:%d", l.config.HTTPPort))
if err := cmd.Run(); err != nil {
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to add HTTP redirect rule: %v", err)
}

// Redirect HTTPS traffic to proxy
cmd = exec.Command("ip", "netns", "exec", l.namespace, "iptables", "-t", "nat", "-A", "OUTPUT",
"-p", "tcp", "--dport", "443", "-j", "DNAT", "--to-destination", fmt.Sprintf("192.168.100.1:%d", l.config.HTTPSPort))
if err := cmd.Run(); err != nil {
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to add HTTPS redirect rule: %v", err)
}

Expand All @@ -329,8 +350,9 @@ func (l *LinuxJail) removeIptables() error {
// removeNamespace removes the network namespace
func (l *LinuxJail) removeNamespace() error {
cmd := exec.Command("ip", "netns", "del", l.namespace)
if err := cmd.Run(); err != nil {
err := cmd.Run()
if err != nil {
return fmt.Errorf("failed to remove namespace: %v", err)
}
return nil
}
}
28 changes: 18 additions & 10 deletions network/macos.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,24 @@ func newMacOSJail(config JailConfig, logger *slog.Logger) (*MacOSNetJail, error)
}, nil
}

// Setup configures PF rules and creates the network jail group
// Setup creates the network jail group and configures PF rules
func (m *MacOSNetJail) Setup(httpPort, httpsPort int) error {
m.logger.Debug("Setup called", "httpPort", httpPort, "httpsPort", httpsPort)
m.config.HTTPPort = httpPort
m.config.HTTPSPort = httpsPort

// Create or get network jail group
m.logger.Debug("Creating or ensuring network jail group")
if err := m.ensureGroup(); err != nil {
err := m.ensureGroup()
if err != nil {
return fmt.Errorf("failed to ensure group: %v", err)
}
m.logger.Debug("Network jail group ready", "groupID", m.groupID)

// Setup PF rules
m.logger.Debug("Setting up PF rules")
if err := m.setupPFRules(); err != nil {
err = m.setupPFRules()
if err != nil {
return fmt.Errorf("failed to setup PF rules: %v", err)
}
m.logger.Debug("PF rules setup completed")
Expand Down Expand Up @@ -165,7 +167,8 @@ func (m *MacOSNetJail) Cleanup() error {

// Remove PF rules
m.logger.Debug("Removing PF rules")
if err := m.removePFRules(); err != nil {
err := m.removePFRules()
if err != nil {
return fmt.Errorf("failed to remove PF rules: %v", err)
}

Expand Down Expand Up @@ -201,7 +204,8 @@ func (m *MacOSNetJail) ensureGroup() error {

// Group doesn't exist, create it
cmd := exec.Command("dseditgroup", "-o", "create", GROUP_NAME)
if err := cmd.Run(); err != nil {
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to create group: %v", err)
}

Expand Down Expand Up @@ -299,13 +303,15 @@ func (m *MacOSNetJail) setupPFRules() error {
}

// Write rules to temp file
if err := os.WriteFile(m.pfRulesPath, []byte(rules), 0644); err != nil {
err = os.WriteFile(m.pfRulesPath, []byte(rules), 0644)
if err != nil {
return fmt.Errorf("failed to write PF rules file: %v", err)
}

// Load rules into anchor
cmd := exec.Command("pfctl", "-a", PF_ANCHOR_NAME, "-f", m.pfRulesPath)
if err := cmd.Run(); err != nil {
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to load PF rules: %v", err)
}

Expand All @@ -330,12 +336,14 @@ anchor "%s"
`, PF_ANCHOR_NAME, PF_ANCHOR_NAME)

// Write and load the main ruleset
if err := os.WriteFile(m.mainRulesPath, []byte(mainRules), 0644); err != nil {
err = os.WriteFile(m.mainRulesPath, []byte(mainRules), 0644)
if err != nil {
return fmt.Errorf("failed to write main PF rules: %v", err)
}

cmd = exec.Command("pfctl", "-f", m.mainRulesPath)
if err := cmd.Run(); err != nil {
err = cmd.Run()
if err != nil {
// Don't fail if main rules can't be loaded, but warn
fmt.Fprintf(os.Stderr, "Warning: failed to load main PF rules: %v\n", err)
}
Expand Down Expand Up @@ -368,4 +376,4 @@ func (m *MacOSNetJail) cleanupTempFiles() {
if m.mainRulesPath != "" {
os.Remove(m.mainRulesPath)
}
}
}
6 changes: 4 additions & 2 deletions proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,17 @@ func (p *ProxyServer) Start(ctx context.Context) error {
// Start HTTP server
go func() {
p.logger.Info("Starting HTTP proxy", "port", p.httpPort)
if err := p.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
err := p.httpServer.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
p.logger.Error("HTTP proxy server error", "error", err)
}
}()

// Start HTTPS server
go func() {
p.logger.Info("Starting HTTPS proxy", "port", p.httpsPort)
if err := p.httpsServer.ListenAndServeTLS("", ""); err != nil && err != http.ErrServerClosed {
err := p.httpsServer.ListenAndServeTLS("", "")
if err != nil && err != http.ErrServerClosed {
p.logger.Error("HTTPS proxy server error", "error", err)
}
}()
Expand Down
Loading
Loading