206 lines
7.2 KiB
Markdown
206 lines
7.2 KiB
Markdown
# Traefik error pages
|
|
|
|
A small, portable Kubernetes deployment that serves a Cloudflare-style
|
|
diagnostic page for reverse-proxy failures while preserving the original HTTP
|
|
status code.
|
|
|
|
Traefik's native Errors middleware matches status codes only; it cannot tell a
|
|
Traefik transport failure from an application's intentional JSON or HTML 5xx.
|
|
The brunner.ninja Traefik release therefore uses the local
|
|
`upstreamErrorPages` filter in `../traefik/upstream-error-pages-plugin.yaml`.
|
|
It replaces only Traefik's exact plain-text upstream failure responses and
|
|
passes application responses through unchanged.
|
|
|
|
The page reports the browser, reverse proxy, optional ForwardAuth provider, and
|
|
upstream states. Routes without ForwardAuth omit the authentication stage
|
|
entirely. Every generated response includes an `X-Error-ID` header and the same
|
|
identifier in the HTML footer.
|
|
|
|
## Repository contents
|
|
|
|
- `values.yaml`: portable defaults with authentication integration disabled.
|
|
- `values-brunner-ninja.yaml`: the live brunner.ninja branding and Authentik
|
|
integration.
|
|
- `templates/`: ConfigMap, nginx Deployment, Service, Traefik Errors middleware,
|
|
and optional ForwardAuth chain.
|
|
- `install.sh`: validates, renders, applies, and waits for a healthy rollout.
|
|
- `test.sh`: renders both the generic and brunner.ninja configurations locally.
|
|
- `ci-deployer.yaml`: restricted Gitea Actions ServiceAccount and token request.
|
|
- `create-ci-kubeconfig.sh`: bootstraps that identity and prints the one-line CI
|
|
kubeconfig secret.
|
|
- `.gitea/workflows/deploy.yml`: validates every push and deploys `main`.
|
|
- `examples/`: global and per-Ingress Traefik attachment examples.
|
|
|
|
No credential value or generated kubeconfig belongs in this repository.
|
|
|
|
## Prerequisites
|
|
|
|
- Kubernetes with a working Traefik installation.
|
|
- Traefik's `middlewares.traefik.io` CRD.
|
|
- Helm 3 or 4 for local rendering.
|
|
- kubectl access with permission to create the initial namespaced resources.
|
|
|
|
The nginx image uses a readable version tag. No registry credentials, persistent
|
|
volumes, cert-manager resources, or public Ingress are required.
|
|
|
|
## Install on brunner.ninja
|
|
|
|
Run the first installation with an administrator kubeconfig:
|
|
|
|
```sh
|
|
./install.sh
|
|
```
|
|
|
|
The script uses namespace `default` and `values-brunner-ninja.yaml` by default.
|
|
It performs a server-side dry run before changing anything, then waits for the
|
|
new pod to become Ready.
|
|
|
|
To use another namespace or values file:
|
|
|
|
```sh
|
|
NAMESPACE=traefik VALUES_FILE=values.yaml ./install.sh
|
|
```
|
|
|
|
The target namespace must already exist. If `fullnameOverride` is changed,
|
|
also pass the resulting Deployment name through `DEPLOYMENT_NAME`.
|
|
|
|
## Connect it to Traefik
|
|
|
|
Installing the chart creates the error backend and a native Errors middleware
|
|
but does not edit an independently managed Traefik release.
|
|
|
|
For brunner.ninja, install the upstream-only local plugin with the Traefik
|
|
release and attach that filter globally:
|
|
|
|
```yaml
|
|
ports:
|
|
websecure:
|
|
http:
|
|
middlewares:
|
|
- default-upstream-error-pages@kubernetescrd
|
|
```
|
|
|
|
See `../traefik/traefik-values.yaml` for the plugin mount and static plugin
|
|
registration. Restart or upgrade Traefik after changing its static entrypoint
|
|
configuration.
|
|
|
|
On clusters where replacing every application 5xx is acceptable, the native
|
|
middleware can still be attached to selected routes using the annotation shown
|
|
in `examples/ingress.yaml`, or added to an IngressRoute:
|
|
|
|
```yaml
|
|
middlewares:
|
|
- name: upstream-errors
|
|
namespace: default
|
|
```
|
|
|
|
The native middleware preserves the original status, asks this service for
|
|
`/{status}.html`, replaces the body, and returns the original 5xx code. Do not
|
|
attach it globally when routes serve APIs: it will replace their JSON 5xx
|
|
bodies too.
|
|
|
|
## Optional ForwardAuth diagnostics
|
|
|
|
Generic installations leave `auth.enabled: false`. Direct upstream failures
|
|
then render three stages and contain no authentication-provider wording.
|
|
|
|
The brunner.ninja values enable a generated chain named `authentik`:
|
|
|
|
1. `authentik-error-context` marks the request and clears spoofed completion
|
|
headers.
|
|
2. `authentik-forward` calls Authentik.
|
|
3. `authentik-error-checked` marks a successful access check.
|
|
|
|
Existing routes continue to reference:
|
|
|
|
```text
|
|
default-authentik@kubernetescrd
|
|
```
|
|
|
|
If Authentik fails, the page shows `brunner.ninja Auth` as failed and the
|
|
upstream as not accessed. If Authentik succeeds but the application fails, Auth
|
|
is shown as working. Customize these resources through the `auth` and
|
|
`branding` sections of the values file.
|
|
|
|
## Validate and update manually
|
|
|
|
Run local rendering tests before committing:
|
|
|
|
```sh
|
|
./test.sh
|
|
```
|
|
|
|
Update `values-brunner-ninja.yaml` or a template, then deploy with the same
|
|
idempotent command used for installation:
|
|
|
|
```sh
|
|
./install.sh
|
|
```
|
|
|
|
The Deployment contains a hash of the rendered ConfigMap. HTML or nginx changes
|
|
therefore create a rolling update automatically. With one replica,
|
|
`maxUnavailable: 0` keeps the old healthy pod until the replacement passes its
|
|
readiness probe. A broken nginx configuration should fail the rollout without
|
|
removing the last working pod.
|
|
|
|
Rollback by reverting the Git commit and running `./install.sh` again.
|
|
|
|
## Automatic deployment with Gitea Actions
|
|
|
|
The workflow validates all pushes and pull requests. A successful push to
|
|
`main` renders `values-brunner-ninja.yaml`, applies the fixed set of resources,
|
|
and polls the Deployment for up to two minutes.
|
|
|
|
CI deliberately does not use `helm upgrade`. It cannot read or write Helm
|
|
release Secrets. Its Role can only patch the already-created ConfigMap,
|
|
Deployment, Service, and five named Traefik Middleware objects. It cannot create
|
|
or delete workloads, read Secrets, or modify Traefik itself.
|
|
|
|
### One-time CI setup
|
|
|
|
1. Install the application manually with `./install.sh`.
|
|
2. With your administrator kubeconfig active, generate the restricted CI
|
|
kubeconfig:
|
|
|
|
```sh
|
|
./create-ci-kubeconfig.sh
|
|
```
|
|
|
|
The script applies `ci-deployer.yaml`, waits for Kubernetes to populate the
|
|
service-account token, and prints one base64 line.
|
|
|
|
3. Create the Gitea Actions repository secret `KUBE_CONFIG_BASE64` and paste
|
|
that line into it. Never add the output to a file in Git.
|
|
4. Confirm `KUBERNETES_API` and `KUBERNETES_TLS_SERVER_NAME` in
|
|
`.gitea/workflows/deploy.yml` match the networking seen by the runner.
|
|
5. Protect the `main` branch. Merging to it changes the globally used error
|
|
backend.
|
|
|
|
The committed `ci-deployer.yaml` is safe: it contains only RBAC declarations
|
|
and an empty service-account-token Secret request. Kubernetes populates the
|
|
actual credential only inside the cluster.
|
|
|
|
To revoke the CI credential:
|
|
|
|
```sh
|
|
kubectl --namespace default delete secret traefik-error-pages-deployer-token
|
|
```
|
|
|
|
Remove or replace `KUBE_CONFIG_BASE64` in Gitea immediately afterward. Running
|
|
`./create-ci-kubeconfig.sh` again recreates a token and prints a replacement
|
|
kubeconfig.
|
|
|
|
## Making another cluster-specific configuration
|
|
|
|
Copy the generic values and edit only the override:
|
|
|
|
```sh
|
|
cp values.yaml values-my-cluster.yaml
|
|
./test.sh values-my-cluster.yaml
|
|
```
|
|
|
|
In practice, keep `values.yaml` as the reusable base and create a small override
|
|
containing only branding and optional ForwardAuth settings, like
|
|
`values-brunner-ninja.yaml`. Do not put API tokens, passwords, TLS keys, or
|
|
kubeconfigs into a values file.
|