登录
首页 >  Golang >  Go问答

注入代理授权并将请求传输到远程代理的端口转发器

来源:stackoverflow

时间:2024-04-14 10:24:36 347浏览 收藏

大家好,今天本人给大家带来文章《注入代理授权并将请求传输到远程代理的端口转发器》,文中内容主要涉及到,如果你对Golang方面的知识点感兴趣,那就请各位朋友继续看下去吧~希望能真正帮到你们,谢谢!

问题内容

我实际上正在使用 go 构建一个基于 selenium 的自动化工具,称为 igopher,并且我想实现本机代理支持。

但是,我遇到了身份验证方面的问题...

我无法将代理凭据发送到 chrome,如果没有它们,它会通过一个警报框要求进行身份验证,而我几乎无法通过 selenium 进行交互(我什至不确定在无头模式下是否可行)。

所以我想到了一个由我的程序本地托管的中间代理系统,它将添加代理授权标头并将请求传输到远程代理:

我发现这个 nodejs 程序非常有用,我想在 go 中重现它。

主要部分如下:

function createportforwarder(local_host, local_port, remote_host, remote_port, buf_proxy_basic_auth, is_remote_https, ignore_https_cert) {
  net.createserver({allowhalfopen: true}, function (socket) {
    var realcon = (is_remote_https ? tls : net).connect({
      port: remote_port, host: remote_host, allowhalfopen: true,
      rejectunauthorized: !ignore_https_cert /*not used when is_remote_https false*/
    });
    realcon.on('data', function (buf) {
      socket.write(buf);
      realcon.__havegotdata = true;
    }).on('end', function () {
      socket.end();
      if (!realcon.__havegotdata && !realcon.__haveshownerror) {
        console.error('[localproxy(:' + local_port + ')][connection to ' + remote_host + ':' + remote_port + '] error: ended by remote peer');
        realcon.__haveshownerror = true;
      }
    }).on('close', function () {
      socket.end();
      if (!realcon.__havegotdata && !realcon.__haveshownerror) {
        console.error('[localproxy(:' + local_port + ')][connection to ' + remote_host + ':' + remote_port + '] error: reset by remote peer');
        realcon.__haveshownerror = true;
      }
    }).on('error', function (err) {
      console.error('[localproxy(:' + local_port + ')][connection to ' + remote_host + ':' + remote_port + '] ' + err);
      realcon.__haveshownerror = true;
    });

    var parser = new httpparser(httpparser.request);
    parser[httpparser.konheaderscomplete] = function (versionmajor, versionminor, headers, method,
                                                      url, statuscode, statusmessage, upgrade,
                                                      shouldkeepalive) {
      parser.__is_headers_complete = true;
      parser.__upgrade = upgrade;
      parser.__method = method;
    };

    var state = state_none;
    socket.on('data', function (buf) {
      if (!parser) {
        realcon.write(buf);
        return
      }

      var buf_ary = [], unsavedstart = 0, buf_len = buf.length;

      for (var i = 0; i < buf_len; i++) {
        //find first lf
        if (state === state_none) {
          if (buf[i] === lf) {
            state = state_found_lf;
          }
          continue;
        }

        //find second cr lf or lf
        if (buf[i] === lf) {
          parser.__is_headers_complete = false;
          parser.execute(buf.slice(unsavedstart, i + 1));

          if (parser.__is_headers_complete) {
            buf_ary.push(buf.slice(unsavedstart, buf[i - 1] === cr ? i - 1 : i));
            //insert auth header
            buf_ary.push(buf_proxy_basic_auth);
            buf_ary.push(state === state_found_lf_cr ? buf_cr_lf_cr_lf : buf_lf_lf);

            // stop intercepting packets if encountered tls and websocket handshake
            if (parser.__method === 5 /*connect*/ || parser.__upgrade) {
              parser.close();
              parser = null;

              buf_ary.push(buf.slice(i + 1));
              realcon.write(buffer.concat(buf_ary));

              state = state_none;
              return;
            }

            unsavedstart = i + 1;
            state = state_none;
          }
          else {
            state = state_found_lf;
          }
        }
        else if (buf[i] === cr && state === state_found_lf) {
          state = state_found_lf_cr;
        } else {
          state = state_none;
        }
      }

      if (unsavedstart < buf_len) {
        buf = buf.slice(unsavedstart, buf_len);
        parser.execute(buf);
        buf_ary.push(buf);
      }

      realcon.write(buffer.concat(buf_ary));

    }).on('end', cleanup).on('close', cleanup).on('error', function (err) {
      if (!socket.__cleanup) {
        console.error('[localproxy(:' + local_port + ')][incoming connection] ' + err);
      }
    });

    function cleanup() {
      socket.__cleanup = true;
      if (parser) {
        parser.close();
        parser = null;
      }
      realcon.end();
    }
  }).on('error', function (err) {
    console.error('[localproxy(:' + local_port + ')] ' + err);
    process.exit(1);
  }).listen(local_port, local_host === '*' ? undefined : local_host, function () {
    console.log('[localproxy(:' + local_port + ')] ok: forward http://' + local_host + ':' + local_port + ' to http' + (is_remote_https ? 's' : '') + '://' + remote_host + ':' + remote_port);
  });
}

可以在 go 中重现吗?我找到了 tcp 转发器的要点,但我不知道是否可以用它编辑请求标头...

如果这是不可能的(这会让我感到惊讶),我仍然可以在我自己的程序中使用这个节点程序,但我真的更愿意避免使节点成为依赖项。

此外,将其本地包含在我的程序中将使与它的交互变得更容易,特别是能够停止或重新启动该服务器。

因此,如果有人有想法、建议或资源,会对我有很大帮助!我被这个问题困扰了很长时间......

提前致谢!

编辑:

我已经尝试使用 reverseproxy 但没有成功,修改后它无法将请求发送到远程代理,因为它是 connect 代理并且 url 的格式为 //ip:port例如://google.com:443

这是我的代码:

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)

        req.host = remoteserverhost

        // 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
}

curl https://google.com --proxy http://127.0.0.1:8880 输出:

INFO[0013] 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: GetBody: ContentLength:0 TransferEncoding:[] Close:false Host:google.com:443 Form:map[] PostForm:map[] MultipartForm: Trailer:map[] RemoteAddr:127.0.0.1:54126 RequestURI:google.com:443 TLS: Cancel: Response: ctx:0xc000674280}  function=func1 line=59

INFO[0013] 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: GetBody: ContentLength:0 TransferEncoding:[] Close:false Host:http://51.178.xx.xx:3128 Form:map[] PostForm:map[] MultipartForm: Trailer:map[] RemoteAddr:127.0.0.1:54126 RequestURI:google.com:443 TLS: Cancel: Response: ctx:0xc000674280}  function=func1 line=67

INFO[0013] Scheme: , Host: google.com:443, Port: 443     function=func1 line=68

2021/03/16 21:53:51 http: proxy error: unsupported protocol scheme ""

解决方案


看看这段代码,它演示了您之前提出的方案

package main

import (
    "encoding/base64"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/http/httputil"
    "net/url"
    "time"

    auth "github.com/abbot/go-http-auth"
)

func Secret(user, realm string) string {
    if user == "john" {
        // password is "hello"
        return "$1$dlPL2MqE$oQmn16q49SqdmhenQuNgs1"
    }
    return ""
}

func serveWithAuth() {
    authenticator := auth.NewBasicAuthenticator("localhost", Secret)
    http.HandleFunc("/", authenticator.Wrap(func(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
        fmt.Fprintf(w, "

Hello, %s!

", r.Username) })) http.ListenAndServe(":8080", nil) } func serveNoAuth(backURL string) { rpURL, err := url.Parse(backURL) if err != nil { log.Fatal(err) } p := NewSingleHostReverseProxy(rpURL) srv := &http.Server{Handler: p, Addr: ":9090"} srv.ListenAndServe() } func NewSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy { rp := httputil.NewSingleHostReverseProxy(target) director := rp.Director rp.Director = func(req *http.Request) { director(req) if target.User != nil { b := base64.StdEncoding.EncodeToString([]byte(target.User.String())) req.Header.Set("Authorization", fmt.Sprintf("Basic %v", string(b))) } } return rp } func main() { go serveWithAuth() go serveNoAuth("http://john:hello@localhost:8080/") <-time.After(time.Second) resp, err := http.Get("http://localhost:9090/") if err != nil { log.Fatal(err) } b, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Printf("%s", b) }

好了,本文到此结束,带大家了解了《注入代理授权并将请求传输到远程代理的端口转发器》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多Golang知识!

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>