做者羅哲軒,Core developer of Apache APISIXgit
etcd 升級到 3.x 版本後,其對外 API 的協議從普通的 HTTP1 切換到了 gRPC。爲了兼顧那些不能使用 gRPC 的特殊羣體,etcd 經過 gRPC-gateway 的方式代理 HTTP1 請求,以 gRPC 形式去訪問新的 gRPC API。(因爲 HTTP1 念起來太過拗口,如下將之簡化成 HTTP,正好和 gRPC 可以對應。請不要糾結 gRPC 也是 HTTP 請求的這種問題。)github
Apache APISIX 開始用 etcd 的時候,用的是 etcd v2 的 API。從 Apache APISIX 2.0 版本起,咱們把依賴的 etcd 版本升級到 3.x。因爲 Lua 生態圈裏面沒有 gRPC 庫,因此 etcd 對 HTTP 的兼容幫了咱們很大的忙,這樣就不用花很大心思去補這個短板了。markdown
從去年 10 月發佈 Apache APISIX 2.0 版本以來,如今已通過去了 8 個月。在實踐過程當中,咱們也發現了 etcd 的 HTTP API 的一些跟 gRPC API 交互的問題。事實上,擁有 gRPC-gateway 並不意味着可以完美支持 HTTP 訪問,這裏仍是有些細微的差異。架構
就在幾天前,etcd 發佈了 v3.5.0 版本。這個版本的發佈,了卻困擾咱們很長時間的一個問題。oop
跟 HTTP 不一樣的是,gRPC 默認限制了一次請求能夠讀取的數據大小。這個限制叫作 「MaxCallRecvMsgSize」,默認是 4MiB。當 Apache APISIX 全量同步 etcd 數據時,假如配置夠多,就會觸發這一上限,報錯 「grpc: received message larger than max」。spa
神奇的是,若是你用 etcdctl 去訪問,這時候卻不會有任何問題。這是由於這個限制是能夠在跟 gRPC server 創建鏈接時動態設置的,etcdctl 給這個限制設置了一個很大的整數,至關於去掉了這一限制。代理
因爲很多用戶碰到過一樣的問題,咱們曾經討論過對策。日誌
一個想法是用增量同步模擬全量同步,這麼作有兩個弊端:code
另外一個想法是修改 etcd。既然可以在 etcdctl 裏面去除限制,爲何不對 gRPC-gateway 一視同仁呢?一樣的改動能夠做用在 gRPC-gateway 上。orm
咱們採用了第二種方案,給 etcd 提了個 PR:github.com/etcd-io/etc…
最新發布的 v3.5.0 版本就包含了咱們貢獻的這個改動。若是你遇到 「grpc: received message larger than max」,不妨試一下這個版本。這一改動也被 etcd 開發者 backport 到 3.4 分支上了。3.4 分支的下一個發佈,也會帶上這個改動。
這件事也說明 gRPC-gateway 並不是百試百靈。即便用了它,也不能保證 HTTP 訪問可以跟 gRPC 訪問有同樣的體驗。
Apache APISIX 增長了對 etcd mTLS 的支持後,有用戶反饋一直無法完成校驗,而用 etcdctl 訪問則是成功的。在跟用戶交流後,我決定拿他的證書來複現下。 在復現過程當中,我注意到 etcd 日誌裏面有這樣的報錯:
2021-06-09 11:10:13.022735 I | embed: rejected connection from "127.0.0.1:50898" (error "tls: failed to verify client's certificate: x509: certificate specifies an incompatible key usage", ServerName "")WARNING: 2021/06/09 11:10:13 grpc: addrConn.createTransport failed to connect to {127.0.0.1:12379 0 }. Err :connection error: desc = "transport: authentication handshake failed: remote error: tls: bad certificate". Reconnecting...
複製代碼
「bad certificate」 錯誤信息,初看像是由於咱們發給 etcd 的客戶端證書不對。但仔細瞧瞧,會發現這個報錯是在 gRPC server 裏面報的。 gRPC-gateway 在 etcd 裏面起到一個代理的做用,把外面的 HTTP 請求變成 gRPC server 能處理的 gRPC 請求。 大致架構以下:
etcdctl ----> gRPC serverApache APISIX ---> gRPC-gateway ---> gRPC server
複製代碼
爲何 etcdctl 直連 gRPC server 能通,而中間加一層 gRPC-gateway 就不行? 原來當 etcd 啓用了客戶端證書校驗以後,用 gRPC-gateway 鏈接 gRPC server 就須要提供一個客戶端證書。猜猜這個證書從哪來? etcd 把配置的服務端證書直接做爲這裏的客戶端證書用了。 一個證書既在服務端上提供驗證,又在客戶端上代表身份,看上去也沒什麼問題。除非…… 除非證書上啓用了 server auth 的拓展,可是沒有啓用 client auth。 對有問題的證書執行
openssl x509 -text -noout -in /tmp/bad.crt
複製代碼
會看到這樣的輸出:
X509v3 extensions: X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication
複製代碼
注意這裏的 「TLS Web Server Authentication」,若是咱們把它改爲 「TLS Web Server Authentication, TLS Web Client Authentication」,抑或不加這個拓展,就沒有問題了。 etcd 上也有關於這個問題的 issue:github.com/etcd-io/etc…
雖然咱們在上文列出了幾點小問題,可是瑕不掩瑜,etcd 對 HTTP 訪問的支持仍是一個很是有用的特性。 感謝 Apache APISIX 的用戶們,正是由於咱們有着廣闊的用戶羣,才能發現 etcd 的這些細節上的問題。咱們做爲 etcd 的一大用戶,在以後的日子裏也將一如既往地跟 etcd 的開發者多多交流。