package main import ( "crypto/tls" "flag" "fmt" "log" "net/http" "net/http/httputil" "net/url" "tailscale.com/client/tailscale" "tailscale.com/tsnet" ) var ( target = flag.String("target", "http://127.0.0.1:3000", "target HTTP server for Grafana") httpsDomainName = flag.String("https-domain-name", "", "your Tailscale HTTPS domain name (tails-scales.ts.net)") hostname = flag.String("hostname", "grafana", "the hostname to use on the tailnet") ) func main() { flag.Parse() u, err := url.Parse(*target) if err != nil { log.Fatal(err) } hdlr := tsAuthMiddleware{ next: httputil.NewSingleHostReverseProxy(u), } srv := &tsnet.Server{ Hostname: *hostname, Logf: log.Printf, } l, err := srv.Listen("tcp", ":443") if err != nil { log.Fatal(err) } l = tls.NewListener(l, &tls.Config{ GetCertificate: func(chi *tls.ClientHelloInfo) (*tls.Certificate, error) { if wantName := fmt.Sprintf("%s.%s", *hostname, *httpsDomainName); chi.ServerName != wantName { return nil, fmt.Errorf("wanted hostname %s, got: %s", wantName, chi.ServerName) } c, err := tailscale.GetCertificate(chi) if err != nil { log.Printf("%s: %v", chi.Conn.RemoteAddr(), err) } return c, err }, }) log.Printf("listening on https://%s.%s", *hostname, *httpsDomainName) log.Fatal(http.Serve(l, hdlr)) } type tsAuthMiddleware struct { next http.Handler } func (t tsAuthMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { userInfo, err := tailscale.WhoIs(r.Context(), r.RemoteAddr) if err != nil { log.Printf("can't get whois response: %v", err) http.Error(w, "can't get whois response: "+err.Error(), http.StatusInternalServerError) return } r.Header.Set("X-Webauth-User", userInfo.UserProfile.LoginName) r.Header.Set("X-Webauth-Name", userInfo.UserProfile.DisplayName) t.next.ServeHTTP(w, r) }