使用自签名证书的 TLS 连接失败
Go TLS connection with self-signed certs failing
当 运行 在我的笔记本电脑上本地使用我自己的 'developer' 证书访问内部服务时,以下简化的测试用例代码有效
如果我 运行 在远程机器上使用动态生成的证书(所有这些都由我组织中的一个单独的团队处理),它会失败并显示 400 和 "No required SSL certificate was sent" 错误
但如果我在远程机器上使用 curl,并指定与我的 Go 代码中引用的证书相同的证书,它将起作用
看来证书不是问题而是 Go 代码,但这本身似乎不是问题,因为它在本地使用我自己的证书
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
"os"
"time"
)
func main() {
transport, transErr := configureTLS()
if transErr != nil {
fmt.Printf("trans error: %s", transErr.Error())
return
}
timeout := time.Duration(1 * time.Second)
client := http.Client{
Transport: transport,
Timeout: timeout,
}
resp, clientErr := client.Get("https://my-service-with-nginx/")
if clientErr != nil {
fmt.Printf("client error: %s", clientErr.Error())
} else {
defer resp.Body.Close()
contents, contErr := ioutil.ReadAll(resp.Body)
if contErr != nil {
fmt.Printf("contents error: %s", contErr.Error())
}
fmt.Printf("\n\ncontents:\n\n%+v", string(contents))
}
}
func configureTLS() (*http.Transport, error) {
certPath := "/path/to/client.crt"
keyPath := "/path/to/client.key"
caPath := "/path/to/ca.crt"
// Load client cert
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return nil, err
}
// Load CA cert
caCert, err := ioutil.ReadFile(caPath)
if err != nil {
return nil, err
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// Setup HTTPS client
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
InsecureSkipVerify: true,
}
tlsConfig.BuildNameToCertificate()
return &http.Transport{TLSClientConfig: tlsConfig}, nil
}
有谁知道为什么会这样?
我认为这可能是 Go 具有的重新协商错误(从 1.6 开始),但我认为这里不是这种情况,否则当 运行 在本地安装应用程序时它会失败(但是它没有,使用我自己的开发证书和 运行ning 在本地工作正常 - 只有当 运行 在具有不同证书的远程实例上时才会出现问题;并且这些证书在工作时不是问题curl
)
使用时很好
所以这里的实际问题部分与我的组织基础设施有关,部分与 nginx 如何使用有关 ssl_client_certificate
。
我们有 int、test 和 live 环境
我被引导相信环境可以相互通信。
所以我在 int 上设置了我的服务,并且我能够从那里使用 curl 与实时环境中的另一个服务设置进行通信。
使用Go跨环境通信时出现问题
我的快速解决方案是确保当我对其他服务进行 GET 时,而不是使用:
https://service.live.me.com
我会使用:
https://service.int.me.com
或:
https://service.test.me.com
根据环境,我的代码在 运行 内。
显然,对于有类似问题但没有相同设置的其他人来说,这不是解决方案。
所以对于那些仍然需要解决方案的人...
我还打算尝试(并且显然为 this guy 工作)是获取我试图与之通信的服务来修改他们的 nginx conf,以便他们将 ssl_client_certificate
设置为不仅指向一个客户端证书,还指向一个 combined 证书(包含整个 CA 链的证书)。
这是因为显然 Go 不会通过其响应发回任何证书除非服务器已通过 CA 及其响应。
我最初怀疑这是 Go 1.6 及更低版本中的经典重新协商错误,但在这种情况下情况并非如此。
希望这对同一条船上的其他人有所帮助。
当 运行 在我的笔记本电脑上本地使用我自己的 'developer' 证书访问内部服务时,以下简化的测试用例代码有效
如果我 运行 在远程机器上使用动态生成的证书(所有这些都由我组织中的一个单独的团队处理),它会失败并显示 400 和 "No required SSL certificate was sent" 错误
但如果我在远程机器上使用 curl,并指定与我的 Go 代码中引用的证书相同的证书,它将起作用
看来证书不是问题而是 Go 代码,但这本身似乎不是问题,因为它在本地使用我自己的证书
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
"os"
"time"
)
func main() {
transport, transErr := configureTLS()
if transErr != nil {
fmt.Printf("trans error: %s", transErr.Error())
return
}
timeout := time.Duration(1 * time.Second)
client := http.Client{
Transport: transport,
Timeout: timeout,
}
resp, clientErr := client.Get("https://my-service-with-nginx/")
if clientErr != nil {
fmt.Printf("client error: %s", clientErr.Error())
} else {
defer resp.Body.Close()
contents, contErr := ioutil.ReadAll(resp.Body)
if contErr != nil {
fmt.Printf("contents error: %s", contErr.Error())
}
fmt.Printf("\n\ncontents:\n\n%+v", string(contents))
}
}
func configureTLS() (*http.Transport, error) {
certPath := "/path/to/client.crt"
keyPath := "/path/to/client.key"
caPath := "/path/to/ca.crt"
// Load client cert
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return nil, err
}
// Load CA cert
caCert, err := ioutil.ReadFile(caPath)
if err != nil {
return nil, err
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// Setup HTTPS client
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
InsecureSkipVerify: true,
}
tlsConfig.BuildNameToCertificate()
return &http.Transport{TLSClientConfig: tlsConfig}, nil
}
有谁知道为什么会这样?
我认为这可能是 Go 具有的重新协商错误(从 1.6 开始),但我认为这里不是这种情况,否则当 运行 在本地安装应用程序时它会失败(但是它没有,使用我自己的开发证书和 运行ning 在本地工作正常 - 只有当 运行 在具有不同证书的远程实例上时才会出现问题;并且这些证书在工作时不是问题curl
)
所以这里的实际问题部分与我的组织基础设施有关,部分与 nginx 如何使用有关 ssl_client_certificate
。
我们有 int、test 和 live 环境
我被引导相信环境可以相互通信。
所以我在 int 上设置了我的服务,并且我能够从那里使用 curl 与实时环境中的另一个服务设置进行通信。
使用Go跨环境通信时出现问题
我的快速解决方案是确保当我对其他服务进行 GET 时,而不是使用:
https://service.live.me.com
我会使用:
https://service.int.me.com
或:
https://service.test.me.com
根据环境,我的代码在 运行 内。
显然,对于有类似问题但没有相同设置的其他人来说,这不是解决方案。
所以对于那些仍然需要解决方案的人...
我还打算尝试(并且显然为 this guy 工作)是获取我试图与之通信的服务来修改他们的 nginx conf,以便他们将 ssl_client_certificate
设置为不仅指向一个客户端证书,还指向一个 combined 证书(包含整个 CA 链的证书)。
这是因为显然 Go 不会通过其响应发回任何证书除非服务器已通过 CA 及其响应。
我最初怀疑这是 Go 1.6 及更低版本中的经典重新协商错误,但在这种情况下情况并非如此。
希望这对同一条船上的其他人有所帮助。