Yeah, I'm not very comfortable with networking so it's possible I'm not 
using the right terms...

I want to be able to allow my users to use, with Selenium (and therefore 
Chrome), a proxy with authentication. The problem is that I cannot send the 
proxy credentials to the Chrome instance or interact with the proxy 
connection alert box.

So basically, what I want to do, is a localhost proxy managed by my program 
which will receive all Chrome CONNECT requests, inject the 
Proxy-Authorization header to authenticate requests and send them back to 
the user remote proxy.

I managed to get something with a TCP tunnel, I relied on the source code 
of io.Copy source code.
I managed to intercept the request from the incoming net.Conn, decode the 
http request from the tcp packet, add the Proxy-Authorization at the end, 
re-encode the packet and finally write it to the outgoing net.Conn (to the 
remote proxy )
But I'm getting a 400 Server Error on the remote proxy side...

You can check all of that here: 
https://github.com/hbollon/IGopher/tree/proxy/internal/proxy
(It's WIP stuff, so it's not cleaned or optimized yet. I just pushed it so 
you could check it out)

Hope I am clear enough ...
Anyways, thanks for your help! :)

Le mercredi 17 mars 2021 à 09:30:22 UTC+1, vlad...@varank.in a écrit :

> I think I didn't get what you're building right. Now, it looks like, 
> instead of implementing a custom RR's director, you need to configure its 
> Transport [1], which will be aware of your auth proxy in the middle. Have a 
> look at net/http.Transport.Proxy field [2] for that.
>
> [1]: https://pkg.go.dev/net/http/httputil?utm_source=godoc#ReverseProxy
> [2]: https://pkg.go.dev/net/http?utm_source=godoc#Transport
>
> On Tuesday, March 16, 2021 at 11:17:49 AM UTC+1 hugo....@gmail.com wrote:
>
>> Thank you for your advice that I applied.
>> But now I have a *407 Proxy Authentication Required* error, while the 
>> header is added to the request...
>> Here is the output:
>>
>> INFO[0019] Pre-Edited request: &{Method:CONNECT URL://google.com:443 
>> Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 
>> Header:map[Proxy-Connection:[Keep-Alive] User-Agent:[curl/7.68.0]] 
>> Body:<nil> GetBody:<nil> ContentLength:0 TransferEncoding:[] Close:false 
>> Host:google.com:443 Form:map[] PostForm:map[] MultipartForm:<nil> 
>> Trailer:map[] RemoteAddr:127.0.0.1:45382 RequestURI:google.com:443 
>> TLS:<nil> Cancel:<nil> Response:<nil> ctx:0xc000814240}  function=func1 
>> line=60
>>
>> INFO[0019] Edited Request: &{Method:CONNECT URL:http://51.178.xx.xx:3128/ 
>> Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 
>> Header:map[Proxy-Authorization:[Basic <auth>] Proxy-Connection:[Keep-Alive] 
>> User-Agent:[curl/7.68.0]] Body:<nil> GetBody:<nil> ContentLength:0 
>> TransferEncoding:[] Close:false Host:google.com:443 Form:map[] 
>> PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr:
>> 127.0.0.1:45382 RequestURI:google.com:443 TLS:<nil> Cancel:<nil> 
>> Response:<nil> ctx:0xc000814240}  function=func1 line=69
>>
>> INFO[0019] Scheme: http, Host: 51.178.xx.xx:3128, Port: 3128  
>> function=func1 line=70
>>
>> INFO[0019] Response: &{Status:407 Proxy Authentication Required 
>> StatusCode:407 Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 
>> Header:map[Content-Language:[en] Content-Length:[3520] 
>> Content-Type:[text/html;charset=utf-8] Date:[Tue, 16 Mar 2021 10:03:44 GMT] 
>> Mime-Version:[1.0] Server:[squid/3.5.27] Vary:[Accept-Language] Via:[1.1 
>> vps799016 (squid/3.5.27)] X-Cache:[MISS from vps799xxx] 
>> X-Cache-Lookup:[NONE from vps799xxx:3128] 
>> X-Squid-Error:[ERR_CACHE_ACCESS_DENIED 0]] Body:0xc0004de180 
>> ContentLength:3520 TransferEncoding:[] Close:false Uncompressed:false 
>> Trailer:map[] Request:0xc0005ee100 TLS:<nil>}  function=PrintResponse 
>> line=33
>>
>>
>> PS: Is it possible on Google Groups to format code snippets? Markdown not 
>> seems to be supported
>>
>> Le mardi 16 mars 2021 à 09:04:31 UTC+1, vlad...@varank.in a écrit :
>>
>>> Hey there,
>>>
>>> Seems the issue hides in the chunk, where you overwrite reverse proxy's 
>>> "Director" method, which NewSingleHostReverseProxy creates internally. 
>>> Since your own director doesn't set the client request's Schema and Host, 
>>> you have to either do that manually or make sure you call the original 
>>> director.
>>>
>>> Try doing the following:
>>>
>>> proxyDirector := proxy.Director // ← keep the original director
>>> d := func(req *http.Request) {
>>>         logrus.Infof("Pre-Edited request: %+v\n", req)
>>>
>>>         proxyDirector(req) // ← call the original director to make sure 
>>> the request will go through the proxy
>>>
>>>         // Inject proxy authentication headers to outgoing request into 
>>> new Header
>>>         basicAuth := "Basic " + 
>>> base64.StdEncoding.EncodeToString([]byte(remoteServerAuth))
>>>         req.Header.Set("Proxy-Authorization", basicAuth)
>>>         logrus.Infof("Edited Request: %+v\n", req)
>>>         logrus.Infof("Scheme: %s, Host: %s, Port: %s\n", req.URL.Scheme, 
>>> req.URL.Host, req.URL.Port())
>>> }
>>> proxy.Director = d
>>>
>>> Also, have a look at the implementation of NewSingleHostReverseProxy 
>>> https://go.googlesource.com/go/+/go1.16/src/net/http/httputil/reverseproxy.go#142
>>>
>>> Cheers,
>>> V.
>>>
>>> On Monday, March 15, 2021 at 11:37:51 PM UTC+1 hugo....@gmail.com wrote:
>>>
>>>> Hi!
>>>> I'm actually building an automation tool based on Selenium with Go 
>>>> called IGopher and I have had a few requests to implement native proxy 
>>>> support.  
>>>> However, I am facing an issue with those with authentication...  
>>>> I can't send the proxy credentials to Chrome and without them it asks 
>>>> through an alert box for authentication that I can hardly interact with 
>>>> through Selenium (I'm not even sure it's possible in headless mode) .
>>>>
>>>> So I thought of an intermediary proxy system hosted locally by my 
>>>> program which will add the *Proxy-Authorization* header and transfer 
>>>> the request to the remote proxy:
>>>>
>>>> [image: IGopher_proxies.jpg]
>>>>
>>>> Something like this:  proxy-login-automator 
>>>> <https://github.com/sjitech/proxy-login-automator>
>>>>
>>>> I'm not very familiar with proxies to be honest, but I tried this 
>>>> approach using *NewSingleHostReverseProxy*: 
>>>> ```go
>>>> var (
>>>> localServerHost  string
>>>> remoteServerHost string
>>>> remoteServerAuth string
>>>> )
>>>>
>>>> // ProxyConfig store all remote proxy configuration
>>>> type ProxyConfig struct {
>>>> IP       string `yaml:"ip"`
>>>> Port     int    `yaml:"port"`
>>>> Username string `yaml:"username"`
>>>> Password string `yaml:"password"`
>>>> Enabled  bool   `yaml:"activated"`
>>>> }
>>>>
>>>> func PrintResponse(r *http.Response) error {
>>>> logrus.Infof("Response: %+v\n", r)
>>>> return nil
>>>> }
>>>>
>>>> // LaunchForwardingProxy launch forward server used to inject proxy 
>>>> authentication header
>>>> // into outgoing requests
>>>> func LaunchForwardingProxy(localPort uint16, remoteProxy ProxyConfig) 
>>>> error {
>>>> localServerHost = fmt.Sprintf("localhost:%d", localPort)
>>>> remoteServerHost = fmt.Sprintf(
>>>> "http://%s:%d";,
>>>> remoteProxy.IP,
>>>> remoteProxy.Port,
>>>> )
>>>> remoteServerAuth = fmt.Sprintf(
>>>> "%s:%s",
>>>> remoteProxy.Username,
>>>> remoteProxy.Password,
>>>> )
>>>>
>>>> remote, err := url.Parse(remoteServerHost)
>>>> if err != nil {
>>>> panic(err)
>>>> }
>>>>
>>>> proxy := httputil.NewSingleHostReverseProxy(remote)
>>>> d := func(req *http.Request) {
>>>> logrus.Infof("Pre-Edited request: %+v\n", req)
>>>> // Inject proxy authentication headers to outgoing request into new 
>>>> Header
>>>> basicAuth := "Basic " + 
>>>> base64.StdEncoding.EncodeToString([]byte(remoteServerAuth))
>>>> req.Header.Set("Proxy-Authorization", basicAuth)
>>>> logrus.Infof("Edited Request: %+v\n", req)
>>>> logrus.Infof("Scheme: %s, Host: %s, Port: %s\n", req.URL.Scheme, 
>>>> req.URL.Host, req.URL.Port())
>>>> }
>>>> proxy.Director = d
>>>> proxy.ModifyResponse = PrintResponse
>>>> http.ListenAndServe(localServerHost, proxy)
>>>>
>>>> return nil
>>>> }
>>>> ```
>>>>
>>>> With this code snippet, I'm able to intercept the request and update 
>>>> the header. 
>>>> However, resending the CONNECT request fails with the following output:
>>>>
>>>> ```
>>>> INFO[0028] Pre-Edited request: &{Method:CONNECT URL://google.com:443 
>>>> Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 
>>>> Header:map[Proxy-Connection:[Keep-Alive] User-Agent:[curl/7.68.0]] 
>>>> Body:<nil> GetBody:<nil> ContentLength:0 TransferEncoding:[] Close:false 
>>>> Host:google.com:443 Form:map[] PostForm:map[] MultipartForm:<nil> 
>>>> Trailer:map[] RemoteAddr:127.0.0.1:35610 RequestURI:google.com:443 
>>>> TLS:<nil> Cancel:<nil> Response:<nil> ctx:0xc000164300}  function=func1 
>>>> line=59
>>>>
>>>> INFO[0028] Edited Request: &{Method:CONNECT URL://google.com:443 
>>>> Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 
>>>> Header:map[Proxy-Authorization:[Basic <auth>] 
>>>> Proxy-Connection:[Keep-Alive] 
>>>> User-Agent:[curl/7.68.0]] Body:<nil> GetBody:<nil> ContentLength:0 
>>>> TransferEncoding:[] Close:false Host:google.com:443 Form:map[] 
>>>> PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr:
>>>> 127.0.0.1:35610 RequestURI:google.com:443 TLS:<nil> Cancel:<nil> 
>>>> Response:<nil> ctx:0xc000164300}  function=func1 line=63
>>>>
>>>> INFO[0028] Scheme: , Host: google.com:443, Port: 443    
>>>>  function=func1 line=64
>>>>
>>>> *2021/03/15 21:35:11 http: proxy error: unsupported protocol scheme ""*
>>>> ```
>>>>
>>>> What am I doing wrong? Is there a way to send a CONNECT request without 
>>>> scheme in Go?
>>>> Maybe I'm doing something wrong or my approach to this problem is 
>>>> wrong. Are there better methods to achieve my goals?
>>>>
>>>> If you have an idea to complete what I did or any other method, please 
>>>> let me know! :)
>>>>
>>>> You can find all IGopher sources here: GitHub repository 
>>>> <https://github.com/hbollon/IGopher> (Proxy stuff excluded)
>>>>
>>>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/83ed087c-120f-4b9e-ba2d-b9e181c11398n%40googlegroups.com.

Reply via email to