我希望连接一个服务器和一些客户端使用mTLS在golang .在我的服务器上,我希望能够生成证书以放在所有客户端上,以便客户端可以与服务器通信,但客户端不能相互通信.然而,我的客户端在端口80上公开Golang http服务器,并且我的服务器通过API请求与客户端通信,而不是反过来(从技术上讲,您可能会质疑我所称的服务器是否真的是客户端,但它是生成证书的单一来源,并且向客户端提供内容,因此为了简单起见,我将坚持使用这种命名).这怎么可能设置成这样呢?需要生成哪些证书?是否可以让客户端使用从单个服务器生成的证书监听请求?

我很可能会使用SmallStep来生成证书,SmallStep示例会很有用,但一般的方法也很好,然后可以单独研究如何使用SmallStep复制它.

我已经查看了一些现有的Golang示例,但它们往往在不同的设置下进行操作:

https://smallstep.com/hello-mtls/doc/server/go

https://venilnoronha.io/a-step-by-step-guide-to-mtls-in-go

以下是目前的代码,我非常确定它错误地使用了证书:

客户端:

step ca certificate "localhost" client.crt client.key

在下面,cert和key变量分别是client.crt和client.key.

caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM([]byte(cert))

// Read the key pair to create certificate
keyPair, err := tls.X509KeyPair([]byte(cert), []byte(key))
if err != nil {
log.Fatal(err)
}

transport = &http.Transport{
IdleConnTimeout:     transportIdleConnTimeout,
MaxIdleConnsPerHost: transportMaxIdleConnsPerHost,
TLSClientConfig: &tls.Config{
    RootCAs:      caCertPool,
    Certificates: []tls.Certificate{keyPair},
},

服务器:

step ca certificate "pm1" server.crt server.key

下面,cert和key变量分别为Server.crt和Server.key.

caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM([]byte(cert))

cert, err := tls.X509KeyPair([]byte(cert), []byte(key))
if err != nil {
    log.Fatal("server: loadkeys: ", err)
}

tlsConfig := tls.Config{
    ClientCAs:    caCertPool,
    ClientAuth:   tls.RequireAndVerifyClientCert,
    Certificates: []tls.Certificate{cert},
    MinVersion:   tls.VersionTLS13,
}

我现在根本没有使用ca.crt(step ca root ca.crt).

推荐答案

  1. 当客户端也是服务器时,需要哪些mTLS证书?

从服务器的Angular 来看:

  • 每项服务都需要自己的证书,以支持HTTPS
  • 信任证书列表,用于authenticate个传入请求(该列表应包括所有authorised个客户端使用的所有证书).

从客户的Angular 来看:

  • 您打算通过mTLS与之通信的所有服务器的证书列表;客户端不会信任未知证书(类似于Web浏览器);通常在此处使用为所有服务签名证书的CA,但如果确实需要,也可以单独添加它们
  • 它自己的证书,用于签署传出请求.

  1. 使用mTLS的客户端[...]expose 端口80上的Golang http服务器

端口(:80:443等)和协议(httphttpsgRPC等)是不同的东西.但是,有一个很强的约定,即在端口80上使用http,在:443上使用https.如果您不关心约定,没有什么可以阻止您在端口:80上使用https.因为您想要实现mTLS,所以您必须在服务器上侦听HTTPS.


  1. [...]但它是生成证书的单一来源,并向客户端提供内容,因此为简单起见,将坚持使用此命名[...],是否可以让客户端使用从单个服务器生成的证书来监听请求?

我不确定我是否理解这里的前提,但证书生成通常不在服务器或客户端的责任范围内,这些服务器或客户端应该使用它们来建立相互信任.

我假设您有一个可以 for each Web服务器签名证书的CA,为简单起见,我们将为它们命名:

µA: must communicate with µB and µC
µB: must communicate only with µA
µC: must communicate only with µA

HTTPS和mTLS也是不同的东西:第一个是协议,第二个是身份验证方法.我会试着逐一回答他们:

100:

要开始在HTTPS上侦听,每个应该接受连接的微服务都应该启动一个侦听器,并加载它自己的.crt.key(我将在配置mTLS时重新讨论这一节,但现在让我们来解决HTTPS):

func main() {
    http.HandleFunc("/hello", HelloServer)
    // Replace 443 with 80 if you really don't care about conventions
    err := http.ListenAndServeTLS(":443", "µ<A|B|C>.crt", "µ<A|B|C>.key", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

100:

在客户端(发送请求的微服务)和服务器(接收请求的微服务)之间相互执行mTLS认证.每个微服务可以是客户端、服务器或两者.

从客户的Angular 来看:

  • 服务器提供的证书必须经过验证和信任
  • 客户端必须加载它自己的证书和sign个传出请求

从服务器的Angular 来看:

  • 通过受信任的客户端证书或CA列表,传入的请求必须为signed
  • 响应必须是带有服务器证书的signed,该证书应该受到发起请求的客户端的信任.

客户A观点:

    // Client A perspective
    client := &http.Client{
        Transport: &http.Transport{
            TLSClientConfig: &tls.Config{
                // provide the CA that signed certificates that are presented
                // by servers B and C
                RootCAs: caCertPool,
                // provide the certificate for microservice A, which is
                // initiating the request
                Certificates: []tls.Certificate{cert}, 
            },
        },
    }

服务器B或C透视图:

tlsConfig := &tls.Config{
    // add only microservice A certificate in this pool; this will allow A 
    // to connect to B and C, but won't allow B to C and C to B 
    ClientCAs: caCertPool, 
    ClientAuth: tls.RequireAndVerifyClientCert,
}
tlsConfig.BuildNameToCertificate()

server := &http.Server{
    Addr:      ":443", // use :80 if you don't care about conventions
    TLSConfig: tlsConfig,
}

server.ListenAndServeTLS("server.crt", "server.key")

Go相关问答推荐

如何在Deliverq中高效地传达客户依赖关系?

如何使用GRPC UnaryClientInterceptor中的`maily`参数?

Golang应用程序:所请求的资源上不存在HTTP-Control-Allow-Origin标头

GORM Find方法中缺少字段

从带有嵌套括号的字符串中提取值

如何获取集群外go Kubernetes客户端的当前命名空间?

Kusto Go API 从多个表查询

如何将Golang测试用例的测试覆盖率值与特定阈值进行比较

Caddy服务器try 打开端口80而不是8090.

如何将验证器标记添加到嵌套字段

Neptune 在连接到启用 IAM 的 Neptune 实例时抛出握手错误错误

如何将 base64 编码的公钥转换为 crypto.PublicKey 或 ecdsa.PublicKey

golang 中的可变参数函数

Go 中的 HTTP 请求验证中间件

在 Golang 中获取谷歌云服务帐户的访问令牌?

从另一个没有重复的确定性 int

Dynamodb.ScanInput - 不能使用expr.Names()(类型 map[string]*string)作为类型 map[string]string

Go Flag 用法 描述 包含 Word 值

gopls 为 github.com/Shopify/sarama 返回错误gopls: no packages returned: packages.Load error

我该如何做错误处理惯用的方式