mirror of https://github.com/tailscale/tailscale/
cmd/microproxy: delete.
Thank goodness. Fixes #2635 Signed-off-by: David Anderson <danderson@tailscale.com>pull/2649/head
parent
c6740da624
commit
d2d55bd63c
@ -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
|
|
||||||
}
|
|
Loading…
Reference in New Issue