diff --git a/cmd/microproxy/microproxy.go b/cmd/microproxy/microproxy.go deleted file mode 100644 index e537ab140..000000000 --- a/cmd/microproxy/microproxy.go +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// microproxy proxies incoming HTTPS connections to another -// destination. Instead of managing its own TLS certificates, it -// borrows issued certificates and keys from an autocert directory. -package main - -import ( - "crypto/tls" - "encoding/json" - "flag" - "fmt" - "io" - "io/ioutil" - "log" - "net/http" - "net/http/httputil" - "net/url" - "path/filepath" - "strings" - "sync" - "time" - - "tailscale.com/logpolicy" - "tailscale.com/tsweb" -) - -var ( - addr = flag.String("addr", ":4430", "server address") - certdir = flag.String("certdir", "", "directory to borrow LetsEncrypt certificates from") - hostname = flag.String("hostname", "", "hostname to serve") - logCollection = flag.String("logcollection", "", "If non-empty, logtail collection to log to") - nodeExporter = flag.String("node-exporter", "http://localhost:9100", "URL of the local prometheus node exporter") - goVarsURL = flag.String("go-vars-url", "http://localhost:8383/debug/vars", "URL of a local Go server's /debug/vars endpoint") - insecure = flag.Bool("insecure", false, "serve over http, for development") -) - -func main() { - flag.Parse() - - if *logCollection != "" { - logpolicy.New(*logCollection) - } - - ne, err := url.Parse(*nodeExporter) - if err != nil { - log.Fatalf("Couldn't parse URL %q: %v", *nodeExporter, err) - } - proxy := httputil.NewSingleHostReverseProxy(ne) - proxy.FlushInterval = time.Second - - if _, err = url.Parse(*goVarsURL); err != nil { - log.Fatalf("Couldn't parse URL %q: %v", *goVarsURL, err) - } - - mux := http.NewServeMux() - tsweb.Debugger(mux) // registers /debug/* - mux.Handle("/metrics", tsweb.Protected(proxy)) - mux.Handle("/varz", tsweb.Protected(tsweb.StdHandler(&goVarsHandler{*goVarsURL}, tsweb.HandlerOptions{ - Quiet200s: true, - Logf: log.Printf, - }))) - - ch := &certHolder{ - hostname: *hostname, - path: filepath.Join(*certdir, *hostname), - } - - httpsrv := &http.Server{ - Addr: *addr, - Handler: mux, - } - - if !*insecure { - httpsrv.TLSConfig = &tls.Config{GetCertificate: ch.GetCertificate} - err = httpsrv.ListenAndServeTLS("", "") - } else { - err = httpsrv.ListenAndServe() - } - if err != nil && err != http.ErrServerClosed { - log.Fatal(err) - } -} - -type goVarsHandler struct { - url string -} - -func promPrint(w io.Writer, prefix string, obj map[string]interface{}) { - for k, i := range obj { - if prefix != "" { - k = prefix + "_" + k - } - switch v := i.(type) { - case map[string]interface{}: - promPrint(w, k, v) - case float64: - const saveConfigReject = "control_save_config_rejected_" - const saveConfig = "control_save_config_" - switch { - case strings.HasPrefix(k, saveConfigReject): - fmt.Fprintf(w, "control_save_config_rejected{reason=%q} %f\n", k[len(saveConfigReject):], v) - case strings.HasPrefix(k, saveConfig): - fmt.Fprintf(w, "control_save_config{reason=%q} %f\n", k[len(saveConfig):], v) - default: - fmt.Fprintf(w, "%s %f\n", k, v) - } - default: - fmt.Fprintf(w, "# Skipping key %q, unhandled type %T\n", k, v) - } - } -} - -func (h *goVarsHandler) ServeHTTPReturn(w http.ResponseWriter, r *http.Request) error { - resp, err := http.Get(h.url) - if err != nil { - return tsweb.Error(http.StatusInternalServerError, "fetch failed", err) - } - defer resp.Body.Close() - var mon map[string]interface{} - if err := json.NewDecoder(resp.Body).Decode(&mon); err != nil { - return tsweb.Error(http.StatusInternalServerError, "fetch failed", err) - } - - w.WriteHeader(http.StatusOK) - promPrint(w, "", mon) - return nil -} - -// certHolder loads and caches a TLS certificate from disk, reloading -// it every hour. -type certHolder struct { - hostname string // only hostname allowed in SNI - path string // path of certificate+key combined PEM file - - mu sync.Mutex - cert *tls.Certificate // cached parsed cert+key - loaded time.Time -} - -func (c *certHolder) GetCertificate(ch *tls.ClientHelloInfo) (*tls.Certificate, error) { - if ch.ServerName != c.hostname { - return nil, fmt.Errorf("wrong client SNI %q", ch.ServerName) - } - c.mu.Lock() - defer c.mu.Unlock() - if time.Since(c.loaded) > time.Hour { - if err := c.loadLocked(); err != nil { - log.Printf("Reloading cert %q: %v", c.path, err) - // continue anyway, we might be able to serve off the stale cert. - } - } - return c.cert, nil -} - -// load reloads the TLS certificate and key from disk. Caller must -// hold mu. -func (c *certHolder) loadLocked() error { - bs, err := ioutil.ReadFile(c.path) - if err != nil { - return fmt.Errorf("reading %q: %v", c.path, err) - } - cert, err := tls.X509KeyPair(bs, bs) - if err != nil { - return fmt.Errorf("parsing %q: %v", c.path, err) - } - - c.cert = &cert - c.loaded = time.Now() - return nil -}