Skip to content

Commit 780f2b1

Browse files
authored
Merge pull request #6 from coder/blink/fix-sudo-user-privileges
fix: drop privileges to original user when running with sudo
2 parents 31f1722 + d62fac5 commit 780f2b1

File tree

3 files changed

+117
-9
lines changed

3 files changed

+117
-9
lines changed

network/linux.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"log/slog"
88
"os"
99
"os/exec"
10+
"os/user"
11+
"strconv"
1012
"syscall"
1113
"time"
1214
)
@@ -97,11 +99,47 @@ func (l *LinuxJail) Execute(command []string, extraEnv map[string]string) error
9799
env = append(env, fmt.Sprintf("%s=%s", key, value))
98100
}
99101

102+
// When running under sudo, restore essential user environment variables
103+
if sudoUser := os.Getenv("SUDO_USER"); sudoUser != "" {
104+
if user, err := user.Lookup(sudoUser); err == nil {
105+
// Set HOME to original user's home directory
106+
env = append(env, fmt.Sprintf("HOME=%s", user.HomeDir))
107+
// Set USER to original username
108+
env = append(env, fmt.Sprintf("USER=%s", sudoUser))
109+
// Set LOGNAME to original username (some tools check this instead of USER)
110+
env = append(env, fmt.Sprintf("LOGNAME=%s", sudoUser))
111+
l.logger.Debug("Restored user environment", "home", user.HomeDir, "user", sudoUser)
112+
}
113+
}
114+
100115
cmd.Env = env
101116
cmd.Stdin = os.Stdin
102117
cmd.Stdout = os.Stdout
103118
cmd.Stderr = os.Stderr
104119

120+
// Drop privileges to original user if running under sudo
121+
if sudoUID := os.Getenv("SUDO_UID"); sudoUID != "" {
122+
if sudoGID := os.Getenv("SUDO_GID"); sudoGID != "" {
123+
uid, err := strconv.Atoi(sudoUID)
124+
if err != nil {
125+
l.logger.Warn("Invalid SUDO_UID, subprocess will run as root", "sudo_uid", sudoUID, "error", err)
126+
} else {
127+
gid, err := strconv.Atoi(sudoGID)
128+
if err != nil {
129+
l.logger.Warn("Invalid SUDO_GID, subprocess will run as root", "sudo_gid", sudoGID, "error", err)
130+
} else {
131+
cmd.SysProcAttr = &syscall.SysProcAttr{
132+
Credential: &syscall.Credential{
133+
Uid: uint32(uid),
134+
Gid: uint32(gid),
135+
},
136+
}
137+
l.logger.Debug("Dropping privileges to original user", "uid", uid, "gid", gid)
138+
}
139+
}
140+
}
141+
}
142+
105143
// Start command
106144
l.logger.Debug("Starting command", "path", cmd.Path, "args", cmd.Args)
107145
err := cmd.Start()

network/macos.go

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"log/slog"
88
"os"
99
"os/exec"
10+
"os/user"
1011
"strconv"
1112
"strings"
1213
"syscall"
@@ -84,16 +85,48 @@ func (m *MacOSNetJail) Execute(command []string, extraEnv map[string]string) err
8485
env = append(env, fmt.Sprintf("%s=%s", key, value))
8586
}
8687

88+
// When running under sudo, restore essential user environment variables
89+
if sudoUser := os.Getenv("SUDO_USER"); sudoUser != "" {
90+
if user, err := user.Lookup(sudoUser); err == nil {
91+
// Set HOME to original user's home directory
92+
env = append(env, fmt.Sprintf("HOME=%s", user.HomeDir))
93+
// Set USER to original username
94+
env = append(env, fmt.Sprintf("USER=%s", sudoUser))
95+
// Set LOGNAME to original username (some tools check this instead of USER)
96+
env = append(env, fmt.Sprintf("LOGNAME=%s", sudoUser))
97+
m.logger.Debug("Restored user environment", "home", user.HomeDir, "user", sudoUser)
98+
}
99+
}
100+
87101
cmd.Env = env
88102
cmd.Stdout = os.Stdout
89103
cmd.Stderr = os.Stderr
90104
cmd.Stdin = os.Stdin
91105

92-
// Set group ID using syscall (like httpjail does)
93-
cmd.SysProcAttr = &syscall.SysProcAttr{
94-
Credential: &syscall.Credential{
95-
Gid: uint32(m.groupID),
96-
},
106+
// Drop privileges to original user if running under sudo
107+
if sudoUID := os.Getenv("SUDO_UID"); sudoUID != "" {
108+
if sudoGID := os.Getenv("SUDO_GID"); sudoGID != "" {
109+
uid, err := strconv.Atoi(sudoUID)
110+
if err != nil {
111+
m.logger.Warn("Invalid SUDO_UID, subprocess will run as root", "sudo_uid", sudoUID, "error", err)
112+
} else {
113+
// Use original user ID but KEEP the jail group for network isolation
114+
cmd.SysProcAttr = &syscall.SysProcAttr{
115+
Credential: &syscall.Credential{
116+
Uid: uint32(uid),
117+
Gid: uint32(m.groupID), // Keep jail group, not original user's group
118+
},
119+
}
120+
m.logger.Debug("Dropping privileges to original user with jail group", "uid", uid, "jail_gid", m.groupID)
121+
}
122+
}
123+
} else {
124+
// Set group ID using syscall (original behavior for non-sudo)
125+
cmd.SysProcAttr = &syscall.SysProcAttr{
126+
Credential: &syscall.Credential{
127+
Gid: uint32(m.groupID),
128+
},
129+
}
97130
}
98131

99132
// Start and wait for command to complete
@@ -334,4 +367,4 @@ func (m *MacOSNetJail) cleanupTempFiles() {
334367
if m.mainRulesPath != "" {
335368
os.Remove(m.mainRulesPath)
336369
}
337-
}
370+
}

tls/tls.go

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import (
1212
"math/big"
1313
"net"
1414
"os"
15+
"os/user"
1516
"path/filepath"
17+
"strconv"
1618
"sync"
1719
"time"
1820
)
@@ -141,6 +143,22 @@ func (cm *CertificateManager) generateCA(keyPath, certPath string) error {
141143
return fmt.Errorf("failed to create config directory: %v", err)
142144
}
143145

146+
// When running under sudo, ensure the directory is owned by the original user
147+
if sudoUser := os.Getenv("SUDO_USER"); sudoUser != "" {
148+
if sudoUID := os.Getenv("SUDO_UID"); sudoUID != "" {
149+
if sudoGID := os.Getenv("SUDO_GID"); sudoGID != "" {
150+
uid, err1 := strconv.Atoi(sudoUID)
151+
gid, err2 := strconv.Atoi(sudoGID)
152+
if err1 == nil && err2 == nil {
153+
// Change ownership of the config directory to the original user
154+
if err := os.Chown(cm.configDir, uid, gid); err != nil {
155+
cm.logger.Warn("Failed to change config directory ownership", "error", err)
156+
}
157+
}
158+
}
159+
}
160+
}
161+
144162
// Generate private key
145163
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
146164
if err != nil {
@@ -295,9 +313,28 @@ func (cm *CertificateManager) generateServerCertificate(hostname string) (*tls.C
295313

296314
// GetConfigDir returns the configuration directory path
297315
func GetConfigDir() (string, error) {
298-
homeDir, err := os.UserHomeDir()
299-
if err != nil {
300-
return "", fmt.Errorf("failed to get user home directory: %v", err)
316+
// When running under sudo, use the original user's home directory
317+
// so the subprocess can access the CA certificate files
318+
var homeDir string
319+
if sudoUser := os.Getenv("SUDO_USER"); sudoUser != "" {
320+
// Get original user's home directory
321+
if user, err := user.Lookup(sudoUser); err == nil {
322+
homeDir = user.HomeDir
323+
} else {
324+
// Fallback to current user if lookup fails
325+
var err2 error
326+
homeDir, err2 = os.UserHomeDir()
327+
if err2 != nil {
328+
return "", fmt.Errorf("failed to get user home directory: %v", err2)
329+
}
330+
}
331+
} else {
332+
// Normal case - use current user's home
333+
var err error
334+
homeDir, err = os.UserHomeDir()
335+
if err != nil {
336+
return "", fmt.Errorf("failed to get user home directory: %v", err)
337+
}
301338
}
302339

303340
// Use platform-specific config directory

0 commit comments

Comments
 (0)