control/controlhttp: don't require valid TLS cert for Noise connection

We don't require any cert at all for Noise-over-plaintext-port-80-HTTP,
so why require a valid cert chain for Noise-over-HTTPS? The reason we use
HTTPS at all is to get through firewalls that allow tcp/443 but not tcp/80,
not because we need the security properties of TLS.

Updates #3198

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2023-02-08 18:24:30 +00:00 committed by Brad Fitzpatrick
parent 2477fc4952
commit fb84ccd82d
3 changed files with 43 additions and 29 deletions

View File

@ -410,10 +410,24 @@ func (a *Dialer) tryURLUpgrade(ctx context.Context, u *url.URL, addr netip.Addr,
tr.TLSClientConfig.NextProtos = []string{}
tr.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{}
tr.TLSClientConfig = tlsdial.Config(a.Hostname, tr.TLSClientConfig)
if a.insecureTLS {
tr.TLSClientConfig.InsecureSkipVerify = true
tr.TLSClientConfig.VerifyConnection = nil
if !tr.TLSClientConfig.InsecureSkipVerify {
panic("unexpected") // should be set by tlsdial.Config
}
verify := tr.TLSClientConfig.VerifyConnection
if verify == nil {
panic("unexpected") // should be set by tlsdial.Config
}
// Demote all cert verification errors to log messages. We don't actually
// care about the TLS security (because we just do the Noise crypto atop whatever
// connection we get, including HTTP port 80 plaintext) so this permits
// middleboxes to MITM their users. All they'll see is some Noise.
tr.TLSClientConfig.VerifyConnection = func(cs tls.ConnectionState) error {
if err := verify(cs); err != nil && a.Logf != nil && !a.omitCertErrorLogging {
a.Logf("warning: TLS cert verificication for %q failed: %v", a.Hostname, err)
}
return nil // regardless
}
tr.DialTLSContext = dnscache.TLSDialer(dialer, dns, tr.TLSClientConfig)
tr.DisableCompression = true

View File

@ -78,9 +78,9 @@ type Dialer struct {
proxyFunc func(*http.Request) (*url.URL, error) // or nil
// For tests only
drainFinished chan struct{}
insecureTLS bool
testFallbackDelay time.Duration
drainFinished chan struct{}
omitCertErrorLogging bool
testFallbackDelay time.Duration
}
func strDef(v1, v2 string) string {

View File

@ -194,16 +194,16 @@ func testControlHTTP(t *testing.T, param httpTestParam) {
}
a := &Dialer{
Hostname: "localhost",
HTTPPort: strconv.Itoa(httpLn.Addr().(*net.TCPAddr).Port),
HTTPSPort: strconv.Itoa(httpsLn.Addr().(*net.TCPAddr).Port),
MachineKey: client,
ControlKey: server.Public(),
ProtocolVersion: testProtocolVersion,
Dialer: new(tsdial.Dialer).SystemDial,
Logf: t.Logf,
insecureTLS: true,
testFallbackDelay: 50 * time.Millisecond,
Hostname: "localhost",
HTTPPort: strconv.Itoa(httpLn.Addr().(*net.TCPAddr).Port),
HTTPSPort: strconv.Itoa(httpsLn.Addr().(*net.TCPAddr).Port),
MachineKey: client,
ControlKey: server.Public(),
ProtocolVersion: testProtocolVersion,
Dialer: new(tsdial.Dialer).SystemDial,
Logf: t.Logf,
omitCertErrorLogging: true,
testFallbackDelay: 50 * time.Millisecond,
}
if proxy != nil {
@ -646,19 +646,19 @@ func TestDialPlan(t *testing.T) {
drained := make(chan struct{})
a := &Dialer{
Hostname: host,
HTTPPort: httpPort,
HTTPSPort: httpsPort,
MachineKey: client,
ControlKey: server.Public(),
ProtocolVersion: testProtocolVersion,
Dialer: dialer.Dial,
Logf: t.Logf,
DialPlan: tt.plan,
proxyFunc: func(*http.Request) (*url.URL, error) { return nil, nil },
drainFinished: drained,
insecureTLS: true,
testFallbackDelay: 50 * time.Millisecond,
Hostname: host,
HTTPPort: httpPort,
HTTPSPort: httpsPort,
MachineKey: client,
ControlKey: server.Public(),
ProtocolVersion: testProtocolVersion,
Dialer: dialer.Dial,
Logf: t.Logf,
DialPlan: tt.plan,
proxyFunc: func(*http.Request) (*url.URL, error) { return nil, nil },
drainFinished: drained,
omitCertErrorLogging: true,
testFallbackDelay: 50 * time.Millisecond,
}
conn, err := a.dial(ctx)