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 = {
|
services_config = {
|
||||||
for k, v in var.services : "${k}.conf" => coalesce(v.config, templatefile("${path.module}/resources/nginx/conf.d/default.conf.tpl", {
|
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
|
http_port = v.http_port
|
||||||
https_port = v.https_port
|
https_port = v.https_port
|
||||||
upstream = v.upstream
|
upstream = v.upstream
|
||||||
@@ -46,5 +47,12 @@ locals {
|
|||||||
|
|
||||||
auth_script = file("${path.module}/resources/auth/auth_server.py")
|
auth_script = file("${path.module}/resources/auth/auth_server.py")
|
||||||
auth_script_checksum = sha256(local.auth_script)
|
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
|
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" {
|
resource "kubernetes_config_map" "auth_script" {
|
||||||
metadata {
|
metadata {
|
||||||
name = "auth-script"
|
name = "auth-script"
|
||||||
@@ -203,6 +216,7 @@ resource "kubernetes_deployment" "reverse_proxy" {
|
|||||||
"checksum/services" = local.services_config_checksum
|
"checksum/services" = local.services_config_checksum
|
||||||
"checksum/wireguard" = local.wireguard_config_checksum
|
"checksum/wireguard" = local.wireguard_config_checksum
|
||||||
"checksum/auth_server" = local.auth_script_checksum
|
"checksum/auth_server" = local.auth_script_checksum
|
||||||
|
"checksum/custom_50x" = local.errors_pages_checksum
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spec {
|
spec {
|
||||||
@@ -360,6 +374,12 @@ resource "kubernetes_deployment" "reverse_proxy" {
|
|||||||
read_only = true
|
read_only = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
volume_mount {
|
||||||
|
name = "nginx-errors"
|
||||||
|
mount_path = "/usr/share/nginx/html/errors"
|
||||||
|
read_only = true
|
||||||
|
}
|
||||||
|
|
||||||
dynamic "volume_mount" {
|
dynamic "volume_mount" {
|
||||||
for_each = length([for v in var.services : v.tls if v.tls == true]) > 0 ? [1] : []
|
for_each = length([for v in var.services : v.tls if v.tls == true]) > 0 ? [1] : []
|
||||||
content {
|
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" {
|
dynamic "volume" {
|
||||||
for_each = length([for v in var.services : v.tls if v.tls == true]) > 0 ? [1] : []
|
for_each = length([for v in var.services : v.tls if v.tls == true]) > 0 ? [1] : []
|
||||||
content {
|
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;
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
%{ endif ~}
|
%{ endif ~}
|
||||||
server_name ${fqdn};
|
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 != "" ~}
|
%{ if custom_snippet != "" ~}
|
||||||
${custom_snippet}
|
${custom_snippet}
|
||||||
%{ endif ~}
|
%{ endif ~}
|
||||||
@@ -40,6 +48,7 @@ location = /auth {
|
|||||||
%{ for k,v in l.headers ~}
|
%{ for k,v in l.headers ~}
|
||||||
proxy_set_header ${k} ${v};
|
proxy_set_header ${k} ${v};
|
||||||
%{ endfor ~}
|
%{ endfor ~}
|
||||||
|
proxy_intercept_errors on;
|
||||||
}
|
}
|
||||||
%{ endfor ~}
|
%{ endfor ~}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user