mirror of
https://github.com/tailscale/tailscale.git
synced 2025-06-29 19:48:38 +00:00
82 lines
2.3 KiB
Go
82 lines
2.3 KiB
Go
![]() |
// Copyright (c) Tailscale Inc & AUTHORS
|
||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||
|
|
||
|
// The proxy-test-server command is a simple HTTP proxy server for testing
|
||
|
// Tailscale's client proxy functionality.
|
||
|
package main
|
||
|
|
||
|
import (
|
||
|
"crypto/tls"
|
||
|
"flag"
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"net"
|
||
|
"net/http"
|
||
|
"os"
|
||
|
"strings"
|
||
|
|
||
|
"golang.org/x/crypto/acme/autocert"
|
||
|
"tailscale.com/net/connectproxy"
|
||
|
"tailscale.com/tempfork/acme"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
listen = flag.String("listen", ":8080", "Address to listen on for HTTPS proxy requests")
|
||
|
hostname = flag.String("hostname", "localhost", "Hostname for the proxy server")
|
||
|
tailscaleOnly = flag.Bool("tailscale-only", true, "Restrict proxy to Tailscale targets only")
|
||
|
extraAllowedHosts = flag.String("allow-hosts", "", "Comma-separated list of allowed target hosts to additionally allow if --tailscale-only is true")
|
||
|
)
|
||
|
|
||
|
func main() {
|
||
|
flag.Parse()
|
||
|
|
||
|
am := &autocert.Manager{
|
||
|
HostPolicy: autocert.HostWhitelist(*hostname),
|
||
|
Prompt: autocert.AcceptTOS,
|
||
|
Cache: autocert.DirCache(os.ExpandEnv("$HOME/.cache/autocert/proxy-test-server")),
|
||
|
}
|
||
|
var allowTarget func(hostPort string) error
|
||
|
if *tailscaleOnly {
|
||
|
allowTarget = func(hostPort string) error {
|
||
|
host, port, err := net.SplitHostPort(hostPort)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("invalid target %q: %v", hostPort, err)
|
||
|
}
|
||
|
if port != "443" {
|
||
|
return fmt.Errorf("target %q must use port 443", hostPort)
|
||
|
}
|
||
|
for allowed := range strings.SplitSeq(*extraAllowedHosts, ",") {
|
||
|
if host == allowed {
|
||
|
return nil // explicitly allowed target
|
||
|
}
|
||
|
}
|
||
|
if !strings.HasSuffix(host, ".tailscale.com") {
|
||
|
return fmt.Errorf("target %q is not a Tailscale host", hostPort)
|
||
|
}
|
||
|
return nil // valid Tailscale target
|
||
|
}
|
||
|
}
|
||
|
|
||
|
go func() {
|
||
|
if err := http.ListenAndServe(":http", am.HTTPHandler(nil)); err != nil {
|
||
|
log.Fatalf("autocert HTTP server failed: %v", err)
|
||
|
}
|
||
|
}()
|
||
|
hs := &http.Server{
|
||
|
Addr: *listen,
|
||
|
Handler: &connectproxy.Handler{
|
||
|
Check: allowTarget,
|
||
|
Logf: log.Printf,
|
||
|
},
|
||
|
TLSConfig: &tls.Config{
|
||
|
GetCertificate: am.GetCertificate,
|
||
|
NextProtos: []string{
|
||
|
"http/1.1", // enable HTTP/2
|
||
|
acme.ALPNProto, // enable tls-alpn ACME challenges
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
log.Printf("Starting proxy-test-server on %s (hostname: %q)\n", *listen, *hostname)
|
||
|
log.Fatal(hs.ListenAndServeTLS("", "")) // cert and key are provided by autocert
|
||
|
}
|