原文地址:TLS
etcd支持用於客戶端到服務器以及對等方(服務器到服務器/集群)通信的自動TLS以及通過客戶端證書的身份驗證.
要啟動並運行,首先要獲得一個成員的CA證書和簽名密鑰對。 建議為集群中的每個成員創建並簽名一個新的密鑰對。
為了方便起見,cfssl工具提供了一個簡單的接口來生成證書,我們在此處提供了使用該工具的示例。 或者,嘗試使用本指南生成自簽名密鑰對。
基本設置
etcd通過命令行參數或環境變量采用了幾種與證書相關的配置選項:
客戶端到服務器的通信:
--cert-file=<path>
:用於SSL/TLS與etcd的連接的證書。設置此選項后,advertise-client-urls可以使用HTTPS模式。
--key-file=<path>
:證書的密鑰。 必須未加密。
--client-cert-auth
:設置此選項后,etcd將檢查所有傳入的HTTPS請求以查找由受信任CA簽名的客戶端證書,不提供有效客戶端證書的請求將失敗。 如果啟用了身份驗證,則證書將為“公用名”字段指定的用戶名提供憑據。
--trusted-ca-file=<path>
:受信任的證書頒發機構。
--auto-tls
:使用自動生成的自簽名證書進行與客戶端的TLS連接。
對等節點(服務器到服務器/集群)間的通信:
對等節點選項的工作方式與客戶端到服務器的選項相同:
--peer-cert-file=<path>
:用於SSL/TLS與對等節點之間的連接的證書。這將用於監聽對等方地址以及向其他對等方發送請求。
--peer-key-file=<path>
:證書的密鑰。 必須未加密。
--peer-client-cert-auth
:設置此選項后,etcd將檢查所有傳入的對等節點請求以查找由受信任CA簽名的客戶端證書.
--peer-trusted-ca-file=<path>
:受信任的證書頒發機構。
--peer-auto-tls
:使用自動生成的自簽名證書進行與對等節點之間的TLS連接。
如果提供了客戶端到服務器或對等節點證書,則還必須設置密鑰。 所有這些配置選項也可以通過環境變量ETCD_CA_FILE
,ETCD_PEER_CA_FILE
等獲得。
--cipher-suites
:服務器/客戶端與對等方之間受支持的TLS密碼套件的逗號分隔列表(空將由Go自動填充)。從v3.2.22+,v3.3.7+
和v3.4+
起可用。
示例1:客戶端通過HTTPS與服務器進行加密傳輸
為此,請准備好CA證書(ca.crt
)和簽名密鑰對(server.crt
,server.key
)。
讓我們配置etcd以逐步提供簡單的HTTPS傳輸安全性:
$ etcd --name infra0 --data-dir infra0 \
--cert-file=/path/to/server.crt --key-file=/path/to/server.key \
--advertise-client-urls=https://127.0.0.1:2379 --listen-client-urls=https://127.0.0.1:2379
這應該可以正常啟動,並且可以通過對etcd用HTTPS方式來測試配置:
$ curl --cacert /path/to/ca.crt https://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -v
該命令應顯示握手成功。 由於我們使用具有自己的證書頒發機構的自簽名證書,因此必須使用--cacert
選項將CA傳遞給curl。 另一種可能性是將CA證書添加到系統的受信任證書目錄(通常在/etc/pki/tls/certs
或/etc/ssl/certs
中)。
OSX10.9+的用戶:OSX 10.9+上的curl 7.30.0無法理解在命令行中傳遞的證書。可以替代的方法是將虛擬ca.crt
直接導入到鑰匙串中,或添加-k
標志來curl
以忽略錯誤。要在沒有-k標志的情況下進行測試,請運行打開的./fixtures/ca/ca.crt
並按照提示進行操作。測試后請刪除此證書!如果有解決方法,請告訴我們。
示例2:使用HTTPS客戶端證書的客戶端到服務器身份驗證
目前,我們已經為etcd客戶端提供了驗證服務器身份並提供傳輸安全性的功能。 但是,我們也可以使用客戶端證書來防止對etcd的未經授權的訪問。
客戶端將向服務器提供其證書,服務器將檢查證書是否由提供的CA簽名並決定是否滿足請求。
為此,需要第一個示例中提到的相同文件,以及由同一證書頒發機構簽名的客戶端密鑰對(client.crt
,client.key
)。
$ etcd --name infra0 --data-dir infra0 \
--client-cert-auth --trusted-ca-file=/path/to/ca.crt --cert-file=/path/to/server.crt --key-file=/path/to/server.key \
--advertise-client-urls https://127.0.0.1:2379 --listen-client-urls https://127.0.0.1:2379
現在,對該服務器嘗試與上述相同的請求:
$ curl --cacert /path/to/ca.crt https://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -v
該請求應該是被服務器拒絕:
...
routines:SSL3_READ_BYTES:sslv3 alert bad certificate
...
為了使其成功,我們需要將CA簽名的客戶端證書提供給服務器:
$ curl --cacert /path/to/ca.crt --cert /path/to/client.crt --key /path/to/client.key \
-L https://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -v
輸出應包括:
...
SSLv3, TLS handshake, CERT verify (15):
...
TLS handshake, Finished (20)
以及服務器的響應:
{
"action": "set",
"node": {
"createdIndex": 12,
"key": "/foo",
"modifiedIndex": 12,
"value": "bar"
}
}
指定密碼套件以阻止較弱的TLS密碼套件。
當使用無效密碼套件請求客戶端問候時,TLS握手將失敗。
例如:
$ etcd \
--cert-file ./server.crt \
--key-file ./server.key \
--trusted-ca-file ./ca.crt \
--cipher-suites TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
然后,客戶端請求必須指定服務器中指定的密碼套件之一:
# 有效的加密套件
$ curl \
--cacert ./ca.crt \
--cert ./server.crt \
--key ./server.key \
-L [CLIENT-URL]/metrics \
--ciphers ECDHE-RSA-AES128-GCM-SHA256
# 成功請求
etcd_server_version{server_version="3.2.22"} 1
...
# 無效的加密套件
$ curl \
--cacert ./ca.crt \
--cert ./server.crt \
--key ./server.key \
-L [CLIENT-URL]/metrics \
--ciphers ECDHE-RSA-DES-CBC3-SHA
# 請求失敗
(35) error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
示例3:集群中的傳輸安全性和客戶端證書
etcd支持與上述對等節點通信相同的模型,這意味着集群中etcd成員之間的通信。
假設我們有這個ca.crt
和兩個由此CA簽名的成員,它們具有自己的密鑰對(member1.crt
和member1.key
,member2.crt
和member2.key
),我們按以下方式啟動etcd:
DISCOVERY_URL=... # from https://discovery.etcd.io/new
# member1
$ etcd --name infra1 --data-dir infra1 \
--peer-client-cert-auth --peer-trusted-ca-file=/path/to/ca.crt --peer-cert-file=/path/to/member1.crt --peer-key-file=/path/to/member1.key \
--initial-advertise-peer-urls=https://10.0.1.10:2380 --listen-peer-urls=https://10.0.1.10:2380 \
--discovery ${DISCOVERY_URL}
# member2
$ etcd --name infra2 --data-dir infra2 \
--peer-client-cert-auth --peer-trusted-ca-file=/path/to/ca.crt --peer-cert-file=/path/to/member2.crt --peer-key-file=/path/to/member2.key \
--initial-advertise-peer-urls=https://10.0.1.11:2380 --listen-peer-urls=https://10.0.1.11:2380 \
--discovery ${DISCOVERY_URL}
etcd成員將形成一個集群,並且集群中成員之間的所有通信都將使用客戶端證書進行加密和身份驗證。 etcd的輸出將顯示它連接以使用HTTPS的地址。
示例4:自動自簽名傳輸安全性
對於需要通信加密而不是身份驗證的情況,etcd支持使用自動生成的自簽名證書來加密其消息。 因為不需要在etcd之外管理證書和密鑰,所以這簡化了部署。
配置etcd以使用帶有--auto-tls
和--peer-auto-tls
標志的自簽名證書進行客戶端和對等節點連接:
DISCOVERY_URL=... # from https://discovery.etcd.io/new
# member1
$ etcd --name infra1 --data-dir infra1 \
--auto-tls --peer-auto-tls \
--initial-advertise-peer-urls=https://10.0.1.10:2380 --listen-peer-urls=https://10.0.1.10:2380 \
--discovery ${DISCOVERY_URL}
# member2
$ etcd --name infra2 --data-dir infra2 \
--auto-tls --peer-auto-tls \
--initial-advertise-peer-urls=https://10.0.1.11:2380 --listen-peer-urls=https://10.0.1.11:2380 \
--discovery ${DISCOVERY_URL}
自簽名證書不會驗證身份,因此curl將返回錯誤:
curl: (60) SSL certificate problem: Invalid certificate chain
要禁用證書鏈檢查,請使用-k
標志調用curl
:
$ curl -k https://127.0.0.1:2379/v2/keys/foo -Xput -d value=bar -v
DNS SRV的注意事項
如果連接是安全的,則etcd proxy
從其客戶端TLS終端,並使用--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:證書對10.138.0.27
有效,而不對10.138.0.2
有效,因為B的遠程IP地址與“使用者備用名稱(SAN)”字段中的地址不匹配。
從v3.2.0開始,服務器在檢查SAN時解析TLS DNSNames。 例如,如果對等節點證書在“使用者備用名稱”(SAN)字段中僅包含DNS名稱(不包含IP地址),則僅當這些DNS名稱上的正向查找(dig b.com
)具有與遠程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
,則出現錯誤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地址。 有關更多詳細信息,請參見問題#8206。
從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反向查找IP 10.138.0.2
以獲取主機名列表。 並且,“主題備用名稱”(SAN)字段中的主機名必須與對等節點B的證書DNS名稱完全匹配或與通配符匹配。 如果反向/正向查找均無效,則返回錯誤"tls: "10.138.0.2" does not match any of DNSNames ["*.example.default.svc","*.example.default.svc.cluster.local"]
。有關更多詳細信息,請參見問題#8268。
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或--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).Certificates
字段不為空或(* tls.ClientHelloInfo).ServerName
不為空且具有有效SNI時,服務器才會調用(* tls.Config).GetCertificate
來重新加載TLS 來自客戶。 以前,etcd始終填充(* tls.Config)
。在初始客戶端TLS握手上的證書為非空。 因此,總是希望客戶端提供匹配的SNI,以便通過TLS驗證並觸發(* tls.Config).GetCertificate
以重新加載TLS數據。
但是,其SAN字段僅包括IP地址不包含任何域名的證書將請求* tls.ClientHelloInfo
帶有空的ServerName
字段,從而無法在初始TLS握手時觸發TLS重新加載;當需要在線更換過期證書時,這將成為一個問題。
現在,(* tls.Config).Certificates
在初始TLS客戶端握手時創建為空,首先觸發(* tls.Config).GetCertificate
,然后在每個新的TLS連接上填充其余證書,即使客戶端SNI為 為空(例如,證書僅包括IP)。
主機白名單的注意事項
etcd --host-whitelist
參數指定HTTP客戶端請求中可接受的主機名。 客戶端源策略可以防止對不安全的etcd服務器的“DNS重新綁定”攻擊。 也就是說,任何網站都可以簡單地創建一個授權的DNS名稱,並將DNS定向到“localhost
”(或任何其他地址)。 然后,偵聽“localhost
”上的etcd服務器的所有HTTP端點都可以訪問,因此容易受到DNS重新綁定攻擊。 有關更多詳細信息,請參見CVE-2018-5702。
客戶原始策略的工作方式如下:
- 如果客戶端通過HTTPS連接是安全的,則允許使用任何主機名。
- 如果客戶端連接不安全且“
HostWhitelist
”不為空,則僅允許其Host字段列在白名單中的HTTP請求。
請注意,無論是否啟用身份驗證,都會實施客戶端來源策略,以進行更嚴格的控制。
默認情況下,etcd --host-whitelist
和embed.Config.HostWhitelist
設置為空以允許所有主機名。請注意,在指定主機名時,不會自動添加回送地址。 要允許環回接口,請手動將其添加到白名單(例如“ localhost
”,“127.0.0.1
”等)。
常見問題
使用TLS客戶端身份驗證時,我看到SSLv3警報握手失敗?
golang
的crypto/tls
軟件包在使用之前檢查證書公鑰的密鑰用法。要使用證書公共密鑰進行客戶端身份驗證,我們需要在創建證書公共密鑰時將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有效,而不對$我的Ip有效”
確保使用主題名稱(成員的公共IP地址)對證書進行簽名。 例如,etcd-ca
工具為其new-cert
命令提供了--ip=
選項。
需要在其使用者名稱中為成員的FQDN簽署證書,使用使用者備用名稱(簡稱IP SAN)添加IP地址。 etcd-ca
工具為其new-cert
命令提供了--domain=
選項,openssl
也可以做到這一點。