feature/featuretags, all: add build features, use existing ones in more places

Saves 270 KB.

Updates #12614

Change-Id: I4c3fe06d32c49edb3a4bb0758a8617d83f291cf5
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2025-09-30 14:47:40 -07:00
committed by Brad Fitzpatrick
parent aa5b2ce83b
commit c45f8813b4
35 changed files with 407 additions and 166 deletions

View File

@@ -1,7 +1,7 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build !ios && !js && !android
//go:build !ios && !js && !android && !ts_omit_useproxy
package netns

View File

@@ -9,13 +9,19 @@ package blockblame
import (
"crypto/x509"
"strings"
"sync"
"tailscale.com/feature/buildfeatures"
)
// VerifyCertificate checks if the given certificate c is issued by a firewall manufacturer
// that is known to block Tailscale connections. It returns true and the Manufacturer of
// the equipment if it is, or false and nil if it is not.
func VerifyCertificate(c *x509.Certificate) (m *Manufacturer, ok bool) {
for _, m := range Manufacturers {
if !buildfeatures.HasDebug {
return nil, false
}
for _, m := range manufacturers() {
if m.match != nil && m.match(c) {
return m, true
}
@@ -33,46 +39,56 @@ type Manufacturer struct {
match matchFunc
}
var Manufacturers = []*Manufacturer{
{
Name: "Aruba Networks",
match: issuerContains("Aruba"),
},
{
Name: "Cisco",
match: issuerContains("Cisco"),
},
{
Name: "Fortinet",
match: matchAny(
issuerContains("Fortinet"),
certEmail("support@fortinet.com"),
),
},
{
Name: "Huawei",
match: certEmail("mobile@huawei.com"),
},
{
Name: "Palo Alto Networks",
match: matchAny(
issuerContains("Palo Alto Networks"),
issuerContains("PAN-FW"),
),
},
{
Name: "Sophos",
match: issuerContains("Sophos"),
},
{
Name: "Ubiquiti",
match: matchAny(
issuerContains("UniFi"),
issuerContains("Ubiquiti"),
),
},
func manufacturers() []*Manufacturer {
manufacturersOnce.Do(func() {
manufacturersList = []*Manufacturer{
{
Name: "Aruba Networks",
match: issuerContains("Aruba"),
},
{
Name: "Cisco",
match: issuerContains("Cisco"),
},
{
Name: "Fortinet",
match: matchAny(
issuerContains("Fortinet"),
certEmail("support@fortinet.com"),
),
},
{
Name: "Huawei",
match: certEmail("mobile@huawei.com"),
},
{
Name: "Palo Alto Networks",
match: matchAny(
issuerContains("Palo Alto Networks"),
issuerContains("PAN-FW"),
),
},
{
Name: "Sophos",
match: issuerContains("Sophos"),
},
{
Name: "Ubiquiti",
match: matchAny(
issuerContains("UniFi"),
issuerContains("Ubiquiti"),
),
},
}
})
return manufacturersList
}
var (
manufacturersOnce sync.Once
manufacturersList []*Manufacturer
)
type matchFunc func(*x509.Certificate) bool
func issuerContains(s string) matchFunc {

View File

@@ -28,6 +28,7 @@ import (
"tailscale.com/derp/derpconst"
"tailscale.com/envknob"
"tailscale.com/feature/buildfeatures"
"tailscale.com/health"
"tailscale.com/hostinfo"
"tailscale.com/net/bakedroots"
@@ -36,12 +37,6 @@ import (
var counterFallbackOK int32 // atomic
// If SSLKEYLOGFILE is set, it's a file to which we write our TLS private keys
// in a way that WireShark can read.
//
// See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
var sslKeyLogFile = os.Getenv("SSLKEYLOGFILE")
var debug = envknob.RegisterBool("TS_DEBUG_TLS_DIAL")
// tlsdialWarningPrinted tracks whether we've printed a warning about a given
@@ -80,13 +75,19 @@ func Config(ht *health.Tracker, base *tls.Config) *tls.Config {
// the real TCP connection) because host is the ultimate hostname, but this
// tls.Config is used for both the proxy and the ultimate target.
if n := sslKeyLogFile; n != "" {
f, err := os.OpenFile(n, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
log.Fatal(err)
if buildfeatures.HasDebug {
// If SSLKEYLOGFILE is set, it's a file to which we write our TLS private keys
// in a way that WireShark can read.
//
// See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
if n := os.Getenv("SSLKEYLOGFILE"); n != "" {
f, err := os.OpenFile(n, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
log.Fatal(err)
}
log.Printf("WARNING: writing to SSLKEYLOGFILE %v", n)
conf.KeyLogWriter = f
}
log.Printf("WARNING: writing to SSLKEYLOGFILE %v", n)
conf.KeyLogWriter = f
}
if conf.InsecureSkipVerify {
@@ -164,10 +165,12 @@ func Config(ht *health.Tracker, base *tls.Config) *tls.Config {
if debug() {
log.Printf("tlsdial(sys %q): %v", dialedHost, errSys)
}
if !buildfeatures.HasBakedRoots || (errSys == nil && !debug()) {
return errSys
}
// Always verify with our baked-in Let's Encrypt certificate,
// so we can log an informational message. This is useful for
// detecting SSL MiTM.
// If we have baked-in LetsEncrypt roots and we either failed above, or
// debug logging is enabled, also verify with LetsEncrypt.
opts.Roots = bakedroots.Get()
_, bakedErr := cs.PeerCertificates[0].Verify(opts)
if debug() {
@@ -239,8 +242,8 @@ func SetConfigExpectedCert(c *tls.Config, certDNSName string) {
if debug() {
log.Printf("tlsdial(sys %q/%q): %v", c.ServerName, certDNSName, errSys)
}
if errSys == nil {
return nil
if !buildfeatures.HasBakedRoots || errSys == nil {
return errSys
}
opts.Roots = bakedroots.Get()
_, err := certs[0].Verify(opts)