英文:
Failed to connect to a server with Golang due x509 certificate relies on legacy Common Name field
问题
我正在尝试连接到一个MongoDB服务器,为了连接,我必须提供一个CA证书文件和一个TLS证书文件。
当我使用以下命令时,没有问题:
$ mongo --host customhost:port DB --authenticationDatabase=DB -u ACCOUNT -p PWD --tls --tlsCAFile /etc/ca-files/new-mongo.ca.crt --tlsCertificateKeyFile /etc/ca-files/new-mongo-client.pem 
但是当我尝试使用mongo连接(也测试了只使用TLS客户端)时,出现以下错误:
failed to connect: x509: certificate relies on legacy Common Name field, use SANs or temporarily enable Common Name matching with GODEBUG=x509ignoreCN=0
如果我使用环境变量,一切正常,但我想知道如何在不使用环境变量的情况下修复它。
以下是我的代码配置和加载证书的方式,我不确定问题是我的代码配置有误还是SSL证书配置有误,但从证书的外观来看,它们似乎是正确的。我感觉加载的证书由于某种原因被忽略了。
证书信息如下:
CA证书:
$ openssl x509 -in /etc/ca-files/new-mongo.ca.crt -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            ....
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = FR, ST = IDF, L = Paris, O = COMP, OU = IT, CN = newmongo
        Validity
            Not Before: Jun 30 13:02:12 2021 GMT
            Not After : Jun 30 13:02:12 2023 GMT
        Subject: C = FR, ST = IDF, L = Paris, O = COMP, OU = IT, CN = newmongo
...
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                ...
            X509v3 Authority Key Identifier: 
                ....
            X509v3 Basic Constraints: critical
                CA:TRUE
客户端证书:
$ openssl x509 -in /etc/ca-files/newmongo-client.pem -text -noout 
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            ...
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = FR, ST = IDF, L = Paris, O = COMP, OU = IT, CN = newmongo
        Validity
            Not Before: Jun 30 13:17:25 2021 GMT
            Not After : Jun 30 13:17:25 2023 GMT
        Subject: C = FR, ST = IDF, L = Paris, O = COMP, OU = IT, CN = newmongo-client
...
        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                DNS:customhost:port, DNS:customhost, DNS:newmongo-client
我有点困惑,不知道问题是我的代码配置TLS和加载证书的方式有问题,还是SSL证书配置有问题,但从证书的外观来看,它们似乎是正确的。我感觉加载的证书由于某种原因被忽略了。
英文:
I'm trying to connect on a mongodb server, to connect I have to provide a CA cert file and also tls cert file.
When I use the following command I don't have issue
$ mongo --host customhost:port DB --authenticationDatabase=DB -u ACCOUNT -p PWD --tls --tlsCAFile /etc/ca-files/new-mongo.ca.crt --tlsCertificateKeyFile /etc/ca-files/new-mongo-client.pem 
But when I try to connect with mongo (and also tested with just a tls client) I have the following error:
failed to connect: x509: certificate relies on legacy Common Name field, use SANs or temporarily enable Common Name matching with GODEBUG=x509ignoreCN=0
If I use the env variable everything works well but I would like to know how to fix it without having to use it.
const CONFIG_DB_CA = "/etc/ca-files/new-mongo.ca.crt"
func main() {
	cer, err := tls.LoadX509KeyPair("mongo-server.crt", "mongo-server.key")
	if err != nil {
		log.Println(err)
		return
	}
	roots := x509.NewCertPool()
	ca, err := ioutil.ReadFile(CONFIG_DB_CA)
	if err != nil {
		fmt.Printf("Failed to read or open CA File: %s.\n", CONFIG_DB_CA)
		return
	}
	roots.AppendCertsFromPEM(ca)
	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{cer},
		RootCAs:      roots,
	}
	conn, err := tls.Dial("tcp", "customhost:port", tlsConfig)
	if err != nil {
		fmt.Printf("failed to connect: %v.\n", err)
		return
	}
	err = conn.VerifyHostname("customhost")
	if err != nil {
		panic("Hostname doesn't match with certificate: " + err.Error())
	}
	for i, cert := range conn.ConnectionState().PeerCertificates {
		prefix := fmt.Sprintf("CERT%d::", i+1)
		fmt.Printf("%sIssuer: %s\n", prefix, cert.Issuer)
		fmt.Printf("%sExpiry: %v\n", prefix, cert.NotAfter.Format(time.RFC850))
		fmt.Printf("%sDNSNames: %v\n\n", prefix, cert.DNSNames)
	}
    
	fmt.Printf("Success!")
}
Certificates:
$ openssl x509 -in /etc/ca-files/new-mongo.ca.crt -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            ....
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = FR, ST = IDF, L = Paris, O = COMP, OU = IT, CN = newmongo
        Validity
            Not Before: Jun 30 13:02:12 2021 GMT
            Not After : Jun 30 13:02:12 2023 GMT
        Subject: C = FR, ST = IDF, L = Paris, O = COMP, OU = IT, CN = newmongo
...
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                ...
            X509v3 Authority Key Identifier: 
                ....
            X509v3 Basic Constraints: critical
                CA:TRUE
$ openssl x509 -in /etc/ca-files/newmongo-client.pem -text -noout 
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            ...
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = FR, ST = IDF, L = Paris, O = COMP, OU = IT, CN = newmongo
        Validity
            Not Before: Jun 30 13:17:25 2021 GMT
            Not After : Jun 30 13:17:25 2023 GMT
        Subject: C = FR, ST = IDF, L = Paris, O = COMP, OU = IT, CN = newmongo-client
...
        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                DNS:customhost:port, DNS:customhost, DNS:newmongo-client
I'm a bit stuck and don't know if the problem is my code configuration of tls and the way I loaded certificates or if it comes from the SSL certificate misconfiguration but from what certificates look fine.
I feel like loaded certificate are ignored for any reason.
答案1
得分: 9
你需要修复问题的源头,并生成一个带有DNS SAN字段的证书-然后Go运行时检查就会消失。
这可以通过使用openssl实现,但是需要一个配置文件,因为SAN字段的选项太广泛,无法适应简单的命令行选项。
总体思路是,创建一个CSR:
openssl req -new \
-subj "${SUBJ_PREFIX}/CN=${DNS}/emailAddress=${EMAIL}" \
-key "${KEY}" \
-addext "subjectAltName = DNS:${DNS}" \
-out "${CSR}";
然后使用你的根CA签署CSR:
openssl ca \
-create_serial \
-cert "${ROOT_CRT}" \
-keyfile "${ROOT_KEY}" \
-days "${CERT_LIFETIME}" \
-in "${CSR}" \
-batch \
-config "${CA_CONF}" \
-out "${CRT}";
上面引用的CA_CONF类似于以下内容:
[ ca ]
default_ca = my_ca
[ my_ca ]
dir = ./db
database = $dir/index.txt
serial = $dir/serial
new_certs_dir = $dir/tmp
x509_extensions = my_cert
name_opt = ca_default
cert_opt = ca_default
default_md = default
policy = policy_match
# 'copy_extensions' will copy over SAN ("X509v3 Subject Alternative Name") from CSR
copy_extensions = copy
[ my_cert ]
basicConstraints = CA:FALSE
nsComment = "generated by https://github.com/me/my-pki"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
[ policy_match ]
# ensure CSR fields match that of delivered Cert
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
检查生成的服务器证书:
openssl x509 -in server.crt -noout -text
应该有一个类似于以下的SAN部分:
X509v3 Subject Alternative Name:
DNS:myserver.com
英文:
You need to fix the problem at the source and generate a certificate with a DNS SAN field - then the Go runtime check will disappear.
This is achievable with openssl but is tricky as it requires a config file - as SAN field options are too broad to fit into simple command-line options.
The general gist is, create a CSR:
openssl req -new \
-subj "${SUBJ_PREFIX}/CN=${DNS}/emailAddress=${EMAIL}" \
-key "${KEY}" \
-addext "subjectAltName = DNS:${DNS}" \
-out "${CSR}"
and then sign the CSR with your root CA:
openssl ca \
-create_serial \
-cert "${ROOT_CRT}" \
-keyfile "${ROOT_KEY}" \
-days "${CERT_LIFETIME}" \
-in "${CSR}" \
-batch \
-config "${CA_CONF}" \
-out "${CRT}"
CA_CONF referenced above looks something like this:
[ ca ]
default_ca      = my_ca
[ my_ca ]
dir             = ./db
database            = $dir/index.txt
serial              = $dir/serial
new_certs_dir   = $dir/tmp
x509_extensions = my_cert
name_opt            = ca_default
cert_opt            = ca_default
default_md          = default
policy              = policy_match
# 'copy_extensions' will copy over SAN ("X509v3 Subject Alternative Name") from CSR
copy_extensions = copy
[ my_cert ]
basicConstraints        = CA:FALSE
nsComment               = "generated by https://github.com/me/my-pki"
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid,issuer
[ policy_match ]
# ensure CSR fields match that of delivered Cert
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional
Inspecting the resulting server cert:
openssl x509 -in server.crt -noout -text
should then have a SAN section like:
X509v3 Subject Alternative Name: 
DNS:myserver.com
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论