Implement workarounds for poor system CA pools

This commit is contained in:
Vladislav Yarmak
2021-04-28 17:56:53 +03:00
parent 67e4886843
commit c42142f5d6
2 changed files with 87 additions and 25 deletions
+51 -10
View File
@@ -6,6 +6,7 @@ import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io"
@@ -20,10 +21,37 @@ const (
PROXY_CONNECT_METHOD = "CONNECT"
PROXY_HOST_HEADER = "Host"
PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization"
MISSING_CHAIN_CERT = `-----BEGIN CERTIFICATE-----
MIID0zCCArugAwIBAgIQVmcdBOpPmUxvEIFHWdJ1lDANBgkqhkiG9w0BAQwFADB7
MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD
VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE
AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTE5MDMxMjAwMDAwMFoXDTI4
MTIzMTIzNTk1OVowgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5
MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBO
ZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgRUNDIENlcnRpZmljYXRpb24gQXV0
aG9yaXR5MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEGqxUWqn5aCPnetUkb1PGWthL
q8bVttHmc3Gu3ZzWDGH926CJA7gFFOxXzu5dP+Ihs8731Ip54KODfi2X0GHE8Znc
JZFjq38wo7Rw4sehM5zzvy5cU7Ffs30yf4o043l5o4HyMIHvMB8GA1UdIwQYMBaA
FKARCiM+lvEH7OKvKe+CpX/QMKS0MB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1
xmNjmjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zARBgNVHSAECjAI
MAYGBFUdIAAwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5j
b20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEEKDAmMCQG
CCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQEM
BQADggEBABns652JLCALBIAdGN5CmXKZFjK9Dpx1WywV4ilAbe7/ctvbq5AfjJXy
ij0IckKJUAfiORVsAYfZFhr1wHUrxeZWEQff2Ji8fJ8ZOd+LygBkc7xGEJuTI42+
FsMuCIKchjN0djsoTI0DQoWz4rIjQtUfenVqGtF8qmchxDM6OW1TyaLtYiKou+JV
bJlsQ2uRl9EMC5MCHdK8aXdJ5htN978UeAOwproLtOGFfy/cQjutdAFI3tZs4RmY
CV4Ks2dH/hzg1cEo70qLRDEmBDeNiXQ2Lu+lIg+DdEmSx/cQwgwp+7e9un/jX9Wf
8qn0dNW44bOwgeThpWOjzOoEeJBuv/c=
-----END CERTIFICATE-----
`
)
var UpstreamBlockedError = errors.New("blocked by upstream")
var missingLinkDER, _ = pem.Decode([]byte(MISSING_CHAIN_CERT))
var missingLink, _ = x509.ParseCertificate(missingLinkDER.Bytes)
type Dialer interface {
Dial(network, address string) (net.Conn, error)
}
@@ -34,18 +62,22 @@ type ContextDialer interface {
}
type ProxyDialer struct {
address string
tlsServerName string
auth AuthProvider
next ContextDialer
address string
tlsServerName string
auth AuthProvider
next ContextDialer
intermediateWorkaround bool
caPool *x509.CertPool
}
func NewProxyDialer(address, tlsServerName string, auth AuthProvider, nextDialer ContextDialer) *ProxyDialer {
func NewProxyDialer(address, tlsServerName string, auth AuthProvider, intermediateWorkaround bool, caPool *x509.CertPool, nextDialer ContextDialer) *ProxyDialer {
return &ProxyDialer{
address: address,
tlsServerName: tlsServerName,
auth: auth,
next: nextDialer,
address: address,
tlsServerName: tlsServerName,
auth: auth,
next: nextDialer,
intermediateWorkaround: intermediateWorkaround,
caPool: caPool,
}
}
@@ -79,7 +111,7 @@ func ProxyDialerFromURL(u *url.URL, next ContextDialer) (*ProxyDialer, error) {
return authHeader
}
}
return NewProxyDialer(address, tlsServerName, auth, next), nil
return NewProxyDialer(address, tlsServerName, auth, false, nil, next), nil
}
func (d *ProxyDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
@@ -105,9 +137,18 @@ func (d *ProxyDialer) DialContext(ctx context.Context, network, address string)
opts := x509.VerifyOptions{
DNSName: d.tlsServerName,
Intermediates: x509.NewCertPool(),
Roots: d.caPool,
}
waRequired := false
for _, cert := range cs.PeerCertificates[1:] {
opts.Intermediates.AddCert(cert)
if d.intermediateWorkaround && !waRequired &&
bytes.Compare(cert.AuthorityKeyId, missingLink.SubjectKeyId) == 0 {
waRequired = true
}
}
if waRequired {
opts.Intermediates.AddCert(missingLink)
}
_, err := cs.PeerCertificates[0].Verify(opts)
return err