grafanauth/grafanauth.go

80 lines
1.8 KiB
Go

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)
}