Skip to content
38 changes: 38 additions & 0 deletions network/linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"log/slog"
"os"
"os/exec"
"os/user"
"strconv"
"syscall"
"time"
)
Expand Down Expand Up @@ -97,11 +99,47 @@ func (l *LinuxJail) Execute(command []string, extraEnv map[string]string) error
env = append(env, fmt.Sprintf("%s=%s", key, value))
}

// When running under sudo, restore essential user environment variables
if sudoUser := os.Getenv("SUDO_USER"); sudoUser != "" {
if user, err := user.Lookup(sudoUser); err == nil {
// Set HOME to original user's home directory
env = append(env, fmt.Sprintf("HOME=%s", user.HomeDir))
// Set USER to original username
env = append(env, fmt.Sprintf("USER=%s", sudoUser))
// Set LOGNAME to original username (some tools check this instead of USER)
env = append(env, fmt.Sprintf("LOGNAME=%s", sudoUser))
l.logger.Debug("Restored user environment", "home", user.HomeDir, "user", sudoUser)
}
}

cmd.Env = env
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

// Drop privileges to original user if running under sudo
if sudoUID := os.Getenv("SUDO_UID"); sudoUID != "" {
if sudoGID := os.Getenv("SUDO_GID"); sudoGID != "" {
uid, err := strconv.Atoi(sudoUID)
if err != nil {
l.logger.Warn("Invalid SUDO_UID, subprocess will run as root", "sudo_uid", sudoUID, "error", err)
} else {
gid, err := strconv.Atoi(sudoGID)
if err != nil {
l.logger.Warn("Invalid SUDO_GID, subprocess will run as root", "sudo_gid", sudoGID, "error", err)
} else {
cmd.SysProcAttr = &syscall.SysProcAttr{
Credential: &syscall.Credential{
Uid: uint32(uid),
Gid: uint32(gid),
},
}
l.logger.Debug("Dropping privileges to original user", "uid", uid, "gid", gid)
}
}
}
}

// Start command
l.logger.Debug("Starting command", "path", cmd.Path, "args", cmd.Args)
err := cmd.Start()
Expand Down
45 changes: 39 additions & 6 deletions network/macos.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"log/slog"
"os"
"os/exec"
"os/user"
"strconv"
"strings"
"syscall"
Expand Down Expand Up @@ -84,16 +85,48 @@ func (m *MacOSNetJail) Execute(command []string, extraEnv map[string]string) err
env = append(env, fmt.Sprintf("%s=%s", key, value))
}

// When running under sudo, restore essential user environment variables
if sudoUser := os.Getenv("SUDO_USER"); sudoUser != "" {
if user, err := user.Lookup(sudoUser); err == nil {
// Set HOME to original user's home directory
env = append(env, fmt.Sprintf("HOME=%s", user.HomeDir))
// Set USER to original username
env = append(env, fmt.Sprintf("USER=%s", sudoUser))
// Set LOGNAME to original username (some tools check this instead of USER)
env = append(env, fmt.Sprintf("LOGNAME=%s", sudoUser))
m.logger.Debug("Restored user environment", "home", user.HomeDir, "user", sudoUser)
}
}

cmd.Env = env
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin

// Set group ID using syscall (like httpjail does)
cmd.SysProcAttr = &syscall.SysProcAttr{
Credential: &syscall.Credential{
Gid: uint32(m.groupID),
},
// Drop privileges to original user if running under sudo
if sudoUID := os.Getenv("SUDO_UID"); sudoUID != "" {
if sudoGID := os.Getenv("SUDO_GID"); sudoGID != "" {
uid, err := strconv.Atoi(sudoUID)
if err != nil {
m.logger.Warn("Invalid SUDO_UID, subprocess will run as root", "sudo_uid", sudoUID, "error", err)
} else {
// Use original user ID but KEEP the jail group for network isolation
cmd.SysProcAttr = &syscall.SysProcAttr{
Credential: &syscall.Credential{
Uid: uint32(uid),
Gid: uint32(m.groupID), // Keep jail group, not original user's group
},
}
m.logger.Debug("Dropping privileges to original user with jail group", "uid", uid, "jail_gid", m.groupID)
}
}
} else {
// Set group ID using syscall (original behavior for non-sudo)
cmd.SysProcAttr = &syscall.SysProcAttr{
Credential: &syscall.Credential{
Gid: uint32(m.groupID),
},
}
}

// Start and wait for command to complete
Expand Down Expand Up @@ -334,4 +367,4 @@ func (m *MacOSNetJail) cleanupTempFiles() {
if m.mainRulesPath != "" {
os.Remove(m.mainRulesPath)
}
}
}
43 changes: 40 additions & 3 deletions tls/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import (
"math/big"
"net"
"os"
"os/user"
"path/filepath"
"strconv"
"sync"
"time"
)
Expand Down Expand Up @@ -141,6 +143,22 @@ func (cm *CertificateManager) generateCA(keyPath, certPath string) error {
return fmt.Errorf("failed to create config directory: %v", err)
}

// When running under sudo, ensure the directory is owned by the original user
if sudoUser := os.Getenv("SUDO_USER"); sudoUser != "" {
if sudoUID := os.Getenv("SUDO_UID"); sudoUID != "" {
if sudoGID := os.Getenv("SUDO_GID"); sudoGID != "" {
uid, err1 := strconv.Atoi(sudoUID)
gid, err2 := strconv.Atoi(sudoGID)
if err1 == nil && err2 == nil {
// Change ownership of the config directory to the original user
if err := os.Chown(cm.configDir, uid, gid); err != nil {
cm.logger.Warn("Failed to change config directory ownership", "error", err)
}
}
}
}
}

// Generate private key
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
Expand Down Expand Up @@ -295,9 +313,28 @@ func (cm *CertificateManager) generateServerCertificate(hostname string) (*tls.C

// GetConfigDir returns the configuration directory path
func GetConfigDir() (string, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("failed to get user home directory: %v", err)
// When running under sudo, use the original user's home directory
// so the subprocess can access the CA certificate files
var homeDir string
if sudoUser := os.Getenv("SUDO_USER"); sudoUser != "" {
// Get original user's home directory
if user, err := user.Lookup(sudoUser); err == nil {
homeDir = user.HomeDir
} else {
// Fallback to current user if lookup fails
var err2 error
homeDir, err2 = os.UserHomeDir()
if err2 != nil {
return "", fmt.Errorf("failed to get user home directory: %v", err2)
}
}
} else {
// Normal case - use current user's home
var err error
homeDir, err = os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("failed to get user home directory: %v", err)
}
}

// Use platform-specific config directory
Expand Down
Loading