-
Couldn't load subscription status.
- Fork 115
golink: listen on HTTPS and redirect HTTP traffic #99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
2fc65c3
23f9f96
5722cf2
5e70935
7917da1
8ac87f5
39b1540
7e903c1
9cabf69
a1ea1da
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,6 +36,7 @@ import ( | |
| "tailscale.com/ipn" | ||
| "tailscale.com/tailcfg" | ||
| "tailscale.com/tsnet" | ||
| "tailscale.com/util/dnsname" | ||
| ) | ||
|
|
||
| const defaultHostname = "go" | ||
|
|
@@ -158,6 +159,7 @@ func Run() error { | |
| return errors.New("--hostname, if specified, cannot be empty") | ||
| } | ||
|
|
||
| // create tsNet server and wait for it to be ready & connected. | ||
| srv := &tsnet.Server{ | ||
| ControlURL: *controlURL, | ||
| Hostname: *hostname, | ||
|
|
@@ -169,17 +171,55 @@ func Run() error { | |
| if err := srv.Start(); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| localClient, _ = srv.LocalClient() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While we're here, maybe now is a good time to localClient, err = srv.LocalClient()
if err != nil {
return err
}There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As long as the server is started, |
||
| out: | ||
| for { | ||
| upCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | ||
| defer cancel() | ||
| status, err := srv.Up(upCtx) | ||
| if err == nil && status != nil { | ||
patrickod marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| break out | ||
| } | ||
| } | ||
|
|
||
| l80, err := srv.Listen("tcp", ":80") | ||
| statusCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | ||
| defer cancel() | ||
| status, err := localClient.Status(statusCtx) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| enableTLS := status.Self.HasCap(tailcfg.CapabilityHTTPS) | ||
| fqdn := strings.TrimSuffix(status.Self.DNSName, ".") | ||
|
|
||
| httpHandler := serveHandler() | ||
| if enableTLS { | ||
| httpsHandler := HSTS(httpHandler) | ||
| httpHandler = redirectHandler(fqdn) | ||
|
|
||
| httpsListener, err := srv.ListenTLS("tcp", ":443") | ||
| if err != nil { | ||
| return err | ||
| } | ||
| log.Println("Listening on :443") | ||
| go func() { | ||
| log.Printf("Serving https://%s/ ...", fqdn) | ||
| if err := http.Serve(httpsListener, httpsHandler); err != nil { | ||
| log.Fatal(err) | ||
| } | ||
| }() | ||
| } | ||
|
|
||
| httpListener, err := srv.Listen("tcp", ":80") | ||
| log.Println("Listening on :80") | ||
| if err != nil { | ||
| return err | ||
| } | ||
| log.Printf("Serving http://%s/ ...", *hostname) | ||
| if err := http.Serve(l80, serveHandler()); err != nil { | ||
| if err := http.Serve(httpListener, httpHandler); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
|
|
@@ -286,6 +326,34 @@ func deleteLinkStats(link *Link) { | |
| db.DeleteStats(link.Short) | ||
| } | ||
|
|
||
| // redirectHandler returns the http.Handler for serving all plaintext HTTP | ||
| // requests. It redirects all requests to the HTTPs version of the same URL. | ||
| func redirectHandler(hostname string) http.Handler { | ||
| return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| http.Redirect(w, r, (&url.URL{Scheme: "https", Host: hostname, Path: r.URL.Path}).String(), http.StatusFound) | ||
| }) | ||
| } | ||
|
|
||
| // HSTS wraps the provided handler and sets Strict-Transport-Security header on | ||
| // responses. It inspects the Host header to ensure we do not specify HSTS | ||
| // response on non fully qualified domain name origins. | ||
| func HSTS(h http.Handler) http.Handler { | ||
| return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| host, found := r.Header["Host"] | ||
| if found { | ||
| host := host[0] | ||
| fqdn, err := dnsname.ToFQDN(host) | ||
| if err == nil { | ||
| segCount := fqdn.NumLabels() | ||
| if segCount > 1 { | ||
| w.Header().Set("Strict-Transport-Security", "max-age=31536000") | ||
| } | ||
| } | ||
| } | ||
| h.ServeHTTP(w, r) | ||
| }) | ||
| } | ||
|
|
||
| // serverHandler returns the main http.Handler for serving all requests. | ||
| func serveHandler() http.Handler { | ||
| mux := http.NewServeMux() | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -114,15 +114,15 @@ <h2 id="api">Application Programming Interface (API)</h2> | |
| Visit <a href="/.export">go/.export</a> to export all saved links and their metadata in <a href="https://jsonlines.org/">JSON Lines format</a>. | ||
| This is useful to create data snapshots that can be restored later. | ||
|
|
||
| <pre>{{`$ curl go/.export | ||
| <pre>{{`$ curl -L go/.export | ||
| {"Short":"go","Long":"http://go","Created":"2022-05-31T13:04:44.741457796-07:00","LastEdit":"2022-05-31T13:04:44.741457796-07:00","Owner":"[email protected]","Clicks":1} | ||
| {"Short":"slack","Long":"https://company.slack.com/{{if .Path}}channels/{{PathEscape .Path}}{{end}}","Created":"2022-06-17T18:05:43.562948451Z","LastEdit":"2022-06-17T18:06:35.811398Z","Owner":"[email protected]","Clicks":4}`}} | ||
| </pre> | ||
|
|
||
| <p> | ||
| Create a new link by sending a POST request with a <code>short</code> and <code>long</code> value: | ||
|
|
||
| <pre>{{`$ curl -d short=cs -d long=https://cs.github.com/ go | ||
| <pre>{{`$ curl -L -d short=cs -d long=https://cs.github.com/ go | ||
| {"Short":"cs","Long":"https://cs.github.com/","Created":"2022-06-03T22:15:29.993978392Z","LastEdit":"2022-06-03T22:15:29.993978392Z","Owner":"[email protected]"}`}} | ||
| </pre> | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.