Envoy Gateway TLS 與安全:把 HTTPS 接起來,別讓 API 裸奔
HTTP 跑通只是暖身。只要你的服務不是活在石器時代,正式環境幾乎一定要上 HTTPS。這篇就來把 Envoy Gateway 的 TLS 基礎打好,避免你的 API 還在網路上裸奔。
TLS 在 Gateway 裡是怎麼放的
最常見的做法是:在 Gateway listener 做 TLS termination。
也就是:
- client 用 HTTPS 打進來
- Gateway 負責握手、解憑證
- 後面再把流量轉給 Service
一個基本 HTTPS listener 長這樣:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: eg
spec:
gatewayClassName: eg
listeners:
- name: http
protocol: HTTP
port: 80
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- kind: Secret
group: ""
name: example-cert這段的重點只有兩個:
protocol: HTTPStls.mode: Terminate
意思就是:TLS 在這裡結束,由 Gateway 來處理憑證。
HTTPS + HTTPRoute 跟 TLSRoute 有什麼不同
這一段很重要,因為很多人第一次看到 TLSRoute 會想問:
「不是已經有 HTTPS listener 了嗎?為什麼還要多一個 TLSRoute?」
差別在於 Gateway 有沒有終止 TLS:
| 場景 | 常見組合 | 說明 |
|---|---|---|
| Gateway 終止 TLS,之後再看 HTTP 規則 | HTTPS listener + HTTPRoute |
最常見網站/API 場景 |
| Gateway 不解密,只根據 SNI 把加密流量轉送出去 | TLS listener + TLSRoute |
TLS passthrough 場景 |
也就是說:
- 你想在 Gateway 看
path、header、method,通常就得先終止 TLS,然後搭HTTPRoute - 你想保留端到端加密,不在 Gateway 解密,通常才會考慮
TLSRoute
這兩種做法沒有誰高級誰低級,重點是你要先知道自己要的是「終止 TLS 後做 L7 路由」,還是「保留加密只做 SNI 分流」。
先準備憑證 Secret
在測試環境,可以先用自簽憑證。官方範例也是這樣示範:
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 \
-subj '/O=example Inc./CN=example.com' \
-keyout example.com.key \
-out example.com.crt
openssl req -out www.example.com.csr -newkey rsa:2048 -nodes \
-keyout www.example.com.key \
-subj "/CN=www.example.com/O=example organization"
openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key \
-set_serial 0 -in www.example.com.csr -out www.example.com.crt把 cert/key 存成 Kubernetes Secret:
kubectl create secret tls example-cert \
--key=www.example.com.key \
--cert=www.example.com.crt之後 Gateway 就能透過 certificateRefs 指到這個 Secret。
⚠️ 自簽憑證只適合測試。正式環境請搭配 cert-manager 或你的既有憑證管理流程,不要把 demo 流程直接搬進 production,這種事跟把泡麵當營養餐一樣,偶爾可以,長期不行。
把 HTTPS listener 加到既有 Gateway
如果你前一篇已經有 eg Gateway,可以直接 patch:
kubectl patch gateway eg --type=json --patch '
- op: add
path: /spec/listeners/-
value:
name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- kind: Secret
group: ""
name: example-cert
'確認狀態:
kubectl get gateway/eg -o yaml如果 listener 正常,接著就可以打 HTTPS 測試:
curl -v -HHost:www.example.com \
--resolve "www.example.com:443:${GATEWAY_HOST}" \
--cacert example.com.crt \
https://www.example.com/get這條指令很適合本地驗證,尤其在 DNS 還沒正式接好時特別有用。
一個 Gateway 可以掛多個 HTTPS 網域
如果你有多個網域,可以在同一個 Gateway 裡加多個 HTTPS listener。
例如再加一個 foo.example.com:
listeners:
- name: https-main
protocol: HTTPS
port: 443
hostname: www.example.com
tls:
mode: Terminate
certificateRefs:
- name: example-cert
- name: https-foo
protocol: HTTPS
port: 443
hostname: foo.example.com
tls:
mode: Terminate
certificateRefs:
- name: foo-cert然後記得對應的 HTTPRoute 也要補 hostname,不然入口雖然聽得到,路由還是不知道該往哪裡送。
TLSRoute 的心智模型
如果你要的是 TLS passthrough,TLSRoute 的想法可以先記成這樣:
💡
TLSRoute在不同 Gateway API CRD 版本中的apiVersion可能不同,下面先當作概念示意。實際套用時,請以你叢集已安裝的 Gateway API 版本為準。
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: eg-tls
spec:
gatewayClassName: eg
listeners:
- name: tls
protocol: TLS
port: 443
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
name: passthrough-route
spec:
parentRefs:
- name: eg-tls
hostnames:
- "db.example.com"
rules:
- backendRefs:
- name: db-service
port: 5432這類場景通常是:
- 你只根據 SNI hostname 分流
- 加密流量一路保留到 backend
- Gateway 不解析 HTTP path/header
所以 TLSRoute 跟 HTTPRoute 最大的差別,不是都叫 route,而是它們看到的流量層級根本不同。
跨 Namespace 憑證引用要注意什麼
官方文件特別提到一個很實用的能力:
Gateway 可以引用其他 namespace 的憑證 Secret,但需要 ReferenceGrant。
例如平台團隊把憑證統一放在 envoy-gateway-system,應用 Gateway 在 default,這時就不能直接亂指,必須先開授權。
範例:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-default-to-read-cert
namespace: envoy-gateway-system
spec:
from:
- group: gateway.networking.k8s.io
kind: Gateway
namespace: default
to:
- group: ""
kind: Secret這代表:
defaultnamespace 的Gateway- 可以合法引用
envoy-gateway-system裡的Secret
沒有 ReferenceGrant 時,這種 cross-namespace reference 會被視為無效。這是保護機制,不是 Gateway API 故意刁你。
一句話總結
Envoy Gateway 的 TLS 其實沒有你想像中玄學:準備好憑證 Secret,把 HTTPS listener 設好,再確認 route 與 hostname 對得上,基本上就成了。
💡 初學者常犯的錯不是 TLS 太難,而是三件事沒對齊:
hostname、certificateRefs、HTTPRoute hostnames。這三個只要有一個歪掉,流量就會開始演你。
下一步
學會安裝、概念、路由跟 TLS 後,下一篇先來收斂成正式環境比較不容易翻車的做法: 👉 最佳實踐