295 lines
10 KiB
YAML
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">✓</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">✓</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>
|
|
•
|
|
<time><!--# echo var="time_iso8601" encoding="entity" --></time>
|
|
</footer>
|
|
</main>
|
|
</body>
|
|
</html>
|
|
|