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 }
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 }
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 }
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)) }
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? }
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) }
// 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)) }
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)) }
// 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!") } }
// 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 }
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 }
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 }
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)) }
// 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!") }
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 }
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)) }
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)) }
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 }
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) }
// 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 }
// 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 }
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)) }
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)) }
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.") }
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 }
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)) }
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.") }
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 }
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) }
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)) }