Files
homelab-infra/platform/envoy-gateway/README.md
T
panxiao81 4070b75436
yaml / yaml (pull_request) Successful in 10s
feat(flux): 激活 Envoy Gateway 接管
2026-09-10 09:45:25 +00:00

134 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Envoy Gateway — LAN ingress + Authelia forward-auth
The cluster's HTTP entry point for services on `ad.ddupan.top`, and the enforcement
point for Authelia authentication. Envoy's LoadBalancer holds **192.168.10.127** (k3s
ServiceLB); routing is by `Host` header.
- Chart: `oci://docker.io/envoyproxy/gateway-helm` **v1.5.6**, ns `envoy-gateway-system`
- `gateway.yaml` — `GatewayClass eg` + `Gateway eg` (`:80` plaintext, `:443` wildcard TLS)
- Gateway API **v1.3.0**
- Replaced Contour on 2026-07-25 (see [Why not Contour](#why-not-contour))
## Adding a service
1. an `HTTPRoute` with `parentRefs` → `eg` / `envoy-gateway-system`, `sectionName: https`
2. an A record in `samba_ad_extra_a_records` → `192.168.10.127` (`../../infrastructure/samba-ad`), then
`ansible-playbook provision-dc.yml --tags dns`
3. **optionally** a `SecurityPolicy` for Authelia forward-auth — see `../../apps/netbox`
No certificate work: the `:443` listener already serves the `*.ad.ddupan.top` wildcard
from `../cert-manager`. Worked example: `../../apps/netbox`.
## Why not Contour
Contour worked as an ingress, but supports **only the gRPC** Envoy ext_authz protocol —
*"Only the Envoy GRPC authorization protocol will be supported"* (Contour 1.33 docs).
Authelia implements the **HTTP** ExtAuthz filter. The two cannot meet, so Authelia
forward-auth was impossible.
That mattered because **NetBox has no SSO group→role mapping** — its group/superuser
mapping is LDAP-only, and the OIDC pipeline can only assign one static group. Without
forward-auth the options were "promote every user by hand" or "drop SSO and use LDAP,
losing 2FA". Envoy Gateway's `SecurityPolicy.extAuth.http` removes the dilemma.
Envoy Gateway was chosen over Traefik because everything already built — Gateway,
HTTPRoute, wildcard cert, DNS — is Gateway API, so only the GatewayClass and controller
changed. Traefik would have meant reverting to `IngressRoute` + `Middleware`.
## Forward-auth: how it fits together
```
browser ──▶ Envoy ──(ext_authz HTTP)──▶ Authelia ──▶ 200 + Remote-* headers
│ └──▶ 401 ⇒ 302 to auth.ddupan.top
└──▶ upstream app (headers attached)
```
Pieces, each in the directory that owns it:
| where | what |
|---|---|
| `../../apps/netbox/securitypolicy.yaml` | `SecurityPolicy` targeting the app's HTTPRoute |
| `../../apps/authelia/referencegrant-extauth.yaml` | lets a SecurityPolicy in another namespace reference the Authelia Service |
| `../../apps/authelia/values.yaml` | `server.endpoints.authz.ext-authz` + an `access_control` rule |
| `../../apps/netbox/networkpolicy.yaml` | stops anything bypassing Envoy to reach the app directly |
### Three details that cost time
- **`headersToBackend` lives under `extAuth.http`, not `extAuth`.** One level up the API
rejects it: `unknown field "spec.extAuth.headersToBackend"`.
- **`backendRefs.port` is the SERVICE port, not the container port.** The Authelia chart
publishes `80 → targetPort http (9091)`; using `9091` fails with
`TCP Port 9091 not found on service authelia/authelia`.
- **Authelia's session cookie is scoped to `ddupan.top`**, which already covers
`*.ad.ddupan.top` — so SSO works across both without touching the cookie config.
### ⚠ Trust boundary
Apps behind forward-auth trust `Remote-*` headers. Two things make that safe and **both**
must remain true:
1. `headersToBackend` **overrides** any client-supplied value ("coexisting headers will be
overridden"), so a spoofed `Remote-User` cannot survive the hop through Envoy.
2. A `NetworkPolicy` per app restricts pod ingress to `envoy-gateway-system`, so nothing
in-cluster can bypass Envoy. k3s enforces NetworkPolicy (kube-router), so this is real.
Verified by test: a pod in `default` sending `Remote-User: admin` to the app Service gets
**connection refused**, while the same request from `envoy-gateway-system` is served.
Also set `failOpen: false` — if Authelia is down, refuse traffic rather than admit
unauthenticated requests to an app whose auth model is "trust the header".
## Install
### Flux 接管状态
现有 release 为 OCI chart/app `v1.5.6`,且没有 user-supplied values。Git 中固定
同一版本并已完成分阶段 Flux HelmRelease 接管。第一阶段确认 Kustomization Ready、
Helm revision 保持为 1,controller/data-plane Pod UID 与入口响应均未变化后,再通过
独立 PR 解除暂停。OCI 类型 HelmRepository 是按需 chart generator,本身不产生
artifact 或 Ready condition;解除暂停后由 HelmRelease 请求并验证 chart。
`GatewayClass eg` 与 `Gateway eg` 随本目录 Kustomization 由 Flux 管理。接管期间保持
`prune: false`;Gateway API 与 Envoy Gateway CRD 使用 `CreateReplace`,延续现有 Helm
所有权且绝不通过删除 CRD 迁移。Gateway 的三个 `certificateRefs` 最初属于
`kubectl-client-side-apply`;首次 reconcile 已由 Flux 默认 SSA `Override` 接管这些在
Git 中声明且值相同的字段,没有改变 listener spec。不得用 `force: true`,它用于
不可变字段失败时删除重建资源。以下命令保留为 break-glass 手工恢复流程。
```bash
# The Gateway API CRDs may already be owned by another tool's field manager (Contour's
# quickstart used client-side apply), which makes Helm fail with
# "conflict with kubectl-client-side-apply: .spec.versions".
# Transfer ownership WITHOUT deleting the CRDs (deleting them would delete every
# Gateway and HTTPRoute):
helm pull oci://docker.io/envoyproxy/gateway-helm --version v1.5.6 --untar
kubectl apply --server-side --force-conflicts --field-manager=helm \
-f gateway-helm/crds/gatewayapi-crds.yaml
helm upgrade --install envoy-gateway oci://docker.io/envoyproxy/gateway-helm \
--version v1.5.6 -n envoy-gateway-system --create-namespace
kubectl apply -f envoy-gateway/gateway.yaml
```
Only one LoadBalancer can hold `:80/:443` — k3s ServiceLB uses hostPorts, so a second
one's `svclb-*` DaemonSet sits at `0/1` and the Gateway stays `PROGRAMMED: False` until
the previous controller's Service is gone.
## Verify
```bash
kubectl -n envoy-gateway-system get gateway eg # PROGRAMMED=True, ADDRESS 192.168.10.127
kubectl -n envoy-gateway-system get pods # envoy-... 2/2
kubectl -n <ns> get securitypolicy # Accepted=True
curl -s -o /dev/null -w '%{http_code}\n' http://192.168.10.127/ # 404 = serving, no route matched
```
`ss -lntp` shows **nothing** on `:80/:443` even when healthy — ServiceLB forwards via
iptables/NodePort rather than binding. Test with `curl`, not `ss`.
## Outstanding
Contour source manifests were removed from the clean baseline. Live leftovers may
still exist and must be inventoried before deletion; do not infer live state from
the archive cleanup. The legacy Git history retains the retired manifests.