Files
feedc0de 4a68a78f0a
Validate and deploy error pages / Validate manifests (push) Failing after 25s
Validate and deploy error pages / Deploy to Kubernetes (push) Has been skipped
Initial commit
2026-08-08 20:42:38 +02:00

295 lines
10 KiB
YAML

apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "traefik-error-pages.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "traefik-error-pages.labels" . | nindent 4 }}
data:
default.conf: |
map $request_uri $error_code {
default "500";
~^/(?<error_code_from_request>5[0-9][0-9])\.html(?:\?.*)?$ $error_code_from_request;
}
map $error_code $error_title {
default "Upstream service error";
"500" "Internal server error";
"501" "Not implemented";
"502" "Bad gateway";
"503" "Service unavailable";
"504" "Gateway timeout";
"505" "HTTP version not supported";
"507" "Insufficient storage";
"508" "Loop detected";
"511" "Network authentication required";
}
# The optional ForwardAuth chain sets the first marker before access is
# checked and the second marker only after ForwardAuth succeeds.
map "$http_x_error_auth_provider:$http_x_error_auth_checked" $failure_mode {
default upstream-direct;
"{{ include "traefik-error-pages.nginxEscape" .Values.auth.marker }}:passed" upstream-authenticated;
"{{ include "traefik-error-pages.nginxEscape" .Values.auth.marker }}:" auth-failed;
}
map $failure_mode $page_title {
default $error_title;
auth-failed "Authentication service unavailable";
}
map $failure_mode $page_detail {
default "The upstream service for $host returned an error.";
auth-failed "{{ include "traefik-error-pages.nginxEscape" .Values.branding.authenticationName }} could not validate this request, so $host was not contacted.";
}
map $failure_mode $stages_class {
default "stages";
upstream-authenticated "stages four-stages";
auth-failed "stages four-stages";
}
map $failure_mode $show_auth {
default 0;
upstream-authenticated 1;
auth-failed 1;
}
map $failure_mode $auth_css {
default neutral;
upstream-authenticated ok;
auth-failed failed;
}
map $failure_mode $auth_status {
default "Not used";
upstream-authenticated "Working";
auth-failed "Error";
}
map $failure_mode $auth_detail {
default "Authentication is not used by this route";
upstream-authenticated "Access check completed";
auth-failed "Access check could not complete";
}
map $failure_mode $upstream_css {
default failed;
auth-failed neutral;
}
map $failure_mode $error_upstream_status {
default "Error";
auth-failed "Not accessed";
}
map $failure_mode $upstream_detail {
default "$host returned HTTP $error_code";
auth-failed "Blocked safely before forwarding";
}
log_format error_page escape=json
'{"time":"$time_iso8601","error_id":"$request_id","host":"$host",'
'"status":$error_code,"failure_mode":"$failure_mode","client":"$remote_addr"}';
server {
listen 8080;
server_name _;
root /usr/share/nginx/html;
server_tokens off;
charset utf-8;
etag off;
access_log /dev/stdout error_page;
error_log /dev/stderr warn;
location = /healthz {
access_log off;
default_type text/plain;
return 200 "ok\n";
}
location ~ ^/5[0-9][0-9]\.html$ {
ssi on;
try_files /error-page.html =404;
add_header Cache-Control "no-store" always;
add_header Content-Security-Policy "default-src 'none'; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'" always;
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Error-ID $request_id always;
add_header X-Robots-Tag "noindex, nofollow" always;
}
location / {
return 404;
}
}
error-page.html: |
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><!--# echo var="page_title" encoding="entity" --> | <!--# echo var="host" encoding="entity" --></title>
<style>
:root {
color-scheme: light dark;
--bg: #f7f8fa;
--card: #fff;
--text: #22252a;
--muted: #69717d;
--line: #d9dde3;
--ok: #28a745;
--failed: #d33c32;
--neutral: #9aa1aa;
--accent: #f48120;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
padding: 36px 20px;
background: var(--bg);
color: var(--text);
font: 16px/1.5 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
main { width: min(980px, 100%); }
.headline { text-align: center; margin-bottom: 42px; }
.eyebrow {
display: inline-block;
margin-bottom: 10px;
color: var(--accent);
font-size: .78rem;
font-weight: 750;
letter-spacing: .13em;
text-transform: uppercase;
}
h1 { margin: 0 0 10px; font-size: clamp(2rem, 5vw, 3.25rem); line-height: 1.08; }
.headline p { max-width: 680px; margin: 0 auto; color: var(--muted); font-size: 1.08rem; }
.code { white-space: nowrap; }
.stages {
position: relative;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
.four-stages { grid-template-columns: repeat(4, 1fr); }
.stages::before {
content: "";
position: absolute;
top: 39px;
left: 16.666%;
right: 16.666%;
height: 2px;
background: var(--line);
}
.four-stages::before { left: 12.5%; right: 12.5%; }
.stage {
position: relative;
z-index: 1;
min-width: 0;
padding: 0 12px 20px;
text-align: center;
}
.icon {
width: 80px;
height: 80px;
display: grid;
place-items: center;
margin: 0 auto 18px;
border: 3px solid currentColor;
border-radius: 50%;
background: var(--card);
box-shadow: 0 0 0 8px var(--bg);
font-size: 2rem;
font-weight: 800;
}
.ok { color: var(--ok); }
.failed { color: var(--failed); }
.neutral { color: var(--neutral); }
.stage h2 { margin: 0; color: var(--text); font-size: 1.05rem; overflow-wrap: anywhere; }
.role { display: block; margin: 2px 0 8px; color: var(--muted); font-size: .82rem; }
.state { display: block; font-weight: 750; }
.detail { display: block; margin-top: 4px; color: var(--muted); font-size: .78rem; overflow-wrap: anywhere; }
footer {
margin-top: 42px;
padding-top: 20px;
border-top: 1px solid var(--line);
color: var(--muted);
text-align: center;
font-size: .82rem;
}
footer code { color: var(--text); font-family: ui-monospace, SFMono-Regular, Consolas, monospace; }
@media (max-width: 720px) {
.stages { grid-template-columns: 1fr 1fr; row-gap: 26px; }
.stages::before { display: none; }
.headline { margin-bottom: 34px; }
}
@media (max-width: 430px) {
.stages { grid-template-columns: 1fr; }
.stage { display: grid; grid-template-columns: 64px 1fr; column-gap: 16px; text-align: left; }
.icon { grid-row: 1 / 5; width: 64px; height: 64px; margin: 0; font-size: 1.5rem; }
}
@media (prefers-color-scheme: dark) {
:root { --bg: #17191d; --card: #22252a; --text: #f0f1f3; --muted: #a9b0ba; --line: #3a3f47; }
}
</style>
</head>
<body>
<main>
<header class="headline">
<span class="eyebrow">HTTP <span class="code"><!--# echo var="error_code" encoding="entity" --></span></span>
<h1><!--# echo var="page_title" encoding="entity" --></h1>
<p><!--# echo var="page_detail" encoding="entity" --></p>
</header>
<section class="<!--# echo var="stages_class" encoding="none" -->" aria-label="Request status">
<article class="stage">
<div class="icon ok" aria-hidden="true">&#10003;</div>
<h2>You</h2>
<span class="role">Browser</span>
<span class="state ok">Working</span>
<span class="detail">Connected successfully</span>
</article>
<article class="stage">
<div class="icon ok" aria-hidden="true">&#10003;</div>
<h2>{{ .Values.branding.reverseProxyName | html }}</h2>
<span class="role">Reverse proxy</span>
<span class="state ok">Working</span>
<span class="detail">{{ .Values.branding.clusterReachedText | html }}</span>
</article>
<!--# if expr="$show_auth = 1" -->
<article class="stage">
<div class="icon <!--# echo var="auth_css" encoding="none" -->" aria-hidden="true">!</div>
<h2>{{ .Values.branding.authenticationName | html }}</h2>
<span class="role">Access control</span>
<span class="state <!--# echo var="auth_css" encoding="none" -->"><!--# echo var="auth_status" encoding="entity" --></span>
<span class="detail"><!--# echo var="auth_detail" encoding="entity" --></span>
</article>
<!--# endif -->
<article class="stage">
<div class="icon <!--# echo var="upstream_css" encoding="none" -->" aria-hidden="true">!</div>
<h2><!--# echo var="host" encoding="entity" --></h2>
<span class="role">Upstream service</span>
<span class="state <!--# echo var="upstream_css" encoding="none" -->"><!--# echo var="error_upstream_status" encoding="entity" --></span>
<span class="detail"><!--# echo var="upstream_detail" encoding="entity" --></span>
</article>
</section>
<footer>
Error ID: <code><!--# echo var="request_id" encoding="entity" --></code>
&nbsp;&bull;&nbsp;
<time><!--# echo var="time_iso8601" encoding="entity" --></time>
</footer>
</main>
</body>
</html>