-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Package to setup system wide HTTP(S) Proxy on Desktop Platforms #190
Conversation
This reverts commit e793764.
@jyyi1 would you be able to review this PR for me whenever you get a chance since Vini is out? I tested this will Here are the changes to Please let me know if you have any thoughts on adding tests to this package... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @amircybersec ! This is very helpful, I just added some comments.
@jyyi1 Thanks a lot for the feedback. I applied your comments and make the suggested changes. I tested the changes across all platforms again to make sure I didn't break anything. Please let me know if there's any other areas for improvement. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! The general logic looks good, with some additional comments.
x/sysproxy/sysproxy_linux.go
Outdated
|
||
func UnsetProxy() error { | ||
// Execute Linux specific commands to unset proxy | ||
return execCommand("gsettings", "set", "org.gnome.system.proxy", "mode", "none") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it support other modes? If so, do we need to backup the previous settings and restore it here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jyyi1 The other modes are auto
, and manual
. Choosing manual
, the system uses the last used proxy settings that can be manually overwritten. I can potentially backup the previous state before setting the proxy to the new settings and restore the system settings back to the previous state when UnsetProxy
is called.
auto
uses a link to .pac
file that contains the proxy settings.
x/sysproxy/sysproxy_windows.go
Outdated
defer key.Close() | ||
|
||
values := map[string]interface{}{ | ||
"MigrateProxy": 1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this option mean? Do we need to unset it in our UnsetProxy
function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jyyi1 Documentation for MigrateProxy
DWORD was sparse. From what I understood by reading up one some resources I found (1, 2), It appears that this is related to how Internet Explore / Edge migrate their proxy settings.
I am a bit conflicted now since the docs say that IE sets this value to 1 after migrating the settings. BTW, I followed the way GreenTunnel was setting up Windows proxy params and tested the code on Windows successfully. But there could be corner and untested cases that I missed.
If you recall in the office, we looked at Chrome proxy settings was in sync with system proxy settings and we could see the settings switching in the Browser on Windows. I did not however test this with IE. I am looking further into this...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the information. I think we can ignore IE for now because Microsoft has already ended its support on June 15, 2022.
For the Edge, since it's using Chromium, I believe it should behave the same as Chrome.
Let's remove this setting and make sure everything works fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
x/sysproxy/sysproxy_windows.go
Outdated
return fmt.Errorf("unsupported value type") | ||
} | ||
if err != nil { | ||
return err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to unset the registry here, so we don't leave it in an unusable state?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. That's definitely a much safer approach. I will make a copy of settings param before making changes and revert then back to previous state at UnsetProxy
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is exciting!
x/sysproxy/sysproxy_darwin.go
Outdated
// getActiveNetworkInterface finds the active network interface using shell commands. | ||
// https://keith.github.io/xcode-man-pages/networksetup.8.html#listnetworkserviceorder | ||
func getActiveNetworkInterface() (string, error) { | ||
cmd := "networksetup -listnetworkserviceorder | grep `route -n get 0.0.0.0 | grep 'interface' | cut -d ':' -f2` -B 1 | head -n 1 | cut -d ' ' -f2" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer $()
over backticks: https://google.github.io/styleguide/shellguide.html#s6.2-command-substitution
cmd := "networksetup -listnetworkserviceorder | grep `route -n get 0.0.0.0 | grep 'interface' | cut -d ':' -f2` -B 1 | head -n 1 | cut -d ' ' -f2" | |
cmd := "networksetup -listnetworkserviceorder | grep "$(route -n get 0.0.0.0 | grep 'interface' | cut -d ':' -f2)" -B 1 | head -n 1 | cut -d ' ' -f2" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, can you put that on a separate getDefaultInterface()
?
And then provide a getNetworkServiceForInterface(interfaceName string)
that does the networksetup -listnetworkserviceorder
call?
That will be a lot more readable.
x/sysproxy/sysproxy_darwin.go
Outdated
cmdStr := fmt.Sprintf("networksetup -set%sproxy \"%s\" %s %s", ptype, interfaceName, ip, port) | ||
cmd := exec.Command("bash", "-c", cmdStr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Call networksetup
directly instead. There's no need for bash here.
x/sysproxy/sysproxy_darwin.go
Outdated
// https://keith.github.io/xcode-man-pages/networksetup.8.html#setwebproxystate | ||
func removeProxyCommand(ptype ProxyType, interfaceName string) error { | ||
cmdStr := fmt.Sprintf("networksetup -set%sproxystate \"%s\" off", ptype, interfaceName) | ||
cmd := exec.Command("bash", "-c", cmdStr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
x/sysproxy/sysproxy_linux.go
Outdated
|
||
func UnsetProxy() error { | ||
// Execute Linux specific commands to unset proxy | ||
return execCommand("gsettings", "set", "org.gnome.system.proxy", "mode", "none") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also erase the individual proxy entries we added, in case someone else enables the manual mode later.
x/sysproxy/sysproxy_darwin.go
Outdated
httpsProxyType ProxyType = "secureweb" | ||
) | ||
|
||
func SetProxy(ip string, port string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably return not supported on iOS. I think you can check GOOS.
x/sysproxy/sysproxy_linux.go
Outdated
ftpProxyType ProxyType = "ftp" | ||
) | ||
|
||
func SetProxy(ip string, port string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably return not supported on Android. I think you can check GOOS?
x/sysproxy/sysproxy_darwin.go
Outdated
// setProxyCommand sets the specified type of proxy on the given network interface. | ||
// https://keith.github.io/xcode-man-pages/networksetup.8.html#getsecurewebproxy | ||
func setProxyCommand(ptype ProxyType, interfaceName string, ip string, port string) error { | ||
cmdStr := fmt.Sprintf("networksetup -set%sproxy \"%s\" %s %s", ptype, interfaceName, ip, port) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you do a switch based on the type, instead of interpolating the command?
Like
switch proxyType {
case ProxyTypeHTTP:
cmd = exec.Command("networksetup", "-setwebproxy", interfaceName, host, port)
...
}
That will be a lot cleaner to read.
Co-authored-by: J. Yi <93548144+jyyi1@users.noreply.github.com>
redundant likes that got pushed in accidentally Co-authored-by: J. Yi <93548144+jyyi1@users.noreply.github.com>
Co-authored-by: J. Yi <93548144+jyyi1@users.noreply.github.com>
remove print statement Co-authored-by: J. Yi <93548144+jyyi1@users.noreply.github.com>
remove orphan line Co-authored-by: J. Yi <93548144+jyyi1@users.noreply.github.com>
Co-authored-by: J. Yi <93548144+jyyi1@users.noreply.github.com>
redundant code Co-authored-by: J. Yi <93548144+jyyi1@users.noreply.github.com>
Co-authored-by: J. Yi <93548144+jyyi1@users.noreply.github.com>
x/sysproxy/README.md
Outdated
| --- | ----------- | ------ | ------ | | ||
| HTTP | Yes | Yes | Yes | ||
| HTTPS | Yes | Yes | Yes | ||
| SOCKS | No | Yes | Yes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you do support Windows, but it's SOCKS4?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found this socks server package that implements sock4, 4a, and 5:
https://github.com/cybozu-go/usocksd?tab=readme-ov-file
I wanted to test socks proxy on all platforms and see what versions they support in reality. I have tested linux and mac with socks 5 and they work fine. I could not get windows working with socks5 and it only worked with sock4 server in my tests. I have not tested mac and linux against socks4 though...Maybe we can do this later as TODO item.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have updated doc.go to reflect this in the documentation language
x/sysproxy/README.md
Outdated
| SOCKS | No | Yes | Yes | ||
|
||
|
||
`SetupWebProxy` implementation in this package setups both HTTP and HTTPS proxy when they distinguished by the platform. `SetupSOCKSProxy` sets up SOCKS proxy that proxies all TCP-based connections. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to explain what setting a HTTP proxy and setting a HTTPS proxy mean.
x/sysproxy/README.md
Outdated
|
||
On Windows, The client only supports SOCKS4 spec and cannot connecto SOCKS5 proxy. | ||
|
||
Support for FTP Proxy setting was not included due lack of adoption and usage. Username and password authentication was not included due to potential unreliability and untestd behavior. Please note that in SOCKS and HTTP proxy, credentials are communicated in plain text. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say that authentication is not supported because the intended usage it to connect to a proxy running on localhost.
} | ||
} | ||
|
||
func getProxySettings(p ProxyType, interfaceName string) (*proxySettings, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is dead code. We can remove the get stuff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fortuna I am using getProxySettings
in tests to ensure parameters are properly set by the setProxy action.
x/sysproxy/sysproxy_linux.go
Outdated
|
||
func ClearWebProxy() error { | ||
// clear settings | ||
if err := setProxySettings(proxyTypeHTTP, "127.0.0.1", "0"); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same: can we use empty strings? It's more obvious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, I will test it first to make sure it works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fortuna it seems like setting the strings to empty value has unintended consequences. For example, on macos, if I do
networksetup -setwebproxy Wi-Fi "" ""
and then get the setting values, the port is returned as 0 but address is empty:
networksetup -getwebproxy Wi-Fi
Enabled: Yes
Server:
Port: 0
Authenticated Proxy Enabled: 0
This breaks my tests since set and read-back values do not match.
I am concerned changing this causes some stability issues if not throughly tested on all platforms. Since my tests are currently passing on all platforms and I have also tested the proxy behavior on all platforms end-to-end, let's use a default IP and port instead of empty string, if that is okay with you
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is moot since we are not clearing the settings anymore.
x/sysproxy/sysproxy_darwin.go
Outdated
} | ||
|
||
// Unset the web proxy and secure web proxy | ||
if err := disableProxy(proxyTypeHTTP, activeInterface); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disable first, then clear the proxy. Otherwise someone may try to use it with the bogus address. Same for the other platforms.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fortuna I was doing that before (disabling first) but setting proxy parameters automatically change the proxy state to manual and turn it on. I had to change the order otherwise the proxy stayed in ON state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I saw this odd behavior on Linux, we might be able to disable proxy on Windows and MacOS before clearing the settings. Need to investigate this again...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fortuna I confirm both on MacOS and Linux changing the proxy server and port automatically turns on the proxy. On Windows, we have to explicitly enable and disable the proxy settings for changes to take effect. This means we must clear the settings before disabling the proxy - the reverse order would leave the proxy state ON.
If we do not clear / change the setting, we can just disable the proxy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for testing. If we can't clear the settings, perhaps we just disable it then?
We would have to rename the method to DisableXPRoxy()
x/sysproxy/README.md
Outdated
|
||
For example, on MacOS, if you have HTTP Proxy setup and visit a website over HTTPS, your traffic does NOT go through the proxy. | ||
|
||
However, if only HTTPS proxy is setup, both HTTP and HTTPS requests go through the HTTPS proxy. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you set HTTPS, HTTP will use it? That's very counter intuitive.
Does it work the same in the other platforms? It would help to characterize that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fortuna The proxy server running locally is HTTP proxy server in both cases.
In the first experiment, I only setup system's client HTTP proxy settings. In this case, only HTTP requests get routed through the proxy. If I setup system's client HTTPS proxy settings, both HTTP and HTTPS requests get routed through the proxy.
I believe this is just the way the system decides which type of request to route to the proxy. HTTPS is basically the super set in this design.
In our case, we don't care much about this since we are settings up both at the same time on all platforms and call it WebProxy which ends up routing both HTTP and HTTPS requests through the proxy.
Co-authored-by: Vinicius Fortuna <fortuna@users.noreply.github.com>
Co-authored-by: Vinicius Fortuna <fortuna@users.noreply.github.com>
x/sysproxy/sysproxy_darwin.go
Outdated
} | ||
|
||
// Unset the web proxy and secure web proxy | ||
if err := disableProxy(proxyTypeHTTP, activeInterface); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for testing. If we can't clear the settings, perhaps we just disable it then?
We would have to rename the method to DisableXPRoxy()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a few tweaks
Co-authored-by: J. Yi <93548144+jyyi1@users.noreply.github.com>
Co-authored-by: J. Yi <93548144+jyyi1@users.noreply.github.com>
Co-authored-by: J. Yi <93548144+jyyi1@users.noreply.github.com>
Co-authored-by: J. Yi <93548144+jyyi1@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the contribution!
sysproxy
package sets up the system wide Web (HTTP/HTTPS) or SOCK proxy settings on:The package includes tests that sets the settings parameters and read them back to ensure changes have been properly made in the system settings.
The package exports the following methods: