Skip to content

Commit 2fc65c3

Browse files
committed
golink: listen on HTTPS and redirect HTTP traffic
Updates #9 On tailnets with TLS enabled serve HTTP traffic with a separate redirectHandler which sends requests to our HTTPS listener destination. Add `-L` to documented examples of using `curl` to follow these redirects if present. Signed-off-by: Patrick O'Doherty <[email protected]>
1 parent c9212a4 commit 2fc65c3

File tree

2 files changed

+79
-8
lines changed

2 files changed

+79
-8
lines changed

golink.go

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"bytes"
1010
"context"
1111
"crypto/rand"
12+
"crypto/tls"
1213
"embed"
1314
"encoding/base64"
1415
"encoding/json"
@@ -169,18 +170,78 @@ func Run() error {
169170
if err := srv.Start(); err != nil {
170171
return err
171172
}
172-
localClient, _ = srv.LocalClient()
173173

174-
l80, err := srv.Listen("tcp", ":80")
174+
// create tsNet server and wait for it to be ready & connected.
175+
localClient, _ = srv.LocalClient()
176+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
177+
defer cancel()
178+
_, err = srv.Up(ctx)
175179
if err != nil {
176180
return err
177181
}
178182

179-
log.Printf("Serving http://%s/ ...", *hostname)
180-
if err := http.Serve(l80, serveHandler()); err != nil {
181-
return err
183+
enableTLS := len(srv.CertDomains()) > 0
184+
if enableTLS {
185+
// warm the certificate cache for all cert domains to prevent users waiting
186+
// on ACME challenges in-line on their first request.
187+
for _, d := range srv.CertDomains() {
188+
log.Printf("Provisioning TLS certificate for %s ...", d)
189+
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
190+
defer cancel()
191+
192+
_, _, err := localClient.CertPair(ctx, d)
193+
if err != nil {
194+
return err
195+
}
196+
}
197+
198+
redirectFqdn := srv.CertDomains()[0]
199+
// HTTP listener that redirects to our HTTPS listener.
200+
log.Println("Listening on :80")
201+
httpListener, err := srv.Listen("tcp", ":80")
202+
if err != nil {
203+
return err
204+
}
205+
go func() error {
206+
log.Printf("Serving http://%s/ ...", *hostname)
207+
if err := http.Serve(httpListener, redirectHandler(redirectFqdn)); err != nil {
208+
return err
209+
}
210+
return nil
211+
}()
212+
213+
log.Println("Listening on :443")
214+
httpsListener, err := srv.Listen("tcp", ":443")
215+
if err != nil {
216+
return err
217+
}
218+
s := http.Server{
219+
Addr: ":443",
220+
Handler: serveHandler(),
221+
TLSConfig: &tls.Config{
222+
GetCertificate: localClient.GetCertificate,
223+
},
224+
}
225+
226+
log.Printf("Serving https://%s/\n", redirectFqdn)
227+
if err := s.ServeTLS(httpsListener, "", ""); err != nil {
228+
return err
229+
}
230+
return nil
231+
} else {
232+
// no TLS, just serve on :80
233+
log.Println("Listening on :80")
234+
httpListener, err := srv.Listen("tcp", ":80")
235+
if err != nil {
236+
return err
237+
}
238+
log.Printf("Serving http://%s/ ...", *hostname)
239+
if err := http.Serve(httpListener, serveHandler()); err != nil {
240+
return err
241+
}
242+
return nil
182243
}
183-
return nil
244+
184245
}
185246

186247
var (
@@ -286,6 +347,16 @@ func deleteLinkStats(link *Link) {
286347
db.DeleteStats(link.Short)
287348
}
288349

350+
// redirectHandler returns the http.Handler for serving all plaintext HTTP
351+
// requests. It redirects all requests to the HTTPs version of the same URL.
352+
func redirectHandler(hostname string) http.Handler {
353+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
354+
path := r.URL.Path
355+
newUrl := fmt.Sprintf("https://%s%s", hostname, path)
356+
http.Redirect(w, r, newUrl, http.StatusMovedPermanently)
357+
})
358+
}
359+
289360
// serverHandler returns the main http.Handler for serving all requests.
290361
func serveHandler() http.Handler {
291362
mux := http.NewServeMux()

tmpl/help.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,15 @@ <h2 id="api">Application Programming Interface (API)</h2>
114114
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>.
115115
This is useful to create data snapshots that can be restored later.
116116

117-
<pre>{{`$ curl go/.export
117+
<pre>{{`$ curl -L go/.export
118118
{"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}
119119
{"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}`}}
120120
</pre>
121121

122122
<p>
123123
Create a new link by sending a POST request with a <code>short</code> and <code>long</code> value:
124124

125-
<pre>{{`$ curl -d short=cs -d long=https://cs.github.com/ go
125+
<pre>{{`$ curl -L -d short=cs -d long=https://cs.github.com/ go
126126
{"Short":"cs","Long":"https://cs.github.com/","Created":"2022-06-03T22:15:29.993978392Z","LastEdit":"2022-06-03T22:15:29.993978392Z","Owner":"[email protected]"}`}}
127127
</pre>
128128

0 commit comments

Comments
 (0)