如何在 golang 中做代理和 TLS

Posted

技术标签:

【中文标题】如何在 golang 中做代理和 TLS【英文标题】:How to do proxy and TLS in golang 【发布时间】:2020-08-07 14:22:41 【问题描述】:

我正在尝试通过代理路由我的请求,并在 TLS 配置中发送 cert.pem。下面的代码抛出了这个错误 - proxyconnect tcp: tls: first record doesn't look like a TLS handshake。当我将代理 URL 从 https 更改为 HTTP 时,相同的代码可以工作。但是带有 https 的代理 URL 在 python 中有效。以下是我目前的代码

certs := x509.NewCertPool()
pemFile, err := ioutil.ReadFile("cert.pem")
if err != nil 
    return

certs.AppendCertsFromPEM(pemFile)
tlsConfig := &tls.Config
    RootCAs: certs,


proxyUrl, err := url.Parse("https://someproxyurlhere.com:8080")
if err != nil 
    return


t := &http.Transport
    TLSClientConfig: tlsConfig,
    Proxy:           http.ProxyURL(proxyUrl),


client := http.Client
    Transport: t,


reqBody := "some JSON body here"

buff, err := json.Marshal(reqBody)
if err != nil 
    return


req, err := http.NewRequest(http.MethodPost, "https://someurlhere.com", bytes.NewBuffer(buff))
if err != nil 
    return


res, err := client.Do(req)
if err != nil 
    // Error here - proxyconnect tcp: tls: first record does not look like a TLS handshake
    return

defer res.Body.Close()

Python 代码

import requests
os.environ['HTTPS_PROXY'] = 'https://someproxyurlhere.com:8080'
response = requests.post("https://someurlhere.com",
                           json='key': 'value',
                           verify='cert.pem')
print(str(response.content))

【问题讨论】:

“但是带有 https 的代理 URL 在 python 中有效。” - 请也提供有效的 Python 代码来说明你的意思。此外,输出openssl s_client -connect someproxyurlhere.com:8080 将有助于证明代理本身实际上使用 HTTPS - 常见的情况是即使对于 HTTPS URL,代理本身也可以通过 HTTP 访问(代理只是在执行 CONNECT 时通过隧道连接到最终服务器命令,因此这保留了端到端的安全性)。如果同一个代理和端口都可以通过 HTTP 和 HTTPS 访问,那就特别奇怪了。 @SteffenUllrich 使用 python 代码更新 os.environ['HTTPS_PROXY'] = 'https://someproxyurlhere.com:8080' - requests 并不真正关心此处给出的协议(https 与 http)。它将仅使用带有纯 HTTP 的 IP 和端口连接到代理。 【参考方案1】:

当我将代理 URL 从 https 更改为 HTTP 时,相同的代码可以工作。

这是因为您使用的 HTTP 代理需要通过 HTTP 而不是 HTTPS 访问,即使对于 https://.. URL 也是如此。这就是 HTTPS 代理通常的工作方式。请注意,虽然理论上代理可以嗅探它是否获得了普通或 TLS 连接,但实际上代理(和服务器)使用不同的端口用于 HTTP 和 HTTPS - 因此,如果它在一个带有 HTTP 的端口上工作,它很可能无法在与 HTTPS 相同的端口。

proxyconnect tcp: tls: first record 看起来不像 TLS 握手。

这是因为代理对奇怪的 HTTP 请求(实际上是 TLS 握手的开始)回答了一个普通的 HTTP 错误。

但是带有 https 的代理 URL 在 python 中有效。

虽然它看起来有效,但实际上并没有。即使https:// 作为 URL 给出,Python 请求仍将使用纯 HTTP 到代理。

您可以尝试与代理本身建立简单的 TLS 连接,例如使用 Python 或 openssl s_client。它很可能会因为一些握手错误而失败,因为代理实际上并不期待 TLS。

【讨论】:

以上是关于如何在 golang 中做代理和 TLS的主要内容,如果未能解决你的问题,请参考以下文章

Golang + nginx + https

使用 GoLang 获取 TLS 的 Client Hello Info

golang如何创建反向代理

是否可以使用 net/http 在 golang 中托管多个域 TLS?

如何在不强制断开连接的情况下使用 Go TLS 手动验证客户端证书?

用golang将DSA密钥解析为tls配置对象。