diff --git a/network/linux.go b/network/linux.go index 82eacc2..f0d4330 100644 --- a/network/linux.go +++ b/network/linux.go @@ -7,6 +7,8 @@ import ( "log/slog" "os" "os/exec" + "os/user" + "strconv" "syscall" "time" ) @@ -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() diff --git a/network/macos.go b/network/macos.go index 79f8097..d383e8e 100644 --- a/network/macos.go +++ b/network/macos.go @@ -7,6 +7,7 @@ import ( "log/slog" "os" "os/exec" + "os/user" "strconv" "strings" "syscall" @@ -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 @@ -334,4 +367,4 @@ func (m *MacOSNetJail) cleanupTempFiles() { if m.mainRulesPath != "" { os.Remove(m.mainRulesPath) } -} +} \ No newline at end of file diff --git a/tls/tls.go b/tls/tls.go index a9e51b0..07ef6ad 100644 --- a/tls/tls.go +++ b/tls/tls.go @@ -12,7 +12,9 @@ import ( "math/big" "net" "os" + "os/user" "path/filepath" + "strconv" "sync" "time" ) @@ -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 { @@ -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