Open matejvelikonja opened 7 years ago
Are you using goproxy as a classic proxy or as a transparent proxy?
I was able to run the basic example and it works fine for both http and https. If the client is configured to use a proxy server, everything works through the http port. If the proxy is a transparent intercept proxy (ie: using iptable rules or the equivalent to forward ports), the sample also worked just fine.
As a classic.
Which example exactly are you talking about?
Hm, that worked perfectly for me as written.
The example is found in /examples/goproxy-basic.
Well, that is not what I would like. What I need:
curl -> goproxy -> squid (or any other proxy) -> website
and i cant figure out how to do it for https. the http example is in the above comment.
Oh I see. I think you could implement some iptables rules for that. Not sure how it could be implemented in goproxy, if that is possible.
the iptables won't work, since I need to add auth in goproxy. the squid proxy is behind auth and i want to connect to it via goproxy, but the client (curl) does not know the credentials.
@matejvelikonja did you figure how to route https though auth proxy
@vedhavyas unfortunately not yet
Have you tried using NewConnectDialToProxy
. Like below.
handler.Tr = &http.Transport{Proxy: func(req *http.Request) (*url.URL, error) {
return url.Parse("http://username:password@proxy.com/")
}}
handler.ConnectDial = handler.NewConnectDialToProxy("http://username:password@proxy.com/")
I am trying to do the same thing. Anyone succeeded with @geekhckr's answer?
I am trying to do the same thing. Anyone succeeded with @geekhckr's answer? are you successed with his answer?
You basically need something like this:
proxy := goproxy.NewProxyHttpServer()
[...]
proxy.OnRequest().HandleConnectFunc(func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
var proxyUrl *url.URL
proxyUrl, _ = url.Parse("http://PROXY_USER:PROXY_PASSW@PROXY_HOST:PROXY_PORT")
proxy.Tr = &http.Transport{
Proxy: http.ProxyURL(proxyUrl),
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
return goproxy.MitmConnect, host
})
[...]
log.Fatal(http.ListenAndServe(":8080", proxy))
Replacing http://PROXY_USER:PROXY_PASSW@PROXY_HOST:PROXY_PORT
with the actual proxy credentials/address.
Quick edit:
this will work only for https targets, if you also need to connect to http targets, you should also replace the http.Transport
on proxy.OnRequest().DoFunc(...)
or on the proxy.Tr
, eg:
proxy := goproxy.NewProxyHttpServer()
var proxyUrl *url.URL
proxyUrl, _ = url.Parse("http://PROXY_USER:PROXY_PASSW@PROXY_HOST:PROXY_PORT")
proxy.Tr = &http.Transport{
Proxy: http.ProxyURL(proxyUrl),
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
@onegithuber
I am trying to do the same thing. Anyone succeeded with @geekhckr's answer?
are you successed with his answer?
I actually created my own implementation from scratch to make it work.
Hi,
I am trying to chain 2 forward proxies. I have goproxy with the following setup on the machine, and the machine has another goproxy that I would like to forward to. They both need to support HTTP + HTTPS.
If I run the following code, then use curl to test it, it works for HTTP, but not HTTPS:
Use case explanation: The machine has multiple ssh reverse port forwards that have a forward proxy listening on the other end. I need to have a "gateway" forward proxy listening on a static ip:port that will check credentials, pick the next forward proxy and forward to it.
client -> goproxy on 127.0.0.1:8080 (checks creds) -> http://127.0.x.x:xxxx (no credentials)
curl --proxy http://127.0.0.1:8080 https://wtfismyip.com/text
curl: (60) server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none
More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
curl --proxy http://127.0.0.1:8080 http://wtfismyip.com/text
xxx.xxx.xxx.xxx
package main
import (
"crypto/tls"
"net/http"
"net/url"
"github.com/elazarl/goproxy"
)
func startProxy(addr string) {
proxy := goproxy.NewProxyHttpServer()
proxy.OnRequest().HandleConnectFunc(func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
var proxyURL *url.URL
proxyURL, _ = url.Parse("http://127.0.1.1:10001")
proxy.Tr = &http.Transport{
Proxy: http.ProxyURL(proxyURL),
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
return goproxy.MitmConnect, host
})
//proxy.Verbose = true
http.ListenAndServe(addr, proxy)
}
func main() {
startProxy("127.0.0.1:8080")
}
Any insight into what I have wrong would be greatly appreciated!
Update:
This seems to be working for me. Is this the correct approach, or does anyone see an issue with doing this?
func startProxy(addr string) {
proxy := goproxy.NewProxyHttpServer()
proxy.Tr = &http.Transport{Proxy: func(req *http.Request) (*url.URL, error) {
return url.Parse("http://127.0.1.1:10001")
}}
proxy.ConnectDial = proxy.NewConnectDialToProxy("http://127.0.1.1:10001")
http.ListenAndServe(addr, proxy)
}
@alexelisenko did you ever get it working for https?
@dylankilkenny Do you mean to to have the proxy listen and serve via TLS? or dial an HTTPS target?
The Proxy-Authorization
header is not set when using NewConnectDialToProxy
.
Hi everybody, did someone find a solution for this issue?
For the latest version of goproxy
, there are two ways to implement chaining http(s) proxy.
One simple way is to use NewConnectDialToProxy
or NewConnectDialToProxyWithHandler
provided by goproxy
. Below shows a working example. Note the example supports both HTTP or HTTPS proxy to be forwared to, and with or without proxy authorization. Just use a correct urlString
format.
package main
import (
"crypto/tls"
"encoding/base64"
"github.com/elazarl/goproxy"
"log"
"net/http"
"net/url"
)
const urlString = "https://username:password@another-proxy-host:another-proxy-port"
// const urlString = "http://username:password@another-proxy-host:another-proxy-port"
// const urlString = "https://another-proxy-host:another-proxy-port"
// const urlString = "http://another-proxy-host:another-proxy-port"
func main() {
proxyURL, err := url.Parse(urlString)
if err != nil {
log.Fatal(err)
}
proxy := goproxy.NewProxyHttpServer()
proxy.Tr = &http.Transport{
Proxy: http.ProxyURL(proxyURL),
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
username := proxyURL.User.Username()
password, _ := proxyURL.User.Password()
var proxyAuthorization string
if len(username) > 0 || len(password) > 0 {
proxyAuthorization = "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
}
proxy.ConnectDial = proxy.NewConnectDialToProxyWithHandler(urlString, func(req *http.Request) {
if len(proxyAuthorization) > 0 {
req.Header.Set("Proxy-Authorization", proxyAuthorization)
}
})
proxy.Verbose = true
log.Fatal(http.ListenAndServe(":8080", proxy))
}
Another way is to use hijack, which means we can implement customized behavior for CONNECT
request. A complete example is shown in this gist.
@uzxmx The code seems that cannot connect to multiple targets, you have to specify a target in NewConnectDialToProxyWithHandler :(
@xzycn Maybe this will be work
connectDialer := newConnectDialer(proxy)
proxy.ConnectDialWithReq = func(req *http.Request, network string, addr string) (net.Conn, error) {
return connectDialer(req, network, addr)
}
func newConnectDialer(proxy *goproxy.ProxyHttpServer) func(req *http.Request, network, addr string) (net.Conn, error) {
return func(req *http.Request, network, addr string) (net.Conn, error) {
// proxyService have multi targets, you can chose a proxy per req dynamic
u, err := proxyService.GetProxy(req.Context())
if err != nil {
return nil, err
}
proxyAddr := getAddrFromURL(u)
username := u.User.Username()
password, _ := u.User.Password()
var proxyAuthorization string
if len(username) > 0 || len(password) > 0 {
proxyAuthorization = "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
}
isTLS := u.Scheme == "https" || u.Scheme == "wss"
///
connectReq := &http.Request{
Method: "CONNECT",
URL: &url.URL{Opaque: addr},
Host: addr,
Header: make(http.Header),
}
connectReq = connectReq.WithContext(req.Context())
if len(proxyAuthorization) > 0 {
connectReq.Header.Set("Proxy-Authorization", proxyAuthorization)
}
c, err := net.Dial(network, proxyAddr)
if err != nil {
return nil, err
}
if isTLS {
c = tls.Client(c, proxy.Tr.TLSClientConfig)
}
err = connectReq.Write(c)
if err != nil {
return nil, err
}
br := bufio.NewReader(c)
resp, err := http.ReadResponse(br, connectReq)
if err != nil {
_ = c.Close()
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
resp, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
_ = c.Close()
return nil, errors.New("proxy refused connection" + string(resp))
}
return c, nil
}
}
func getAddrFromURL(u *url.URL) string {
addr := u.Host
if !strings.ContainsRune(addr, ':') {
if u.Scheme == "" || u.Scheme == "http" {
addr += ":80"
} else if u.Scheme == "https" {
addr += ":443"
}
}
return addr
}
I use GoProxy as an intermediate proxy. So, just transferring traffic from GoProxy to some other proxy.
It works as intended for the HTTP protocol, but HTTPS does not. It connects directly to the website, instead of connecting first to parent proxy and than to the website. I don't need / want the MITM, so basically, just forwarding of CONNECT protocol to the parent proxy (with auth).
How can I achieve this?
The following code works for HTTP, but not for HTTPS.