DNS SRV的注意事项
从v3.1.0(v3.2.9除外)开始,发现SRV将使用--discovery-srv
标志进行身份验证。通过要求证书在其“使用者备用名称”(SAN/HOST)字段中具有匹配的根域名,可以避免中间人证书攻击。例如,etcd --discovery-srv=etcd.local
仅在提供的证书具有根域etcd.local,
作为“使用者备用名称”(SAN)字段中的条目时,才对对等方/客户端进行身份验证
etcd代理注意事项
如果连接是安全的,etcd代理会从其客户端终止TLS,并使用etcd成员中指定的代理自己的密钥/证书--peer-key-file及
--peer-cert-file
与etcd成员进行通信。
代理通过--advertise-client-urls
和--advertise-peer-urls
给定成员与etcd成员进行通信。它将客户端请求转发到etcd成员通告的客户端URL,并通过etcd成员通告的邻居URL同步初始群集配置。
为etcd成员启用客户端身份验证后,管理员必须确保代理--peer-cert-file
选项中指定的对等证书对该身份验证有效。如果启用了对等身份验证,则代理的对等证书也必须对对等身份验证有效。
TLS验证注意事项
从v3.2.0开始,TLS证书将在每个客户端连接上重新加载。这在不停止etcd服务器的情况下替换到期证书时很有用;可以通过用新证书覆盖旧证书来完成。刷新每个连接的证书应该没有太多的开销,但是将来可以通过缓存层进行改进。示例测试可以在这里找到。
从v3.2.0开始,服务器使用错误的IP拒绝传入的对等证书SAN
。例如,如果对等证书在“使用者备用名称”(SAN)字段中包含任何IP地址,则服务器仅在远程IP地址与那些IP地址之一匹配时,才对对等身份进行身份验证。这是为了防止未经授权的端点加入群集。例如,对等B的CSR(带有cfssl
)为:
{
"CN": "etcd peer",
"hosts": [
"*.example.default.svc",
"*.example.default.svc.cluster.local",
"10.138.0.27"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "CA",
"ST": "San Francisco"
}
]
}
当对等端B的实际IP地址10.138.0.2
不是时10.138.0.27
。当对等体B尝试加入群集时,对等体A将拒绝B并显示错误消息x509: certificate is valid for 10.138.0.27, not 10.138.0.2
,因为B的远程IP地址与“使用者备用名称(SAN)”字段中的地址不匹配(证书中一定要包含已有的设备IP)。
从v3.2.0开始,服务器DNS Names
在检查时解析TLS SAN
。例如,如果对等证书在“使用者备用名称”(SAN)字段中仅包含DNS名称(不包含IP地址),则仅当dig b.com
这些DNS名称上的正向查找(dig b.com)具有与远程(就是邻居的ip地址)IP地址匹配的IP时,服务器才会对对等身份进行身份验证。例如,对等B的CSR(带有cfssl
)为:
{
"CN": "etcd peer",
"hosts": [
"b.com"
],
当对等体B的远程IP地址为时10.138.0.2
。当对等方B尝试加入群集时,对等方A查找传入的主机b.com
以获取IP地址列表(例如dig b.com
)。如果列表不包含IP 10.138.0.2
,则拒绝B ,并显示错误消息tls: 10.138.0.2 does not match any of DNSNames ["b.com"]
。
从v3.2.2开始,如果IP匹配,服务器将接受连接,而不会检查DNS条目。例如,如果对等证书在“使用者备用名称(SAN)”字段中包含IP地址和DNS名称,并且远程IP地址与这些IP地址之一匹配,则服务器将接受连接而无需进一步检查DNS名称。例如,对等B的CSR(带有cfssl
)为:
{
"CN": "etcd peer",
"hosts": [
"invalid.domain",
"10.138.0.2"
],
当对等方B的远程IP地址是10.138.0.2
并且invalid.domain
是无效主机时。当对等方B尝试加入群集时,对等方A成功地对B进行了身份验证,因为“使用者备用名称(SAN)”字段具有有效的匹配IP地址。
从v3.2.5开始,服务器支持在通配符DNS上进行反向查找SAN
。例如,如果对等证书在“使用者备用名称(SAN)”字段中仅包含DNS名称(不包含IP地址),则服务器首先对远程IP地址进行反向查找,以获得映射到该地址的名称列表(例如nslookup IPADDR
)。如果这些名称列表中的名称与对等证书中的DNS名称(通过完全匹配或通配符匹配)匹配,则接受连接。如果没有匹配项,则服务器会在对等证书中对每个DNS条目进行正向查找(例如,当条目为example.default.svc时查找*.example.default.svc
),并且仅在主机的解析地址具有与对等方的远程IP地址匹配的IP地址时才接受连接。例如,对等B的CSR(带有cfssl
)为:
{
"CN": "etcd peer",
"hosts": [
"*.example.default.svc",
"*.example.default.svc.cluster.local"
],
当对等体B的远程IP地址为时10.138.0.2
。当对等体B尝试加入群集时,对等体A反向查找IP10.138.0.2
以获取主机名列表。而且,“主题备用名称”(SAN)字段中的主机名必须与对等B的证书DNS名称完全匹配或与通配符匹配。那么就接受连接。如果反向/正向查找均无效,则返回error "tls: "10.138.0.2" does not match any of DNSNames ["*.example.default.svc","*.example.default.svc.cluster.local"]
。
v3.3.0添加了etcd --peer-cert-allowed-cn
标记,以支持基于CN(通用名称)的对等连接身份验证。Kubernetes TLS引导涉及为etcd成员和其他系统组件(例如API服务器,kubelet等)生成动态证书。为每个组件维护不同的CA可提供对etcd群集的更严格的访问控制,但通常很乏味。当--peer-cert-allowed-cn
指定的标志时,节点只能与甚至具有共享的CA匹配通用名加入。例如,三节点集群中的每个成员都设置有CSR(带有cfssl
),如下所示:
{
"CN": "etcd.local",
"hosts": [
"m1.etcd.local",
"127.0.0.1",
"localhost"
],
{
"CN": "etcd.local",
"hosts": [
"m2.etcd.local",
"127.0.0.1",
"localhost"
],
{
"CN": "etcd.local",
"hosts": [
"m3.etcd.local",
"127.0.0.1",
"localhost"
],
如果--peer-cert-allowed-cn etcd.local
给出,只有具有匹配的通用名称的对等方将被认证。CSR中具有不同CN或不同CN的节点--peer-cert-allowed-cn
将被拒绝:
$ etcd --peer-cert-allowed-cn m1.etcd.local
I | embed: rejected connection from "127.0.0.1:48044" (error "CommonName authentication failed", ServerName "m1.etcd.local")
I | embed: rejected connection from "127.0.0.1:55702" (error "remote error: tls: bad certificate", ServerName "m3.etcd.local")
每个过程都应以以下内容开始:
etcd --peer-cert-allowed-cn etcd.local
I | pkg/netutil: resolving m3.etcd.local:32380 to 127.0.0.1:32380
I | pkg/netutil: resolving m2.etcd.local:22380 to 127.0.0.1:22380
I | pkg/netutil: resolving m1.etcd.local:2380 to 127.0.0.1:2380
I | etcdserver: published {Name:m3 ClientURLs:[https://m3.etcd.local:32379]} to cluster 9db03f09b20de32b
I | embed: ready to serve client requests
I | etcdserver: published {Name:m1 ClientURLs:[https://m1.etcd.local:2379]} to cluster 9db03f09b20de32b
I | embed: ready to serve client requests
I | etcdserver: published {Name:m2 ClientURLs:[https://m2.etcd.local:22379]} to cluster 9db03f09b20de32b
I | embed: ready to serve client requests
I | embed: serving client requests on 127.0.0.1:32379
I | embed: serving client requests on 127.0.0.1:22379
I | embed: serving client requests on 127.0.0.1:2379
v3.2.19和v3.3.4修复了当证书SAN字段仅包含IP地址但不包含域名时TLS重新加载的问题。例如,为成员设置了CSR(带有cfssl
),如下所示:
{
"CN": "etcd.local",
"hosts": [
"127.0.0.1"
],
在Go中,(*tls.Config).GetCertificate
当且仅当服务器的(*tls.Config).Certificates
字段不为空,或者(*tls.ClientHelloInfo).ServerName
来自客户端的有效SNI不为空时,服务器才要求TLS重新加载。以前,etcd始终(*tls.Config).Certificates
在初始客户端TLS握手中填充为非空。因此,总是希望客户端提供匹配的SNI,以便通过TLS验证并触发(*tls.Config).GetCertificate
以重新加载TLS资产。
但是,其SAN字段不包含任何域名但仅IP地址的证书将*tls.ClientHelloInfo
使用空ServerName
字段进行请求,因此无法在初始TLS握手时触发TLS重新加载;当需要在线更换过期的证书时,这将成为一个问题。(意思就是以前单独只含有IP无法触发TLS重载)
现在,(*tls.Config).Certificates
在初始TLS客户端握手时被创建为空,首先触发(*tls.Config).GetCertificate
,然后在每个新的TLS连接上填充其余证书,即使客户端SNI为空(例如,证书仅包含IP)。
主机白名单注意事项
etcd --host-whitelist
标志指定HTTP客户端请求中可接受的主机名。客户端源策略可以防止对不安全的etcd服务器的“ DNS绑定”攻击。也就是说,任何网站都可以简单地创建一个授权的DNS名称,并将DNS定向到"localhost"
(或任何其他地址)。然后,正在侦听的etcd服务器的所有HTTP端点"localhost"
都可以访问,因此容易受到DNS重新绑定攻击。
客户来源政策的工作方式如下:
- 如果客户端连接通过HTTPS是安全的,则允许使用任何主机名。
- 如果客户端连接不安全
"HostWhitelist"
且不为空,则仅允许其“主机”字段列在白名单中的HTTP请求。(那就是考虑安全问题https与白名单部署二选一)
请注意,无论是否启用身份验证,都会执行客户端来源策略,以进行更严格的控制。
默认情况下,etcd --host-whitelist
并且embed.Config.HostWhitelist
设置为空以允许所有主机名。请注意,在指定主机名时,不会自动添加回环地址。为了允许环回接口,将它们添加到白名单中手动(例如"localhost"
,"127.0.0.1"
等)。
经常问的问题
使用TLS客户端身份验证时,我看到SSLv3警报握手失败?
该crypto/tls
封装golang
检查证书公钥在使用之前的密钥使用。使用该证书的公钥做客户端验证,我们需要添加clientAuth
到Extended Key Usage
创建证书公钥时。
这是操作方法:
将以下部分添加到openssl.cnf中:
[ ssl_client ]
...
extendedKeyUsage = clientAuth
...
创建证书时,请确保在-extensions
标志中引用它:
$ openssl ca -config openssl.cnf -policy policy_anything -extensions ssl_client -out certs/machine.crt -infiles machine.csr
通过对等证书身份验证,我收到“证书对127.0.0.1有效,而不对$ MY_IP有效”
确保使用主题名称(成员的公用IP地址)对证书进行签名。etcd-ca
例如,该工具--ip=
为其new-cert
命令提供了一个选项。
需要在其使用者名称中为成员的FQDN签署证书,使用使用者备用名称(简称IP SAN)添加IP地址。该etcd-ca
工具提供了--domain=
选项的new-cert
命令和OpenSSL,可以使它了。
https://etcd.io/docs/v3.4