Traefik 常用 Middleware 介紹與應用

Middlewares 是 Traefik 用來調整請求 (Request) 和回應 (Response) 的強大工具。它們像是一個個串連起來的管道,請求通過時會被依照順序處理。

本章將介紹幾個最實用的 Middleware。

身份驗證 (BasicAuth)

BasicAuth 是最快為服務加上密碼保護的方法。我們可以用它來保護 Traefik Dashboard 或是尚未準備好公開的測試站。

產生密碼

首先,你需要產生一組經過加密的帳號密碼。你可以使用 htpasswd 工具 (Apache utils) 或線上工具。

# user: test, password: testpassword
echo $(htpasswd -nb test testpassword)
# 輸出: test:$apr1$H6...

$apr1$... 中的 $ 替換成 $$ (因為 YAML 中 $ 是變數符號,需要轉義),或者直接用單引號包起來。

定義與應用 Middleware

labels:
  # 定義一個名為 "auth" 的 Middleware
  - 'traefik.http.middlewares.auth.basicauth.users=test:$apr1$H6...'
  # 將 "auth" Middleware 套用到 Router "whoami"
  - 'traefik.http.routers.whoami.middlewares=auth'

現在,存取 whoami 服務時,瀏覽器就會跳出帳號密碼輸入框了。

路徑重寫 (StripPrefix)

有時候你想把服務掛在子路徑下,例如 /api/v1,但後端服務本身預期的是根路徑 /。這時就需要 StripPrefix

labels:
  # 定義路由規則:匹配 /api 開頭的請求
  - 'traefik.http.routers.backend.rule=PathPrefix(`/api`)'
  # 定義 Middleware:去除 /api 前綴
  - 'traefik.http.middlewares.strip-api.stripprefix.prefixes=/api'
  # 套用 Middleware
  - 'traefik.http.routers.backend.middlewares=strip-api'

請求流程:

  1. 使用者請求 http://example.com/api/users
  2. Traefik 匹配到 /api
  3. StripPrefix 去除 /api,剩下 /users
  4. 後端服務收到 GET /users

流量限制 (RateLimit)

保護你的服務免受 DDoS 攻擊或濫用。

預設情況下,Traefik 是根據 客戶端來源 IP (Client IP) 來限制流量的。每個 IP 擁有獨立的配額。

如果你的 Traefik 前面還有其他 Load Balancer (如 Cloudflare, AWS ELB),所有的請求來源 IP 可能都會變成該 Load Balancer 的 IP。這時你需要設定 forwardedHeaders 信任代理伺服器,Traefik 才能讀取 X-Forwarded-For 取得真實的客戶端 IP。
labels:
  # 定義 rate limit middleware
  # average: 每秒平均允許 100 個請求 (Refill Rate)
  - 'traefik.http.middlewares.ratelimit.ratelimit.average=100'
  # burst: 令牌桶 (Bucket) 大小。決定了瞬間最大允許的請求數量
  # 這裡設為 150,表示允許瞬間突發 150 個請求 (消耗完 150 個 token 後,需等待回補)
  - 'traefik.http.middlewares.ratelimit.ratelimit.burst=150'
  # 套用
  - 'traefik.http.routers.whoami.middlewares=ratelimit'

超過限制的請求會收到 429 Too Many Requests

為什麼需要 Average 和 Burst?

Traefik 使用的是 Token Bucket (令牌桶) 演算法,你可以把它想像成一個水桶:

  • Burst (水桶大小):決定了這個桶子最多能裝多少水。這代表了系統能承受的瞬間突發流量。在此範例中,我們設為 150,表示桶子滿的時候,使用者可以瞬間發送 150 個請求而不會被阻擋。
  • Average (補水速度):決定了每秒鐘有多少水流進桶子。這代表了長期平均速率。在此範例中,100 表示每秒鐘會補充 100 個額度到桶子裡(直到桶子滿為止)。

如果只設定 Burst 會發生什麼事? 如果只給桶子大小 (Burst),但沒有補水速度 (Average),那桶子裡的水用完就沒了,使用者再也無法發送請求。所以必須有一個速率來持續補充額度。

這個機制的好處是:允許偶爾的突發流量 (因為桶子有庫存),但在長時間下,流量會被限制在 Average 的速率。

壓縮 (Compress)

自動啟用 Gzip 或 Brotli 壓縮回應內容,節省頻寬並加快載入速度。

labels:
  # 定義 Compress Middleware (預設設定即可)
  - 'traefik.http.middlewares.compress.compress=true'
  # 套用
  - 'traefik.http.routers.whoami.middlewares=compress'

注意:Middleware 的順序很重要。通常我們會把 Compress 放在 StripPrefixAuthentication 之後,因為沒必要壓縮被拒絕的請求。

但在 Traefik 中,Middleware 的執行順序是由它們在 middlewares 列表中的順序決定的嗎?

不完全是。在 Traefik v2/v3,如果透過 Docker Label 定義,並且在 Router 上引用多個 Middleware,例如 middlewares=auth,compress,它們會依照定義好的順序執行。

推薦方式:Chain Middleware

串聯 (Chain)

如果你有多個 Router 都需要同一組 Middleware (例如:HTTPS Redirect + Auth + RateLimit),重複寫很麻煩。你可以定義一個 Chain。

這個例子比較適合在 File Provider 中定義,但在 Docker 中也可以:

labels:
  # 定義 chain
  - 'traefik.http.middlewares.secured.chain.middlewares=auth,ratelimit,compress'
  # 路由直接引用 chain
  - 'traefik.http.routers.whoami.middlewares=secured'

這樣管理起來就整潔多了!

安全性標頭 (Headers)

最後,為了讓網站更安全 (滿足 HSTS, XS-Protection 等),你可以使用 Headers Middleware:

labels:
  - 'traefik.http.middlewares.security-headers.headers.sslredirect=true'
  - 'traefik.http.middlewares.security-headers.headers.stsSeconds=315360000'
  - 'traefik.http.middlewares.security-headers.headers.browserXssFilter=true'
  - 'traefik.http.routers.whoami.middlewares=security-headers'

善用這些 Middlewares,你就可以在不修改後端程式碼的情況下,為整個基礎架構加上許多強大的功能。