使用NGINX和NGINX Plus速率限速
NGINX最有用但經常被誤解和配置錯誤的特徵之一就是速率限制。 它允許您限制用戶在給定時間段內可以執行的HTTP請求數量。
速率限制可以用於安全目的,例如減慢暴力密碼猜測攻擊。 它可以通過將傳入請求率限制為真實用戶的典型值,並且(通過日誌記錄)來識別目標URL,可以幫助防止DDoS攻擊。 更一般地說,它用於保護上游應用伺服器免受同時因太多用戶請求而被壓跨。
在本文中,我們將介紹使用NGINX進行速率限制的基礎知識以及更高級的配置。 速率限制在NGINX Plus中的工作方式相同。
NGINX速率限制的工作原理
NGINX速率限制使用泄漏桶演算法,其在電信和分組交換計算機網路中被廣泛使用,用於在帶寬有限時處理突發性。 就好比它是一個桶,從頂部倒水,然後從底部漏水; 如果倒水速度超過其漏水速率,則桶會溢出。 在請求處理方面,水表示來自客戶端的請求,桶表示根據先進先出(FIFO)調度演算法請求等待處理的隊列。 泄漏的水代表離開緩衝區的請求,由伺服器進行處理,溢出表示被丟棄和不再服務的請求。
配置基本速率限制
速率限制配置有兩個主要的指令:limit_req_zone和limit_req,如下例所示:
limit_req_zone指令定義了速率限制的參數,而limit_req可以在出現的上下文中啟用速率限制(在該示例中,對於/ login /的所有請求)。
limit_req_zone指令通常在http塊中定義,使其可在多個上下文中使用。它需要以下三個參數:
Key - 定義應用限制的請求特性。在該示例中,它是NGINX變數$ binary_remote_addr,它保存客戶端IP地址的二進位表示形式。這意味著我們將每個唯一IP地址限制為由第三個參數定義的請求速率。 (我們使用這個變數是因為它比使用$ remote_addr表示的客戶端ip地址更少的空間。)
Zone - 定義用於存儲每個IP地址狀態的共享內存區域以及訪問請求限制URL的頻率。將信息保存在共享內存中意味著可以在NGINX工作進程之間共享。定義有兩個部分:zone = keyword標識的區域名稱以及冒號後面的大小。16,000個IP地址的狀態信息大概需要1M位元組,所以我們的區域可以存儲大約16萬個地址。
如果NGINX需要添加新條目時而存儲空間不足,則其會刪除最舊的條目。如果釋放的空間仍然不足以容納新記錄,則NGINX返回狀態碼503(服務暫時不可用)。另外,為了防止內存耗盡,每次NGINX創建一個新條目時,它最多刪除兩個在前60秒內未使用的條目。
Rate - 設置最大請求率。在該示例中,速率不能超過每秒10個請求。 NGINX實際上以毫秒的粒度跟蹤請求,所以這個限制對應於每100毫秒1個請求。因為我們不允許突發(見下一部分),這意味著如果請求在前一個允許的請求後面的100毫秒內到達則會被拒絕。
limit_req_zone指令設置速率限制和共享內存區域的參數,但實際上並不限制請求速率。為此,您需要通過在那裡添加一個limit_req指令來將限制應用於特定location或server塊。在這個例子中,我們是對/ login /的速率限制請求。
所以現在每個唯一的IP地址限制在/ login /的每秒10個請求 - 或者更準確地說,在前一個IP地址的100毫秒內不能請求該URL。
處理突發事件
如果我們在100毫秒內得到2個請求怎麼辦? 對於第二個請求,NGINX向客戶端返回狀態碼503。 這可能不是我們想要的,因為應用程序本質上是突發的。 相反,我們希望緩衝任何超額的請求並及時為他們提供服務。 這是我們使用burst參數來限制_req,就像這個更新的配置一樣:
burst參數定義了客戶端可以執行多少請求,超出區域指定的速率(使用我們的例子中的mylimit區域,速率限制為每秒10個請求,或每100毫秒1個請求)。 一個早於100毫秒之前到達的請求會被放入隊列中,這裡我們將隊列大小設置為20。
這意味著如果21個請求同時從給定的IP地址到達,則NGINX會立即將第一個請求轉發到上游伺服器組,並將其餘20個隊列放入隊列中。 然後它每100毫秒轉發一個排隊的請求,只有當一個傳入的請求使隊列請求的數量超過20時,才返回503。
無延時的排隊
具有突發性的配置導致流量流暢,但是不太實用,因為它可能使您的站點看起來很慢。 在我們的示例中,隊列中的第20個數據包等待2秒轉發,此時對客戶端的響應可能不再有用。 要解決這種情況,請添加nodelay參數以及burst參數:
使用nodelay參數,NGINX仍然根據burst參數分配隊列中的插槽,並施加配置的速率限制,但不排除排隊請求的轉發。相反,當請求到達「太早」時,只要在隊列中有可用的插槽,NGINX將立即轉發。它將該插槽標記為「已佔用」,並且不會將其釋放以供另一個請求使用,直到適當的時間過去(在我們的示例中,在100毫秒之後)。
假設如前所述,20槽的隊列是空的,21個請求從給定的IP地址同時到達。 NGINX立即轉發所有21個請求,並標記隊列中的20個插槽,然後每100毫秒釋放1個插槽。 (如果有25個請求,NGINX將立即轉發其中21個,標記20個插槽,並拒絕4個請求,狀態為503)。
現在假設第一組請求後的101毫秒被轉發,另外20個請求同時到達。隊列中只有1個槽已被釋放,所以NGINX轉發1個請求,並拒絕其他19,狀態為503。如果在20個新請求到達之前已經過去了501毫秒,則5個槽是空閑的,所以NGINX立即轉發5個請求並拒絕15 。
效果相當於每秒10個請求的速率限制。 如果您想強制使用速率限制,而不限制請求之間的允許間距,則nodelay選項將非常有用。
注意:對於大多數部署,我們建議將burst和nodelay參數包含在limit_req指令中。
高級配置示例
通過將基本速率限制與其他NGINX功能相結合,您可以實現更細微的流量限制。
白名單
此示例顯示如何對不在「白名單」的任何人的請求強制設置費率限制。
此示例使用geo和map指令。 geo塊為白名單中的IP地址分配0到$ limit,對所有其他IP地址分配1。 然後,我們使用map將這些值轉換為關鍵字,以便:
如果$ limit為0,則將$ limit_key設置為空字元串
如果$ limit為1,則將$ limit_key設置為客戶端的二進位格式的IP地址
把兩個對齊,$ limit_key設置為白名單IP地址的空字元串,否則設置為客戶端的IP地址。 當limit_req_zone目錄(Key)的第一個參數是空字元串時,不會應用限制,因此白名單IP地址(10.0.0.0/8和192.168.0.0/24子網)不受限制。 所有其他IP地址每秒限制為5個請求。
limit_req指令對/ location應用限制,允許在配置的限制內突發最多10個報文,轉發時間不會有延時
在Location中包含多個limit_req指令
您可以在單個Location包含多個limit_req偽指令。 符合給定請求的所有限制都將被應用,這意味著使用最嚴格的限制。 例如,如果多個指令施加延遲,則使用最長的延遲。 類似地,如果這是任何指令的效果,即使其他指令允許它們通過,請求也被拒絕。
擴展前面的例子,我們可以對白名單上的IP地址應用速率限制:
白名單上的IP地址沒有匹配到第一個速率限制(req_zone),但匹配第二個(req_zone_wl),因此每秒限制為15個請求。 不在白名單上的IP地址匹配兩個速率限制,因此限制性更強:每秒5個請求。
配置相關功能
記錄
默認情況下,NGINX記錄由於速率限制而延遲或丟棄的請求,如本示例所示:
日誌條目中的欄位包括::
- 指示日誌條目記錄速率限制
excess - 每毫秒超過此請求所表示的配置速率的請求數
zone - 定義施加速率限制的區域
client - 發出請求的客戶端的IP地址
server - 伺服器的IP地址或主機名
request - 客戶端提供的實際HTTP請求
host - 主機HTTP頭的值
默認情況下,NGINX在error級別記錄拒絕請求,如上例中的[error]所示。 (它將延遲請求記錄在一個級別以下,默認情況下為info。)要更改日誌記錄級別,請使用limit_req_log_level指令。 在這裡我們設置拒絕請求登錄在warn級別:
發送給客戶端的錯誤代碼
默認情況下,當客戶端超過其速率限制時,NGINX響應狀態碼503(服務暫時不可用)。 使用limit_req_status指令設置不同的狀態代碼(在本示例中為444):
拒絕對特定Location的所有請求
如果您要拒絕所有針對特定網址的請求,而不僅僅是限制它們,請為其配置Location,並包含deny all指令:
結論
我們已經介紹了NGINX和NGINX Plus提供的許多速率限制功能,包括為HTTP請求設置不同location的請求率,以及配置其他功能來限制速率,例如突發和節點參數。 我們還涵蓋了對白名單和黑名單客戶端IP地址應用不同限制的高級配置,並解釋了如何記錄拒絕和延遲的請求。


※那個吃電的女人,她家在哪兒?
※關於建立全國強直性脊柱炎科研協作組的通知
※女人要懂得這些,你才會過得更好
※略評一下小米Note3和Iphone X-人臉解鎖、人臉支付將大行其道,但贏家卻是寥寥幾個
※他是不是你內心的擺渡人
TAG:公眾號 |