From b7df69fa55629cfc3528d9daaea4e7e1dd7de005 Mon Sep 17 00:00:00 2001 From: Hooman Date: Thu, 29 Jun 2017 13:51:55 -0700 Subject: [PATCH 1/9] Make Broker run standalone --- broker/broker.go | 41 +++++++++++++++++++++++++++++++-- broker/metrics.go | 2 +- broker/snowflake-broker_test.go | 2 +- broker/snowflake-heap.go | 2 +- 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/broker/broker.go b/broker/broker.go index 1673601..8550400 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -3,7 +3,7 @@ Broker acts as the HTTP signaling channel. It matches clients and snowflake proxies by passing corresponding SessionDescriptions in order to negotiate a WebRTC connection. */ -package snowflake_broker +package main import ( "container/heap" @@ -13,6 +13,8 @@ import ( "net" "net/http" "time" + "sync" + "os" ) const ( @@ -226,7 +228,19 @@ func ipHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte(remoteAddr)) } -func init() { +func main() { + + if len(os.Args) < 3 { + log.Println("Usage: broker cert cert_key") + os.Exit(1) + } + + cert := os.Args[1] + log.Println("Using cert file:", cert) + cert_key := os.Args[2] + log.Println("Using cert key file: ", cert_key) + + ctx := NewBrokerContext() go ctx.Broker() @@ -238,4 +252,27 @@ func init() { http.Handle("/client", SnowflakeHandler{ctx, clientOffers}) http.Handle("/answer", SnowflakeHandler{ctx, proxyAnswers}) http.Handle("/debug", SnowflakeHandler{ctx, debugHandler}) + + var wg sync.WaitGroup + wg.Add(2) + + //Run HTTP server + go func(){ + defer wg.Done() + err := http.ListenAndServe(":80", nil) + if err != nil { + log.Println("ListenAndServe: ", err) + } + }() + + //Run HTTPS server + go func(){ + defer wg.Done() + err := http.ListenAndServeTLS(":443", cert, cert_key, nil) + if err != nil { + log.Println("ListenAndServeTLS: ", err) + } + }() + + wg.Wait() } diff --git a/broker/metrics.go b/broker/metrics.go index f64d1cc..002fdfe 100644 --- a/broker/metrics.go +++ b/broker/metrics.go @@ -1,4 +1,4 @@ -package snowflake_broker +package main import ( // "golang.org/x/net/internal/timeseries" diff --git a/broker/snowflake-broker_test.go b/broker/snowflake-broker_test.go index 44940a3..109d7df 100644 --- a/broker/snowflake-broker_test.go +++ b/broker/snowflake-broker_test.go @@ -1,4 +1,4 @@ -package snowflake_broker +package main import ( "bytes" diff --git a/broker/snowflake-heap.go b/broker/snowflake-heap.go index cf249fe..419956f 100644 --- a/broker/snowflake-heap.go +++ b/broker/snowflake-heap.go @@ -2,7 +2,7 @@ Keeping track of pending available snowflake proxies. */ -package snowflake_broker +package main /* The Snowflake struct contains a single interaction From edbe18d411cc53aab673927baf32a298d20c6972 Mon Sep 17 00:00:00 2001 From: Hooman Date: Tue, 11 Jul 2017 13:33:00 -0700 Subject: [PATCH 2/9] Fixed README.md and added cert and key options --- broker/README.md | 17 ++++------------- broker/app.yaml | 10 ---------- broker/broker.go | 20 +++++++++++++++----- 3 files changed, 19 insertions(+), 28 deletions(-) delete mode 100644 broker/app.yaml diff --git a/broker/README.md b/broker/README.md index ab6af99..1efe9fc 100644 --- a/broker/README.md +++ b/broker/README.md @@ -22,18 +22,9 @@ The Broker expects: ### Running your own -You can run your own Broker on either localhost or appengine. -(Other CDNs will be supported soon.) +You can run your own Broker on localhost, you'll need to pass a TLS +certificate file using `--cert` option and the corresponding private key +file using `--key` option. - -To run on localhost, run `dev_appserver.py` or equivalent from this -directory. (on arch, I use the wrapper script `dev_appserver-go`) - -To run on appengine, you can spin up your own instance with an arbitrary -name, and use `appcfg.py`. - -In both cases, you'll need to provide the URL of the custom broker +You'll need to provide the URL of the custom broker to the client plugin using the `--url $URL` flag. - -See more detailed appengine instructions -[here](https://cloud.google.com/appengine/docs/go/). diff --git a/broker/app.yaml b/broker/app.yaml deleted file mode 100644 index 14fcf0a..0000000 --- a/broker/app.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# override this with appcfg.py -A $YOUR_APP_ID -application: snowflake-reg -version: 1 -runtime: go -api_version: go1 - -handlers: -- url: /.* - script: _go_app - secure: always diff --git a/broker/broker.go b/broker/broker.go index 8550400..1932b81 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -15,6 +15,7 @@ import ( "time" "sync" "os" + "flag" ) const ( @@ -228,18 +229,27 @@ func ipHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte(remoteAddr)) } -func main() { +var cert, cert_key string - if len(os.Args) < 3 { - log.Println("Usage: broker cert cert_key") +func init() { + flag.StringVar(&cert, "cert", "", "TLS certificate file") + flag.StringVar(&cert_key, "key", "", "TLS key file") + + flag.Parse() + + + if cert == "" || cert_key == "" { + log.Println("Missing options, exiting.") + fmt.Println("Usage:") + flag.PrintDefaults() os.Exit(1) } - cert := os.Args[1] log.Println("Using cert file:", cert) - cert_key := os.Args[2] log.Println("Using cert key file: ", cert_key) +} +func main() { ctx := NewBrokerContext() From 3f4f5d2292416dc7aeb6d091c174e20d779fe947 Mon Sep 17 00:00:00 2001 From: Hooman Date: Wed, 12 Jul 2017 12:07:28 -0700 Subject: [PATCH 3/9] Server ports as parameters --- broker/broker.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/broker/broker.go b/broker/broker.go index 1932b81..ee6f509 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -229,12 +229,15 @@ func ipHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte(remoteAddr)) } -var cert, cert_key string +var cert, cert_key, http_port, https_port string func init() { flag.StringVar(&cert, "cert", "", "TLS certificate file") flag.StringVar(&cert_key, "key", "", "TLS key file") + flag.StringVar(&http_port, "webPort", "80", "HTTP port number") + flag.StringVar(&https_port, "tlsPort", "443", "HTTPS port number") + flag.Parse() @@ -269,7 +272,7 @@ func main() { //Run HTTP server go func(){ defer wg.Done() - err := http.ListenAndServe(":80", nil) + err := http.ListenAndServe(":" + http_port, nil) if err != nil { log.Println("ListenAndServe: ", err) } @@ -278,7 +281,7 @@ func main() { //Run HTTPS server go func(){ defer wg.Done() - err := http.ListenAndServeTLS(":443", cert, cert_key, nil) + err := http.ListenAndServeTLS(":" + https_port, cert, cert_key, nil) if err != nil { log.Println("ListenAndServeTLS: ", err) } From eaa82b8f9a0d3bafe32062ececbd4f8473bfcf86 Mon Sep 17 00:00:00 2001 From: David Fifield Date: Fri, 14 Jul 2017 20:00:25 -0700 Subject: [PATCH 4/9] Move flag code out of init into main. --- broker/broker.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/broker/broker.go b/broker/broker.go index ee6f509..e3aecb1 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -229,9 +229,9 @@ func ipHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte(remoteAddr)) } -var cert, cert_key, http_port, https_port string +func main() { + var cert, cert_key, http_port, https_port string -func init() { flag.StringVar(&cert, "cert", "", "TLS certificate file") flag.StringVar(&cert_key, "key", "", "TLS key file") @@ -250,9 +250,6 @@ func init() { log.Println("Using cert file:", cert) log.Println("Using cert key file: ", cert_key) -} - -func main() { ctx := NewBrokerContext() From 1966612113d1583f9bbe458611e7ffcf19b8f257 Mon Sep 17 00:00:00 2001 From: David Fifield Date: Fri, 14 Jul 2017 20:06:00 -0700 Subject: [PATCH 5/9] go fmt --- broker/broker.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/broker/broker.go b/broker/broker.go index e3aecb1..f56f31b 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -7,15 +7,15 @@ package main import ( "container/heap" + "flag" "fmt" "io/ioutil" "log" "net" "net/http" - "time" - "sync" "os" - "flag" + "sync" + "time" ) const ( @@ -240,7 +240,6 @@ func main() { flag.Parse() - if cert == "" || cert_key == "" { log.Println("Missing options, exiting.") fmt.Println("Usage:") @@ -267,18 +266,18 @@ func main() { wg.Add(2) //Run HTTP server - go func(){ + go func() { defer wg.Done() - err := http.ListenAndServe(":" + http_port, nil) + err := http.ListenAndServe(":"+http_port, nil) if err != nil { log.Println("ListenAndServe: ", err) } }() //Run HTTPS server - go func(){ + go func() { defer wg.Done() - err := http.ListenAndServeTLS(":" + https_port, cert, cert_key, nil) + err := http.ListenAndServeTLS(":"+https_port, cert, cert_key, nil) if err != nil { log.Println("ListenAndServeTLS: ", err) } From 2d89aa0b7bf10c571f934b846c363cf4a2b9b761 Mon Sep 17 00:00:00 2001 From: David Fifield Date: Fri, 14 Jul 2017 20:25:46 -0700 Subject: [PATCH 6/9] autocert (Let's Encrypt) for broker. Replaces --cert and --key with --acme-hostnames and --acme-email. --- broker/README.md | 26 +++++++++++++++--- broker/broker.go | 68 +++++++++++++++++++++++------------------------- 2 files changed, 56 insertions(+), 38 deletions(-) diff --git a/broker/README.md b/broker/README.md index 1efe9fc..8d8c968 100644 --- a/broker/README.md +++ b/broker/README.md @@ -22,9 +22,29 @@ The Broker expects: ### Running your own -You can run your own Broker on localhost, you'll need to pass a TLS -certificate file using `--cert` option and the corresponding private key -file using `--key` option. +The server uses TLS by default. +There is a `--disable-tls` option for testing purposes, +but you should use TLS in production. + +The server automatically fetches certificates +from [Let's Encrypt](https://en.wikipedia.org/wiki/Let's_Encrypt) as needed. +Use the `--acme-hostnames` option to tell the server +what hostnames it may request certificates for. +You can optionally provide a contact email address, +using the `--acme-email` option, +so that Let's Encrypt can inform you of any problems. + +In order to fetch certificates automatically, +the server needs to be listening on port 443 (the default). +On Linux, you can use the `setcap` program, +part of libcap2, to enable the broker to bind to low-numbered ports +without having to run as root: +``` +setcap 'cap_net_bind_service=+ep' /usr/local/bin/broker +``` +You can control the listening port with the --tlsPort +or --webPort options (--webPort is honored only when +also using --disable-tls). You'll need to provide the URL of the custom broker to the client plugin using the `--url $URL` flag. diff --git a/broker/broker.go b/broker/broker.go index f56f31b..9892fc3 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -7,15 +7,17 @@ package main import ( "container/heap" + "crypto/tls" "flag" "fmt" "io/ioutil" "log" "net" "net/http" - "os" - "sync" + "strings" "time" + + "golang.org/x/crypto/acme/autocert" ) const ( @@ -230,26 +232,18 @@ func ipHandler(w http.ResponseWriter, r *http.Request) { } func main() { - var cert, cert_key, http_port, https_port string - - flag.StringVar(&cert, "cert", "", "TLS certificate file") - flag.StringVar(&cert_key, "key", "", "TLS key file") + var acmeEmail string + var acmeHostnamesCommas string + var disableTLS bool + var http_port, https_port string + flag.StringVar(&acmeEmail, "acme-email", "", "optional contact email for Let's Encrypt notifications") + flag.StringVar(&acmeHostnamesCommas, "acme-hostnames", "", "comma-separated hostnames for TLS certificate") + flag.BoolVar(&disableTLS, "disable-tls", false, "don't use HTTPS") flag.StringVar(&http_port, "webPort", "80", "HTTP port number") flag.StringVar(&https_port, "tlsPort", "443", "HTTPS port number") - flag.Parse() - if cert == "" || cert_key == "" { - log.Println("Missing options, exiting.") - fmt.Println("Usage:") - flag.PrintDefaults() - os.Exit(1) - } - - log.Println("Using cert file:", cert) - log.Println("Using cert key file: ", cert_key) - ctx := NewBrokerContext() go ctx.Broker() @@ -262,26 +256,30 @@ func main() { http.Handle("/answer", SnowflakeHandler{ctx, proxyAnswers}) http.Handle("/debug", SnowflakeHandler{ctx, debugHandler}) - var wg sync.WaitGroup - wg.Add(2) + var err error + var server http.Server - //Run HTTP server - go func() { - defer wg.Done() - err := http.ListenAndServe(":"+http_port, nil) - if err != nil { - log.Println("ListenAndServe: ", err) + if acmeHostnamesCommas != "" { + acmeHostnames := strings.Split(acmeHostnamesCommas, ",") + log.Printf("ACME hostnames: %q", acmeHostnames) + + certManager := autocert.Manager{ + Prompt: autocert.AcceptTOS, + HostPolicy: autocert.HostWhitelist(acmeHostnames...), + Email: acmeEmail, } - }() - //Run HTTPS server - go func() { - defer wg.Done() - err := http.ListenAndServeTLS(":"+https_port, cert, cert_key, nil) - if err != nil { - log.Println("ListenAndServeTLS: ", err) - } - }() + server.Addr = net.JoinHostPort("", https_port) + server.TLSConfig = &tls.Config{GetCertificate: certManager.GetCertificate} + err = server.ListenAndServeTLS("", "") + } else if disableTLS { + server.Addr = net.JoinHostPort("", http_port) + err = server.ListenAndServe() + } else { + log.Fatal("the --acme-hostnames or --disable-tls option is required") + } - wg.Wait() + if err != nil { + log.Fatal(err) + } } From afe771690364e14586f24d98b65f0a2172cac1cc Mon Sep 17 00:00:00 2001 From: David Fifield Date: Fri, 14 Jul 2017 20:29:02 -0700 Subject: [PATCH 7/9] Replace --webPort and --tlsPort with a single --addr option. --- broker/README.md | 4 +--- broker/broker.go | 11 +++++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/broker/README.md b/broker/README.md index 8d8c968..38a8029 100644 --- a/broker/README.md +++ b/broker/README.md @@ -42,9 +42,7 @@ without having to run as root: ``` setcap 'cap_net_bind_service=+ep' /usr/local/bin/broker ``` -You can control the listening port with the --tlsPort -or --webPort options (--webPort is honored only when -also using --disable-tls). +You can control the listening port with the --addr option. You'll need to provide the URL of the custom broker to the client plugin using the `--url $URL` flag. diff --git a/broker/broker.go b/broker/broker.go index 9892fc3..a41265b 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -234,14 +234,13 @@ func ipHandler(w http.ResponseWriter, r *http.Request) { func main() { var acmeEmail string var acmeHostnamesCommas string + var addr string var disableTLS bool - var http_port, https_port string flag.StringVar(&acmeEmail, "acme-email", "", "optional contact email for Let's Encrypt notifications") flag.StringVar(&acmeHostnamesCommas, "acme-hostnames", "", "comma-separated hostnames for TLS certificate") + flag.StringVar(&addr, "addr", ":443", "address to listen on") flag.BoolVar(&disableTLS, "disable-tls", false, "don't use HTTPS") - flag.StringVar(&http_port, "webPort", "80", "HTTP port number") - flag.StringVar(&https_port, "tlsPort", "443", "HTTPS port number") flag.Parse() ctx := NewBrokerContext() @@ -257,7 +256,9 @@ func main() { http.Handle("/debug", SnowflakeHandler{ctx, debugHandler}) var err error - var server http.Server + server := http.Server{ + Addr: addr, + } if acmeHostnamesCommas != "" { acmeHostnames := strings.Split(acmeHostnamesCommas, ",") @@ -269,11 +270,9 @@ func main() { Email: acmeEmail, } - server.Addr = net.JoinHostPort("", https_port) server.TLSConfig = &tls.Config{GetCertificate: certManager.GetCertificate} err = server.ListenAndServeTLS("", "") } else if disableTLS { - server.Addr = net.JoinHostPort("", http_port) err = server.ListenAndServe() } else { log.Fatal("the --acme-hostnames or --disable-tls option is required") From a150a991d0c2ba66ca9e567bd088c603c9434775 Mon Sep 17 00:00:00 2001 From: David Fifield Date: Sat, 15 Jul 2017 11:48:28 -0700 Subject: [PATCH 8/9] Copy appengine directory from meek commit 6057a9e9d6. --- appengine/README | 31 +++++++++++ appengine/app.yaml | 10 ++++ appengine/reflect.go | 127 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 appengine/README create mode 100644 appengine/app.yaml create mode 100644 appengine/reflect.go diff --git a/appengine/README b/appengine/README new file mode 100644 index 0000000..d71277a --- /dev/null +++ b/appengine/README @@ -0,0 +1,31 @@ +This component runs on Google App Engine. It lies between meek-client +and meek-server. The App Engine component receives requests from the +client and forwards them to the server, then receives responses from the +server and forwards them to the client. + +You need the Go App Engine SDK in order to deploy the app. + https://cloud.google.com/sdk/docs/#linux +After unpacking, install the app-engine-go component: + google-cloud-sdk/bin/gcloud components install app-engine-go + +To test locally, run + google-cloud-sdk/bin/dev_appserver.py app.yaml +The app will be running at http://127.0.0.1:8080/. You can test broker +forwarding function by browsing to http://127.0.0.1:8000/ip. + +To deploy to App Engine, first create a new project and app. You have to +think of a unique name (marked as "" in the commands). You only +have to do the "create" step once; subsequent times you can go straight +to the "deploy" step. This command will open a browser window so you can +log in to a Google account. + google-cloud-sdk/bin/gcloud projects create + google-cloud-sdk/bin/gcloud app create --project= +Then to deploy the project, run: + google-cloud-sdk/bin/gcloud app deploy --project= + +To configure meek-client to talk to the App Engine app, provide +"https://.appspot.com/" as the url and "www.google.com" as the +front domain. + UseBridges 1 + Bridge meek 0.0.2.0:1 url=https://example.appspot.com/ front=www.google.com + ClientTransportPlugin meek exec ./meek-client --log meek-client.log diff --git a/appengine/app.yaml b/appengine/app.yaml new file mode 100644 index 0000000..ff6efc1 --- /dev/null +++ b/appengine/app.yaml @@ -0,0 +1,10 @@ +runtime: go +api_version: go1 +automatic_scaling: + max_idle_instances: 2 + min_pending_latency: 1000ms + +handlers: +- url: /.* + script: _go_app + secure: always diff --git a/appengine/reflect.go b/appengine/reflect.go new file mode 100644 index 0000000..ccf11f3 --- /dev/null +++ b/appengine/reflect.go @@ -0,0 +1,127 @@ +// A web app for Google App Engine that proxies HTTP requests and responses to a +// Tor relay running meek-server. +package reflect + +import ( + "io" + "net" + "net/http" + "net/url" + "time" + + "appengine" + "appengine/urlfetch" +) + +const ( + forwardURL = "https://meek.bamsoftware.com/" + // A timeout of 0 means to use the App Engine default (5 seconds). + urlFetchTimeout = 20 * time.Second +) + +var context appengine.Context + +// Join two URL paths. +func pathJoin(a, b string) string { + if len(a) > 0 && a[len(a)-1] == '/' { + a = a[:len(a)-1] + } + if len(b) == 0 || b[0] != '/' { + b = "/" + b + } + return a + b +} + +// We reflect only a whitelisted set of header fields. In requests, the full +// list includes things like User-Agent and X-Appengine-Country that the Tor +// bridge doesn't need to know. In responses, there may be things like +// Transfer-Encoding that interfere with App Engine's own hop-by-hop headers. +var reflectedHeaderFields = []string{ + "Content-Type", + "X-Session-Id", +} + +// Get the original client IP address as a string. When using the standard +// net/http server, Request.RemoteAddr is a "host:port" string; however App +// Engine seems to use just "host". We check for both to be safe. +func getClientAddr(r *http.Request) string { + host, _, err := net.SplitHostPort(r.RemoteAddr) + if err == nil { + return host + } + return r.RemoteAddr +} + +// Make a copy of r, with the URL being changed to be relative to forwardURL, +// and including only the headers in reflectedHeaderFields. +func copyRequest(r *http.Request) (*http.Request, error) { + u, err := url.Parse(forwardURL) + if err != nil { + return nil, err + } + // Append the requested path to the path in forwardURL, so that + // forwardURL can be something like "https://example.com/reflect". + u.Path = pathJoin(u.Path, r.URL.Path) + c, err := http.NewRequest(r.Method, u.String(), r.Body) + if err != nil { + return nil, err + } + for _, key := range reflectedHeaderFields { + values, ok := r.Header[key] + if ok { + for _, value := range values { + c.Header.Add(key, value) + } + } + } + // Set the original client IP address in a Meek-IP header. We would use + // X-Forwarded-For, but App Engine prohibits setting that header: + // https://cloud.google.com/appengine/docs/standard/go/outbound-requests#request_headers + // We could use Forwarded from RFC 7239, but other CDNs already use + // X-Forwarded-For and this way we only need one parser. + c.Header.Add("Meek-IP", getClientAddr(r)) + return c, nil +} + +func handler(w http.ResponseWriter, r *http.Request) { + context = appengine.NewContext(r) + fr, err := copyRequest(r) + if err != nil { + context.Errorf("copyRequest: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + // Use urlfetch.Transport directly instead of urlfetch.Client because we + // want only a single HTTP transaction, not following redirects. + transport := urlfetch.Transport{ + Context: context, + // Despite the name, Transport.Deadline is really a timeout and + // not an absolute deadline as used in the net package. In + // other words it is a time.Duration, not a time.Time. + Deadline: urlFetchTimeout, + } + resp, err := transport.RoundTrip(fr) + if err != nil { + context.Errorf("RoundTrip: %s", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer resp.Body.Close() + for _, key := range reflectedHeaderFields { + values, ok := resp.Header[key] + if ok { + for _, value := range values { + w.Header().Add(key, value) + } + } + } + w.WriteHeader(resp.StatusCode) + n, err := io.Copy(w, resp.Body) + if err != nil { + context.Errorf("io.Copy after %d bytes: %s", n, err) + } +} + +func init() { + http.HandleFunc("/", handler) +} From 827972e2bfebdfb0555228f69de48711a7e5f1b1 Mon Sep 17 00:00:00 2001 From: David Fifield Date: Sat, 15 Jul 2017 12:04:59 -0700 Subject: [PATCH 9/9] Customize reflect.go for Snowflake broker. Don't need Meek-IP. --- appengine/README | 18 +++++++----------- appengine/app.yaml | 3 --- appengine/reflect.go | 31 ++++++------------------------- 3 files changed, 13 insertions(+), 39 deletions(-) diff --git a/appengine/README b/appengine/README index d71277a..bec9fcb 100644 --- a/appengine/README +++ b/appengine/README @@ -1,7 +1,5 @@ -This component runs on Google App Engine. It lies between meek-client -and meek-server. The App Engine component receives requests from the -client and forwards them to the server, then receives responses from the -server and forwards them to the client. +This component runs on Google App Engine. It reflects domain-fronted +requests from a client to the Snowflake broker. You need the Go App Engine SDK in order to deploy the app. https://cloud.google.com/sdk/docs/#linux @@ -10,8 +8,7 @@ After unpacking, install the app-engine-go component: To test locally, run google-cloud-sdk/bin/dev_appserver.py app.yaml -The app will be running at http://127.0.0.1:8080/. You can test broker -forwarding function by browsing to http://127.0.0.1:8000/ip. +The app will be running at http://127.0.0.1:8080/. To deploy to App Engine, first create a new project and app. You have to think of a unique name (marked as "" in the commands). You only @@ -23,9 +20,8 @@ log in to a Google account. Then to deploy the project, run: google-cloud-sdk/bin/gcloud app deploy --project= -To configure meek-client to talk to the App Engine app, provide -"https://.appspot.com/" as the url and "www.google.com" as the -front domain. +To configure the Snowflake client to talk to the App Engine app, provide +"https://.appspot.com/" as the --url option. UseBridges 1 - Bridge meek 0.0.2.0:1 url=https://example.appspot.com/ front=www.google.com - ClientTransportPlugin meek exec ./meek-client --log meek-client.log + Bridge snowflake 0.0.2.0:1 + ClientTransportPlugin snowflake exec ./client -url https://.appspot.com/ -front www.google.com diff --git a/appengine/app.yaml b/appengine/app.yaml index ff6efc1..44df436 100644 --- a/appengine/app.yaml +++ b/appengine/app.yaml @@ -1,8 +1,5 @@ runtime: go api_version: go1 -automatic_scaling: - max_idle_instances: 2 - min_pending_latency: 1000ms handlers: - url: /.* diff --git a/appengine/reflect.go b/appengine/reflect.go index ccf11f3..f6b5336 100644 --- a/appengine/reflect.go +++ b/appengine/reflect.go @@ -1,10 +1,9 @@ -// A web app for Google App Engine that proxies HTTP requests and responses to a -// Tor relay running meek-server. +// A web app for Google App Engine that proxies HTTP requests and responses to +// the Snowflake broker. package reflect import ( "io" - "net" "net/http" "net/url" "time" @@ -14,7 +13,7 @@ import ( ) const ( - forwardURL = "https://meek.bamsoftware.com/" + forwardURL = "https://snowflake-broker.bamsoftware.com/" // A timeout of 0 means to use the App Engine default (5 seconds). urlFetchTimeout = 20 * time.Second ) @@ -32,26 +31,14 @@ func pathJoin(a, b string) string { return a + b } -// We reflect only a whitelisted set of header fields. In requests, the full -// list includes things like User-Agent and X-Appengine-Country that the Tor -// bridge doesn't need to know. In responses, there may be things like -// Transfer-Encoding that interfere with App Engine's own hop-by-hop headers. +// We reflect only a whitelisted set of header fields. Otherwise, we may copy +// headers like Transfer-Encoding that interfere with App Engine's own +// hop-by-hop headers. var reflectedHeaderFields = []string{ "Content-Type", "X-Session-Id", } -// Get the original client IP address as a string. When using the standard -// net/http server, Request.RemoteAddr is a "host:port" string; however App -// Engine seems to use just "host". We check for both to be safe. -func getClientAddr(r *http.Request) string { - host, _, err := net.SplitHostPort(r.RemoteAddr) - if err == nil { - return host - } - return r.RemoteAddr -} - // Make a copy of r, with the URL being changed to be relative to forwardURL, // and including only the headers in reflectedHeaderFields. func copyRequest(r *http.Request) (*http.Request, error) { @@ -74,12 +61,6 @@ func copyRequest(r *http.Request) (*http.Request, error) { } } } - // Set the original client IP address in a Meek-IP header. We would use - // X-Forwarded-For, but App Engine prohibits setting that header: - // https://cloud.google.com/appengine/docs/standard/go/outbound-requests#request_headers - // We could use Forwarded from RFC 7239, but other CDNs already use - // X-Forwarded-For and this way we only need one parser. - c.Header.Add("Meek-IP", getClientAddr(r)) return c, nil }