Пример #1
0
func handleDialDirectConnection(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	// see who we're gonna call
	req.ParseForm()
	switch req.Method {
	case "POST":
		connectionID := req.Form.Get("connectionID")
		if connectionID == "" {
			log.Printf("Missing connectionID")
			return nil, goproxy.NewResponse(req,
				goproxy.ContentTypeText, http.StatusBadRequest,
				"Missing connectionID.")
		}
		log.Printf("handleDirectConnection has connectionID: %s", connectionID)

		invitation := getInvitation(connectionID)
		if invitation == nil {
			log.Printf("Wrong connectionID")
			return nil, goproxy.NewResponse(req,
				goproxy.ContentTypeText, http.StatusBadRequest,
				"Proxy has been restarted since this page was served. Please log in again.")
		}

		//log.Printf("invitation is: %#v", invitation)

		inviteeName, inviteeHost, err := eccentric.ParseCN(invitation.InviteeCN)
		check(err)
		log.Printf("invitee is: %v, %v", inviteeName, inviteeHost)

		// fetch our own identity
		ourCreds := getLoggedInCreds(inviteeHost)
		if ourCreds == nil {
			// should not happen, as we need to be logged in to get the dial-button, but anyway
			log.Println("Site says to dial a direct connection but you are not logged in.")
			return nil, goproxy.NewResponse(req,
				goproxy.ContentTypeText, http.StatusInternalServerError,
				"Ecca Proxy error: Site says to dial a direct connection but you haven't logged in. Please log in first, then try again.")
		}

		log.Printf("our creds are: %v\n", ourCreds.CN)
		ourCert, err := tls.X509KeyPair(ourCreds.Cert, ourCreds.Priv)
		check(err)
		//log.Printf("ourCert is: %#v", ourCert)

		// call out and show the response
		response := dialDirectConnection(invitation, ourCert)
		return nil, goproxy.NewResponse(req, goproxy.ContentTypeText, http.StatusOK, response)
	}

	log.Printf("Unexpected method: %#v", req.Method)
	return nil, nil
}
Пример #2
0
func requestHanderFunc(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	urlOld := r.URL.String()
	log.Println("raw_url->", urlOld)
	r.Header.Set("Connection", "Close")
	r.Header.Del("Proxy-Connection")

	if conf.IsProxyHost(urlOld) {
		log.Println("direct IsProxyHost->", urlOld)
		return r, nil
	}

	proxy := conf.GetOneProxy()
	if proxy == nil {
		log.Println("no proxy")
		return r, goproxy.NewResponse(r, goproxy.ContentTypeHtml, http.StatusBadGateway, "no proxy")
	}

	urlNew, _, err := proxy.GenReqUrl(urlOld)

	if err != nil {
		log.Println("encryptURL", urlOld, "failed", err)
		return r, goproxy.NewResponse(r, goproxy.ContentTypeHtml, http.StatusBadGateway, "encrypt url failed")
	}

	log.Println(urlOld, "--->", urlNew)
	//	var err error
	r.URL, err = url.Parse(urlNew)

	if err != nil {
		log.Println("parse new url failed", err)
		return r, goproxy.NewResponse(r, goproxy.ContentTypeHtml, http.StatusBadGateway, "create url failed,check proxy url")
	}

	r.Host = r.URL.Host

	r.Header.Add("is_client", "1")
	r.Header.Set(kxKey, proxy.SecertKey)
	if conf.HiddenIp {
		r.Header.Set("hidden_ip", "1")
	}

	kxutil.HeaderEnc(r.Header)
	//	body:=r.Body
	//	reader := kxutil.CipherStreamReader(proxy.SecertKey, encodeURL, body)
	//	r.Body = ioutil.NopCloser(reader)
	//	r.Header.Set("_kx_enc_","1")
	//	panic("a")

	return r, nil
}
Пример #3
0
func (h *FilteringHandler) OnRequest(r *http.Request, ctx *goproxy.ProxyCtx) (
	*http.Request, *http.Response) {

	host := r.URL.Host
	if host == "" {
		host = r.Host
	}
	rq := &adblock.Request{
		URL:          r.URL.String(),
		Domain:       host,
		OriginDomain: getReferrerDomain(r),
	}
	rules := h.Cache.Rules()
	start := time.Now()
	matched, id := rules.Matcher.Match(rq)
	end := time.Now()
	duration := end.Sub(start) / time.Millisecond
	if matched {
		rule := rules.Rules[id]
		log.Printf("rejected in %dms: %s\n", duration, r.URL.String())
		log.Printf("  by %s\n", rule)
		return r, goproxy.NewResponse(r, goproxy.ContentTypeText,
			http.StatusNotFound, "Not Found")
	}
	ctx.UserData = &ProxyState{
		Duration: duration,
		URL:      r.URL.String(),
	}
	return r, nil
}
Пример #4
0
func main() {

	var templateFilePath string
	if len(os.Args) == 1 {
		templateFilePath = "./template.html"
	} else if len(os.Args) == 2 {
		templateFilePath = os.Args[1]
	} else {
		panic("Unknown number of arguments. Please enter a template file")
	}

	content, err := ioutil.ReadFile(templateFilePath)
	if err != nil {
		panic(err)
	}

	var htmlStr string = string(content)

	proxy := goproxy.NewProxyHttpServer()
	proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*$"))).HandleConnect(goproxy.AlwaysMitm)
	proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
		return req, goproxy.NewResponse(req, goproxy.ContentTypeHtml, http.StatusForbidden, htmlStr)
	})

	log.Fatalln(http.ListenAndServe(":8401", proxy))
}
Пример #5
0
func initUpstreamProxy() {
	go func() {
		proxy := goproxy.NewProxyHttpServer()

		proxy.OnRequest().DoFunc(
			func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
				if !hasExpectedCustomHeaders(r.Header) {
					ctx.Logf("missing expected headers: %+v", ctx.Req.Header)
					return nil, goproxy.NewResponse(r, goproxy.ContentTypeText, http.StatusUnauthorized, "")
				}
				return r, nil
			})

		proxy.OnRequest().HandleConnectFunc(
			func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
				if !hasExpectedCustomHeaders(ctx.Req.Header) {
					ctx.Logf("missing expected headers: %+v", ctx.Req.Header)
					return goproxy.RejectConnect, host
				}
				return goproxy.OkConnect, host
			})

		err := http.ListenAndServe("127.0.0.1:2161", proxy)
		if err != nil {
			fmt.Printf("upstream proxy failed: %s", err)
		}
	}()

	// TODO: wait until listener is active?
}
Пример #6
0
func listPackages(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	val, _, _, err := cn.Get("packages")
	if err != nil {
		return r, nil
	}
	log.Println("MEMCACHED FROM GO SERVER")
	return r, goproxy.NewResponse(r, "application/json", http.StatusOK, val)
}
Пример #7
0
// display the configuration
func (bl *BlockClock) conf(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	bl.update()

	return r, goproxy.NewResponse(r,
		goproxy.ContentTypeText,
		http.StatusOK,
		fmt.Sprintf("hosts %s\nstart blocking at: %s", bl.Hosts, bl.LockAt))
}
Пример #8
0
func getPackage(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	elements := strings.Split(r.URL.Path, "/")
	packageName := elements[len(elements)-1]

	var name, url string
	if err := pool.QueryRow("getPackage", packageName).Scan(&name, &url); err != nil {
		if err == pgx.ErrNoRows {
			return r, goproxy.NewResponse(r, "text/html", http.StatusNotFound, "Package not found")
		}
		return r, goproxy.NewResponse(r, "text/html", http.StatusInternalServerError, "Internal server error")
	}

	data, err := json.Marshal(Package{Name: name, URL: url})
	if err != nil {
		return r, goproxy.NewResponse(r, "text/html", http.StatusInternalServerError, "Internal server error")
	}
	return r, goproxy.NewResponse(r, "application/json", http.StatusOK, string(data))
}
Пример #9
0
// getResponse returns stored response from cache
func (d *DBClient) getResponse(req *http.Request) *http.Response {
	log.Info("Returning response")

	key := getRequestFingerprint(req)
	var payload Payload

	payloadBts, err := redis.Bytes(d.cache.get(key))

	if err == nil {
		log.Info("Decoding bytes")
		// getting cache response
		err = json.Unmarshal(payloadBts, &payload)
		if err != nil {
			log.Error(err)
			// what now?
		}

		newResponse := &http.Response{}
		newResponse.Request = req
		// adding headers
		newResponse.Header = make(http.Header)
		if len(payload.Response.Headers) > 0 {
			for k, values := range payload.Response.Headers {
				// headers is a map, appending each value
				for _, v := range values {
					newResponse.Header.Add(k, v)
				}

			}
		}
		newResponse.Header.Set("Gen-Proxy", "Playback")
		// adding body
		buf := bytes.NewBuffer(payload.Response.Body)
		newResponse.ContentLength = int64(buf.Len())
		newResponse.Body = ioutil.NopCloser(buf)

		newResponse.StatusCode = payload.Response.Status

		log.WithFields(log.Fields{
			"key":        key,
			"status":     payload.Response.Status,
			"bodyLength": newResponse.ContentLength,
		}).Info("Response found, returning")

		return newResponse

	} else {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Failed to retrieve response from cache")
		// return error? if we return nil - proxy forwards request to original destination
		return goproxy.NewResponse(req,
			goproxy.ContentTypeText, http.StatusPreconditionFailed,
			"Coudldn't find recorded request, please record it first!")
	}

}
Пример #10
0
// eccaProxy: proxy the user requests and authenticate with the credentials we know.
func eccaProxy(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	//log.Println("\n\n\nRequest is ", req.Method, req.URL.String())
	ctx.Logf("Start-of-eccaProxy handler")
	for _, c := range req.Cookies() {
		ctx.Logf("Cookie send by the client is: %#v", c.Name)
	}

	// set the scheme to https so we connect upstream securely
	if req.URL.Scheme == "http" {
		req.URL.Scheme = "https"
	}

	// Copy the body because we need it untouched. But we also need to parse
	// the POST parameters and that eats the original buffer with the body
	body, err := ioutil.ReadAll(req.Body)
	check(err)
	req.Body.Close() // close it before replacing. Prevents leaking file descriptors.

	// give the data back immedeately.
	req.Body = ioutil.NopCloser(bytes.NewReader(body))

	// Read the parameters
	req.ParseForm()                                    // eats req.Body
	req.Body = ioutil.NopCloser(bytes.NewReader(body)) // copy back in again

	// Check for POST method with 'encrypt', 'sign' or 'initiate-direct-connection' parameter.
	if req.Method == "POST" {
		if req.Form.Get("initiate-direct-connection") == "required" {
			// create a direct connection listener, awaiting reply
			return initiateDirectConnection(req)
		} else if req.Form.Get("encrypt") == "required" {
			// transparantly encrypt and sign for a private message
			return encryptMessage(req)
		} else if req.Form.Get("sign") == "required" || req.Form.Get("sign") == "optional" {
			// transparently sign the message before publication
			return signMessage(req, ctx)
		}
	}

	// Fetch the request from upstream
	resp, err := fetchRequest(req, ctx)
	if err != nil {
		ctx.Warnf("There was an error fetching the users' request: %v", err)
		return req, goproxy.NewResponse(req,
			goproxy.ContentTypeText, http.StatusInternalServerError,
			"Some server error!")
	}

	ctx.Logf("response is %#v", resp)
	for _, c := range resp.Cookies() {
		ctx.Logf("Cookie send by the server is: %#v\n", c.Name)
	}
	ctx.Logf("End-of-eccaProxy handler")
	//log.Printf("Sleeping for 10 seconds...\n")
	//time.Sleep(10 * time.Second)
	return nil, resp // let goproxy send our response
}
Пример #11
0
func (h *FilteringHandler) OnResponse(r *http.Response,
	ctx *goproxy.ProxyCtx) *http.Response {

	if r == nil {
		// Happens if RoundTrip fails
		return r
	}

	state, ok := ctx.UserData.(*ProxyState)
	if !ok {
		// The request was rejected by the previous handler
		return r
	}

	duration2 := time.Duration(0)
	mediaType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
	if err == nil && len(mediaType) > 0 {
		host := ctx.Req.URL.Host
		if host == "" {
			host = ctx.Req.Host
		}
		rq := &adblock.Request{
			URL:          ctx.Req.URL.String(),
			Domain:       host,
			OriginDomain: getReferrerDomain(ctx.Req),
			ContentType:  mediaType,
			Timeout:      h.MatchTimeout,
		}
		// Second level filtering, based on returned content
		rules := h.Cache.Rules()
		start := time.Now()
		matched, id, err := rules.Matcher.Match(rq)
		if err != nil {
			log.Printf("error: matching %s with domain=%s, origin=%, content-type: %s, "+
				"failed: %s", rq.URL, rq.Domain, rq.OriginDomain, rq.ContentType, err)
		}
		end := time.Now()
		duration2 = end.Sub(start) / time.Millisecond
		if matched {
			r.Body.Close()
			rule := rules.Rules[id]
			log.Printf("rejected in %d/%dms: %s\n", state.Duration, duration2,
				state.URL)
			log.Printf("  by %s\n", rule)
			return goproxy.NewResponse(ctx.Req, goproxy.ContentTypeText,
				http.StatusNotFound, "Not Found")
		}
	}

	if atomic.LoadUint64(logRequests)%2 == 1 {
		logRequest(ctx.Req)
		logResponse(r)
	}
	log.Printf("accepted in %d/%dms: %s\n", state.Duration, duration2, state.URL)
	return r
}
Пример #12
0
func responseFromResponseRecorder(req *http.Request, w *httptest.ResponseRecorder) (*http.Request, *http.Response) {
	resp := goproxy.NewResponse(req, "", w.Code, w.Body.String())
	resp.Header = make(http.Header)
	for key, vals := range w.HeaderMap {
		for _, val := range vals {
			resp.Header.Add(key, val)
		}
	}
	return req, resp
}
Пример #13
0
func main() {
	verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
	docRoot := flag.String("root", ".", "document root directory")
	address := flag.String("http", ":8080", `HTTP service address (e.g., ":8080")`)
	flag.Parse()
	proxy := goproxy.NewProxyHttpServer()
	proxy.Verbose = *verbose

	proxy.OnRequest(reqMethodIs("GET", "HEAD")).DoFunc(
		func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			filename := path.Join(*docRoot, ctx.Req.URL.Path)
			if !exists(filename) {
				return req, nil
			}

			bytes, err := ioutil.ReadFile(filename)
			if err != nil {
				ctx.Warnf("%s", err)
				return req, nil
			}
			resp := goproxy.NewResponse(req, "application/octet-stream",
				http.StatusOK, string(bytes))
			ctx.Logf("return response from local %s", filename)
			return req, resp
		})

	proxy.OnResponse(respReqMethodIs("GET", "HEAD")).Do(
		goproxy.HandleBytes(
			func(b []byte, ctx *goproxy.ProxyCtx) []byte {
				if ctx.Req.Method != "GET" || hasRespHeader(ctx.Resp, "Location") {
					return b
				}

				filename := path.Join(*docRoot, ctx.Req.URL.Path)
				if exists(filename) {
					return b
				}

				dir := path.Dir(filename)
				err := os.MkdirAll(dir, 0755)
				if err != nil {
					ctx.Warnf("cannot create directory: %s", dir)
				}

				err = ioutil.WriteFile(filename, b, 0644)
				if err != nil {
					ctx.Warnf("cannot write file: %s", filename)
				}

				ctx.Logf("save cache to %s", filename)

				return b
			}))
	log.Fatal(http.ListenAndServe(*address, proxy))
}
Пример #14
0
// getResponse returns stored response from cache
func (d *DBClient) getResponse(req *http.Request) *http.Response {

	reqBody, err := ioutil.ReadAll(req.Body)

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Got error when reading request body")
	}

	key := getRequestFingerprint(req, reqBody)

	payloadBts, err := d.cache.Get([]byte(key))

	if err == nil {
		// getting cache response
		payload, err := decodePayload(payloadBts)
		if err != nil {
			log.WithFields(log.Fields{
				"error": err.Error(),
			}).Error("Failed to decode payload")
		}

		c := NewConstructor(req, *payload)

		if d.cfg.middleware != "" {
			_ = c.ApplyMiddleware(d.cfg.middleware)
		}

		response := c.reconstructResponse()

		log.WithFields(log.Fields{
			"key":        key,
			"status":     payload.Response.Status,
			"bodyLength": response.ContentLength,
		}).Info("Response found, returning")

		return response

	}

	log.WithFields(log.Fields{
		"error":       err.Error(),
		"query":       req.URL.RawQuery,
		"path":        req.URL.RawPath,
		"destination": req.Host,
		"method":      req.Method,
	}).Warn("Failed to retrieve response from cache")
	// return error? if we return nil - proxy forwards request to original destination
	return goproxy.NewResponse(req,
		goproxy.ContentTypeText, http.StatusPreconditionFailed,
		"Coudldn't find recorded request, please record it first!")

}
Пример #15
0
func newCachedResponse(shared bool, cacheRequest *cacheRequest, resource *Resource) *http.Response {

	statusCode, contentLength, headers, body, err := newResponseParameters(shared, cacheRequest, resource)

	if err != nil {
		return goproxy.NewResponse(cacheRequest.Request, goproxy.ContentTypeText, http.StatusInternalServerError, "Error calculating age: "+err.Error())
	}

	cachedResponse := newResponse(cacheRequest.Request, statusCode, contentLength, headers, body)

	return cachedResponse
}
Пример #16
0
func main() {
	verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
	addr := flag.String("addr", ":8080", "proxy listen address")
	flag.Parse()
	proxy := goproxy.NewProxyHttpServer()
	proxy.Verbose = *verbose

	atc := atc.NewAirTrafficControl()

	responses := make(chan responseStatus)

	go func() {
		for {
			msg := <-responses
			fmt.Println("respons host", msg.host, "status", msg.status, "len", msg.length)

			if isFailure(msg.status) {
				atc.ReportFailure(msg.host)
			} else {
				atc.ReportSuccess(msg.host)
			}
		}
	}()

	proxy.OnRequest().DoFunc(
		func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			cleared := atc.GetClearance(r.Host)

			if !cleared {
				fmt.Println("NOT CLEARED!")
				return r, goproxy.NewResponse(r,
					goproxy.ContentTypeText, http.StatusGatewayTimeout,
					"circuit broken")
			}

			return r, nil
		})

	proxy.OnResponse().DoFunc(
		func(r *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
			if r != nil {
				responses <- responseStatus{r.Request.Host, r.StatusCode, r.ContentLength}
			} else {
				fmt.Println("response ERROR:", ctx.Error)
				responses <- responseStatus{ctx.Req.Host, -1, -1}
			}
			return r
		})

	fmt.Println("STARTING")
	log.Fatal(http.ListenAndServe(*addr, proxy))
}
Пример #17
0
func main() {
	proxy := goproxy.NewProxyHttpServer()
	proxy.OnRequest(goproxy.DstHostIs("www.reddit.com")).DoFunc(
		func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			if h, _, _ := time.Now().Clock(); h >= 8 && h <= 17 {
				return r, goproxy.NewResponse(r,
					goproxy.ContentTypeText, http.StatusForbidden,
					"Don't waste your time!")
			}
			return r, nil
		})
	log.Fatalln(http.ListenAndServe(":8080", proxy))
}
Пример #18
0
func (runCommand *RunCommand) Execute(configLoader ConfigLoader) error {
	config, err := configLoader()

	if err != nil {
		return err
	}

	proxy := goproxy.NewProxyHttpServer()

	//Verbose if either config or RunCommand is set to verbose
	proxy.Verbose = config.Verbose || runCommand.IsVerbose

	var matchCondition = goproxy.ReqConditionFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) bool {
		_, ok := config.Rewrites[req.URL.String()]

		log.Printf("Trying to match %s", req.URL.String())

		if ok {
			log.Printf("Matched %s", req.URL.String())
		}

		return ok
	})

	proxy.OnRequest(matchCondition).DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
		dstFile, _ := config.Rewrites[req.URL.Path]
		fileBytes, err := ioutil.ReadFile(dstFile)
		if err == nil {
			return req, goproxy.NewResponse(req, "text/css", 200, string(fileBytes[:]))
		} else {
			log.Printf("Error reading file %s", dstFile)
			return req, nil
		}
	})

	configChan := proxyconfig.StartWatching(proxyconfig.DefaultConfigFile)

	go func() {
		for {
			config = <-configChan
			proxy.Verbose = config.Verbose
		}
	}()

	log.Printf("Starting proxyserver localhost:8080 with config %+v", *config)
	log.Fatal(http.ListenAndServe(":8080", proxy))

	return nil
}
Пример #19
0
func DoHTTPRequest(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	var success bool
	var flowIndex int
	var response string
	success, flowIndex = drymartini.FindOrGenPath(myDryMartini, DefaultCircLength, DefaultCircLength)
	if !success {
		return r, goproxy.NewResponse(r, goproxy.ContentTypeText, http.StatusInternalServerError, "error building circuit")
	}
	response, success = drymartini.SendData(myDryMartini, flowIndex, r.URL.String())
	if !success {
		log.Printf("there was an error sending the request:%s\n", r.URL.String())
		return r, goproxy.NewResponse(r, goproxy.ContentTypeText, http.StatusInternalServerError, "error sending request")
	}
	var contentType string = http.DetectContentType([]byte(response))
	dbg.Printf("detected response as having content-type:%s\n", pVerb, contentType)
	dbg.Printf("request url was:%s\n", pVerb, r.URL.String())

	//gotta set the content-type manually, detectContentType doesn't seem to work for .css
	//kept setting it as "text/plain". there's probably a much nice way than this, whatevs. fake it til you make it
	if strings.Contains(r.URL.String(), ".css") {
		contentType = "text/css"
	}
	return r, goproxy.NewResponse(r, contentType, http.StatusOK, response)
}
Пример #20
0
// signMessage signs a message with our current logged in account (private key) and adds the signature to the original request. Then it sends it on to the web site.
func signMessage(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	cleartext := req.Form.Get("cleartext")
	// cleartext is the message, for now we ignore the title and other fields in the signature

	// get the current logged in account (and private key)
	// TODO: change to use the server certificate Root CA identity. Not the req.URL.Host.
	log.Printf("req.URL.Host is: %v\n ", req.URL.Host)
	creds := getLoggedInCreds(req.URL.Host)

	if creds == nil {
		if req.Form.Get("sign") == "required" {
			log.Println("Form says to sign a message but no user is logged in.\nConfigure server to require login before handing the form.\nHint: Use the ecca.LoggedInHandler.")
			return nil, goproxy.NewResponse(req,
				goproxy.ContentTypeText, http.StatusInternalServerError,
				"Ecca Proxy error: Server says to sign your message but you haven't logged in. Please log in first, then type your message again. Later we might cache your data and redirect you to the login-screen.")
		}

		// creds is nil but signing is optional. Send the current form unchanged to the upstream server.
		// TODO: show this to the user and confirm to prevent mistakes.
		resp, err := fetchRequest(req, ctx)
		check(err)
		return nil, resp
	}

	log.Printf("creds are: %v\n", creds.CN)
	signature, err := Sign(creds.Priv, creds.Cert, cleartext)
	log.Printf("signature is: %#v\n", signature)
	check(err)
	req.Form.Set("signature", signature) // add signature to the request

	// TODO: refactor this duplicate code from encryptMessage
	client2, err := makeClient(req.URL.Host)
	if err != nil {
		log.Println("error is ", err)
		return nil, nil
	}
	log.Printf("Client to send signed message to: %#v\n", client2)

	log.Printf("POSTING Form to service: %#v\n", req.Form)
	resp, err := client2.PostForm(req.URL.String(), req.Form)
	if err != nil {
		log.Println("error is ", err)
		return nil, nil
	}
	return nil, resp
}
Пример #21
0
// encrypt a message with a public key from a certificate in the url.
func encryptMessage(req *http.Request) (*http.Request, *http.Response) {
	// First, check our credentials
	// get the current logged in account (and private key)

	// TODO: change to use the server certificate Root CA identity. Not the req.URL.Host.
	// log.Printf("req.tls.ConnectionState is %#v", req.TLS) => returns nil
	// Need to connect to the site, fetch and check the server cert;
	// Then we know the site's cert and RootCa;
	// With that rootCA we fetch our credentials.

	log.Printf("req.URL.Host is: %v\n ", req.URL.Host)
	creds := getLoggedInCreds(req.URL.Host)

	if creds == nil {
		log.Println("Form says to encrypt and sign a message but no user is logged in.\nConfigure server to require login before handing the form.\nHint: Use the ecca.LoggedInHandler.")
		return nil, goproxy.NewResponse(req,
			goproxy.ContentTypeText, http.StatusInternalServerError,
			"Ecca Proxy error: Server says to sign your message but you haven't logged in. Please log in first, then type your message again. Later we might cache your data and redirect you to the login-screen.")
	}
	log.Printf("creds are: %v\n", creds.CN)

	// Second, fetch the recipients public key from where the server tells us to expect it.
	certPEM, err := fetchCertificatePEM(req.Form.Get("certificate_url"))
	check(err)

	// Do the actual signing and encrypting
	cleartext := req.Form.Get("cleartext")
	ciphertext := SignAndEncryptPEM(creds.Priv, creds.Cert, certPEM, cleartext)
	req.Form.Set("ciphertext", string(ciphertext))

	// TODO: refactor this duplicate code from signMessage
	client2, err := makeClient(req.URL.Host)
	if err != nil {
		log.Println("error is ", err)
		return nil, nil
	}

	resp, err := client2.PostForm(req.URL.String(), req.Form)
	if err != nil {
		log.Println("error is ", err)
		return nil, nil
	}
	return nil, resp
}
Пример #22
0
func main() {
	verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
	addr := flag.String("addr", ":8080", "proxy listen address")
	match := flag.String("match", ".clouddrive.com:443", "Only allow requests destined to this suffix match")
	flag.Parse()

	syslogger, err := syslog.New(syslog.LOG_CRIT, "cfsync-proxy")
	if err != nil {
		log.Fatalln("Unable to setup syslog!")
	}

	//log.SetOutput(syslogger)
	logger := log.New(syslogger, "", log.LstdFlags)
	log.SetOutput(syslogger)
	runtime.GOMAXPROCS(runtime.NumCPU())
	proxy := goproxy.NewProxyHttpServer()
	proxy.Logger = logger
	proxy.Verbose = *verbose
	proxy.OnRequest().HandleConnectFunc(func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
		if strings.HasSuffix(host, *match) {
			log.Printf("Allowing - %s %s %s", ctx.Req.RemoteAddr, ctx.Req.Method, ctx.Req.RequestURI)
			return goproxy.OkConnect, host
		} else {
			log.Printf("Rejecting - %s %s %s", ctx.Req.RemoteAddr, ctx.Req.Method, ctx.Req.RequestURI)
			return goproxy.RejectConnect, host
		}

	})
	proxy.OnRequest().DoFunc(func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
		if strings.HasSuffix(r.URL.Host, *match) {
			log.Printf("Allowing - %s %s %s", ctx.Req.RemoteAddr, ctx.Req.Method, ctx.Req.RequestURI)
			return r, nil
		} else {
			// Note that unless the request container :443 we'll reject it!
			log.Printf("Rejecting - %s %s %s", ctx.Req.RemoteAddr, ctx.Req.Method, ctx.Req.RequestURI)
			return r, goproxy.NewResponse(r, goproxy.ContentTypeText,
				http.StatusForbidden, "Endpoint not allowed.")
		}
	})
	log.Printf("container-sync proxy starting up on '%v'\n", *addr)
	log.Printf("endpoints restricted to: '%v'\n", *match)
	log.Fatal(http.ListenAndServe(*addr, proxy))
}
Пример #23
0
func main() {
	certLoader := certs.NewS3Loader(os.Getenv("S3_KEY"), os.Getenv("S3_SECRET"), os.Getenv("S3_REGION"), os.Getenv("S3_BUCKETNAME"))
	certificates, err := certLoader.Load()
	if err != nil {
		Log.Fatal(err)
	}
	certLoader.Listen(certificates)

	ingressWatcher := ingress.New(*user, *password, *masterHost, *watchPath, *getPath, *masterCACertPath)
	ingresses, err := ingressWatcher.Get()
	if err != nil {
		log.Fatal(err)
	}
	ingressWatcher.Listen(ingresses)

	admin.NewServer(*adminPort, certLoader).Start()

	proxyCertFile := filepath.Join(*proxyCertPath, "cert.pem")
	proxyKeyFile := filepath.Join(*proxyCertPath, "key.pem")
	proxyCertificate, err := tls.LoadX509KeyPair(proxyCertFile, proxyKeyFile)
	if err != nil {
		Log.Fatal(err)
	}

	proxyTLSConfig := &tls.Config{}
	proxyTLSConfig.Certificates = []tls.Certificate{proxyCertificate}
	proxyTLSConfig.GetCertificate = func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
		certificates := certLoader.Get()
		if cert, ok := certificates[hello.ServerName]; ok {
			return cert, nil
		} else {
			return nil, fmt.Errorf("cert not found for ClientHello server name: %s\n", hello.ServerName)
		}
	}

	proxyAddress := ":" + strconv.Itoa(int(*proxyPort))
	conn, err := net.Listen("tcp", proxyAddress)
	if nil != err {
		Log.Fatal(err)
	}
	proxyTLSListener := tls.NewListener(conn, proxyTLSConfig)

	proxy := goproxy.NewProxyHttpServer()
	proxy.OnRequest().DoFunc(
		func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			ingresses := ingressWatcher.FromCache()

			rule, ok := ingresses.RuleForHost(r.Host)
			if !ok {
				return r, goproxy.NewResponse(r, goproxy.ContentTypeText, http.StatusNotFound, "No default backend")
			}

			backend, ok := rule.BackendForPath("http", r.URL.Path)
			if !ok {
				return r, goproxy.NewResponse(r, goproxy.ContentTypeText, http.StatusNotFound, fmt.Sprintf("No rule for path: %+v\n", r))
			}
			r.Host = backend.ServiceName + ":" + backend.ServicePort

			return r, nil
		})

	// Start the proxy server
	proxyServer := &http.Server{Addr: proxyAddress, Handler: proxy}
	Log.Printf(fmt.Sprintf("Proxy server listening on port %d\n", *proxyPort))
	Log.Fatal(proxyServer.Serve(proxyTLSListener))
}
Пример #24
0
func initiateDirectConnection(req *http.Request) (*http.Request, *http.Response) {
	switch req.Method {
	case "POST":
		// Test the simplest things first, the application parameter :-)
		application := req.Form.Get("application")
		if application != "chat" && application != "voice" {
			log.Printf("Wrong application, only chat/voice allowed, got %#v", application)
			return nil, goproxy.NewResponse(req,
				goproxy.ContentTypeText, http.StatusInternalServerError,
				"Ecca Proxy error: Wrong application, only chat/voice allowed.")
		}

		// get the current logged in account (and private key)
		// TODO: get these from the site
		log.Printf("req.URL.Host is: %v\n ", req.URL.Host)

		ourCreds := getLoggedInCreds(req.URL.Host)
		if ourCreds == nil {
			log.Println("Site says to initiate a direct connection but you are not logged in.\nConfigure server to require login before handing the form.\nHint: Use the ecca.LoggedInHandler.")
			return nil, goproxy.NewResponse(req,
				goproxy.ContentTypeText, http.StatusInternalServerError,
				"Ecca Proxy error: Site says to initiate a direct connection but you haven't logged in. Please log in first, then try again.")
		}
		log.Printf("our creds are: %v\n", ourCreds.CN)

		ourCert, err := eccentric.ParseCertByteA(ourCreds.Cert)
		check(err)

		// get the one who signed our cert.
		ourFPCACert, err := eccentric.FetchCN(ourCert.Issuer.CommonName)
		check(err)

		// Fetch certificate of addressee
		// First, fetch the recipients public key from where the server tells us to expect it.
		recipCertURL := req.Form.Get("certificate_url")
		recipCertPEM, err := fetchCertificatePEM(recipCertURL)
		check(err)
		recipCert, err := eccentric.ParseCertByteA(recipCertPEM)
		check(err)

		// TODO: Check it for unicity/MitM at the registry-of-honesty.eccentric-authentication.org
		// TODO: Store the results for later

		// Create (and start) a Tor-Listener for this recipient
		listener := createTorListener(ourCreds, recipCert, application)
		// Store listening address in database to listen again at restart of the proxy.
		// I.E. make these listening endpoints permanent.
		storeListener(listener)

		hostname := getHostname(ourCreds.Hostname)
		invitation := DCInvitation{
			Application:     application,
			InviteeCN:       recipCert.Subject.CommonName,
			Endpoint:        listener.OnionAddress,
			ListenerCN:      ourCreds.CN + "@@" + hostname,
			ListenerFPCAPEM: eccentric.PEMEncode(ourFPCACert),
		}
		log.Printf("Make invitation from %v, at %v to %v", invitation.ListenerCN, invitation.Endpoint, invitation.InviteeCN)

		var message = encodeInvitation(invitation)
		ciphertext := SignAndEncryptPEM(ourCreds.Priv, ourCreds.Cert, recipCertPEM, message)
		req.Form.Set("ciphertext", string(ciphertext))
		// Send the invitation to site for delivery to the recipient
		// TODO: refactor this duplicate code from encryptMessage
		client2, err := makeClient(req.URL.Host)
		if err != nil {
			log.Println("error is ", err)
			return nil, nil
		}
		log.Printf("Client to send invitation to: %#v\n", client2)

		resp, err := client2.PostForm(req.URL.String(), req.Form)
		if err != nil {
			log.Println("error is ", err)
			return nil, nil
		}
		return nil, resp
	}

	log.Printf("Unexpected method: %#v", req)
	return nil, goproxy.NewResponse(req, goproxy.ContentTypeText, http.StatusBadRequest, "Unexpected method.")
}
Пример #25
0
func CreateProxy(whiteList, blackList []*regexp.Regexp, verbose bool,
	whiteListUpdates, blackListUpdates chan string) (*goproxy.ProxyHttpServer, error) {
	// Start longpoll subscription manager
	longpollManager, lpErr := golongpoll.StartLongpoll(
		golongpoll.Options{
			LoggingEnabled:                 false,
			MaxLongpollTimeoutSeconds:      120,
			MaxEventBufferSize:             1000,
			EventTimeToLiveSeconds:         240,
			DeleteEventAfterFirstRetrieval: false,
		})
	if lpErr != nil {
		log.Fatalf("Error creating longpoll manager: %v", lpErr)
	}
	// Create and start control server for controlling proxy behavior
	ctlServer := controls.NewControlServer(vars.ProxyControlPort, longpollManager.SubscriptionHandler, whiteListUpdates, blackListUpdates)
	ctlServer.Serve()

	// Manually allowed/blocked sites:
	manualWhiteList := make(map[string]bool)
	manualBlackList := make(map[string]bool)

	// Create and start our content blocking proxy:
	proxy := goproxy.NewProxyHttpServer()
	proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
	proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
		// Prevent upgrades to https so we can easily see everything as plain
		if req.URL.Scheme == "https" {
			req.URL.Scheme = "http"
		}
		urlString := req.URL.String()

		// Check for any updates to our whitelist/blacklist values
		checkWhiteBlackListUpdates(manualWhiteList, manualBlackList,
			whiteListUpdates, blackListUpdates)

		// Now apply whitelist/blacklist rules:
		for _, w := range whiteList {
			if w.MatchString(urlString) {
				// whitelisted by rules, but was this specific URL blacklisted
				// by user?
				if _, ok := manualBlackList[strings.TrimSpace(urlString)]; ok {
					// stop trying to find whitelist matches
					log.Printf("user-DENIED whitelisting:  %s\n", req.URL)
					break
				} else {
					log.Printf("WHITELISTED:  %s\n", req.URL)
					notifyProxyEvent("Allowed", req, longpollManager)
					return req, nil
				}
			}
		}
		// Check if this was explicitly whitelisted by user:
		if _, ok := manualWhiteList[strings.TrimSpace(urlString)]; ok {
			// no need to consider blacklists, serve content
			log.Printf("user-eplicit WHITELISTED:  %s\n", req.URL)
			notifyProxyEvent("Allowed", req, longpollManager)
			return req, nil
		}

		// See if we're manually allowing this page thru on time only
		if strings.HasSuffix(urlString, vars.ProxyExceptionString) {
			urlString := urlString[:len(urlString)-len(vars.ProxyExceptionString)]
			u, uErr := url.Parse(urlString)
			if uErr == nil {
				req.URL = u
				log.Printf("MANUALLY ALLOWED: %s\n", req.URL)
				notifyProxyEvent("Manually Allowed", req, longpollManager)
				return req, nil
			} else {
				log.Printf("ERROR trying to rewrite URL. Url: %s, Error: %s", urlString, uErr)
				return req, goproxy.NewResponse(req,
					goproxy.ContentTypeHtml, http.StatusForbidden,
					fmt.Sprintf(`<html>
                            <head><title>BAD URL</title></head>
                            <body>
                                <h1>Ehhh.... wut?</h1>
                                <hr />
                                <h2>Error rewriting URL:</h2>
                                <p style="color: black; font-family: monospace; background: #DDDDDD; padding: 20px;">%s</p>
                                <p>Error:</p>
                                <p style="color: red; font-family: monospace; background: #DDDDDD; padding: 20px;">%s</p>
                            </body>
                        </html>`, urlString, uErr))
			}
		}
		for _, b := range blackList {
			if b.MatchString(urlString) {
				log.Printf("BLACKLISTED:  %s\n", req.URL)
				notifyProxyEvent("Blocked", req, longpollManager)
				return req, goproxy.NewResponse(req,
					goproxy.ContentTypeHtml, http.StatusForbidden,
					fmt.Sprintf(`<html>
                            <head><title>BLOCKED</title></head>
                            <body>
                                <h1>I pity the fool!</h1>
                                <hr />
                                <h2>Webpage Blocked</h2>
                                <p style="color: black; font-family: monospace; background: #DDDDDD; padding: 20px;">%s</p>
                                <p><a href="%s%s">Continue to Webpage just this once.</a></p>
                                <p>or...</p>
                                <p><a href="http://127.0.0.1:%s/add-wl?url=%s&continue_to_page=yes">Add to Whitelist and continue.</a></p>
                            </body>
                        </html>`, req.URL, req.URL, vars.ProxyExceptionString,
						vars.ProxyControlPort, url.QueryEscape(req.URL.String())))
			}
		}
		log.Printf("NOT MATCHED: (allow by default) %s\n", req.URL)
		notifyProxyEvent("Not matched, default allowed", req, longpollManager)
		return req, nil
	})

	proxy.OnResponse(goproxy_html.IsHtml).Do(goproxy_html.HandleString(
		func(s string, ctx *goproxy.ProxyCtx) string {
			if strings.HasPrefix(ctx.Req.URL.Host, "http://127.0.0.1:") ||
				strings.HasPrefix(ctx.Req.URL.Host, "http://127.0.0.1/") ||
				strings.HasPrefix(ctx.Req.URL.Host, "127.0.0.1/") ||
				strings.HasPrefix(ctx.Req.URL.Host, "127.0.0.1:") {
				// Don't inject on our own content.
				// TODO: move this logic next to IsHtml so this func
				return s
			}
			// Don't inject iframe into responses that aren't successful
			// ie 2xx response codes.
			// Mainly this is to avoid injecting on our own block page,
			// but it probably doesn't make sense for other failed pages either
			if ctx.Resp.StatusCode < 200 || ctx.Resp.StatusCode >= 300 {
				// show page as-is
				// remember: blocking content is already enforced by this point,
				return s
			}
			match := vars.StartBodyTagMatcher.FindIndex([]byte(s))
			if match != nil && len(match) >= 2 {
				// TODO: make this more efficient by using a stream or some sort
				// of stringbuilder like thing that doesn't require mashing
				// giant strings together.
				return s[:match[1]] +
					// TODO: should this script get injected after the iframe to prevent a potential race condition?
					getParentControlScript() +
					"<div id=\"proxyblock-glass-overlay\" onclick=\"glassClose(this);\" style=\"position: fixed; top: 0; right: 0; left: 0; bottom: 0; background: #000000; opacity: 0.3; z-index: 99999998; display: none;\"></div>" +
					"<div id=\"proxyblock-controls\" style=\"position: fixed; height: 42px; width: 230px; top: 4px; right: 8px; z-index: 99999999;\">" +
					"<iframe id=\"proxyblock-frame\" scrolling=\"no\" style=\"overflow: hidden; background-color: #FFFFFF; border: 2px solid black; width: 100%; height: 100%;\" " +
					"src=\"http://127.0.0.1:" + vars.ProxyControlPort + pagecontrols.GetPageControlsUrl(ctx.Req.URL.String()) +
					"\"></iframe>" +
					"</div>" +
					s[match[1]:]
			} else {
				log.Printf("WARNING: No starting body tag found, must not be html, no injection.")
				return s
			}
		}))
	proxy.Verbose = verbose
	return proxy, nil
}
Пример #26
0
func main() {
	rpcPort := flag.Int("rpcPort", 9090, "port for groupcache ipc")
	proxyPort := flag.Int("proxyPort", 8080, "port for http proxy")
	peerPort := flag.Int("peerPort", 7070, "port for receiving peer updates")

	flag.Parse()

	me := "http://localhost:" + strconv.Itoa(*rpcPort)
	poolOpts := &groupcache.HTTPPoolOptions{
		BasePath: "/stuff",
	}

	cache := groupcache.NewHTTPPoolOpts(me, poolOpts)

	groupcache.NewGroup("", 64<<20, groupcache.GetterFunc(
		func(ctx groupcache.Context, key string, dest groupcache.Sink) error {
			log.V(2).Info("trying to get ", key)
			client := &http.Client{
				Timeout: time.Second * 5,
			}
			res, err := client.Get(key)
			if err != nil {
				return err
			}

			defer res.Body.Close()
			if res.StatusCode != http.StatusOK {
				return errors.New("server returned: " + string(res.Status))
			}

			data, err := ioutil.ReadAll(res.Body)
			if err != nil {
				return err
			}
			dest.SetBytes(data)
			return nil
		},
	))

	cache.Set("http://localhost:9090", "http://localhost:9091", "http://localhost:9092")

	proxy := goproxy.NewProxyHttpServer()
	//proxy.Verbose = true
	proxy.OnRequest().DoFunc(
		func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			log.V(2).Infof("Proxy got request for: %+v", *r.URL)
			r.URL = &url.URL{
				Path: "/stuff/" + r.URL.Scheme + "://" + r.URL.Host + r.URL.Path,
			}
			pr := NewProxyResponse()
			cache.ServeHTTP(pr, r)
			log.V(3).Infof("body: %v", pr.response.Body)
			data, err := ioutil.ReadAll(pr.response.Body)
			if err != nil {
				log.V(1).Infof("Failed to read response %v", err)
			}
			return r, goproxy.NewResponse(r, goproxy.ContentTypeText, http.StatusForbidden, string(data))
		},
	)

	mux := http.NewServeMux()
	mux.HandleFunc("/peers", func(w http.ResponseWriter, r *http.Request) {
		data, err := ioutil.ReadAll(r.Body)
		if err != nil {
			log.V(1).Infof("Failed to read response %v", err)
		}
		cache.Set(strings.Split(string(data), ",")...)
	})

	log.Infoln("Starting rpc instance.")
	go func() { log.Fatal(http.ListenAndServe(":"+strconv.Itoa(*rpcPort), cache)) }()

	log.Infoln("Starting peer update listener.")
	go func() { log.Fatal(http.ListenAndServe(":"+strconv.Itoa(*peerPort), mux)) }()
	log.Infoln("Starting proxy instance.")
	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(*proxyPort), proxy))
}
Пример #27
0
func handleDirectConnections(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	return nil, goproxy.NewResponse(req,
		goproxy.ContentTypeText, http.StatusOK,
		"Ecca Proxy message: Expect a list of your open ports to others to connect to you.")
}
Пример #28
0
func handleSelect(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
	var originalURL *url.URL
	var err error
	req.ParseForm()
	log.Printf("Form parameters are: %v\n", req.Form)
	var originalRequest = req.Form.Get("originalRequest")
	originalURL, err = url.Parse(originalRequest)
	if err != nil {
		log.Fatal("Error parsing originalRequest parameter: ", err)
	}
	log.Println("got param (originalRequest): ", originalRequest)

	switch req.Method {
	case "GET":
		// User has no active logins.
		// Show available client certificates for URI.Host
		creds := getCreds(originalURL.Host)
		buf := execTemplate(selectTemplate, "select", creds)
		resp := makeResponse(req, 200, "text/html", buf)
		return nil, resp
	case "POST":
		var cred *credentials
		if req.Form.Get("anonymous") != "" {
			// register with random cn
			cred, err = registerAnonymous(originalURL.Host)
		}

		if req.Form.Get("register") != "" {
			// register with given cn
			cn := req.Form.Get("cn")
			cred, err = registerCN(originalURL.Host, cn)
		}

		if cn := req.Form.Get("login"); cn != "" {
			cred = getCred(originalURL.Host, cn)
		}

		if err != nil {
			resp := goproxy.NewResponse(req, "text/plain", 500, fmt.Sprintf("Error: %#v\n", err))
			return nil, resp
		}
		//TODO: make sure at least on of these actions above has success
		if cred == nil {
			log.Fatal("cred should not be nil")
		}
		login(originalURL.Host, *cred)

		// embed the original site in an iframe in our management frame.

		originalURL.Scheme = "http" // send users follow up requests back to us
		var data = map[string]string{
			"Hostname": originalURL.Host,
			"CN":       cred.CN,
			"URL":      originalURL.String(),
		}
		buf := execTemplate(embedTemplate, "embed", data)
		resp := makeResponse(req, 200, "text/html", buf)
		return nil, resp
	}
	log.Printf("Unexpected method: %#v", req.Method)
	return nil, nil
}
Пример #29
0
func serve(ctx *cli.Context) error {
	clientID := ctx.String("client-id")
	clientSecret := ctx.String("client-secret")

	var config settings.Config
	var err error
	if ctx.String("config") != "" {
		config, err = settings.Parse(ctx.String("config"))
		if err != nil {
			logs.Error(err)
		}
	}

	if config.Debug() {
		logs.Level(logs.DebugLevel)
	}

	redisSettings, err := config.Redis()
	client := redis.NewClient(&redis.Options{Addr: redisSettings.String()})
	if _, err := client.Ping().Result(); err != nil {
		return err
	}
	logs.Debug("Connected to Redis at %s", redisSettings.String())
	store := components.NewRedisStore(client)

	proxy := goproxy.NewProxyHttpServer()
	if config.Debug() {
		proxy.Verbose = true
	}

	// Treat only requests with an SID cookie or POSTing username and password.
	var session = goproxy.ReqConditionFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) bool {
		_, err := req.Cookie("SID")
		return err == nil || (req.Method == "POST" && req.FormValue("username") != "" && req.FormValue("password") != "") // The form is already parsed.
	})

	proxy.NonproxyHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		dump, _ := httputil.DumpRequest(req, true)
		fmt.Println(string(dump))
		req.URL.Scheme = req.Header.Get("X-Scheme")
		req.URL.Host = req.Host
		proxy.ServeHTTP(w, req)
	})

	proxy.OnRequest(session).DoFunc(
		func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
			//dump, _ := httputil.DumpRequest(req, true)
			//fmt.Println(string(dump))
			cookie, err := req.Cookie("SID")
			if err == nil {
				session, err := store.Load(cookie.Value)
				if err != nil {
					return req, goproxy.NewResponse(req, "text/plain", http.StatusForbidden, "Invalid cookie")
				}
				req.Header.Del("Cookie")
				req.Header.Add("Authorization", "Bearer "+session.AccessToken)
				return req, nil
			}

			// Perform an OAuth "Resource Owner Password Credentials Grant"
			req.Form.Add("grant_type", "password")
			req.SetBasicAuth(clientID, clientSecret)

			// We must update the body and the content size for our new post value.
			var buffer io.Reader = strings.NewReader(req.Form.Encode())
			req.Body = ioutil.NopCloser(buffer)
			switch v := buffer.(type) {
			case *bytes.Buffer:
				req.ContentLength = int64(v.Len())
			case *bytes.Reader:
				req.ContentLength = int64(v.Len())
			case *strings.Reader:
				req.ContentLength = int64(v.Len())
			}

			//req.RequestURI = "" // Must be removed for client requests
			client := &http.Client{}
			resp, err := client.Do(req)
			if err != nil {
				return req, nil
			}
			defer resp.Body.Close()
			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				return req, nil
			}

			// TODO: Check http status for errors
			access := new(components.AccessData)
			if err := json.Unmarshal(body, access); err != nil {
				return req, nil
			}
			session := &components.Session{
				ID:           strings.TrimRight(base64.StdEncoding.EncodeToString(uuid.NewRandom()), "="),
				AccessToken:  access.AccessToken,
				RefreshToken: access.RefreshToken,
				ExpiresIn:    access.ExpiresIn,
			}
			if err := store.Save(session); err != nil {
				return req, nil
			}

			// TODO: Give a json response to clients
			resp = goproxy.NewResponse(req, "text/plain", http.StatusOK, "")
			cookie = &http.Cookie{Name: "SID", Value: session.ID}
			resp.Header.Add("Set-Cookie", cookie.String())
			return req, resp
		},
	)
	server, err := config.Server()
	if err != nil {
		logs.Critical(err)
		os.Exit(1)
	}
	logs.Info("Listening on %s", server.String())
	return http.ListenAndServe(server.String(), proxy)
}
Пример #30
0
func myproxy() {

    pname := "HttpProxy4U"
    pver := "1.0.0"

    // get current working directory
    cwd, err := os.Getwd()
    checkError(err)

    // parse input parameters
	var port, wlistFile, blistFile, rulesFile, aname, apwd, tdir, rdir string
	var verbose int
    var interval int64
	flag.StringVar(&port, "port", ":9998", "Proxy port number")
	flag.StringVar(&tdir, "tmpl-dir",
            filepath.Join(cwd, "static/tmpl"), "Template directory")
	flag.StringVar(&rdir, "rule-dir",
            filepath.Join(cwd, "static/rules"), "Rules directory")
	flag.StringVar(&wlistFile, "whitelist",
            filepath.Join(rdir, "whitelist.txt"), "White list file")
	flag.StringVar(&blistFile, "blacklist",
            filepath.Join(rdir, "blacklist.txt"), "Black list file")
	flag.StringVar(&rulesFile, "rules",
            filepath.Join(rdir, "rules.txt"), "Rules list file")
	flag.IntVar(&verbose, "verbose", 0, "logging level")
	flag.Int64Var(&interval, "interval", 300, "reload interval")
	flag.StringVar(&aname, "login", "admin", "Admin login name")
	flag.StringVar(&apwd, "password", "test", "Admin password")
	flag.Parse()

	// init proxy server
	proxy := goproxy.NewProxyHttpServer()
    proxy.Verbose = false // true
    if verbose > 1 {
        proxy.Verbose = true
    }
	msg := fmt.Sprintf("port=%s, verbose=%d, wlist=%s, blist=%s, rule=%s",
            port, verbose, wlistFile, blistFile, rulesFile)
	log.Println(msg)

    // read out client settings
	whitelist := readTxtFile(wlistFile)
	log.Println("White list:", whitelist)
	blacklist := readTxtFile(blistFile)
	log.Println("Black list:", blacklist)
	ruleslist := parseRules(readCSVFile(rulesFile))
	log.Println("Rules list:", ruleslist)
    lastRead := time.Now().UTC().Unix()

    // local variables
    var rules []string
    for _, r := range ruleslist {
        rules = append(rules, r.ToCSV())
    }
    tcss := "main.tmpl.css"
    tmplData := map[string]interface{}{}
    css := parseTmpl(tdir, tcss, tmplData)
    tfooter := "footer.tmpl.html"
    tmplData["package"] = pname
    tmplData["version"] = pver
    tmplData["css"] = css
    footer := parseTmpl(tdir, tfooter, tmplData)
    tmplData["whitelist"] = strings.Join(whitelist, "\n")
    tmplData["blacklist"] = strings.Join(blacklist, "\n")
    tmplData["ruleslist"] = strings.Join(rules, "\n")
    tmplData["footer"] = footer

	// admin handler
//    proxy.OnRequest(goproxy.IsLocalHost).DoFunc(
    proxy.OnRequest(goproxy.DstHostIs("")).DoFunc(
		func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
            path := html.EscapeString(r.URL.Path)
            log.Println("admin interface", path)
            if path == "/admin" {
                tauth := "auth.tmpl.html"
                u := r.FormValue("login")
                p := r.FormValue("password")
                if u == aname && p == apwd {
                    log.Println("access admin interface")
                } else {
                    return r, goproxy.NewResponse(r, goproxy.ContentTypeHtml,
                        http.StatusOK, parseTmpl(tdir, tauth, tmplData))
                }
                tpage := "admin.tmpl.html"
                return r, goproxy.NewResponse(r, goproxy.ContentTypeHtml,
                    http.StatusOK, parseTmpl(tdir, tpage, tmplData))
            } else if path == "/save" {
                wlist := strip(r.FormValue("whitelist"))
                blist := strip(r.FormValue("blacklist"))
                rlist := strip(r.FormValue("ruleslist"))
                saveList(wlistFile, wlist)
                saveList(blistFile, blist)
                saveList(rulesFile, rlist)
                tmplData["whitelist"] = wlist
                tmplData["blacklist"] = blist
                tmplData["ruleslist"] = rlist
                tpage := "save.tmpl.html"
                tmplData["body"] = fmt.Sprintf("New rules has been saved on %s",
                        time.Now())
                return r, goproxy.NewResponse(r, goproxy.ContentTypeHtml,
                    http.StatusOK, parseTmpl(tdir, tpage, tmplData))
            } else {
                tpage := "index.tmpl.html"
                return r, goproxy.NewResponse(r, goproxy.ContentTypeHtml,
                    http.StatusOK, parseTmpl(tdir, tpage, tmplData))
            }
		})

	// restrict certain sites on time based rules
	for _, rule := range ruleslist {
		proxy.OnRequest(goproxy.DstHostIs(rule.Url)).DoFunc(
			func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
                // reload maps if necessary
                unix := time.Now().UTC().Unix()
                if  unix-lastRead > interval {
                    ruleslist := parseRules(readCSVFile(rulesFile))
                    if verbose > 0 {
                        log.Println("Rules list:", ruleslist)
                    }
                    lastRead = unix
                }
				h, _, _ := time.Now().Clock()
				if h < rule.MinHour && h > rule.MaxHour {
					return r, goproxy.NewResponse(r,
						goproxy.ContentTypeText, http.StatusForbidden,
						"Your exceed your time window on this site")
				}
				return r, nil
			})
	}

	// filter white/black lists
	proxy.OnRequest().DoFunc(
		func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
            // reload maps if necessary
            unix := time.Now().UTC().Unix()
            if  unix-lastRead > interval {
                whitelist := readTxtFile(wlistFile)
                blacklist := readTxtFile(blistFile)
                if verbose > 0 {
                    log.Println("Reload white list:", whitelist)
                    log.Println("Reload black list:", blacklist)
                }
                lastRead = unix
            }
			pat1 := strings.Join(whitelist, "|")
			expect1 := false // match=false means site not in whitelist
			match1, err := regexp.MatchString(pat1, r.URL.Host)
			if err != nil {
				log.Println("ERROR: fail in match", pat1, r.URL.Host)
			}
			pat2 := strings.Join(blacklist, "|")
			expect2 := true // match=true means site is in blacklist
			match2, err := regexp.MatchString(pat2, r.URL.Host)
			if err != nil {
				log.Println("ERROR: fail in match", pat2, r.URL.Host)
			}
			if match2 == expect2 || match1 == expect1 {
				path := html.EscapeString(r.URL.Path)
				if verbose > 0 {
					log.Println(r.URL.Host, path)
				}
				return r, goproxy.NewResponse(r,
					goproxy.ContentTypeText, http.StatusForbidden,
					"This site is not accessible to you")
			}
			return r, nil
		})

	// start and log http proxy server
	log.Fatal(http.ListenAndServe(port, proxy))
}