From 4d5386001abb52f31e6d20109834251265ed0ab1 Mon Sep 17 00:00:00 2001 From: Xe Date: Fri, 19 Nov 2021 10:11:27 -0500 Subject: [PATCH] cmd/tailtlsproxy: introduce HTTPS proxy bridge This allows you to bridge existing HTTP/S services into your tailnet using the Let's Encrypt[1] functionality. This will allow you to run multiple services on the same computer and still have them point at different target HTTP services. This also includes an example systemd template unit that lets you easily set up multiple instances of tailtlsproxy on the same machine. [1]: https://tailscale.com/blog/tls-certs/ Signed-off-by: Xe --- cmd/tailtlsproxy/main.go | 86 +++++++++++++++++++++ cmd/tailtlsproxy/tailtlsproxy-test.defaults | 2 + cmd/tailtlsproxy/tailtlsproxy@.service | 19 +++++ 3 files changed, 107 insertions(+) create mode 100644 cmd/tailtlsproxy/main.go create mode 100644 cmd/tailtlsproxy/tailtlsproxy-test.defaults create mode 100644 cmd/tailtlsproxy/tailtlsproxy@.service diff --git a/cmd/tailtlsproxy/main.go b/cmd/tailtlsproxy/main.go new file mode 100644 index 000000000..b5fe35a48 --- /dev/null +++ b/cmd/tailtlsproxy/main.go @@ -0,0 +1,86 @@ +// Copyright (c) 2021 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. + +package main + +import ( + "crypto/tls" + "flag" + "log" + "net/http" + "net/http/httputil" + "net/url" + "os" + "time" + + "tailscale.com/client/tailscale" + "tailscale.com/tsnet" +) + +func envOr(name, def string) string { + if val, ok := os.LookupEnv(name); ok { + return val + } + + return def +} + +var ( + bind = flag.String("bind", ":443", "TCP hostport to bind to for TLS http traffic") + hostname = flag.String("hostname", envOr("HOSTNAME", "toolname"), "hostname to register on tailnet (can be set with $HOSTNAME)") + auth = flag.Bool("auth", false, "if set, authenticate with tailscale (enables verbose logging)") + v = flag.Bool("v", false, "if set, enable verbose tailscale logs") + to = flag.String("to", envOr("TO", "http://127.0.0.1:3030"), "HTTP/S url to reverse proxy to (can be set with $TO)") +) + +func main() { + os.Setenv("TAILSCALE_USE_WIP_CODE", "true") + flag.Parse() + + srv := tsnet.Server{ + Hostname: *hostname, + } + + if *auth || *v { + srv.Logf = log.Printf + } else { + srv.Logf = func(string, ...interface{}) {} + } + + if *auth { + os.Setenv("TS_LOGIN", "1") + } + + ln, err := srv.Listen("tcp", *bind) + if err != nil { + log.Fatal(err) + } + + u, err := url.Parse(*to) + if err != nil { + log.Fatalf("%s wasn't a valid URL: %v", *to, err) + } + + h := httputil.NewSingleHostReverseProxy(u) + + ln = tls.NewListener(ln, &tls.Config{ + GetCertificate: func(chi *tls.ClientHelloInfo) (*tls.Certificate, error) { + c, err := tailscale.GetCertificate(chi) + if err != nil { + log.Println(err) + } + return c, err + }, + }) + + s := &http.Server{ + IdleTimeout: 5 * time.Minute, + Addr: *bind, + Handler: h, + } + + log.Printf("listening for https on https://%s.your.tailcert.domain and forwarding to %s", *hostname, *to) + + log.Fatal(s.Serve(ln)) +} diff --git a/cmd/tailtlsproxy/tailtlsproxy-test.defaults b/cmd/tailtlsproxy/tailtlsproxy-test.defaults new file mode 100644 index 000000000..9a5e3991d --- /dev/null +++ b/cmd/tailtlsproxy/tailtlsproxy-test.defaults @@ -0,0 +1,2 @@ +HOSTNAME=test +TO=http://127.0.0.1:3030 diff --git a/cmd/tailtlsproxy/tailtlsproxy@.service b/cmd/tailtlsproxy/tailtlsproxy@.service new file mode 100644 index 000000000..78505da39 --- /dev/null +++ b/cmd/tailtlsproxy/tailtlsproxy@.service @@ -0,0 +1,19 @@ +[Unit] +Description=Tailscale TLS Proxy bridge for %i +After=network.target + +[Service] +Environment=HOME=/var/lib/private/tailtlsproxy-%i +EnvironmentFile=/etc/default/tailtlsproxy-%i +ExecStart=/usr/bin/tailtlsproxy +Restart=on-failure +RuntimeDirectory=tailtlsproxy-%i +RuntimeDirectoryMode=0755 +StateDirectory=tailtlsproxy-%i +StateDirectoryMode=0700 +CacheDirectory=tailtlsproxy-%i +CacheDirectoryMode=0750 +DynamicUser=yes + +[Install] +WantedBy=multi-user.target \ No newline at end of file