tailscale/cmd/proxy-test-server/proxy-test-server.go
Brad Fitzpatrick e92eb6b17b
Some checks failed
checklocks / checklocks (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
Dockerfile build / deploy (push) Has been cancelled
CI / gomod-cache (push) Has been cancelled
CI / fuzz (push) Has been cancelled
update-flake / update-flake (push) Has been cancelled
CI / race-root-integration (1/4) (push) Has been cancelled
CI / race-root-integration (2/4) (push) Has been cancelled
CI / race-root-integration (3/4) (push) Has been cancelled
CI / race-root-integration (4/4) (push) Has been cancelled
CI / test (-race, amd64, 1/3) (push) Has been cancelled
CI / test (-race, amd64, 2/3) (push) Has been cancelled
CI / test (-race, amd64, 3/3) (push) Has been cancelled
CI / test (386) (push) Has been cancelled
CI / test (amd64) (push) Has been cancelled
CI / windows (push) Has been cancelled
CI / privileged (push) Has been cancelled
CI / vm (push) Has been cancelled
CI / cross (386, linux) (push) Has been cancelled
CI / cross (amd64, darwin) (push) Has been cancelled
CI / cross (amd64, freebsd) (push) Has been cancelled
CI / cross (amd64, openbsd) (push) Has been cancelled
CI / cross (amd64, windows) (push) Has been cancelled
CI / cross (arm, 5, linux) (push) Has been cancelled
CI / cross (arm, 7, linux) (push) Has been cancelled
CI / cross (arm64, darwin) (push) Has been cancelled
CI / cross (arm64, linux) (push) Has been cancelled
CI / cross (arm64, windows) (push) Has been cancelled
CI / cross (loong64, linux) (push) Has been cancelled
CI / ios (push) Has been cancelled
CI / crossmin (amd64, illumos) (push) Has been cancelled
CI / crossmin (amd64, plan9) (push) Has been cancelled
CI / crossmin (amd64, solaris) (push) Has been cancelled
CI / crossmin (ppc64, aix) (push) Has been cancelled
CI / android (push) Has been cancelled
CI / wasm (push) Has been cancelled
CI / tailscale_go (push) Has been cancelled
CI / depaware (push) Has been cancelled
CI / go_generate (push) Has been cancelled
CI / go_mod_tidy (push) Has been cancelled
CI / licenses (push) Has been cancelled
CI / staticcheck (${{ matrix.name }}) (--with-tags-all=darwin, arm64, darwin, macOS) (push) Has been cancelled
CI / staticcheck (${{ matrix.name }}) (--with-tags-all=linux, amd64, linux, Linux) (push) Has been cancelled
CI / staticcheck (${{ matrix.name }}) (--with-tags-all=windows, amd64, windows, Windows) (push) Has been cancelled
CI / staticcheck (${{ matrix.name }}) (--without-tags-any=windows,darwin,linux --shard=1/4, amd64, linux, Portable (1/4)) (push) Has been cancelled
CI / staticcheck (${{ matrix.name }}) (--without-tags-any=windows,darwin,linux --shard=2/4, amd64, linux, Portable (2/4)) (push) Has been cancelled
CI / staticcheck (${{ matrix.name }}) (--without-tags-any=windows,darwin,linux --shard=3/4, amd64, linux, Portable (3/4)) (push) Has been cancelled
CI / staticcheck (${{ matrix.name }}) (--without-tags-any=windows,darwin,linux --shard=4/4, amd64, linux, Portable (4/4)) (push) Has been cancelled
CI / notify_slack (push) Has been cancelled
CI / merge_blocker (push) Has been cancelled
CI / check_mergeability_strict (push) Has been cancelled
CI / check_mergeability (push) Has been cancelled
net/tlsdial: fix TLS cert validation of HTTPS proxies
If you had HTTPS_PROXY=https://some-valid-cert.example.com running a
CONNECT proxy, we should've been able to do a TLS CONNECT request to
e.g. controlplane.tailscale.com:443 through that, and I'm pretty sure
it used to work, but refactorings and lack of integration tests made
it regress.

It probably regressed when we added the baked-in LetsEncrypt root cert
validation fallback code, which was testing against the wrong hostname
(the ultimate one, not the one which we were being asked to validate)

Fixes #16222

Change-Id: If014e395f830e2f87f056f588edacad5c15e91bc
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2025-06-18 14:20:39 -07:00

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
}