禁用通用名称验证-Go HTTP Client

How do I disable common name validation inside of a go http client. I am doing mutual TLS with a common CA and hence common name validation means nothing.

The tls docs say,

// ServerName is used to verify the hostname on the returned
// certificates unless InsecureSkipVerify is given. It is also included
// in the client's handshake to support virtual hosting unless it is
// an IP address.
ServerName string

I don't want to do InsecureSkipVerify but I don't want to validate the common name.

You would pass a tls.Config struct with your own VerifyPeerCertificate function, and then you would check the certificate yourself.

VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error

If normal verification fails then the handshake will abort before considering this callback. If normal verification is disabled by setting InsecureSkipVerify then this callback will be considered but the verifiedChains argument will always be nil.

You can look here for an example of how to verify a certificate. Iif you look here, you'll see that part of even this verification process includes checking the hostname, but luckily you'll see that it skips it if it's set to the empty string.

So, basically you write your own VerifyPeerCertificate function, convert the rawCerts [][]byte, which I think would look something like:

customVerify := func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
    roots := x509.NewCertPool()
    for _, rawCert := range rawCerts {
        cert, _ := x509.ParseCertificate(rawCert)
        roots.AddCert(cert)
    }
    opts := x509.VerifyOptions{
        Roots:   roots,
    }
    _, err := cert.Verify(opts)
    return err
}

conf := tls.Config{
    //...
    VerifyPeerCertificate: customVerify,
}

Normal https post like this

pool := x509.NewCertPool()
caStr, err := ioutil.ReadFile(serverCAFile)
if err != nil {
    return nil, fmt.Errorf("read server ca file fail")
}
pool.AppendCertsFromPEM(caStr)
tr := &http.Transport{
    TLSClientConfig: &tls.Config{
        RootCAs:    pool,
    },
}

client := &http.Client{Transport: tr}
client.Post(url, bodyType, body)

But if your url is use ip(ex. https://127.0.0.1:8080/api/test) or you URL is not match certificate common name, and you want to only ignore certificate common name check, should do like this

pool := x509.NewCertPool()
caStr, err := ioutil.ReadFile(serverCAFile)
if err != nil {
    return nil, fmt.Errorf("read server ca file fail")
}
block, _ := pem.Decode(caStr)
if block == nil {
    return nil, fmt.Errorf("Decode ca file fail")
}
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
    return nil, fmt.Errorf("Decode ca block file fail")
}

cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
    return nil, fmt.Errorf("ParseCertificate ca block file fail")
}

pool.AddCert(cert)

tr := &http.Transport{
    TLSClientConfig: &tls.Config{
        RootCAs:    pool,
        ServerName: cert.Subject.CommonName,  //manual set ServerName
    },
}

client := &http.Client{Transport: tr}
client.Post(url, bodyType, body)