Skip to content
Open
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
63 changes: 51 additions & 12 deletions internal/sysproxy/system_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"os/exec"
"regexp"
"strings"

"github.com/getlantern/elevate"
)

var (
Expand All @@ -16,6 +18,15 @@ var (
platformSpecificExcludedHosts []byte
)

type command struct {
name string
args []string
}

func (c command) String() string {
return fmt.Sprintf("%s %s", c.name, strings.Join(c.args, " "))
}

// setSystemProxy sets the system proxy to the proxy address.
func setSystemProxy(pacURL string) error {
cmd := exec.Command("sh", "-c", "scutil --nwi | grep 'Network interfaces' | cut -d ' ' -f 3")
Expand Down Expand Up @@ -43,19 +54,34 @@ func setSystemProxy(pacURL string) error {
return errors.New("no network service found")
}

cmd = exec.Command("networksetup", "-setwebproxystate", networkService, "off")
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("unset web proxy for network service %q: %v (%q)", networkService, err, out)
cmds := []command{
{
name: "networksetup",
args: []string{"-setwebproxystate", networkService, "off"},
},
{
name: "networksetup",
args: []string{"-setsecurewebproxystate", networkService, "off"},
},
{
name: "networksetup",
args: []string{"-setautoproxyurl", networkService, pacURL},
},
}

cmd = exec.Command("networksetup", "-setsecurewebproxystate", networkService, "off")
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("unset secure web proxy for network service %q: %v (%q)", networkService, err, out)
var retryCommands []string
for _, c := range cmds {
cmd := exec.Command(c.name, c.args...)
if _, err := cmd.CombinedOutput(); err != nil {
retryCommands = append(retryCommands, c.String())
}
}

cmd = exec.Command("networksetup", "-setautoproxyurl", networkService, pacURL)
if out, err = cmd.CombinedOutput(); err != nil {
return fmt.Errorf("set autoproxyurl to %q for network service %q: %v (%q)", pacURL, networkService, err, out)
if retryCommands != nil {
cmd = elevate.WithPrompt("System changes required to activate proxy").Command("sh", "-c", strings.Join(retryCommands, " "))
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("network setup with root privileges: %w (%q)", err, out)
}
Comment on lines +81 to +84
Copy link
Member

@anfragment anfragment Jun 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like unfortunately elevate doesn't allow executing networksetup commands when "Require an administrator password to access system-wide settings" is on. Instead of setting uid to 0 like sudo does, elevate only sets euid to 0, which is apparently not enough for networksetup.

Here are two ideas on how to move forward:

  1. Run the commands via AppleScript with osascript -e "do shell script "<;-separated commands>" with administrator privileges with prompt "Authorize Zen to modify system proxy settings". This works, however the displayed prompt shows osascript in the title, which might confuse some users:
image
  1. Another solution that may work is to just call networksetup -setautoproxyurl without the other two commands. I'm struggling to find any official documentation on this, but local testing suggests that "automatic proxy configuration" (PAC) takes precedence over "web proxy" and "secure web proxy", which probably makes setting their state to "off" redundant.

@ccarstens @AitakattaSora thoughts on this?

}

// There's no need to set autoproxystate to on, as setting the URL already does that.
Expand All @@ -68,9 +94,22 @@ func unsetSystemProxy() error {
return errors.New("trying to unset system proxy without setting it first")
}

cmd := exec.Command("networksetup", "-setautoproxystate", networkService, "off")
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("set autoproxystate to off for network service %q: %v (%q)", networkService, err, out)
cmd := command{
name: "networksetup",
args: []string{"-setautoproxystate", networkService, "off"},
}

var retry bool
c := exec.Command(cmd.name, cmd.args...)
if _, err := c.CombinedOutput(); err != nil {
retry = true
}

if retry {
cmd := elevate.WithPrompt("System changes required to deactivate proxy").Command(cmd.name, cmd.args...)
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("proxy deactivation with root privileges: %w (%q)", err, out)
}
}

networkService = ""
Expand Down