goproxy后面的gPRC返回证书错误,无需代理即可正常运行

时间:2019-07-03 12:41:21

标签: docker ssl go proxy grpc

我有一个gRPC客户端和服务器,均由ssl证书保护。这些代理之间如果没有代理,效果很好。作为测试,当我故意创建有缺陷的证书时,它会失败。在这篇文章的后面证明这不是证书问题。

gRPC服务器代码:

// Creates a new gRPC server
// Create the TLS credentials
creds, err := credentials.NewServerTLSFromFile("configs/cert/servercert.pem", "configs/cert/serverkey.pem")
if err != nil {
    log.Fatalf("could not load TLS keys: %s", err)
}
// Create an array of gRPC options with the credentials
opts := []grpc.ServerOption{grpc.Creds(creds)}
// create a gRPC server object
s := grpc.NewServer(opts...)

gRPC客户端代码:

// Create the client TLS credentials
creds, err := credentials.NewClientTLSFromFile("configs/cert/servercert.pem", "")
if err != nil {
    log.Fatalf("could not load tls cert: %s", err)
}

conn, err := grpc.Dial(grpcUri, grpc.WithTransportCredentials(creds))
if err != nil {
    log.Fatalf("Unable to connect: %v", err)
}

现在,我正在尝试使用转发代理(该代理经过了IVE测试,并且可以在常规HTTP api请求上正常运行)。但是,对于通过代理的gRPC请求,它总是会失败。

我正在使用cuttle,它在以下设置中内部使用goproxy。请注意,InsecureSkipVerifytrue都已尝试过false布尔值。根据我对SSL的(有限的)理解,需要false,因为它会在线检查证书,并且它们是自签名的,因此自然会失败。但是,我再次尝试了truefalse

// Config proxy.
proxy := goproxy.NewProxyHttpServer()
proxy.Tr = &http.Transport{
    // Config TLS cert verification.
    TLSClientConfig: &tls.Config{InsecureSkipVerify: !cfg.TLSVerify},
    Proxy:           http.ProxyFromEnvironment,
}

在gRPC客户端和服务器之间运行代理会导致以下错误:

  

传输:身份验证握手失败:x509:证书已签名   由未知权限(可能由于“ x509:无效签名:   父证书无法在这种证书上签名”   验证候选授权证书“测试服务器”

这将表明这是一个证书问题,但是,如前所述和测试,gRPC在没有代理的情况下也可以完美运行。

还请注意:我不想在代理后面运行gRPC,但由于开发环境而不得不这样做。 gRPC服务器和代理在同一台docker机器上运行。具有相同的IP地址将导致以下配置相互抵消(相信我无论如何都可以尝试)。

ENV http_proxy 192.168.99.100:3128
ENV https_proxy 192.168.99.100:3128
ENV no_proxy 192.168.99.100 # <- this would be the gRPC server IP, which is the same as the proxy. resulting in nothing being run through a proxy.

拆分docker中解决的ip可以解决此问题,但是,我什么也没学,也想解决。我尝试像回答here这样的配置来设置不同的docker内部IP,但是,该IP将保持为空(仅会设置网络),并且对新IP的访问只会超时。

1 个答案:

答案 0 :(得分:4)

背景:

TLS连接的每个末端都需要预先安排的信任。大多数客户端在连接到远程主机时都使用系统信任链(GeoTrust,DigiCert CA的受信任证书都在此处列出,并允许您安全地访问https://facebook.comhttps://google.com等站点)

go在使用TLS时,在联系服务器时将默认为系统信任链。在开发自定义解决方案时,在此系统信任链中,您的应用服务器的公共证书很可能不是 。因此,您有两种选择:

  • 通过InsecureSkipVerify: true禁用信任(DON'T做到这一点!)
  • 向您的客户添加自定义信任

您的应用程序服务器很可能具有自签名证书,因此获取此证书的公共证书部分为easy。 您还可以使用openssl之类的工具查看服务器的公共证书-使用链接的解决方案,您不仅可以为自己的开发服务器而且可以为任何其他远程服务获取公共证书-仅提供主机名和端口即可。


因此,请总结一下您的情况。你有:

Client <- TLS -> Server

但是想要:

Client <-TLS-> Proxy <-TLS-> Server

因此,您的客户端现在无需信任服务器,而只需信任代理服务器-因为它只是直接与代理服务器通信。 代理很可能会具有自签名证书(请参阅上文中有关如何提取信任证书的信息)。 有了它后,更新您的go代码以使用此自定义信任文件,如下所示:

// Get the SystemCertPool, continue with an empty pool on error
rootCAs, err := x509.SystemCertPool() // <- probably not needed, if we're only ever talking to this single proxy
if err != nil || rootCAs == nil {
    rootCAs = x509.NewCertPool()
}

// Read in the custom trust file
certs, err := ioutil.ReadFile(localTrustFile)
if err != nil {
    log.Fatalf("Failed to append %q to RootCAs: %v", localTrustFile, err)
}

// Append our cert to the system pool
if ok := rootCAs.AppendCertsFromPEM(certs); !ok {
    log.Fatalf("failed to append custom cert")
}

tlsConfig := &tls.Config{
    RootCAs: rootCAs,
}

代理也将需要信任服务器-因此,如果服务器的证书不在系统信任链中,则它将需要类似上面的tls.Config设置。