微服务内部通信时的 SSL 证书主机名问题

Posted

技术标签:

【中文标题】微服务内部通信时的 SSL 证书主机名问题【英文标题】:SSL certificate hostname issue in case of microservices internal communication 【发布时间】:2019-12-10 03:10:53 【问题描述】:

我必须使用 SSL 保护微服务内部通信。我所有的微服务都是 Spring Boot 应用程序,我们使用 Zookeeper 作为发现服务器。内部服务通信通过rest模板和feign客户端进行。我们使用功能区作为客户端负载均衡器。 我们在所有微服务中设置了以下属性

spring.application.name=Application1
spring.cloud.zookeeper.discovery.enabled=true
spring.cloud.zookeeper.connectString=localhost:2181
spring.cloud.zookeeper.enabled=true
server.port=7800
spring.cloud.zookeeper.discovery.instance-ssl-port=7801

server.ssl.enabled=true
server.ssl.key-store-type=JKS
server.ssl.key-store=classpath:LP-PF1HMVQU.jks
server.ssl.key-store-password=123456
server.ssl.key-alias=LP-PF1HMVQU
server.ssl.protocol=TLS

我们必须使用自签名证书,我已经生成了相同的证书并将证书导入 JRE 信任存储以使通信成为可能(SSL 握手)。这里的问题是我必须保持证书的 CN 与系统的主机名 (LP-PF1HMVQU) 相同。这是因为当服务注册到 zookeeper 时,它会将机器名或主机名存储为其地址,并且在握手期间返回相同的地址。

在 zookeeper 上注册的服务

"name":"employee-service","id":"b4c2204a-b00c-4102-b609-17d7f73f35d7","address":"LP-PF1HMVQU","port":7800,"sslPort":null,"payload":"@class":"org.springframework.cloud.zookeeper.discovery.ZookeeperInstance","id":"application-1","name":"employee-service","metadata":,"registrationTimeUTC":1564658801106,"serviceType":"DYNAMIC","uriSpec":"parts":["value":"scheme","variable":true,"value":"://","variable":false,"value":"address","variable":true,"value":":","variable":false,"value":"port","variable":true]

现在在生产环境中,我们将为每个服务提供一个 docker 容器,并且每个服务可以有多个 docker 容器。这些 docker 容器注册到 zookeeper 并保存容器的 IP 地址。

我应该如何创建证书,CN 名称应该是什么,以便它匹配任何 IP 地址。我已尝试使用通配符 * 作为 CN 名称,但它不起作用。

请建议如何实现。

【问题讨论】:

【参考方案1】:

CN 名称应该是什么,以便与任何 IP 地址匹配

不要那样做,它不会起作用(如预期的那样)

HTTPS 和 IP 地址

虽然从技术上讲,您可以在 HTTPS URL 中拥有 IP 地址,例如 https://192.0.2.42/whatever,但它只会给您带来麻烦,因为附加的证书需要提及该 IP,而不是名称,因为没有。这在技术上是可能的(参见https://1.1.1.1/ 提供的证书)只是更困难/超出标准情况。

因此,我建议改为注册您想要的任何域名,然后将其用作满足您所有需求的后缀。如果该名称是可公开解析的,您甚至可以获得他们的 DV 证书。如果没有,您可能需要自己设置私有 CA。

你也说过:

证书的 CN 与我的系统主机名相同 (LP-PF1HMVQU)

这很奇怪,因为它不是全名,而且肯定无法解析,因此没有 CA 会同意签署那种证书。如果这确实是您想要的(但请参见下文),那么无论如何您都需要自己的私有 CA。

CN 和 SAN

第一个证书只有一个“主题”部分,您可以在其中放置一个 DN 对象,即某个实体的 X.400 描述,该实体可以是个人、组织等抽象实体、电子邮件地址或主机名,用于网站。

所以最初我们将网站主机名作为主题中的CN 组件,并且浏览器与之匹配。

但很快就需要为多个名称提供一个证书,从“root”与“www”二元性开始,因此创建了一个扩展:主题备用名称。

在那里,可以输入其他名称(或我们将在下面看到的 IP 地址)。现在的浏览器(以及更普遍的 HTTP 客户端)大多忽略主题中的内容,因此 CN,只关注“SAN”部分。

所以这是您需要放置 IP 地址的地方。

让我们再看看https://1.1.1.1/返回的证书。您可以在其中看到主题部分的以下内容:

CN = cloudflare-dns.com
O = "Cloudflare, Inc."
L = San Francisco
ST = California
C = US

对于 SAN:

Not Critical
DNS Name: cloudflare-dns.com
DNS Name: *.cloudflare-dns.com
DNS Name: one.one.one.one
IP Address: 1.1.1.1
IP Address: 1.0.0.1
IP Address: 162.159.132.53
IP Address: 2606:4700:4700::1111
IP Address: 2606:4700:4700::1001
IP Address: 2606:4700:4700::64
IP Address: 2606:4700:4700::6400
IP Address: 162.159.36.1
IP Address: 162.159.46.1

因此,如果您真的想在证书中包含 IP 地址,那么您需要这样做。如果你自己管理CA,那么技术上问题不大(你需要确保使用

完整的细节(规格)在https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.6 您可以在此处和其他地方找到很多关于如何使用 IP 地址所需的 SAN 扩展来制作适当的 CSR/证书的说明。

请注意,SAN 中的域名可以使用通配符,例如 *.example.com,但 IP 地址不能使用通配符。因此,您的“它与任何 IP 地址匹配”似乎不是目标。看起来您可能想要一个空白证书来绕过身份验证并使您的系统正常工作?如果是这样,请不要这样做并放弃这个想法:没有身份验证的 TLS 与纯文本一样糟糕,即使交换已加密,因为您也不知道自己在加密谁。

【讨论】:

以上是关于微服务内部通信时的 SSL 证书主机名问题的主要内容,如果未能解决你的问题,请参考以下文章

通过 Docker 主机名在两个微服务之间进行通信

与 HTTPS 微服务通信时,HTTPS 网关 API 无法验证第一个证书

Rails 3:OpenSSL::SSL::SSLError:主机名与服务器证书不匹配

删除对象时的微服务通信

微服务内部通信

OpenSSL::SSL::SSLError(主机名“smtp.mandrillapp.com”与服务器证书不匹配)