Skip to content
Snippets Groups Projects
Commit 0baa898c authored by Rohith Jayawardene's avatar Rohith Jayawardene Committed by GitHub
Browse files

Travis (#194)

* Travis

- updating the travis build to use golang 1.8 as well

* - updating the goproxy to use coreos's fork, the original doesn't look like it's maintained anymore
parent 7c37acab
Branches
Tags
No related merge requests found
......@@ -10,6 +10,7 @@ services:
language: go
go:
- 1.7
- 1.8
install:
- go get github.com/tools/godep
script:
......
{
"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",
......
......@@ -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.
......
......@@ -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)
......@@ -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())
}
// 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))
}
......
......@@ -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
}
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
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment