Skip to content

Instantly share code, notes, and snippets.

@stapelberg
Last active April 1, 2022 08:49
Show Gist options
  • Save stapelberg/ed592f3cde504f459d6be45ad64f4552 to your computer and use it in GitHub Desktop.
Save stapelberg/ed592f3cde504f459d6be45ad64f4552 to your computer and use it in GitHub Desktop.
// Tailscale Frontend: It uses tailscale-as-a-library to
// listen on a port, independently from the operating system network, i.e. you
// can run an nginx server on :80 and :443 without impacting the frontend.
//
// set up DNS, e.g.:
// prometheus.ts.zekjur.net A 100.117.6.125
//
// frontend% TAILSCALE_USE_WIP_CODE=true tailscalefrontend -hostname=srv.example.net -allowed_user=michael@example.net
//
// (first login requires running with TS_LOGIN=1 environment variable to print link for the browser)
//
// client% curl -v https://prometheus.ts.zekjur.net
package main
import (
"flag"
"fmt"
"html"
"log"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"tailscale.com/client/tailscale"
"tailscale.com/tsnet"
)
func main() {
hostname := flag.String("hostname", "frontend", "tailscale hostname")
allowedUser := flag.String("allowed_user", "", "the name of a tailscale user to allow")
flag.Parse()
s := &tsnet.Server{
Hostname: *hostname,
}
reverseProxyHosts := map[string]string{
"alertmanager.ts.zekjur.net": "http://alertmanager:9093/",
"prometheus.ts.zekjur.net": "http://prometheus:9090/",
"grafana.ts.zekjur.net": "http://grafana:3000/",
}
httpsrv := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
who, err := tailscale.WhoIs(r.Context(), r.RemoteAddr)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if who.UserProfile.LoginName != *allowedUser || *allowedUser == "" {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
if target, ok := reverseProxyHosts[r.Host]; ok {
origin, _ := url.Parse(target)
director := func(req *http.Request) {
req.Header.Add("X-Forwarded-Host", req.Host)
req.Header.Add("X-Origin-Host", origin.Host)
req.URL.Scheme = "http"
req.URL.Host = origin.Host
}
proxy := &httputil.ReverseProxy{Director: director}
proxy.ServeHTTP(w, r)
return
}
http.Error(w, "not found", http.StatusNotFound)
}),
}
ln, err := s.Listen("tcp", ":443")
if err != nil {
log.Fatal(err)
}
const certFile = "/persistent/ssl/frontend.crt"
const keyFile = "/persistent/ssl/frontend.key"
log.Fatal(httpsrv.ServeTLS(ln, certFile, keyFile))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment