cmd/tip: redirect http://tip.golang.org to https
At some point we switched tip.golang.org to run in GKE, which terminates TLS directly on port 443. This requires a new technique for detecting a plain HTTP connection. In addition we may want to run talks.golang.org on App Engine Flex, which uses an X-Forwarded-Proto header to indicate HTTP, so let's prepare for that possibility. Fixes golang/go#19759. Change-Id: Iddc567214c5d28f61c405db065aa1b3f2c92fd85 Reviewed-on: https://go-review.googlesource.com/38800 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
620ecdb8d7
commit
37a1062ad0
|
|
@ -30,4 +30,3 @@ Kubernetes instructions:
|
||||||
|
|
||||||
TODO(bradfitz): flesh out these instructions as I gain experience
|
TODO(bradfitz): flesh out these instructions as I gain experience
|
||||||
with updating this over time. Also: move talks.golang.org to GKE too?
|
with updating this over time. Also: move talks.golang.org to GKE too?
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,15 +56,14 @@ func main() {
|
||||||
|
|
||||||
p := &Proxy{builder: b}
|
p := &Proxy{builder: b}
|
||||||
go p.run()
|
go p.run()
|
||||||
http.Handle("/", httpsOnlyHandler{p})
|
mux := newServeMux(p)
|
||||||
http.HandleFunc("/_ah/health", p.serveHealthCheck)
|
|
||||||
|
|
||||||
log.Printf("Starting up tip server for builder %q", os.Getenv(k))
|
log.Printf("Starting up tip server for builder %q", os.Getenv(k))
|
||||||
|
|
||||||
errc := make(chan error)
|
errc := make(chan error)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
errc <- http.ListenAndServe(":8080", nil)
|
errc <- http.ListenAndServe(":8080", mux)
|
||||||
}()
|
}()
|
||||||
if *autoCertDomain != "" {
|
if *autoCertDomain != "" {
|
||||||
log.Printf("Listening on port 443 with LetsEncrypt support on domain %q", *autoCertDomain)
|
log.Printf("Listening on port 443 with LetsEncrypt support on domain %q", *autoCertDomain)
|
||||||
|
|
@ -74,6 +73,7 @@ func main() {
|
||||||
}
|
}
|
||||||
s := &http.Server{
|
s := &http.Server{
|
||||||
Addr: ":https",
|
Addr: ":https",
|
||||||
|
Handler: mux,
|
||||||
TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
|
TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
|
|
@ -245,6 +245,13 @@ func (p *Proxy) poll() {
|
||||||
p.cmd = cmd
|
p.cmd = cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newServeMux(p *Proxy) http.Handler {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.Handle("/", httpsOnlyHandler{p})
|
||||||
|
mux.HandleFunc("/_ah/health", p.serveHealthCheck)
|
||||||
|
return mux
|
||||||
|
}
|
||||||
|
|
||||||
func waitReady(b Builder, hostport string) error {
|
func waitReady(b Builder, hostport string) error {
|
||||||
var err error
|
var err error
|
||||||
deadline := time.Now().Add(startTimeout)
|
deadline := time.Now().Add(startTimeout)
|
||||||
|
|
@ -360,20 +367,36 @@ func getOK(url string) (body []byte, err error) {
|
||||||
return body, nil
|
return body, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// httpsOnlyHandler redirects requests to "http://example.com/foo?bar"
|
// httpsOnlyHandler redirects requests to "http://example.com/foo?bar" to
|
||||||
// to "https://example.com/foo?bar"
|
// "https://example.com/foo?bar". It should be used when the server is listening
|
||||||
|
// for HTTP traffic behind a proxy that terminates TLS traffic, not when the Go
|
||||||
|
// server is terminating TLS directly.
|
||||||
type httpsOnlyHandler struct {
|
type httpsOnlyHandler struct {
|
||||||
h http.Handler
|
h http.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isProxiedReq checks whether the server is running behind a proxy that may be
|
||||||
|
// terminating TLS.
|
||||||
|
func isProxiedReq(r *http.Request) bool {
|
||||||
|
if _, ok := r.Header["X-Appengine-Https"]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := r.Header["X-Forwarded-Proto"]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (h httpsOnlyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h httpsOnlyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Header.Get("X-Appengine-Https") == "off" {
|
if r.Header.Get("X-Appengine-Https") == "off" || r.Header.Get("X-Forwarded-Proto") == "http" ||
|
||||||
|
(!isProxiedReq(r) && r.TLS == nil) {
|
||||||
r.URL.Scheme = "https"
|
r.URL.Scheme = "https"
|
||||||
r.URL.Host = r.Host
|
r.URL.Host = r.Host
|
||||||
http.Redirect(w, r, r.URL.String(), http.StatusFound)
|
http.Redirect(w, r, r.URL.String(), http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if r.Header.Get("X-Appengine-Https") == "on" {
|
if r.Header.Get("X-Appengine-Https") == "on" || r.Header.Get("X-Forwarded-Proto") == "https" ||
|
||||||
|
(!isProxiedReq(r) && r.TLS != nil) {
|
||||||
// Only set this header when we're actually in production.
|
// Only set this header when we're actually in production.
|
||||||
w.Header().Set("Strict-Transport-Security", "max-age=31536000; preload")
|
w.Header().Set("Strict-Transport-Security", "max-age=31536000; preload")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by the Apache 2.0
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTipRedirects(t *testing.T) {
|
||||||
|
mux := newServeMux(&Proxy{builder: &godocBuilder{}})
|
||||||
|
req := httptest.NewRequest("GET", "http://example.com/foo?bar=baz", nil)
|
||||||
|
req.Header.Set("X-Forwarded-Proto", "http")
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
mux.ServeHTTP(w, req)
|
||||||
|
if w.Code != 302 {
|
||||||
|
t.Errorf("expected Code to be 302, got %d", w.Code)
|
||||||
|
}
|
||||||
|
want := "https://example.com/foo?bar=baz"
|
||||||
|
if loc := w.Header().Get("Location"); loc != want {
|
||||||
|
t.Errorf("Location header: got %s, want %s", loc, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue