diff --git a/.travis.yml b/.travis.yml index 56196ff3b99563f57452e4b479e1015240fb442b..d24b9c9f6c71eff88f6f2fdbe47fec5e80e10616 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ services: language: go go: - 1.7 +- 1.8 install: - go get github.com/tools/godep script: diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index e5f883bfb5d3de81d3de3eeac757b3e45287103a..e3b3024c28393482455e346d1bffc006a98055dc 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,7 +1,7 @@ { "ImportPath": "github.com/gambol99/keycloak-proxy", "GoVersion": "go1.7", - "GodepVersion": "v75", + "GodepVersion": "v79", "Deps": [ { "ImportPath": "github.com/Sirupsen/logrus", @@ -72,8 +72,8 @@ }, { "ImportPath": "github.com/gambol99/goproxy", - "Comment": "v1.0-96-g6016d23", - "Rev": "16403edea88bc5b716703dca58a5e94ad8b11879" + "Comment": "v1.0-87-gc3b6ff1", + "Rev": "c3b6ff1178d68ec9e93f7c996f41a3df89931d9f" }, { "ImportPath": "github.com/fsnotify/fsnotify", diff --git a/vendor/github.com/gambol99/goproxy/README.md b/vendor/github.com/gambol99/goproxy/README.md index b8ef757101f91900226c4e8a0447e2b9ba7e6e13..347993f7879a4573d9d436b28355a2840c17483f 100644 --- a/vendor/github.com/gambol99/goproxy/README.md +++ b/vendor/github.com/gambol99/goproxy/README.md @@ -36,7 +36,7 @@ as customable as goproxy intend to be. The main difference is, Fiddler is not intended to be used as a real proxy. A possible use case that suits goproxy but -not Fiddler, is, gathering statistics on page load times for a certain website over a week. +not Fiddler, is, gathering statisitics on page load times for a certain website over a week. With goproxy you could ask all your users to set their proxy to a dedicated machine running a goproxy server. Fiddler is a GUI app not designed to be ran like a server for multiple users. diff --git a/vendor/github.com/gambol99/goproxy/certs.go b/vendor/github.com/gambol99/goproxy/certs.go index 8da2e6240a9ab123adcd435540cbbb6526f4e2a5..1e8febdef78c474ba3c405eb637bb15507c34e08 100644 --- a/vendor/github.com/gambol99/goproxy/certs.go +++ b/vendor/github.com/gambol99/goproxy/certs.go @@ -22,35 +22,90 @@ var defaultTLSConfig = &tls.Config{ } var CA_CERT = []byte(`-----BEGIN CERTIFICATE----- -MIICSjCCAbWgAwIBAgIBADALBgkqhkiG9w0BAQUwSjEjMCEGA1UEChMaZ2l0aHVi -LmNvbS9lbGF6YXJsL2dvcHJveHkxIzAhBgNVBAMTGmdpdGh1Yi5jb20vZWxhemFy -bC9nb3Byb3h5MB4XDTAwMDEwMTAwMDAwMFoXDTQ5MTIzMTIzNTk1OVowSjEjMCEG -A1UEChMaZ2l0aHViLmNvbS9lbGF6YXJsL2dvcHJveHkxIzAhBgNVBAMTGmdpdGh1 -Yi5jb20vZWxhemFybC9nb3Byb3h5MIGdMAsGCSqGSIb3DQEBAQOBjQAwgYkCgYEA -vz9BbCaJjxs73Tvcq3leP32hAGerQ1RgvlZ68Z4nZmoVHfl+2Nr/m0dmW+GdOfpT -cs/KzfJjYGr/84x524fiuR8GdZ0HOtXJzyF5seoWnbBIuyr1PbEpgRhGQMqqOUuj -YExeLbfNHPIoJ8XZ1Vzyv3YxjbmjWA+S/uOe9HWtDbMCAwEAAaNGMEQwDgYDVR0P -AQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8w -DAYDVR0RBAUwA4IBKjALBgkqhkiG9w0BAQUDgYEAIcL8huSmGMompNujsvePTUnM -oEUKtX4Eh/+s+DSfV/TyI0I+3GiPpLplEgFWuoBIJGios0r1dKh5N0TGjxX/RmGm -qo7E4jjJuo8Gs5U8/fgThZmshax2lwLtbRNwhvUVr65GdahLsZz8I+hySLuatVvR -qHHq/FQORIiNyNpq/Hg= +MIIF8jCCA9qgAwIBAgIUAp68XvvuMwaTCeQQjGxHEZhJcPgwDQYJKoZIhvcNAQEN +BQAwgZAxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZy +YW5jaXNjbzEUMBIGA1UEChMLQ29yZU9TLCBJbmMxIjAgBgNVBAsTGWdpdGh1Yi5j +b20vY29yZW9zL2dvcHJveHkxIjAgBgNVBAMTGWdpdGh1Yi5jb20vY29yZW9zL2dv +cHJveHkwHhcNMTcwMjIyMjI0NjAwWhcNMjIwMjIxMjI0NjAwWjCBkDELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRQwEgYD +VQQKEwtDb3JlT1MsIEluYzEiMCAGA1UECxMZZ2l0aHViLmNvbS9jb3Jlb3MvZ29w +cm94eTEiMCAGA1UEAxMZZ2l0aHViLmNvbS9jb3Jlb3MvZ29wcm94eTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBANghh+Y4gUYyIY1YzAgHBuLjTt13z5ED +tbjksaK8kUMofaYCnQRrepTORB3xpxn9cIXpmmPND/c3pUZz+sSbidZF+Rfkz+G4 +oo5I04X7R2iFrw9jECcavr7qGqOt+vhep9iqVaioSWTKZoXY9FOxTpEUfdKyLE8K +7rd6PfdjkbZ2ZqoKdRHARBE6QlTJv+elIRM6kzshx0oSdZfaUvLdmXFqHdfKz8gb +FABaa8Aca6r76wHRnhK3+RQ2p+Wfz45/ZMxqQVIMr2mbiCCL4lSUPkUrid6L5DoA +4A4pLqY+Y0cX2qIF+lmgUwUPvunqM7dA9toHjwBgeB1yF+jVOhFOizB/dXSCsBA2 +D47raP3REIxE6N05pEpGZqUPatcdyakP1Dan9aVO2W3o7P/LGPBdB5AiwVxRiVPx +dNrz+UcE3dCPBrc9bxJLjyD7PtgPTrZ+hzR/kBfBW/+jvBRKIB5NCNSuQEUN99it +P1fIencEz1ghaWhCAU5tQutnVBu8d0YlgjfnaD4vCJYEprifAiIpgZH3HaiHfG14 +UXFbdU0MmuIrie2PMAGmtnjhWHG0vc8f2THRklq/CtbfNWtPzUfYv+rpaOFw7bj+ +km0n3kVuGzWZhXPixe63TATR5lK9p21CZPXS+2mW/8uswBiJ4aT5lUZmCf/i1xyn +Gzgv9so56vjFAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBSYLcfHmyPv3jmdTyBs/lu5PZLxPTANBgkqhkiG9w0BAQ0F +AAOCAgEAppb+3MhGcL/IjqyRIWCc587eVMMtuHwGlczpMfXnf8TU0MPrvOdTPrqg +a+AEc1W8O6IGrowmgbZVgfr7Pw0BL4VcSdAEbP7QcbPmRAudF+/xdm5vylJmB339 +4Yq5v1G2Ya5AN5PAHx3WVOA7s/caz30DGrsAoNhezlrk8WJRRxrQUauNveXyxNya +urBrqjqNVUWAlv2fRHuNXsxMvG32MWQ1BoPa9Ix+dUWYeeWkxS5E9oNo99TRRTHB +WX4SpsIynmhE+pFEzu2HVP2k5GE5+i78F9/N0cgI5QMSzgYxSebyPEflL1ziqWoq +4xio69OM0Cq+GCxvOZmvKaNlOjwJa6NWa8IyumNFclTLNzMlBaDkVE9Tb5VEBphO +ODzSxFMuAfXba4UlRjG9+BLg8ayzu2t9amB+B5/3/1+gkx1TE26jZNwleU6MnpFv +umPqPa9AW2ZPobCTGjV3MwOIKFyO/cPt4fI6BpKF7jYhUYzsht/BZqQZU/rWY6h8 +GSEH8LcJk+Paaw03HzZWSbMp2oXNTz/BpQjuAdWAkeS3yhx5gvHIysWHvPRT017b +Tx+Va9+T4DElT0HtmR/w27bXNL9Urj38ETSlbYoilL7ZKltj8djXmqkI1ZF8T1zk +tj1zk0XYkestBgfrk9XYwvvL7DLyRPPZTAiVq/o5xn5zWp/y40M= -----END CERTIFICATE-----`) var CA_KEY = []byte(`-----BEGIN RSA PRIVATE KEY----- -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 -----END RSA PRIVATE KEY-----`) var GoproxyCa, goproxyCaErr = tls.X509KeyPair(CA_CERT, CA_KEY) diff --git a/vendor/github.com/gambol99/goproxy/https.go b/vendor/github.com/gambol99/goproxy/https.go index 139f88d51936f2c502c6b92a4575a8a603ed3ef3..a9fa6ada69e3e245cbec2b44999ed6e82140dc6d 100644 --- a/vendor/github.com/gambol99/goproxy/https.go +++ b/vendor/github.com/gambol99/goproxy/https.go @@ -10,10 +10,8 @@ import ( "net/http" "net/url" "os" - "regexp" "strconv" "strings" - "sync" "sync/atomic" ) @@ -33,7 +31,6 @@ var ( MitmConnect = &ConnectAction{Action: ConnectMitm, TLSConfig: TLSConfigFromCA(&GoproxyCa)} HTTPMitmConnect = &ConnectAction{Action: ConnectHTTPMitm, TLSConfig: TLSConfigFromCA(&GoproxyCa)} RejectConnect = &ConnectAction{Action: ConnectReject, TLSConfig: TLSConfigFromCA(&GoproxyCa)} - httpsRegexp = regexp.MustCompile(`^https:\/\/`) ) type ConnectAction struct { @@ -101,25 +98,8 @@ func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request } ctx.Logf("Accepting CONNECT to %s", host) proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n")) - - targetTCP, targetOK := targetSiteCon.(*net.TCPConn) - proxyClientTCP, clientOK := proxyClient.(*net.TCPConn) - if targetOK && clientOK { - go copyAndClose(ctx, targetTCP, proxyClientTCP) - go copyAndClose(ctx, proxyClientTCP, targetTCP) - } else { - go func() { - var wg sync.WaitGroup - wg.Add(2) - go copyOrWarn(ctx, targetSiteCon, proxyClient, &wg) - go copyOrWarn(ctx, proxyClient, targetSiteCon, &wg) - wg.Wait() - proxyClient.Close() - targetSiteCon.Close() - - }() - } - + go copyAndClose(ctx, targetSiteCon, proxyClient) + go copyAndClose(ctx, proxyClient, targetSiteCon) case ConnectHijack: ctx.Logf("Hijacking CONNECT to %s", host) proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n")) @@ -153,7 +133,6 @@ func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request httpError(proxyClient, ctx, err) return } - defer resp.Body.Close() } resp = proxy.filterResponse(resp, ctx) if err := resp.Write(proxyClient); err != nil { @@ -186,9 +165,9 @@ func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request } defer rawClientTls.Close() clientTlsReader := bufio.NewReader(rawClientTls) + for !isEof(clientTlsReader) { req, err := http.ReadRequest(clientTlsReader) - var ctx = &ProxyCtx{Req: req, Session: atomic.AddInt64(&proxy.sess, 1), proxy: proxy} if err != nil && err != io.EOF { return } @@ -196,12 +175,10 @@ func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request ctx.Warnf("Cannot read TLS request from mitm'd client %v %v", r.Host, err) return } + req.RemoteAddr = r.RemoteAddr // since we're converting the request, need to carry over the original connecting IP as well ctx.Logf("req %v", r.Host) - - if !httpsRegexp.MatchString(req.URL.String()) { - req.URL, err = url.Parse("https://" + r.Host + req.URL.String()) - } + req.URL, err = url.Parse("https://" + r.Host + req.URL.String()) // Bug fix which goproxy fails to provide request // information URL in the context when does HTTPS MITM @@ -209,6 +186,11 @@ func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request req, resp := proxy.filterRequest(req, ctx) if resp == nil { + if isWebSocketRequest(req) { + ctx.Logf("Request looks like websocket upgrade.") + proxy.serveWebsocketTLS(ctx, w, req, tlsConfig, rawClientTls) + return + } if err != nil { ctx.Warnf("Illegal URL %s", "https://"+r.Host+req.URL.Path) return @@ -221,9 +203,8 @@ func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request } ctx.Logf("resp %v", resp.Status) } - resp = proxy.filterResponse(resp, ctx) - defer resp.Body.Close() + resp = proxy.filterResponse(resp, ctx) text := resp.Status statusCode := strconv.Itoa(resp.StatusCode) + " " if strings.HasPrefix(text, statusCode) { @@ -284,20 +265,15 @@ func httpError(w io.WriteCloser, ctx *ProxyCtx, err error) { } } -func copyOrWarn(ctx *ProxyCtx, dst io.Writer, src io.Reader, wg *sync.WaitGroup) { - if _, err := io.Copy(dst, src); err != nil { +func copyAndClose(ctx *ProxyCtx, w, r net.Conn) { + connOk := true + if _, err := io.Copy(w, r); err != nil { + connOk = false ctx.Warnf("Error copying to client: %s", err) } - wg.Done() -} - -func copyAndClose(ctx *ProxyCtx, dst, src *net.TCPConn) { - if _, err := io.Copy(dst, src); err != nil { - ctx.Warnf("Error copying to client: %s", err) + if err := r.Close(); err != nil && connOk { + ctx.Warnf("Error closing: %s", err) } - - dst.CloseWrite() - src.CloseRead() } func dialerFromEnv(proxy *ProxyHttpServer) func(network, addr string) (net.Conn, error) { @@ -341,19 +317,15 @@ func (proxy *ProxyHttpServer) NewConnectDialToProxy(https_proxy string) func(net c.Close() return nil, err } - defer resp.Body.Close() if resp.StatusCode != 200 { - resp, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } + resp, _ := ioutil.ReadAll(resp.Body) c.Close() return nil, errors.New("proxy refused connection" + string(resp)) } return c, nil } } - if u.Scheme == "https" { + if u.Scheme == "https" || u.Scheme == "wss" { if strings.IndexRune(u.Host, ':') == -1 { u.Host += ":443" } @@ -379,12 +351,9 @@ func (proxy *ProxyHttpServer) NewConnectDialToProxy(https_proxy string) func(net c.Close() return nil, err } - defer resp.Body.Close() if resp.StatusCode != 200 { - body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 500)) - if err != nil { - return nil, err - } + body, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 500)) + resp.Body.Close() c.Close() return nil, errors.New("proxy refused connection" + string(body)) } diff --git a/vendor/github.com/gambol99/goproxy/proxy.go b/vendor/github.com/gambol99/goproxy/proxy.go index 5c69597af56e9ef08804ce5a90e267af428f15cf..a83f0489263c8e3db155ec4bb6546f5c07b594ba 100644 --- a/vendor/github.com/gambol99/goproxy/proxy.go +++ b/vendor/github.com/gambol99/goproxy/proxy.go @@ -19,6 +19,7 @@ type ProxyHttpServer struct { // setting Verbose to true will log information on each request sent to the proxy Verbose bool Logger *log.Logger + isReverseProxy bool NonproxyHandler http.Handler reqHandlers []ReqHandler respHandlers []RespHandler @@ -75,7 +76,7 @@ func removeProxyHeaders(ctx *ProxyCtx, r *http.Request) { // and would wrap the response body with the relevant reader. r.Header.Del("Accept-Encoding") // curl can add that, see - // https://jdebp.eu./FGA/web-proxy-connection-header.html + // http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/web-proxy-connection-header.html r.Header.Del("Proxy-Connection") r.Header.Del("Proxy-Authenticate") r.Header.Del("Proxy-Authorization") @@ -98,20 +99,30 @@ func (proxy *ProxyHttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) var err error ctx.Logf("Got request %v %v %v %v", r.URL.Path, r.Host, r.Method, r.URL.String()) - if !r.URL.IsAbs() { + + if (proxy.isReverseProxy && r.URL.IsAbs()) || (!proxy.isReverseProxy && !r.URL.IsAbs()) { proxy.NonproxyHandler.ServeHTTP(w, r) return } r, resp := proxy.filterRequest(r, ctx) if resp == nil { + if proxy.isReverseProxy && (r.URL.Scheme == "" || r.URL.Host == "") { + panic("ReverseProxy did not rewrite request's Scheme or Host") + } + + if isWebSocketRequest(r) { + ctx.Logf("Request looks like websocket upgrade.") + proxy.serveWebsocket(ctx, w, r) + } + removeProxyHeaders(ctx, r) resp, err = ctx.RoundTrip(r) if err != nil { ctx.Error = err resp = proxy.filterResponse(nil, ctx) if resp == nil { - ctx.Logf("error read response %v %v:", r.URL.Host, err.Error()) + ctx.Logf("Error read response %v %v:", r.URL.Host, err.Error()) http.Error(w, err.Error(), 500) return } @@ -120,7 +131,7 @@ func (proxy *ProxyHttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) } origBody := resp.Body resp = proxy.filterResponse(resp, ctx) - defer origBody.Close() + ctx.Logf("Copying response to client %v [%d]", resp.Status, resp.StatusCode) // http.ResponseWriter will take care of filling the correct response length // Setting it now, might impose wrong value, contradicting the actual new @@ -149,11 +160,26 @@ func NewProxyHttpServer() *ProxyHttpServer { respHandlers: []RespHandler{}, httpsHandlers: []HttpsHandler{}, NonproxyHandler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - http.Error(w, "This is a proxy server. Does not respond to non-proxy requests.", 500) + http.Error(w, "This is a forward proxy server. Does not respond to non-proxy requests.", 500) }), - Tr: &http.Transport{TLSClientConfig: tlsClientSkipVerify, - Proxy: http.ProxyFromEnvironment}, + Tr: &http.Transport{ + TLSClientConfig: tlsClientSkipVerify, + Proxy: http.ProxyFromEnvironment, + }, } + proxy.ConnectDial = dialerFromEnv(&proxy) + return &proxy } + +func NewReverseProxyHttpServer() *ProxyHttpServer { + reverseProxy := NewProxyHttpServer() + + reverseProxy.isReverseProxy = true + reverseProxy.NonproxyHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + http.Error(w, "This is a reverse proxy server. Does not respond to proxy requests.", 500) + }) + + return reverseProxy +} diff --git a/vendor/github.com/gambol99/goproxy/websocket.go b/vendor/github.com/gambol99/goproxy/websocket.go new file mode 100644 index 0000000000000000000000000000000000000000..2a9699117a9e88d995dae3e626bae4d4c1d43026 --- /dev/null +++ b/vendor/github.com/gambol99/goproxy/websocket.go @@ -0,0 +1,121 @@ +package goproxy + +import ( + "bufio" + "crypto/tls" + "io" + "net/http" + "net/url" + "strings" +) + +func headerContains(header http.Header, name string, value string) bool { + for _, v := range header[name] { + for _, s := range strings.Split(v, ",") { + if strings.EqualFold(value, strings.TrimSpace(s)) { + return true + } + } + } + return false +} + +func isWebSocketRequest(r *http.Request) bool { + return headerContains(r.Header, "Connection", "upgrade") && + headerContains(r.Header, "Upgrade", "websocket") +} + +func (proxy *ProxyHttpServer) serveWebsocketTLS(ctx *ProxyCtx, w http.ResponseWriter, req *http.Request, tlsConfig *tls.Config, clientConn *tls.Conn) { + targetURL := url.URL{Scheme: "wss", Host: req.URL.Host, Path: req.URL.Path} + + // Connect to upstream + targetConn, err := tls.Dial("tcp", targetURL.Host, tlsConfig) + if err != nil { + ctx.Warnf("Error dialing target site: %v", err) + return + } + defer targetConn.Close() + + // Perform handshake + if err := proxy.websocketHandshake(ctx, req, targetConn, clientConn); err != nil { + ctx.Warnf("Websocket handshake error: %v", err) + return + } + + // Proxy wss connection + proxy.proxyWebsocket(ctx, targetConn, clientConn) +} + +func (proxy *ProxyHttpServer) serveWebsocket(ctx *ProxyCtx, w http.ResponseWriter, req *http.Request) { + targetURL := url.URL{Scheme: "ws", Host: req.URL.Host, Path: req.URL.Path} + + targetConn, err := proxy.connectDial("tcp", targetURL.Host) + if err != nil { + ctx.Warnf("Error dialing target site: %v", err) + return + } + defer targetConn.Close() + + // Connect to Client + hj, ok := w.(http.Hijacker) + if !ok { + panic("httpserver does not support hijacking") + } + clientConn, _, err := hj.Hijack() + if err != nil { + ctx.Warnf("Hijack error: %v", err) + return + } + + // Perform handshake + if err := proxy.websocketHandshake(ctx, req, targetConn, clientConn); err != nil { + ctx.Warnf("Websocket handshake error: %v", err) + return + } + + // Proxy ws connection + proxy.proxyWebsocket(ctx, targetConn, clientConn) +} + +func (proxy *ProxyHttpServer) websocketHandshake(ctx *ProxyCtx, req *http.Request, targetSiteConn io.ReadWriter, clientConn io.ReadWriter) error { + // write handshake request to target + err := req.Write(targetSiteConn) + if err != nil { + ctx.Warnf("Error writing upgrade request: %v", err) + return err + } + + targetTLSReader := bufio.NewReader(targetSiteConn) + + // Read handshake response from target + resp, err := http.ReadResponse(targetTLSReader, req) + if err != nil { + ctx.Warnf("Error reading handhsake response %v", err) + return err + } + + // Run response through handlers + resp = proxy.filterResponse(resp, ctx) + + // Proxy handshake back to client + err = resp.Write(clientConn) + if err != nil { + ctx.Warnf("Error writing handshake response: %v", err) + return err + } + return nil +} + +func (proxy *ProxyHttpServer) proxyWebsocket(ctx *ProxyCtx, dest io.ReadWriter, source io.ReadWriter) { + errChan := make(chan error, 2) + cp := func(dst io.Writer, src io.Reader) { + _, err := io.Copy(dst, src) + ctx.Warnf("Websocket error: %v", err) + errChan <- err + } + + // Start proxying websocket data + go cp(dest, source) + go cp(source, dest) + <-errChan +}