feat(nginx-reverse-proxy): add custom error pages and update configuration for error handling
This commit is contained in:
@@ -24,6 +24,7 @@ locals {
|
||||
|
||||
services_config = {
|
||||
for k, v in var.services : "${k}.conf" => coalesce(v.config, templatefile("${path.module}/resources/nginx/conf.d/default.conf.tpl", {
|
||||
errors = local.error_codes
|
||||
http_port = v.http_port
|
||||
https_port = v.https_port
|
||||
upstream = v.upstream
|
||||
@@ -46,5 +47,12 @@ locals {
|
||||
|
||||
auth_script = file("${path.module}/resources/auth/auth_server.py")
|
||||
auth_script_checksum = sha256(local.auth_script)
|
||||
|
||||
error_codes = [for f in fileset("${path.module}/resources/errors", "*.html") : split(".", f)[0]]
|
||||
errors_pages = {
|
||||
for f in local.error_codes :
|
||||
"${f}.html" => file("${path.module}/resources/errors/${f}.html")
|
||||
}
|
||||
errors_pages_checksum = sha256(jsonencode(local.errors_pages))
|
||||
}
|
||||
|
||||
|
||||
@@ -102,6 +102,19 @@ resource "kubernetes_config_map" "nginx_default_config" {
|
||||
data = local.services_config
|
||||
}
|
||||
|
||||
resource "kubernetes_config_map" "nginx_custom_50x_page" {
|
||||
|
||||
metadata {
|
||||
name = "nginx-errors"
|
||||
namespace = kubernetes_namespace.reverse_proxy.metadata[0].name
|
||||
}
|
||||
|
||||
data = {
|
||||
for k, v in local.errors_pages : k => v
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
resource "kubernetes_config_map" "auth_script" {
|
||||
metadata {
|
||||
name = "auth-script"
|
||||
@@ -203,6 +216,7 @@ resource "kubernetes_deployment" "reverse_proxy" {
|
||||
"checksum/services" = local.services_config_checksum
|
||||
"checksum/wireguard" = local.wireguard_config_checksum
|
||||
"checksum/auth_server" = local.auth_script_checksum
|
||||
"checksum/custom_50x" = local.errors_pages_checksum
|
||||
}
|
||||
}
|
||||
spec {
|
||||
@@ -360,6 +374,12 @@ resource "kubernetes_deployment" "reverse_proxy" {
|
||||
read_only = true
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
name = "nginx-errors"
|
||||
mount_path = "/usr/share/nginx/html/errors"
|
||||
read_only = true
|
||||
}
|
||||
|
||||
dynamic "volume_mount" {
|
||||
for_each = length([for v in var.services : v.tls if v.tls == true]) > 0 ? [1] : []
|
||||
content {
|
||||
@@ -411,6 +431,13 @@ resource "kubernetes_deployment" "reverse_proxy" {
|
||||
}
|
||||
}
|
||||
|
||||
volume {
|
||||
name = "nginx-errors"
|
||||
config_map {
|
||||
name = kubernetes_config_map.nginx_custom_50x_page.metadata[0].name
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "volume" {
|
||||
for_each = length([for v in var.services : v.tls if v.tls == true]) > 0 ? [1] : []
|
||||
content {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!*.html
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>400 - Bad Request</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #121212;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 500px;
|
||||
}
|
||||
.icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 20px;
|
||||
color: #f39c12;
|
||||
}
|
||||
h1 { font-size: 2rem; margin-bottom: 10px; }
|
||||
p { font-size: 1rem; color: #cccccc; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="icon">❌</div>
|
||||
<h1>400 - Bad Request</h1>
|
||||
<p>Oops! The server couldn't understand your request.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>401 - Unauthorized</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #121212;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 500px;
|
||||
}
|
||||
.icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 20px;
|
||||
color: #f39c12;
|
||||
}
|
||||
h1 { font-size: 2rem; margin-bottom: 10px; }
|
||||
p { font-size: 1rem; color: #cccccc; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="icon">🔒</div>
|
||||
<h1>401 - Unauthorized</h1>
|
||||
<p>You need to log in to access this resource.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>403 - Forbidden</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #121212;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 500px;
|
||||
}
|
||||
.icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 20px;
|
||||
color: #f39c12;
|
||||
}
|
||||
h1 { font-size: 2rem; margin-bottom: 10px; }
|
||||
p { font-size: 1rem; color: #cccccc; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="icon">🚫</div>
|
||||
<h1>403 - Forbidden</h1>
|
||||
<p>You don't have permission to view this page.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>404 - Page Not Found</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #121212;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 500px;
|
||||
}
|
||||
.icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 20px;
|
||||
color: #f39c12;
|
||||
}
|
||||
h1 { font-size: 2rem; margin-bottom: 10px; }
|
||||
p { font-size: 1rem; color: #cccccc; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="icon">🔍</div>
|
||||
<h1>404 - Page Not Found</h1>
|
||||
<p>We couldn't find the page you're looking for.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>408 - Request Timeout</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #121212;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 500px;
|
||||
}
|
||||
.icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 20px;
|
||||
color: #f39c12;
|
||||
}
|
||||
h1 { font-size: 2rem; margin-bottom: 10px; }
|
||||
p { font-size: 1rem; color: #cccccc; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="icon">⏳</div>
|
||||
<h1>408 - Request Timeout</h1>
|
||||
<p>The server took too long to respond. Try again later.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>429 - Too Many Requests</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #121212;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 500px;
|
||||
}
|
||||
.icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 20px;
|
||||
color: #f39c12;
|
||||
}
|
||||
h1 { font-size: 2rem; margin-bottom: 10px; }
|
||||
p { font-size: 1rem; color: #cccccc; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="icon">🚦</div>
|
||||
<h1>429 - Too Many Requests</h1>
|
||||
<p>You're making too many requests. Please slow down.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>500 - Internal Server Error</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #121212;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 500px;
|
||||
}
|
||||
.icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 20px;
|
||||
color: #f39c12;
|
||||
}
|
||||
h1 { font-size: 2rem; margin-bottom: 10px; }
|
||||
p { font-size: 1rem; color: #cccccc; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="icon">⚠️</div>
|
||||
<h1>500 - Internal Server Error</h1>
|
||||
<p>Something went wrong on our end. We're working on it.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>502 - Bad Gateway</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #121212;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 500px;
|
||||
}
|
||||
.icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 20px;
|
||||
color: #f39c12;
|
||||
}
|
||||
h1 { font-size: 2rem; margin-bottom: 10px; }
|
||||
p { font-size: 1rem; color: #cccccc; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="icon">🔌</div>
|
||||
<h1>502 - Bad Gateway</h1>
|
||||
<p>The server received an invalid response from upstream.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>503 - Service Unavailable</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #121212;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 500px;
|
||||
}
|
||||
.icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 20px;
|
||||
color: #f39c12;
|
||||
}
|
||||
h1 { font-size: 2rem; margin-bottom: 10px; }
|
||||
p { font-size: 1rem; color: #cccccc; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="icon">🛠️</div>
|
||||
<h1>503 - Service Unavailable</h1>
|
||||
<p>We're doing some maintenance. Please check back soon.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>504 - Gateway Timeout</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #121212;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 500px;
|
||||
}
|
||||
.icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 20px;
|
||||
color: #f39c12;
|
||||
}
|
||||
h1 { font-size: 2rem; margin-bottom: 10px; }
|
||||
p { font-size: 1rem; color: #cccccc; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="icon">📡</div>
|
||||
<h1>504 - Gateway Timeout</h1>
|
||||
<p>The upstream server didn't respond in time.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -7,6 +7,14 @@ server {
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
%{ endif ~}
|
||||
server_name ${fqdn};
|
||||
# Custom error page for upstream errors
|
||||
%{ for e in errors ~}
|
||||
error_page ${e} /errors/${e}.html;
|
||||
location = /errors/${e}.html {
|
||||
root /usr/share/nginx/html;
|
||||
internal;
|
||||
}
|
||||
%{ endfor ~}
|
||||
%{ if custom_snippet != "" ~}
|
||||
${custom_snippet}
|
||||
%{ endif ~}
|
||||
@@ -40,6 +48,7 @@ location = /auth {
|
||||
%{ for k,v in l.headers ~}
|
||||
proxy_set_header ${k} ${v};
|
||||
%{ endfor ~}
|
||||
proxy_intercept_errors on;
|
||||
}
|
||||
%{ endfor ~}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user