【问题标题】:Create HTTPS test server for any client为任何客户端创建 HTTPS 测试服务器
【发布时间】:2019-07-20 19:48:33
【问题描述】:

NewTLSServer 创建的服务器可以验证从它显式创建的客户端的调用:

ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello, client")
}))
defer ts.Close()

client := ts.Client()
res, err := client.Get(ts.URL)

client := ts.Client() 行中。

但是,我有一个生产程序,我想将其设置为使用 ts.URL 作为其主机。我得到了

x509: certificate signed by unknown authority

我调用它时出错。

如何设置ts 以像普通HTTPS 服务器一样向客户端进行身份验证?

【问题讨论】:

  • 要么你需要使用正确的证书,要么创建一个不安全的连接到你的 HTTPS 服务器,有点像这样:stackoverflow.com/a/12122718/4121573
  • @Adonis 是的,在我们的生产代码中启用InsecureSkipVerify 的可能性不是一个选项。我将如何“使用正确的证书”?这基本上就是我在原始问题中所要求的。
  • 您能说明一下您的情况吗?从问题来看,您似乎正在尝试使用 httptest.NewTLSServer 来提供生产中的安全流量。
  • @Adrian 不,我绝对不会尝试在生产中使用它。我试图在测试项目中生产组件的真实调用时模拟外部生产组件。由于它在调用中与之通信的其他生产组件使用 HTTPS,因此我需要来自 NewTLSServer 的模拟服务器也使用 HTTPS。
  • @ChrisRedford 我从未尝试为 httptest 生成我自己的证书(或使用有效证书)。但是,理论上,您可以使用ts := NewUnstartedServer 而不是ts := NewTLSServer。然后,您可以调整ts.TLS,它是https://golang.org/pkg/crypto/tls/#Config(您将加载另一个证书)。然后你可以ts.StartTLS()(因为它是一个“未启动的服务器”)。哦,别忘了defer ts.Close()。正如我所说,我从未尝试过,但它可能会奏效。

标签: go


【解决方案1】:

从 Go 1.11 开始,由于 HTTPS 的机制,这根本不可能在 _test.go 程序中完全完成。

但是,您可以执行单个证书签名并生成server.crtserver.key 文件,然后在本地目录中的_test.go 程序中无限期地引用它们。

一次性.crt.key 一代

这是 Daksh Shah 的 Medium 文章 How to get HTTPS working on your local development environment in 5 minutes 中指定步骤的精简版,可在 Mac 上运行。

在您想要 server.crtserver.key 文件的目录中,创建两个配置文件

server.csr.cnf

[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn

[dn]
C=US
ST=RandomState
L=RandomCity
O=RandomOrganization
OU=RandomOrganizationUnit
emailAddress=hello@example.com
CN = localhost

v3.ext

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost
IP.1 = 127.0.0.1

然后在那个目录下输入以下命令

openssl genrsa -des3 -out rootCA.key 2048 
# create a passphrase
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem -config server.csr.cnf
# enter passphrase
openssl req -new -sha256 -nodes -out server.csr -newkey rsa:2048 -keyout server.key -config server.csr.cnf
openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 500 -sha256 -extfile v3.ext
# enter passphrase

最后,通过运行让你的系统信任你用来签署文件的证书

open rootCA.pem

这应该会在 Keychain Acces 应用程序中打开证书,该证书将在 Certificates 部分中找到并命名为 localhost。然后永远信任

  • 按回车键打开它的窗口
  • 按空格键向下旋转信任
  • 将“使用此证书时:”更改为 始终信任
  • 关闭窗口并验证您的决定

注意:我已经从命令行尝试了security add-trusted-cert 的许多排列,尽管它会将证书添加到钥匙串并将其标记为“始终信任”,但我的 Go 程序只是不会相信它。只有 GUI 方法使系统处于我的 Go 程序将信任证书的状态。

您使用 HTTPS 在本地运行的任何 Go 程序现在都将信任您使用 server.crtserver.key 运行的服务器。

运行服务器

您可以创建使用这些凭据的*httptest.Server 实例

func NewLocalHTTPSTestServer(handler http.Handler) (*httptest.Server, error) {
    ts := httptest.NewUnstartedServer(handler)
    cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
    if err != nil {
        return nil, err
    }
    ts.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
    ts.StartTLS()
    return ts, nil
}

这是一个示例用法:

func TestLocalHTTPSserver(t *testing.T) {
    ts, err := NewLocalHTTPSTestServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "Hello, client")
    }))
    assert.Nil(t, err)
    defer ts.Close()

    res, err := http.Get(ts.URL)
    assert.Nil(t, err)

    greeting, err := ioutil.ReadAll(res.Body)
    res.Body.Close()
    assert.Nil(t, err)

    assert.Equal(t, "Hello, client", string(greeting))
}

【讨论】:

  • “钥匙串访问”部分是不幸的(例如,该条目可用于危及系统其他部分的安全性)。您的代码最好使用空白证书池并仅将您的测试 CA 添加到其中,而不是尝试使用系统证书池。我什至对生产服务器也这样做(让管理它们的操作人员选择是使用系统池还是传入明确的 CA 证书集)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-29
  • 2016-10-31
  • 1970-01-01
  • 1970-01-01
  • 2013-09-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多