diff --git a/modules/kubernetes/actualbudget/factory/main.tf b/modules/kubernetes/actualbudget/factory/main.tf
index 315fed2f..d76a7f94 100644
--- a/modules/kubernetes/actualbudget/factory/main.tf
+++ b/modules/kubernetes/actualbudget/factory/main.tf
@@ -93,11 +93,7 @@ module "ingress" {
namespace = "actualbudget"
name = "budget-${var.name}"
tls_secret_name = var.tls_secret_name
- extra_annotations = {
- "nginx.ingress.kubernetes.io/proxy-body-size" : "0",
- "nginx.ingress.kubernetes.io/client-max-body-size" : "0"
- }
- rybbit_site_id = "3e6b6b68088a"
+ rybbit_site_id = "3e6b6b68088a"
}
diff --git a/modules/kubernetes/affine/main.tf b/modules/kubernetes/affine/main.tf
index 73020279..2e1a482b 100644
--- a/modules/kubernetes/affine/main.tf
+++ b/modules/kubernetes/affine/main.tf
@@ -211,7 +211,4 @@ module "ingress" {
name = "affine"
tls_secret_name = var.tls_secret_name
max_body_size = "500m"
- extra_annotations = {
- "nginx.ingress.kubernetes.io/proxy-body-size" : "500m"
- }
}
diff --git a/modules/kubernetes/audiobookshelf/main.tf b/modules/kubernetes/audiobookshelf/main.tf
index 62d1207b..2a42eb2a 100644
--- a/modules/kubernetes/audiobookshelf/main.tf
+++ b/modules/kubernetes/audiobookshelf/main.tf
@@ -129,10 +129,6 @@ module "ingress" {
namespace = kubernetes_namespace.audiobookshelf.metadata[0].name
name = "audiobookshelf"
tls_secret_name = var.tls_secret_name
- extra_annotations = {
- "nginx.ingress.kubernetes.io/proxy-body-size" : "0",
- "nginx.ingress.kubernetes.io/client-max-body-size" : "0"
- }
- rybbit_site_id = "b38fda4285df"
+ rybbit_site_id = "b38fda4285df"
}
diff --git a/modules/kubernetes/authentik/main.tf b/modules/kubernetes/authentik/main.tf
index 483e3ef0..71730e21 100644
--- a/modules/kubernetes/authentik/main.tf
+++ b/modules/kubernetes/authentik/main.tf
@@ -40,11 +40,13 @@ resource "kubernetes_ingress_v1" "authentik" {
name = "authentik"
namespace = kubernetes_namespace.authentik.metadata[0].name
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["authentik.viktorbarzin.me"]
secret_name = var.tls_secret_name
diff --git a/modules/kubernetes/blog/main.tf b/modules/kubernetes/blog/main.tf
index eef7860e..afe3fb73 100644
--- a/modules/kubernetes/blog/main.tf
+++ b/modules/kubernetes/blog/main.tf
@@ -113,26 +113,13 @@ resource "kubernetes_ingress_v1" "blog" {
name = "blog-ingress"
namespace = kubernetes_namespace.website.metadata[0].name
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
- "nginx.ingress.kubernetes.io/configuration-snippet" = <<-EOT
- # Only modify HTML
- sub_filter_types text/html;
- sub_filter_once off;
-
- # Disable compression so sub_filter works
- proxy_set_header Accept-Encoding "";
-
- # Inject analytics before
- sub_filter '' '
-
- ';
- EOT
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd,website-rybbit-analytics@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["viktorbarzin.me"]
secret_name = var.tls_secret_name
@@ -171,3 +158,25 @@ resource "kubernetes_ingress_v1" "blog" {
}
}
}
+
+# Rybbit analytics middleware for blog
+resource "kubernetes_manifest" "rybbit_analytics" {
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "Middleware"
+ metadata = {
+ name = "rybbit-analytics"
+ namespace = kubernetes_namespace.website.metadata[0].name
+ }
+ spec = {
+ plugin = {
+ rewritebody = {
+ rewrites = [{
+ regex = ""
+ replacement = ""
+ }]
+ }
+ }
+ }
+ }
+}
diff --git a/modules/kubernetes/calibre/main.tf b/modules/kubernetes/calibre/main.tf
index 32594172..daa5fceb 100644
--- a/modules/kubernetes/calibre/main.tf
+++ b/modules/kubernetes/calibre/main.tf
@@ -224,8 +224,6 @@ module "ingress" {
name = "calibre"
tls_secret_name = var.tls_secret_name
extra_annotations = {
- "nginx.ingress.kubernetes.io/proxy-body-size" : "5000m"
-
"gethomepage.dev/enabled" = "true"
"gethomepage.dev/description" = "Book library"
# gethomepage.dev/group: Media
@@ -239,10 +237,8 @@ module "ingress" {
# gethomepage.dev/weight: 10 # optional
# gethomepage.dev/instance: "public" # optional
}
- rybbit_site_id = "17a5c7fbb077"
- additional_configuration_snippet = <<-EOF
- more_set_headers "Content-Security-Policy: script-src 'self' 'unsafe-inline' 'unsafe-eval' https://rybbit.viktorbarzin.me";
- EOF
+ rybbit_site_id = "17a5c7fbb077"
+ custom_content_security_policy = "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://rybbit.viktorbarzin.me"
}
# Stacks - Anna's Archive Download Manager
diff --git a/modules/kubernetes/crowdsec/main.tf b/modules/kubernetes/crowdsec/main.tf
index e914beb7..e272bcd5 100644
--- a/modules/kubernetes/crowdsec/main.tf
+++ b/modules/kubernetes/crowdsec/main.tf
@@ -181,25 +181,13 @@ resource "kubernetes_service" "crowdsec-web" {
}
}
module "ingress" {
- source = "../ingress_factory"
- namespace = kubernetes_namespace.crowdsec.metadata[0].name
- name = "crowdsec-web"
- protected = true
- tls_secret_name = var.tls_secret_name
- extra_annotations = {
- # "crowdsec.io/bouncer-mode" : "bypass"
- "nginx.ingress.kubernetes.io/server-snippet" : <<-EOF
- # --- Disable CrowdSec for this host ---
- set $crowdsec_bypass 1;
- access_by_lua_block {
- -- Skip calling CrowdSec for this server
- if ngx.var.crowdsec_bypass == "1" then
- return
- end
- }
- EOF
- }
- rybbit_site_id = "d09137795ccc"
+ source = "../ingress_factory"
+ namespace = kubernetes_namespace.crowdsec.metadata[0].name
+ name = "crowdsec-web"
+ protected = true
+ tls_secret_name = var.tls_secret_name
+ exclude_crowdsec = true
+ rybbit_site_id = "d09137795ccc"
}
# CronJob to import public blocklists into CrowdSec
diff --git a/modules/kubernetes/dawarich/main.tf b/modules/kubernetes/dawarich/main.tf
index 215b18ef..aeb345e6 100644
--- a/modules/kubernetes/dawarich/main.tf
+++ b/modules/kubernetes/dawarich/main.tf
@@ -323,16 +323,5 @@ module "ingress" {
namespace = kubernetes_namespace.dawarich.metadata[0].name
name = "dawarich"
tls_secret_name = var.tls_secret_name
- extra_annotations = {
- "nginx.ingress.kubernetes.io/limit-connections" : 100
- "nginx.ingress.kubernetes.io/limit-rps" : 50
- "nginx.ingress.kubernetes.io/limit-rpm" : 1000
- "nginx.ingress.kubernetes.io/limit-burst-multiplier" : 500
- "nginx.ingress.kubernetes.io/limit-rate-after" : 1000
- "nginx.ingress.kubernetes.io/configuration-snippet" = <<-EOF
- limit_req_status 429;
- limit_conn_status 429;
- EOF
- }
- rybbit_site_id = "0abfd409f2fb"
+ rybbit_site_id = "0abfd409f2fb"
}
diff --git a/modules/kubernetes/discount-bandit/main.tf b/modules/kubernetes/discount-bandit/main.tf
index 1d605a84..b3e4b140 100644
--- a/modules/kubernetes/discount-bandit/main.tf
+++ b/modules/kubernetes/discount-bandit/main.tf
@@ -103,11 +103,13 @@ resource "kubernetes_ingress_v1" "discount-bandit" {
name = "discount-bandit"
namespace = kubernetes_namespace.discount-bandit.metadata[0].name
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["discount.viktorbarzin.me"]
secret_name = var.tls_secret_name
diff --git a/modules/kubernetes/ebook2audiobook/main.tf b/modules/kubernetes/ebook2audiobook/main.tf
index cb184db6..00820a7a 100644
--- a/modules/kubernetes/ebook2audiobook/main.tf
+++ b/modules/kubernetes/ebook2audiobook/main.tf
@@ -405,6 +405,5 @@ module "audiblez-web-ingress" {
tls_secret_name = var.tls_secret_name
protected = true
max_body_size = "500m" # Allow large EPUB uploads
- proxy_timeout = 3600 # Long timeout for conversions
}
diff --git a/modules/kubernetes/excalidraw/main.tf b/modules/kubernetes/excalidraw/main.tf
index c07a06e7..99d6b14a 100644
--- a/modules/kubernetes/excalidraw/main.tf
+++ b/modules/kubernetes/excalidraw/main.tf
@@ -103,7 +103,4 @@ module "ingress" {
name = "draw"
tls_secret_name = var.tls_secret_name
protected = true
- extra_annotations = {
- "nginx.ingress.kubernetes.io/auth-response-headers" = "X-authentik-username,X-authentik-email,X-authentik-name"
- }
}
diff --git a/modules/kubernetes/f1-stream/main.tf b/modules/kubernetes/f1-stream/main.tf
index 24caf9da..dd662925 100644
--- a/modules/kubernetes/f1-stream/main.tf
+++ b/modules/kubernetes/f1-stream/main.tf
@@ -87,9 +87,5 @@ module "ingress" {
namespace = kubernetes_namespace.f1-stream.metadata[0].name
name = "f1"
tls_secret_name = var.tls_secret_name
- extra_annotations = {
- "nginx.ingress.kubernetes.io/force-ssl-redirect" : "false"
- "nginx.ingress.kubernetes.io/ssl-redirect" : "false"
- }
- rybbit_site_id = "7e69786f66d5"
+ rybbit_site_id = "7e69786f66d5"
}
diff --git a/modules/kubernetes/finance_app/main.tf b/modules/kubernetes/finance_app/main.tf
index b2539e4d..9f21f0ec 100644
--- a/modules/kubernetes/finance_app/main.tf
+++ b/modules/kubernetes/finance_app/main.tf
@@ -252,16 +252,13 @@ resource "kubernetes_ingress_v1" "finance_app" {
name = "finance-app"
namespace = kubernetes_namespace.finance_app.metadata[0].name
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
- #"nginx.ingress.kubernetes.io/auth-url"= "https://oauth-provider/auth"
- #"nginx.ingress.kubernetes.io/auth-signin"= "https://oauth-provider/sign_in?rd=$request_uri"
- "nginx.ingress.kubernetes.io/proxy-connect-timeout" = "600"
- "nginx.ingress.kubernetes.io/proxy-send-timeout" = "600"
- "nginx.ingress.kubernetes.io/proxy-read-timeout" = "600"
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["finance.viktorbarzin.me"]
secret_name = var.tls_secret_name
diff --git a/modules/kubernetes/forgejo/main.tf b/modules/kubernetes/forgejo/main.tf
index b1960ff0..dd460203 100644
--- a/modules/kubernetes/forgejo/main.tf
+++ b/modules/kubernetes/forgejo/main.tf
@@ -99,7 +99,4 @@ module "ingress" {
namespace = kubernetes_namespace.forgejo.metadata[0].name
name = "forgejo"
tls_secret_name = var.tls_secret_name
- extra_annotations = {
- "nginx.ingress.kubernetes.io/proxy-body-size" : "20000m"
- }
}
diff --git a/modules/kubernetes/frigate/main.tf b/modules/kubernetes/frigate/main.tf
index 32322951..1d8b962a 100644
--- a/modules/kubernetes/frigate/main.tf
+++ b/modules/kubernetes/frigate/main.tf
@@ -196,20 +196,7 @@ module "ingress" {
name = "frigate"
tls_secret_name = var.tls_secret_name
protected = true
- extra_annotations = {
- "nginx.ingress.kubernetes.io/proxy-body-size" : "20000m"
- # Websockets
- "nginx.org/websocket-services" : "frigate"
- "nginx.ingress.kubernetes.io/proxy-set-header" : "Upgrade $http_upgrade"
- "nginx.ingress.kubernetes.io/proxy-set-header" : "Connection $connection_upgrade"
- "nginx.ingress.kubernetes.io/proxy-redirect-from" : "off"
-
- "nginx.ingress.kubernetes.io/limit-rps" : 50000
- "nginx.ingress.kubernetes.io/limit-rpm" : 1000000
- "nginx.ingress.kubernetes.io/limit-burst-multiplier" : 50000
- "nginx.ingress.kubernetes.io/limit-rate-after" : 100000
- }
- rybbit_site_id = "0d4044069ff5"
+ rybbit_site_id = "0d4044069ff5"
}
module "ingress-internal" {
@@ -222,17 +209,4 @@ module "ingress-internal" {
tls_secret_name = var.tls_secret_name
allow_local_access_only = true
ssl_redirect = false
- extra_annotations = {
- "nginx.ingress.kubernetes.io/proxy-body-size" : "20000m"
- # Websockets
- "nginx.org/websocket-services" : "frigate"
- "nginx.ingress.kubernetes.io/proxy-set-header" : "Upgrade $http_upgrade"
- "nginx.ingress.kubernetes.io/proxy-set-header" : "Connection $connection_upgrade"
- "nginx.ingress.kubernetes.io/proxy-redirect-from" : "off"
-
- "nginx.ingress.kubernetes.io/limit-rps" : 50000
- "nginx.ingress.kubernetes.io/limit-rpm" : 1000000
- "nginx.ingress.kubernetes.io/limit-burst-multiplier" : 50000
- "nginx.ingress.kubernetes.io/limit-rate-after" : 100000
- }
}
diff --git a/modules/kubernetes/hackmd/main.tf b/modules/kubernetes/hackmd/main.tf
index e8bbdaed..1e21b003 100644
--- a/modules/kubernetes/hackmd/main.tf
+++ b/modules/kubernetes/hackmd/main.tf
@@ -150,7 +150,4 @@ module "ingress" {
namespace = kubernetes_namespace.hackmd.metadata[0].name
name = "hackmd"
tls_secret_name = var.tls_secret_name
- extra_annotations = {
- "nginx.ingress.kubernetes.io/proxy-body-size" : "20000m"
- }
}
diff --git a/modules/kubernetes/home_assistant/main.tf b/modules/kubernetes/home_assistant/main.tf
index 38a9119b..8d025efa 100644
--- a/modules/kubernetes/home_assistant/main.tf
+++ b/modules/kubernetes/home_assistant/main.tf
@@ -206,14 +206,14 @@ resource "kubernetes_ingress_v1" "home-assistant-ui" {
name = "home-assistant-ui-ingress"
namespace = kubernetes_namespace.home_assistant.metadata[0].name
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
- "nginx.ingress.kubernetes.io/force-ssl-redirect" = "true"
- "nginx.ingress.kubernetes.io/auth-tls-verify-client" = "on"
- "nginx.ingress.kubernetes.io/auth-tls-secret" = var.client_certificate_secret_name
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
+ "traefik.ingress.kubernetes.io/router.tls.options" = "traefik-mtls@kubernetescrd"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["home-assistant.viktorbarzin.me"]
secret_name = var.tls_secret_name
diff --git a/modules/kubernetes/homepage/values.yaml b/modules/kubernetes/homepage/values.yaml
index 00234f49..121ee4ea 100644
--- a/modules/kubernetes/homepage/values.yaml
+++ b/modules/kubernetes/homepage/values.yaml
@@ -45,7 +45,7 @@ ingress:
gethomepage.dev/description: "A modern, secure, highly customizable application dashboard."
gethomepage.dev/group: "A New Group"
gethomepage.dev/icon: "homepage.png"
- ingressClassName: "nginx"
+ ingressClassName: "traefik"
hosts:
- host: &host "home.viktorbarzin.me"
paths:
diff --git a/modules/kubernetes/immich/main.tf b/modules/kubernetes/immich/main.tf
index 3a833362..cd785535 100644
--- a/modules/kubernetes/immich/main.tf
+++ b/modules/kubernetes/immich/main.tf
@@ -460,66 +460,8 @@ resource "kubernetes_ingress_v1" "ingress" {
namespace = kubernetes_namespace.immich.metadata[0].name
name = "immich"
annotations = {
- # NOTE: when changing - test video playback from mobile and web!
- # Easy to break!
-
- "kubernetes.io/ingress.class" = "nginx"
- "nginx.ingress.kubernetes.io/backend-protocol" = "HTTP"
-
- # As per https://immich.app/docs/administration/reverse-proxy
- "nginx.org/websocket-services" : "immich-server"
- # Websockets
- "nginx.ingress.kubernetes.io/proxy-set-header" : "Upgrade $http_upgrade"
- "nginx.ingress.kubernetes.io/proxy-set-header" : "Connection $connection_upgrade" # this makes a difference for web!!!
- "nginx.ingress.kubernetes.io/proxy-redirect-from" : "off"
- # Timeouts
- "nginx.ingress.kubernetes.io/proxy-read-timeout" : "6000s",
- "nginx.ingress.kubernetes.io/proxy-send-timeout" : "6000s",
-
- "nginx.ingress.kubernetes.io/proxy-connect-timeout" : "60s"
-
- # Allow big uploads
- "nginx.ingress.kubernetes.io/proxy-body-size" : "0"
- "nginx.ingress.kubernetes.io/proxy-buffering" : "off"
- "nginx.ingress.kubernetes.io/proxy-request-buffering" : "off"
- "nginx.ingress.kubernetes.io/proxy-http-version" : "1.1"
- # "nginx.ingress.kubernetes.io/client-body-buffer-size" : "512m"
- # "nginx.ingress.kubernetes.io/proxy-buffers-number" : "4"
-
- # More lenient DDOS protection as to not confuse with image loading
- "nginx.ingress.kubernetes.io/limit-connections" : 5000
- "nginx.ingress.kubernetes.io/limit-rps" : 100
- "nginx.ingress.kubernetes.io/limit-rpm" : 6000
- "nginx.ingress.kubernetes.io/limit-burst-multiplier" : 10
-
- # good for downloading big files - https://www.pdxdev.com/nginx-content-delivery/configuring-nginx-for-large-file-transfers/
- "nginx.ingress.kubernetes.io/configuration-snippet" : <
- sub_filter '' '
-
- ';
- EOF
-
- "nginx.ingress.kubernetes.io/enable-modsecurity" : "false" # this is important!!!; setting it to true enables buffering and can lead to ooms when ploading big files
- "nginx.ingress.kubernetes.io/enable-owasp-modsecurity-crs" : "false"
-
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-immich-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd,immich-rybbit-analytics@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
"gethomepage.dev/enabled" = "true"
"gethomepage.dev/description" = "Photos library"
@@ -533,6 +475,7 @@ resource "kubernetes_ingress_v1" "ingress" {
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["immich.viktorbarzin.me"]
secret_name = var.tls_secret_name
@@ -724,3 +667,25 @@ resource "kubernetes_cron_job_v1" "postgresql-backup" {
# protected = true
# }
+# Rybbit analytics middleware for Immich
+resource "kubernetes_manifest" "rybbit_analytics" {
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "Middleware"
+ metadata = {
+ name = "rybbit-analytics"
+ namespace = kubernetes_namespace.immich.metadata[0].name
+ }
+ spec = {
+ plugin = {
+ rewritebody = {
+ rewrites = [{
+ regex = ""
+ replacement = ""
+ }]
+ }
+ }
+ }
+ }
+}
+
diff --git a/modules/kubernetes/jellyfin/main.tf b/modules/kubernetes/jellyfin/main.tf
index eb7decb7..eb1dfb74 100644
--- a/modules/kubernetes/jellyfin/main.tf
+++ b/modules/kubernetes/jellyfin/main.tf
@@ -113,12 +113,13 @@ resource "kubernetes_ingress_v1" "jellyfin" {
name = "jellyfin"
namespace = kubernetes_namespace.jellyfin.metadata[0].name
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
- "nginx.ingress.kubernetes.io/proxy-body-size" : "5000m"
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["jellyfin.viktorbarzin.me"]
secret_name = var.tls_secret_name
diff --git a/modules/kubernetes/kafka/main.tf b/modules/kubernetes/kafka/main.tf
index 51e15d5c..a9cd605f 100644
--- a/modules/kubernetes/kafka/main.tf
+++ b/modules/kubernetes/kafka/main.tf
@@ -110,14 +110,14 @@ resource "kubernetes_ingress_v1" "kafka-ui" {
name = "kafka-ui-ingress"
namespace = kubernetes_namespace.kafka.metadata[0].name
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
- "nginx.ingress.kubernetes.io/force-ssl-redirect" = "true"
- "nginx.ingress.kubernetes.io/auth-tls-verify-client" = "on"
- "nginx.ingress.kubernetes.io/auth-tls-secret" = var.client_certificate_secret_name
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
+ "traefik.ingress.kubernetes.io/router.tls.options" = "traefik-mtls@kubernetescrd"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["kafka.viktorbarzin.me"]
secret_name = var.tls_secret_name
diff --git a/modules/kubernetes/monitoring/grafana_chart_values.yaml b/modules/kubernetes/monitoring/grafana_chart_values.yaml
index 1560bb12..9a32eece 100644
--- a/modules/kubernetes/monitoring/grafana_chart_values.yaml
+++ b/modules/kubernetes/monitoring/grafana_chart_values.yaml
@@ -6,14 +6,10 @@ persistence:
existingClaim: "grafana-pvc"
ingress:
enabled: "true"
+ ingressClassName: "traefik"
annotations:
- kubernetes.io/ingress.class: nginx
- # nginx.ingress.kubernetes.io/auth-url: "https://oauth2.viktorbarzin.me/oauth2/auth"
- # nginx.ingress.kubernetes.io/auth-signin: "https://oauth2.viktorbarzin.me/oauth2/start?rd=/redirect/$http_host$escaped_request_uri"
- nginx.ingress.kubernetes.io/auth-url: "http://ak-outpost-authentik-embedded-outpost.authentik.svc.cluster.local:9000/outpost.goauthentik.io/auth/nginx"
- nginx.ingress.kubernetes.io/auth-signin: "https://authentik.viktorbarzin.me/outpost.goauthentik.io/start?rd=$scheme%3A%2F%2F$host$escaped_request_uri"
- nginx.ingress.kubernetes.io/auth-response-headers: "Set-Cookie,X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid"
- nginx.ingress.kubernetes.io/auth-snippet: "proxy_set_header X-Forwarded-Host $http_host;"
+ traefik.ingress.kubernetes.io/router.middlewares: "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd,traefik-authentik-forward-auth@kubernetescrd"
+ traefik.ingress.kubernetes.io/router.entrypoints: "websecure"
tls:
- secretName: "tls-secret"
hosts:
diff --git a/modules/kubernetes/monitoring/main.tf b/modules/kubernetes/monitoring/main.tf
index 12fadc03..6cf08256 100644
--- a/modules/kubernetes/monitoring/main.tf
+++ b/modules/kubernetes/monitoring/main.tf
@@ -75,17 +75,36 @@ resource "kubernetes_cron_job_v1" "monitor_prom" {
}
}
+resource "kubernetes_manifest" "status_redirect_middleware" {
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "Middleware"
+ metadata = {
+ name = "status-redirect"
+ namespace = kubernetes_namespace.monitoring.metadata[0].name
+ }
+ spec = {
+ redirectRegex = {
+ regex = ".*"
+ replacement = "https://hetrixtools.com/r/38981b548b5d38b052aca8d01285a3f3/"
+ permanent = true
+ }
+ }
+ }
+}
+
resource "kubernetes_ingress_v1" "status" {
metadata {
name = "hetrix-redirect-ingress"
namespace = kubernetes_namespace.monitoring.metadata[0].name
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
- "nginx.ingress.kubernetes.io/permanent-redirect" = "https://hetrixtools.com/r/38981b548b5d38b052aca8d01285a3f3/"
+ "traefik.ingress.kubernetes.io/router.middlewares" = "monitoring-status-redirect@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["status.viktorbarzin.me"]
secret_name = var.tls_secret_name
@@ -99,7 +118,7 @@ resource "kubernetes_ingress_v1" "status" {
service {
name = "not-used"
port {
- number = 80 # redirected by annotation
+ number = 80 # redirected by middleware
}
}
}
@@ -109,17 +128,36 @@ resource "kubernetes_ingress_v1" "status" {
}
}
+resource "kubernetes_manifest" "yotovski_redirect_middleware" {
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "Middleware"
+ metadata = {
+ name = "yotovski-redirect"
+ namespace = kubernetes_namespace.monitoring.metadata[0].name
+ }
+ spec = {
+ redirectRegex = {
+ regex = ".*"
+ replacement = "https://hetrixtools.com/r/2ba9d7a5e017794db0fd91f0115a8b3b/"
+ permanent = true
+ }
+ }
+ }
+}
+
resource "kubernetes_ingress_v1" "status_yotovski" {
metadata {
name = "hetrix-yotovski-redirect-ingress"
namespace = kubernetes_namespace.monitoring.metadata[0].name
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
- "nginx.ingress.kubernetes.io/permanent-redirect" = "https://hetrixtools.com/r/2ba9d7a5e017794db0fd91f0115a8b3b/"
+ "traefik.ingress.kubernetes.io/router.middlewares" = "monitoring-yotovski-redirect@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["yotovski-status.viktorbarzin.me"]
secret_name = var.tls_secret_name
@@ -131,7 +169,7 @@ resource "kubernetes_ingress_v1" "status_yotovski" {
path = "/"
backend {
service {
- name = "not-used" # redirected by annotation
+ name = "not-used" # redirected by middleware
port {
number = 80
}
diff --git a/modules/kubernetes/monitoring/prometheus_chart_values.tpl b/modules/kubernetes/monitoring/prometheus_chart_values.tpl
index c65e9358..c869dc3e 100755
--- a/modules/kubernetes/monitoring/prometheus_chart_values.tpl
+++ b/modules/kubernetes/monitoring/prometheus_chart_values.tpl
@@ -11,19 +11,10 @@ alertmanager:
baseURL: "https://alertmanager.viktorbarzin.me"
ingress:
enabled: true
+ ingressClassName: "traefik"
annotations:
- kubernetes.io/ingress.class: nginx
- nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
- # Enable client certificate authentication
- # nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
- # Create the secret containing the trusted ca certificates
- # nginx.ingress.kubernetes.io/auth-tls-secret: "default/ca-secret"
- # nginx.ingress.kubernetes.io/auth-url: "https://oauth2.viktorbarzin.me/oauth2/auth"
- # nginx.ingress.kubernetes.io/auth-signin: "https://oauth2.viktorbarzin.me/oauth2/start?rd=/redirect/$http_host$escaped_request_uri"
- nginx.ingress.kubernetes.io/auth-url: "http://ak-outpost-authentik-embedded-outpost.authentik.svc.cluster.local:9000/outpost.goauthentik.io/auth/nginx"
- nginx.ingress.kubernetes.io/auth-signin: "https://authentik.viktorbarzin.me/outpost.goauthentik.io/start?rd=$scheme%3A%2F%2F$host$escaped_request_uri"
- nginx.ingress.kubernetes.io/auth-response-headers: "Set-Cookie,X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid"
- nginx.ingress.kubernetes.io/auth-snippet: "proxy_set_header X-Forwarded-Host $http_host;"
+ traefik.ingress.kubernetes.io/router.middlewares: "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd,traefik-authentik-forward-auth@kubernetescrd"
+ traefik.ingress.kubernetes.io/router.entrypoints: "websecure"
tls:
- secretName: "tls-secret"
hosts:
@@ -100,19 +91,10 @@ server:
mountPath: /data/wal # Standard path for the chart
ingress:
enabled: true
+ ingressClassName: "traefik"
annotations:
- kubernetes.io/ingress.class: nginx
- nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
- # Enable client certificate authentication
- # nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
- # Create the secret containing the trusted ca certificates
- # nginx.ingress.kubernetes.io/auth-tls-secret: "default/ca-secret"
- # nginx.ingress.kubernetes.io/auth-url: "https://oauth2.viktorbarzin.me/oauth2/auth"
- # nginx.ingress.kubernetes.io/auth-signin: "https://oauth2.viktorbarzin.me/oauth2/start?rd=/redirect/$http_host$escaped_request_uri"
- "nginx.ingress.kubernetes.io/auth-url": "http://ak-outpost-authentik-embedded-outpost.authentik.svc.cluster.local:9000/outpost.goauthentik.io/auth/nginx"
- "nginx.ingress.kubernetes.io/auth-signin": "https://authentik.viktorbarzin.me/outpost.goauthentik.io/start?rd=$scheme%3A%2F%2F$host$escaped_request_uri"
- "nginx.ingress.kubernetes.io/auth-response-headers": "Set-Cookie,X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid"
- "nginx.ingress.kubernetes.io/auth-snippet": "proxy_set_header X-Forwarded-Host $http_host;"
+ traefik.ingress.kubernetes.io/router.middlewares: "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd,traefik-authentik-forward-auth@kubernetescrd"
+ traefik.ingress.kubernetes.io/router.entrypoints: "websecure"
gethomepage.dev/enabled: "true"
gethomepage.dev/description: "Prometheus"
diff --git a/modules/kubernetes/n8n/main.tf b/modules/kubernetes/n8n/main.tf
index 77c06fe4..6a7b59a8 100644
--- a/modules/kubernetes/n8n/main.tf
+++ b/modules/kubernetes/n8n/main.tf
@@ -134,7 +134,4 @@ module "ingress" {
namespace = kubernetes_namespace.n8n.metadata[0].name
name = "n8n"
tls_secret_name = var.tls_secret_name
- extra_annotations = {
- "nginx.ingress.kubernetes.io/proxy-body-size" : "20000m"
- }
}
diff --git a/modules/kubernetes/nextcloud/main.tf b/modules/kubernetes/nextcloud/main.tf
index fc8c854b..a35828e3 100755
--- a/modules/kubernetes/nextcloud/main.tf
+++ b/modules/kubernetes/nextcloud/main.tf
@@ -154,13 +154,7 @@ module "ingress" {
name = "nextcloud"
tls_secret_name = var.tls_secret_name
port = 8080
- extra_annotations = {
- "nginx.ingress.kubernetes.io/client-max-body-size" : "0"
- "nginx.ingress.kubernetes.io/proxy-body-size" : "0",
- "nginx.ingress.kubernetes.io/limit-rps" : 1000 # Increased to allow webdav syncing
- "nginx.ingress.kubernetes.io/limit-rpm" : 60000
- }
- rybbit_site_id = "5a3bfe59a3fe"
+ rybbit_site_id = "5a3bfe59a3fe"
}
module "whiteboard_ingress" {
@@ -169,18 +163,6 @@ module "whiteboard_ingress" {
name = "whiteboard"
tls_secret_name = var.tls_secret_name
port = 80
- extra_annotations = {
- "nginx.ingress.kubernetes.io/client-max-body-size" : "0"
- "nginx.ingress.kubernetes.io/proxy-body-size" : "0",
-
- # Websockets
- # "nginx.ingress.kubernetes.io/proxy-set-header" : "Upgrade $http_upgrade"
- # "nginx.ingress.kubernetes.io/proxy-set-header" : "Connection $connection_upgrade" # this makes a difference for web!!!
-
- # Timeouts
- "nginx.ingress.kubernetes.io/proxy-read-timeout" : "6000s",
- "nginx.ingress.kubernetes.io/proxy-send-timeout" : "6000s",
- }
}
resource "kubernetes_config_map" "backup-script" {
diff --git a/modules/kubernetes/nginx-ingress/main.tf b/modules/kubernetes/nginx-ingress/main.tf
deleted file mode 100644
index ab511f7a..00000000
--- a/modules/kubernetes/nginx-ingress/main.tf
+++ /dev/null
@@ -1,910 +0,0 @@
-# module "nginx-controller" {
-# source = "terraform-iaac/nginx-controller/helm"
-# namespace = "ingress-nginx-test"
-# create_namespace = true
-# atomic = true
-# ingress_class_is_default = false
-# ingress_class_name = "nginx-test"
-# }
-variable "honeypotapikey" {
- default = null
-}
-variable "crowdsec_api_key" {}
-variable "crowdsec_captcha_secret_key" {}
-variable "crowdsec_captcha_site_key" {}
-variable "tier" { type = string }
-
-resource "kubernetes_namespace" "ingress_nginx" {
- metadata {
- name = "ingress-nginx"
- labels = {
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- # "istio-injection" : "enabled"
- }
- }
-}
-resource "kubernetes_service_account" "ingress_nginx" {
- metadata {
- name = "ingress-nginx"
- namespace = "ingress-nginx"
- labels = {
- "app.kubernetes.io/component" = "controller"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.13.1"
- }
- }
- automount_service_account_token = true
-}
-
-# Jobs create a cert and modify this secret. This is problematic as TF recreates it every time
-# Instead, on each fresh install, uncomment this, get nginx working and comment it.
-# Also rm from state: tf state rm module.kubernetes_cluster.module.nginx-ingress.kubernetes_service_account.ingress_nginx_admission
-resource "kubernetes_service_account" "ingress_nginx_admission" {
- metadata {
- name = "ingress-nginx-admission"
- namespace = "ingress-nginx"
- labels = {
- "app.kubernetes.io/component" = "admission-webhook"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.13.1"
- }
- }
-}
-resource "kubernetes_role" "ingress_nginx" {
- metadata {
- name = "ingress-nginx"
- namespace = "ingress-nginx"
- labels = {
- "app.kubernetes.io/component" = "controller"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.13.1"
- }
- }
- rule {
- verbs = ["get"]
- api_groups = [""]
- resources = ["namespaces"]
- }
- rule {
- verbs = ["get", "list", "watch", "update", "create", "delete"]
- api_groups = [""]
- resources = ["configmaps", "pods", "secrets", "endpoints"]
- }
- rule {
- verbs = ["get", "list", "watch"]
- api_groups = [""]
- resources = ["services"]
- }
- rule {
- verbs = ["get", "list", "watch"]
- api_groups = ["networking.k8s.io"]
- resources = ["ingresses"]
- }
- rule {
- verbs = ["update"]
- api_groups = ["networking.k8s.io"]
- resources = ["ingresses/status"]
- }
- rule {
- verbs = ["get", "list", "watch"]
- api_groups = ["networking.k8s.io"]
- resources = ["ingressclasses"]
- }
- rule {
- verbs = ["get", "update"]
- api_groups = ["coordination.k8s.io"]
- resources = ["leases"]
- resource_names = ["ingress-nginx-leader"]
- }
- rule {
- verbs = ["create"]
- api_groups = ["coordination.k8s.io"]
- resources = ["leases"]
- }
- rule {
- verbs = ["create", "patch"]
- api_groups = [""]
- resources = ["events"]
- }
- rule {
- verbs = ["list", "watch", "get"]
- api_groups = ["discovery.k8s.io"]
- resources = ["endpointslices"]
- }
-}
-resource "kubernetes_role" "ingress_nginx_admission" {
- metadata {
- name = "ingress-nginx-admission"
- namespace = "ingress-nginx"
- labels = {
- "app.kubernetes.io/component" = "admission-webhook"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.13.1"
- }
- }
- rule {
- verbs = ["get", "create", "patch", "update", "watch", "list"]
- api_groups = [""]
- resources = ["secrets"]
- }
-}
-resource "kubernetes_cluster_role" "ingress_nginx" {
- metadata {
- name = "ingress-nginx"
- labels = {
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.13.1"
- }
- }
- rule {
- verbs = ["list", "watch"]
- api_groups = [""]
- resources = ["configmaps", "endpoints", "nodes", "pods", "secrets", "namespaces"]
- }
- rule {
- verbs = ["list", "watch"]
- api_groups = ["coordination.k8s.io"]
- resources = ["leases"]
- }
- rule {
- verbs = ["get"]
- api_groups = [""]
- resources = ["nodes"]
- }
- rule {
- verbs = ["get", "list", "watch"]
- api_groups = [""]
- resources = ["services"]
- }
- rule {
- verbs = ["get", "list", "watch"]
- api_groups = ["networking.k8s.io"]
- resources = ["ingresses"]
- }
- rule {
- verbs = ["create", "patch"]
- api_groups = [""]
- resources = ["events"]
- }
- rule {
- verbs = ["update"]
- api_groups = ["networking.k8s.io"]
- resources = ["ingresses/status"]
- }
- rule {
- verbs = ["get", "list", "watch"]
- api_groups = ["networking.k8s.io"]
- resources = ["ingressclasses"]
- }
- rule {
- verbs = ["list", "watch", "get"]
- api_groups = ["discovery.k8s.io"]
- resources = ["endpointslices"]
- }
-}
-resource "kubernetes_cluster_role" "ingress_nginx_admission" {
- metadata {
- name = "ingress-nginx-admission"
- labels = {
- "app.kubernetes.io/component" = "admission-webhook"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.13.1"
- }
- }
- rule {
- verbs = ["get", "update"]
- api_groups = ["admissionregistration.k8s.io"]
- resources = ["validatingwebhookconfigurations"]
- }
-}
-resource "kubernetes_role_binding" "ingress_nginx" {
- metadata {
- name = "ingress-nginx"
- namespace = "ingress-nginx"
- labels = {
- "app.kubernetes.io/component" = "controller"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.13.1"
- }
- }
- subject {
- kind = "ServiceAccount"
- name = "ingress-nginx"
- namespace = "ingress-nginx"
- }
- role_ref {
- api_group = "rbac.authorization.k8s.io"
- kind = "Role"
- name = "ingress-nginx"
- }
-}
-resource "kubernetes_role_binding" "ingress_nginx_admission" {
- metadata {
- name = "ingress-nginx-admission"
- namespace = "ingress-nginx"
- labels = {
- "app.kubernetes.io/component" = "admission-webhook"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.13.1"
- }
- }
- subject {
- kind = "ServiceAccount"
- name = "ingress-nginx-admission"
- namespace = "ingress-nginx"
- }
- role_ref {
- api_group = "rbac.authorization.k8s.io"
- kind = "Role"
- name = "ingress-nginx-admission"
- }
-}
-resource "kubernetes_cluster_role_binding" "ingress_nginx" {
- metadata {
- name = "ingress-nginx"
- labels = {
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.13.1"
- }
- }
- subject {
- kind = "ServiceAccount"
- name = "ingress-nginx"
- namespace = "ingress-nginx"
- }
- role_ref {
- api_group = "rbac.authorization.k8s.io"
- kind = "ClusterRole"
- name = "ingress-nginx"
- }
-}
-resource "kubernetes_cluster_role_binding" "ingress_nginx_admission" {
- metadata {
- name = "ingress-nginx-admission"
- labels = {
- "app.kubernetes.io/component" = "admission-webhook"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.13.1"
- }
- }
- subject {
- kind = "ServiceAccount"
- name = "ingress-nginx-admission"
- namespace = "ingress-nginx"
- }
- role_ref {
- api_group = "rbac.authorization.k8s.io"
- kind = "ClusterRole"
- name = "ingress-nginx-admission"
- }
-}
-resource "kubernetes_config_map" "ingress_nginx_controller" {
- metadata {
- name = "ingress-nginx-controller"
- namespace = "ingress-nginx"
- labels = {
- "app.kubernetes.io/component" = "controller"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.13.1"
- }
- }
- data = {
- use-forwarded-headers = "true"
- compute-full-forwarded-for = "true"
- enable-real-ip = "true"
- allow-snippet-annotations = true
- limit-req-status-code = 429
- limit-conn-status-code = 429
- enable-modsecurity = true
- enable-owasp-modsecurity-crs = false
- modsecurity-snippet : <<-EOT
- SecRuleEngine On
- ${var.honeypotapikey != null ? format("%s %s", "SecHttpBlKey", var.honeypotapikey) : ""}
- SecAction "id:900500,\
- phase:1,\
- nolog,\
- pass,\
- t:none,\
- setvar:tx.block_search_ip=0,\
- setvar:tx.block_suspicious_ip=1,\
- setvar:tx.block_harvester_ip=1,\
- setvar:tx.block_spammer_ip=1"
- EOT
- plugins = "crowdsec"
- # plugins = ""
- lua-shared-dicts = "crowdsec_cache: 50m"
- http-snippet : <<-EOT
- proxy_cache_path /tmp/nginx-cache levels=1:2 keys_zone=static-cache:2m max_size=100m inactive=7d use_temp_path=off;
- proxy_cache_key $scheme$proxy_host$request_uri;
- proxy_cache_lock on;
- proxy_cache_use_stale updating;
- EOT
- server-snippet : <<-EOT
- lua_ssl_trusted_certificate "/etc/ssl/certs/ca-certificates.crt"; # Captcha
- #resolver local=on ipv6=off valid=600s;
- EOT
- # first own works
- # log-format-upstream : <<-EOT
- # $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_cf_connecting_ip" "$http_cf_ray" "$http_x_forwarded_for" "$host";
- # EOT
-
- # ketpt do debug why it's invalid syntax lol
- # log-format-upstream : <<-EOT
- # $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_cf_connecting_ip" "$http_cf_ray" "$http_x_forwarded_for" "$host";
- # EOT
- }
-}
-
-resource "kubernetes_config_map" "udp_services" {
- metadata {
- name = "udp-services"
- namespace = "ingress-nginx"
- }
- data = {
- 53 : "technitium/technitium-dns:53"
- # 8554 : "frigate/frigate:8554"
- }
-}
-resource "kubernetes_config_map" "tcp_services" {
- metadata {
- name = "tcp-services"
- namespace = "ingress-nginx"
- }
- data = {
- # 9443 : "wireguard/xray:7443" // reality
- # 8554 : "frigate/frigate:8554"
- }
-}
-resource "kubernetes_service" "ingress_nginx_controller" {
- metadata {
- name = "ingress-nginx-controller"
- namespace = "ingress-nginx"
- labels = {
- "app.kubernetes.io/component" = "controller"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.13.1"
- }
- }
- spec {
- port {
- name = "http"
- protocol = "TCP"
- port = 80
- target_port = "http"
- }
- port {
- name = "https"
- protocol = "TCP"
- port = 443
- target_port = "https"
- }
- port {
- name = "dns"
- protocol = "UDP"
- port = 53
- target_port = "dns"
- }
- # port {
- # name = "frigate-rtsptcp"
- # port = 8554
- # protocol = "TCP"
- # }
- # port {
- # name = "frigate-rtspudp"
- # port = 8554
- # protocol = "UDP"
- # }
- # port {
- # name = "xray-reality"
- # protocol = "TCP"
- # port = 9443 # expose tcp port here
- # target_port = "9443"
- # }
- selector = {
- "app.kubernetes.io/component" = "controller"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- }
- type = "LoadBalancer"
- external_traffic_policy = "Local" // see https://metallb.universe.tf/usage/
- # ip_families = ["IPv4"]
- }
-}
-resource "kubernetes_service" "ingress_nginx_controller_admission" {
- metadata {
- name = "ingress-nginx-controller-admission"
- namespace = "ingress-nginx"
- labels = {
- "app.kubernetes.io/component" = "controller"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.13.1"
- }
- }
- spec {
- port {
- name = "https-webhook"
- port = 443
- target_port = "webhook"
- }
- selector = {
- "app.kubernetes.io/component" = "controller"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- }
- type = "ClusterIP"
- }
-}
-resource "kubernetes_deployment" "ingress_nginx_controller" {
- metadata {
- name = "ingress-nginx-controller"
- namespace = "ingress-nginx"
- labels = {
- "app.kubernetes.io/component" = "controller"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.13.1"
- tier = var.tier
- }
- annotations = {
- "reloader.stakater.com/search" = "true"
- }
- }
- spec {
- replicas = 3
-
- selector {
- match_labels = {
- "app.kubernetes.io/component" = "controller"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- }
- }
- template {
- metadata {
- labels = {
- "app.kubernetes.io/component" = "controller"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.13.1"
- "app" = "ingress-nginx"
- }
- annotations = {
- "prometheus.io/scrape" = "true"
- "prometheus.io/port" = 10254
-
- "diun.enable" = "true"
- "diun.include_tags" = "^v\\d+(?:\\.\\d+)?(?:\\.\\d+)?.*$"
- }
- }
- spec {
- volume {
- name = "webhook-cert"
- secret {
- secret_name = "ingress-nginx-admission"
- }
- }
- # volume {
- # name = "modsecurity"
- # config_map {
- # name = "modsecurity"
- # }
- # }
-
- ## Crowdsec
- init_container {
- name = "init-clone-crowdsec-bouncer"
- image = "crowdsecurity/lua-bouncer-plugin"
- env {
- name = "API_URL"
- value = "http://crowdsec-service.crowdsec.svc.cluster.local:8080"
- }
- env {
- // if you can't connect with bouncer not found, regenerate api key with:
- // "cscli bouncers add nginx" on the lapi
- name = "API_KEY"
- value = var.crowdsec_api_key
- }
- env {
- name = "MODE"
- value = "stream"
- }
- env {
- name = "CAPTCHA_PROVIDER"
- value = "hcaptcha"
- }
- env {
- name = "BOUNCING_ON_TYPE"
- value = "all"
- # value = "ban"
- }
- env {
- name = "SECRET_KEY"
- value = var.crowdsec_captcha_secret_key
- }
- env {
- name = "SITE_KEY"
- value = var.crowdsec_captcha_site_key
- }
-
- # env {
- # name = "DISABLE_RUN"
- # value = "true"
- # }
- env {
- name = "BAN_TEMPLATE_PATH"
- value = "/etc/nginx/lua/plugins/crowdsec/templates/ban.html"
- }
- env {
- name = "CAPTCHA_TEMPLATE_PATH"
- value = "/etc/nginx/lua/plugins/crowdsec/templates/captcha.html"
- }
- env {
- name = "BOUNCER_CONFIG"
- value = "/crowdsec/crowdsec-bouncer.conf"
- }
- # command = ["sh", "-c", "sh /docker_start.sh; mkdir -p /lua_plugins/crowdsec/; cp -r /crowdsec /lua_plugins/; chown -R 101:101 /lua_plugins/"]
- command = ["sh", "-c", "sh /docker_start.sh; mkdir -p /lua_plugins/crowdsec/; cp -R /crowdsec/* /lua_plugins/crowdsec/"]
-
- volume_mount {
- name = "crowdsec"
- mount_path = "/lua_plugins"
- }
- }
- # Share bouncer config
- volume {
- name = "crowdsec"
- empty_dir {
- }
- }
- container {
- name = "controller"
- # https://github.com/kubernetes/ingress-nginx
- image = "registry.k8s.io/ingress-nginx/controller:v1.11.8"
- args = ["/nginx-ingress-controller", "--election-id=ingress-nginx-leader", "--controller-class=k8s.io/ingress-nginx", "--ingress-class=nginx", "--configmap=$(POD_NAMESPACE)/ingress-nginx-controller", "--validating-webhook=:8443", "--validating-webhook-certificate=/usr/local/certificates/cert", "--validating-webhook-key=/usr/local/certificates/key", "--udp-services-configmap", "ingress-nginx/udp-services", "--tcp-services-configmap", "ingress-nginx/tcp-services"]
- volume_mount {
- name = "crowdsec"
- mount_path = "/etc/nginx/lua/plugins/crowdsec"
- sub_path = "crowdsec"
- }
- port {
- name = "http"
- container_port = 80
- protocol = "TCP"
- }
- port {
- name = "https"
- container_port = 443
- protocol = "TCP"
- }
- port {
- name = "dns"
- container_port = 53
- protocol = "UDP"
- }
- # port {
- # name = "xray-reality"
- # container_port = 9443 # expose port here
- # protocol = "TCP"
- # }
- port {
- name = "webhook"
- container_port = 8443
- protocol = "TCP"
- }
- # port {
- # name = "frigate-rtsptcp"
- # container_port = 8554
- # protocol = "TCP"
- # }
- # port {
- # name = "frigate-rtspudp"
- # container_port = 8554
- # protocol = "UDP"
- # }
- port {
- name = "metrics"
- container_port = 10254
- protocol = "TCP"
- }
- env {
- name = "POD_NAME"
- value_from {
- field_ref {
- field_path = "metadata.name"
- }
- }
- }
- env {
- name = "POD_NAMESPACE"
- value_from {
- field_ref {
- field_path = "metadata.namespace"
- }
- }
- }
- env {
- name = "LD_PRELOAD"
- value = "/usr/local/lib/libmimalloc.so"
- }
- resources {
- requests = {
- cpu = "100m"
- memory = "90Mi"
- }
- }
- volume_mount {
- name = "webhook-cert"
- read_only = true
- mount_path = "/usr/local/certificates/"
- }
- # Not used atm
- # volume_mount {
- # name = "modsecurity"
- # read_only = true
- # mount_path = "/etc/nginx/modsecurity"
- # # sub_path = "modsecurity.conf"
- # }
- liveness_probe {
- http_get {
- path = "/healthz"
- port = "10254"
- scheme = "HTTP"
- }
- initial_delay_seconds = 10
- timeout_seconds = 1
- period_seconds = 10
- success_threshold = 1
- failure_threshold = 5
- }
- readiness_probe {
- http_get {
- path = "/healthz"
- port = "10254"
- scheme = "HTTP"
- }
- initial_delay_seconds = 10
- timeout_seconds = 1
- period_seconds = 10
- success_threshold = 1
- failure_threshold = 3
- }
- lifecycle {
- pre_stop {
- exec {
- command = ["/wait-shutdown"]
- }
- }
- }
- image_pull_policy = "IfNotPresent"
- security_context {
- capabilities {
- add = ["NET_BIND_SERVICE"]
- drop = ["ALL"]
- }
- run_as_user = 101
- allow_privilege_escalation = true
- }
- }
- termination_grace_period_seconds = 300
- dns_policy = "ClusterFirst"
- node_selector = {
- "kubernetes.io/os" = "linux"
- }
- service_account_name = "ingress-nginx"
- }
- }
- strategy {
- type = "RollingUpdate"
- rolling_update {
- max_unavailable = "1"
- max_surge = "2"
- }
- }
-
- revision_history_limit = 10
- }
-}
-resource "kubernetes_job" "ingress_nginx_admission_create" {
- metadata {
- name = "ingress-nginx-admission-create"
- namespace = "ingress-nginx"
- labels = {
- "app.kubernetes.io/component" = "admission-webhook"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.8.2"
- }
- }
- spec {
- template {
- metadata {
- name = "ingress-nginx-admission-create"
- labels = {
- "app.kubernetes.io/component" = "admission-webhook"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.8.2"
- }
- }
- spec {
- container {
- name = "create"
- image = "registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230407@sha256:543c40fd093964bc9ab509d3e791f9989963021f1e9e4c9c7b6700b02bfb227b"
- args = ["create", "--host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc", "--namespace=$(POD_NAMESPACE)", "--secret-name=ingress-nginx-admission"]
- env {
- name = "POD_NAMESPACE"
- value_from {
- field_ref {
- field_path = "metadata.namespace"
- }
- }
- }
- image_pull_policy = "IfNotPresent"
- }
- restart_policy = "OnFailure"
- node_selector = {
- "kubernetes.io/os" = "linux"
- }
- service_account_name = "ingress-nginx-admission"
- security_context {
- run_as_user = 2000
- run_as_non_root = true
- fs_group = 2000
- }
- }
- }
- }
-}
-
-# Jobs create a cert and modify this secret. This is problematic as TF recreates it every time
-# Instead, on each fresh install, uncomment this, get nginx working and comment it.
-# Also rm from state: tf state rm module.kubernetes_cluster.module.nginx-ingress.kubernetes_job.ingress_nginx_admission_patch
-# resource "kubernetes_job" "ingress_nginx_admission_patch" {
-# metadata {
-# name = "ingress-nginx-admission-patch"
-# namespace = "ingress-nginx"
-# labels = {
-# "app.kubernetes.io/component" = "admission-webhook"
-# "app.kubernetes.io/instance" = "ingress-nginx"
-# "app.kubernetes.io/name" = "ingress-nginx"
-# "app.kubernetes.io/part-of" = "ingress-nginx"
-# "app.kubernetes.io/version" = "1.13.1"
-# }
-# }
-# spec {
-# template {
-# metadata {
-# name = "ingress-nginx-admission-patch"
-# labels = {
-# "app.kubernetes.io/component" = "admission-webhook"
-# "app.kubernetes.io/instance" = "ingress-nginx"
-# "app.kubernetes.io/name" = "ingress-nginx"
-# "app.kubernetes.io/part-of" = "ingress-nginx"
-# "app.kubernetes.io/version" = "1.13.1"
-# }
-# }
-# spec {
-# container {
-# name = "patch"
-# image = "registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230407@sha256:543c40fd093964bc9ab509d3e791f9989963021f1e9e4c9c7b6700b02bfb227b"
-# args = ["patch", "--webhook-name=ingress-nginx-admission", "--namespace=$(POD_NAMESPACE)", "--patch-mutating=false", "--secret-name=ingress-nginx-admission", "--patch-failure-policy=Fail"]
-# env {
-# name = "POD_NAMESPACE"
-# value_from {
-# field_ref {
-# field_path = "metadata.namespace"
-# }
-# }
-# }
-# image_pull_policy = "IfNotPresent"
-# }
-# restart_policy = "OnFailure"
-# node_selector = {
-# "kubernetes.io/os" = "linux"
-# }
-# service_account_name = "ingress-nginx-admission"
-# security_context {
-# run_as_user = 2000
-# run_as_non_root = true
-# fs_group = 2000
-# }
-# }
-# }
-# }
-# }
-resource "kubernetes_ingress_class" "nginx" {
- metadata {
- name = "nginx"
- labels = {
- "app.kubernetes.io/component" = "controller"
- "app.kubernetes.io/instance" = "ingress-nginx"
- "app.kubernetes.io/name" = "ingress-nginx"
- "app.kubernetes.io/part-of" = "ingress-nginx"
- "app.kubernetes.io/version" = "1.13.1"
- }
- }
- spec {
- controller = "k8s.io/ingress-nginx"
- }
-}
-
-# Jobs create a cert and modify this secret. This is problematic as TF recreates it every time
-# Instead, on each fresh install, uncomment this, get nginx working and comment it.
-# Also rm from state: tf state rm module.kubernetes_cluster.module.nginx-ingress.kubernetes_service_account.ingress_nginx_admission
-# resource "kubernetes_validating_webhook_configuration" "ingress_nginx_admission" {
-# metadata {
-# name = "ingress-nginx-admission"
-# labels = {
-# "app.kubernetes.io/component" = "admission-webhook"
-# "app.kubernetes.io/instance" = "ingress-nginx"
-# "app.kubernetes.io/name" = "ingress-nginx"
-# "app.kubernetes.io/part-of" = "ingress-nginx"
-# "app.kubernetes.io/version" = "1.13.1"
-# }
-# }
-# webhook {
-# name = "validate.nginx.ingress.kubernetes.io"
-# client_config {
-# service {
-# namespace = "ingress-nginx"
-# name = "ingress-nginx-controller-admission"
-# path = "/networking/v1/ingresses"
-# }
-# }
-# rule {
-# api_versions = ["v1"]
-# api_groups = ["networking.k8s.io"]
-# resources = ["ingresses"]
-# operations = ["CREATE", "UPDATE"]
-# }
-# failure_policy = "Fail"
-# match_policy = "Equivalent"
-# side_effects = "None"
-# admission_review_versions = ["v1"]
-# }
-# }
-
-resource "kubernetes_config_map" "modsecurity" {
- metadata {
- name = "modsecurity"
- namespace = "ingress-nginx"
- annotations = {
- "reloader.stakater.com/match" = "true"
- }
- }
-
- data = {
- "modsecurity.conf" = file("${path.module}/modsecurity.conf")
- }
-}
diff --git a/modules/kubernetes/nginx-ingress/modsecurity.conf b/modules/kubernetes/nginx-ingress/modsecurity.conf
deleted file mode 100644
index a9582678..00000000
--- a/modules/kubernetes/nginx-ingress/modsecurity.conf
+++ /dev/null
@@ -1,289 +0,0 @@
-### NOT USED ATM
-
-
-# -- Rule engine initialization ----------------------------------------------
-
-# Enable ModSecurity, attaching it to every transaction. Use detection
-# only to start with, because that minimises the chances of post-installation
-# disruption.
-#
-SecRuleEngine DetectionOnly
-
-
-# -- Request body handling ---------------------------------------------------
-
-# Allow ModSecurity to access request bodies. If you don't, ModSecurity
-# won't be able to see any POST parameters, which opens a large security
-# hole for attackers to exploit.
-#
-SecRequestBodyAccess On
-
-
-# Enable XML request body parser.
-# Initiate XML Processor in case of xml content-type
-#
-SecRule REQUEST_HEADERS:Content-Type "^(?:application(?:/soap\+|/)|text/)xml" \
- "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
-
-# Enable JSON request body parser.
-# Initiate JSON Processor in case of JSON content-type; change accordingly
-# if your application does not use 'application/json'
-#
-SecRule REQUEST_HEADERS:Content-Type "^application/json" \
- "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
-
-# Sample rule to enable JSON request body parser for more subtypes.
-# Uncomment or adapt this rule if you want to engage the JSON
-# Processor for "+json" subtypes
-#
-#SecRule REQUEST_HEADERS:Content-Type "^application/[a-z0-9.-]+[+]json" \
-# "id:'200006',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
-
-# Maximum request body size we will accept for buffering. If you support
-# file uploads then the value given on the first line has to be as large
-# as the largest file you are willing to accept. The second value refers
-# to the size of data, with files excluded. You want to keep that value as
-# low as practical.
-#
-SecRequestBodyLimit 13107200
-SecRequestBodyNoFilesLimit 131072
-
-# What to do if the request body size is above our configured limit.
-# Keep in mind that this setting will automatically be set to ProcessPartial
-# when SecRuleEngine is set to DetectionOnly mode in order to minimize
-# disruptions when initially deploying ModSecurity.
-#
-SecRequestBodyLimitAction Reject
-
-# Maximum parsing depth allowed for JSON objects. You want to keep this
-# value as low as practical.
-#
-SecRequestBodyJsonDepthLimit 512
-
-# Maximum number of args allowed per request. You want to keep this
-# value as low as practical. The value should match that in rule 200007.
-SecArgumentsLimit 1000
-
-# If SecArgumentsLimit has been set, you probably want to reject any
-# request body that has only been partly parsed. The value used in this
-# rule should match what was used with SecArgumentsLimit
-SecRule &ARGS "@ge 1000" \
-"id:'200007', phase:2,t:none,log,deny,status:400,msg:'Failed to fully parse request body due to large argument count',severity:2"
-
-# Verify that we've correctly processed the request body.
-# As a rule of thumb, when failing to process a request body
-# you should reject the request (when deployed in blocking mode)
-# or log a high-severity alert (when deployed in detection-only mode).
-#
-SecRule REQBODY_ERROR "!@eq 0" \
-"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"
-
-# By default be strict with what we accept in the multipart/form-data
-# request body. If the rule below proves to be too strict for your
-# environment consider changing it to detection-only. You are encouraged
-# _not_ to remove it altogether.
-#
-SecRule MULTIPART_STRICT_ERROR "!@eq 0" \
-"id:'200003',phase:2,t:none,log,deny,status:400, \
-msg:'Multipart request body failed strict validation: \
-PE %{REQBODY_PROCESSOR_ERROR}, \
-BQ %{MULTIPART_BOUNDARY_QUOTED}, \
-BW %{MULTIPART_BOUNDARY_WHITESPACE}, \
-DB %{MULTIPART_DATA_BEFORE}, \
-DA %{MULTIPART_DATA_AFTER}, \
-HF %{MULTIPART_HEADER_FOLDING}, \
-LF %{MULTIPART_LF_LINE}, \
-SM %{MULTIPART_MISSING_SEMICOLON}, \
-IQ %{MULTIPART_INVALID_QUOTING}, \
-IP %{MULTIPART_INVALID_PART}, \
-IH %{MULTIPART_INVALID_HEADER_FOLDING}, \
-FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
-
-# Did we see anything that might be a boundary?
-#
-# Here is a short description about the ModSecurity Multipart parser: the
-# parser returns with value 0, if all "boundary-like" line matches with
-# the boundary string which given in MIME header. In any other cases it returns
-# with different value, eg. 1 or 2.
-#
-# The RFC 1341 descript the multipart content-type and its syntax must contains
-# only three mandatory lines (above the content):
-# * Content-Type: multipart/mixed; boundary=BOUNDARY_STRING
-# * --BOUNDARY_STRING
-# * --BOUNDARY_STRING--
-#
-# First line indicates, that this is a multipart content, second shows that
-# here starts a part of the multipart content, third shows the end of content.
-#
-# If there are any other lines, which starts with "--", then it should be
-# another boundary id - or not.
-#
-# After 3.0.3, there are two kinds of types of boundary errors: strict and permissive.
-#
-# If multipart content contains the three necessary lines with correct order, but
-# there are one or more lines with "--", then parser returns with value 2 (non-zero).
-#
-# If some of the necessary lines (usually the start or end) misses, or the order
-# is wrong, then parser returns with value 1 (also a non-zero).
-#
-# You can choose, which one is what you need. The example below contains the
-# 'strict' mode, which means if there are any lines with start of "--", then
-# ModSecurity blocked the content. But the next, commented example contains
-# the 'permissive' mode, then you check only if the necessary lines exists in
-# correct order. Whit this, you can enable to upload PEM files (eg "----BEGIN.."),
-# or other text files, which contains eg. HTTP headers.
-#
-# The difference is only the operator - in strict mode (first) the content blocked
-# in case of any non-zero value. In permissive mode (second, commented) the
-# content blocked only if the value is explicit 1. If it 0 or 2, the content will
-# allowed.
-#
-
-#
-# See #1747 and #1924 for further information on the possible values for
-# MULTIPART_UNMATCHED_BOUNDARY.
-#
-SecRule MULTIPART_UNMATCHED_BOUNDARY "@eq 1" \
- "id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'"
-
-
-# PCRE Tuning
-# We want to avoid a potential RegEx DoS condition
-#
-SecPcreMatchLimit 1000
-SecPcreMatchLimitRecursion 1000
-
-# Some internal errors will set flags in TX and we will need to look for these.
-# All of these are prefixed with "MSC_". The following flags currently exist:
-#
-# MSC_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded.
-#
-SecRule TX:/^MSC_/ "!@streq 0" \
- "id:'200005',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'"
-
-
-# -- Response body handling --------------------------------------------------
-
-# Allow ModSecurity to access response bodies.
-# You should have this directive enabled in order to identify errors
-# and data leakage issues.
-#
-# Do keep in mind that enabling this directive does increases both
-# memory consumption and response latency.
-#
-SecResponseBodyAccess On
-
-# Which response MIME types do you want to inspect? You should adjust the
-# configuration below to catch documents but avoid static files
-# (e.g., images and archives).
-#
-SecResponseBodyMimeType text/plain text/html text/xml
-
-# Buffer response bodies of up to 512 KB in length.
-SecResponseBodyLimit 524288
-
-# What happens when we encounter a response body larger than the configured
-# limit? By default, we process what we have and let the rest through.
-# That's somewhat less secure, but does not break any legitimate pages.
-#
-SecResponseBodyLimitAction ProcessPartial
-
-
-# -- Filesystem configuration ------------------------------------------------
-
-# The location where ModSecurity stores temporary files (for example, when
-# it needs to handle a file upload that is larger than the configured limit).
-#
-# This default setting is chosen due to all systems have /tmp available however,
-# this is less than ideal. It is recommended that you specify a location that's private.
-#
-SecTmpDir /tmp/
-
-# The location where ModSecurity will keep its persistent data. This default setting
-# is chosen due to all systems have /tmp available however, it
-# too should be updated to a place that other users can't access.
-#
-SecDataDir /tmp/
-
-
-# -- File uploads handling configuration -------------------------------------
-
-# The location where ModSecurity stores intercepted uploaded files. This
-# location must be private to ModSecurity. You don't want other users on
-# the server to access the files, do you?
-#
-#SecUploadDir /opt/modsecurity/var/upload/
-
-# By default, only keep the files that were determined to be unusual
-# in some way (by an external inspection script). For this to work you
-# will also need at least one file inspection rule.
-#
-#SecUploadKeepFiles RelevantOnly
-
-# Uploaded files are by default created with permissions that do not allow
-# any other user to access them. You may need to relax that if you want to
-# interface ModSecurity to an external program (e.g., an anti-virus).
-#
-#SecUploadFileMode 0600
-
-
-# -- Debug log configuration -------------------------------------------------
-
-# The default debug log configuration is to duplicate the error, warning
-# and notice messages from the error log.
-#
-#SecDebugLog /opt/modsecurity/var/log/debug.log
-#SecDebugLogLevel 3
-
-
-# -- Audit log configuration -------------------------------------------------
-
-# Log the transactions that are marked by a rule, as well as those that
-# trigger a server error (determined by a 5xx or 4xx, excluding 404,
-# level response status codes).
-#
-SecAuditEngine RelevantOnly
-SecAuditLogRelevantStatus "^(?:5|4(?!04))"
-
-# Log everything we know about a transaction.
-SecAuditLogParts ABIJDEFHZ
-
-# Use a single file for logging. This is much easier to look at, but
-# assumes that you will use the audit log only ocassionally.
-#
-SecAuditLogType Concurrent
-SecAuditLog /var/log/modsec_audit.log
-
-# Specify the path for concurrent audit logging.
-#SecAuditLogStorageDir /opt/modsecurity/var/audit/
-
-
-# -- Miscellaneous -----------------------------------------------------------
-
-# Use the most commonly used application/x-www-form-urlencoded parameter
-# separator. There's probably only one application somewhere that uses
-# something else so don't expect to change this value.
-#
-SecArgumentSeparator &
-
-# Settle on version 0 (zero) cookies, as that is what most applications
-# use. Using an incorrect cookie version may open your installation to
-# evasion attacks (against the rules that examine named cookies).
-#
-SecCookieFormat 0
-
-# Specify your Unicode Code Point.
-# This mapping is used by the t:urlDecodeUni transformation function
-# to properly map encoded data to your language. Properly setting
-# these directives helps to reduce false positives and negatives.
-#
-SecUnicodeMapFile unicode.mapping 20127
-
-# Improve the quality of ModSecurity by sharing information about your
-# current ModSecurity version and dependencies versions.
-# The following information will be shared: ModSecurity version,
-# Web Server version, APR version, PCRE version, Lua version, Libxml2
-# version, Anonymous unique id for host.
-SecStatusEngine On
-
-SecAuditLogStorageDir /var/log/audit/
diff --git a/modules/kubernetes/oauth-proxy/main.tf b/modules/kubernetes/oauth-proxy/main.tf
index e7a24dcf..f4f5149d 100644
--- a/modules/kubernetes/oauth-proxy/main.tf
+++ b/modules/kubernetes/oauth-proxy/main.tf
@@ -215,11 +215,13 @@ resource "kubernetes_ingress_v1" "oauth" {
name = "oauth2"
namespace = "oauth2"
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["oauth2.viktorbarzin.me"]
secret_name = var.tls_secret_name
diff --git a/modules/kubernetes/ollama/main.tf b/modules/kubernetes/ollama/main.tf
index fac512e5..c08b6700 100644
--- a/modules/kubernetes/ollama/main.tf
+++ b/modules/kubernetes/ollama/main.tf
@@ -169,7 +169,6 @@ module "ollama-api-ingress" {
allow_local_access_only = true # Restricts to 10.0.0.0/8, 192.168.1.0/24
ssl_redirect = false
port = 11434
- proxy_timeout = 300 # Longer timeout for model inference
}
# Web UI
diff --git a/modules/kubernetes/openid_help_page/main.tf b/modules/kubernetes/openid_help_page/main.tf
index 338b92e1..5aa72783 100644
--- a/modules/kubernetes/openid_help_page/main.tf
+++ b/modules/kubernetes/openid_help_page/main.tf
@@ -83,11 +83,13 @@ resource "kubernetes_ingress_v1" "openid_help_page" {
name = "openid-help-page"
namespace = "openid-help-page"
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["kubectl.viktorbarzin.me"]
secret_name = var.tls_secret_name
diff --git a/modules/kubernetes/owntracks/main.tf b/modules/kubernetes/owntracks/main.tf
index 9a68196c..ea79dabe 100644
--- a/modules/kubernetes/owntracks/main.tf
+++ b/modules/kubernetes/owntracks/main.tf
@@ -140,8 +140,22 @@ module "ingress" {
tls_secret_name = var.tls_secret_name
port = 443
extra_annotations = {
- "nginx.ingress.kubernetes.io/auth-type" = "basic" # support only basic auth; can't use authentik
- "nginx.ingress.kubernetes.io/auth-secret" = kubernetes_secret.basic_auth.metadata[0].name
- "nginx.ingress.kubernetes.io/auth-realm" = "Authentication Required"
+ "traefik.ingress.kubernetes.io/router.middlewares" = "owntracks-basic-auth@kubernetescrd,traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd"
+ }
+}
+
+resource "kubernetes_manifest" "basic_auth_middleware" {
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "Middleware"
+ metadata = {
+ name = "basic-auth"
+ namespace = kubernetes_namespace.owntracks.metadata[0].name
+ }
+ spec = {
+ basicAuth = {
+ secret = kubernetes_secret.basic_auth.metadata[0].name
+ }
+ }
}
}
diff --git a/modules/kubernetes/paperless-ngx/main.tf b/modules/kubernetes/paperless-ngx/main.tf
index e2bcce71..24aa6e9b 100644
--- a/modules/kubernetes/paperless-ngx/main.tf
+++ b/modules/kubernetes/paperless-ngx/main.tf
@@ -154,10 +154,6 @@ module "ingress" {
tls_secret_name = var.tls_secret_name
port = 80
extra_annotations = {
- "nginx.ingress.kubernetes.io/proxy-body-size" : "0"
- # see https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md#rate-limiting for all annotations
- # "nginx.ingress.kubernetes.io/limit-rpm": "5"
-
"gethomepage.dev/enabled" = "true"
"gethomepage.dev/description" = "Document library"
# gethomepage.dev/group: Media
diff --git a/modules/kubernetes/pihole/main.tf b/modules/kubernetes/pihole/main.tf
index 1dc39eb6..ede0c04a 100644
--- a/modules/kubernetes/pihole/main.tf
+++ b/modules/kubernetes/pihole/main.tf
@@ -169,13 +169,14 @@ resource "kubernetes_ingress_v1" "pihole" {
name = "pihole-ingress"
namespace = kubernetes_namespace.pihole.metadata[0].name
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
- "nginx.ingress.kubernetes.io/auth-tls-verify-client" = "on"
- "nginx.ingress.kubernetes.io/auth-tls-secret" = "default/ca-secret"
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
+ "traefik.ingress.kubernetes.io/router.tls.options" = "traefik-mtls@kubernetescrd"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["pihole.viktorbarzin.me"]
secret_name = var.tls_secret_name
diff --git a/modules/kubernetes/plotting-book/main.tf b/modules/kubernetes/plotting-book/main.tf
index 692737ee..c67e5fd0 100644
--- a/modules/kubernetes/plotting-book/main.tf
+++ b/modules/kubernetes/plotting-book/main.tf
@@ -89,9 +89,5 @@ module "ingress" {
name = "plotting-book"
tls_secret_name = var.tls_secret_name
- additional_configuration_snippet = <<-EOF
- # Override CSP to allow data: URIs and blob: for database/workers
- proxy_hide_header Content-Security-Policy;
- add_header Content-Security-Policy "default-src 'self' blob: data:; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; worker-src 'self' blob:; connect-src 'self' blob:; frame-ancestors 'self' *.viktorbarzin.me viktorbarzin.me" always;
- EOF
+ custom_content_security_policy = "default-src 'self' blob: data:; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; worker-src 'self' blob:; connect-src 'self' blob:; frame-ancestors 'self' *.viktorbarzin.me viktorbarzin.me"
}
diff --git a/modules/kubernetes/privatebin/main.tf b/modules/kubernetes/privatebin/main.tf
index 199fe729..0c5a2f4a 100644
--- a/modules/kubernetes/privatebin/main.tf
+++ b/modules/kubernetes/privatebin/main.tf
@@ -89,13 +89,11 @@ resource "kubernetes_service" "privatebin" {
}
module "ingress" {
- source = "../ingress_factory"
- namespace = kubernetes_namespace.privatebin.metadata[0].name
- name = "privatebin"
- host = "pb"
- tls_secret_name = var.tls_secret_name
- rybbit_site_id = "3ae810b0476d"
- additional_configuration_snippet = <<-EOF
- more_set_headers "Content-Security-Policy: script-src 'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval' https://rybbit.viktorbarzin.me";
- EOF
+ source = "../ingress_factory"
+ namespace = kubernetes_namespace.privatebin.metadata[0].name
+ name = "privatebin"
+ host = "pb"
+ tls_secret_name = var.tls_secret_name
+ rybbit_site_id = "3ae810b0476d"
+ custom_content_security_policy = "script-src 'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval' https://rybbit.viktorbarzin.me"
}
diff --git a/modules/kubernetes/real-estate-crawler/main.tf b/modules/kubernetes/real-estate-crawler/main.tf
index b6ef374a..6b6bc7b1 100644
--- a/modules/kubernetes/real-estate-crawler/main.tf
+++ b/modules/kubernetes/real-estate-crawler/main.tf
@@ -211,38 +211,13 @@ resource "kubernetes_ingress_v1" "proxied-ingress" {
name = "realestate-crawler"
namespace = kubernetes_namespace.realestate-crawler.metadata[0].name
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
- "nginx.ingress.kubernetes.io/backend-protocol" = "http"
-
- # "nginx.ingress.kubernetes.io/auth-url" : var.protected ? "http://ak-outpost-authentik-embedded-outpost.authentik.svc.cluster.local:9000/outpost.goauthentik.io/auth/nginx" : null
- # "nginx.ingress.kubernetes.io/auth-signin" : var.protected ? "https://authentik.viktorbarzin.me/outpost.goauthentik.io/start?rd=$scheme%3A%2F%2F$host$escaped_request_uri" : null
- # "nginx.ingress.kubernetes.io/auth-snippet" : var.protected ? "proxy_set_header X-Forwarded-Host $http_host;" : null
-
- "nginx.ingress.kubernetes.io/configuration-snippet" = <<-EOF
- limit_req_status 429;
- limit_conn_status 429;
-
- # Rybbit Analytics
- # Only modify HTML
- sub_filter_types text/html;
- sub_filter_once off;
-
- # Disable compression so sub_filter works
- proxy_set_header Accept-Encoding "";
-
- # Inject analytics before
- sub_filter '' '
-
- ';
- EOF
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd,realestate-crawler-rybbit-analytics@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
-
-
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["wrongmove.viktorbarzin.me"]
secret_name = var.tls_secret_name
@@ -473,3 +448,25 @@ resource "kubernetes_cron_job_v1" "scrape-rightmove" {
}
}
}
+
+# Rybbit analytics middleware for real-estate-crawler
+resource "kubernetes_manifest" "rybbit_analytics" {
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "Middleware"
+ metadata = {
+ name = "rybbit-analytics"
+ namespace = kubernetes_namespace.realestate-crawler.metadata[0].name
+ }
+ spec = {
+ plugin = {
+ rewritebody = {
+ rewrites = [{
+ regex = ""
+ replacement = ""
+ }]
+ }
+ }
+ }
+ }
+}
diff --git a/modules/kubernetes/reverse_proxy/factory/main.tf b/modules/kubernetes/reverse_proxy/factory/main.tf
index 5e758246..b194ff54 100644
--- a/modules/kubernetes/reverse_proxy/factory/main.tf
+++ b/modules/kubernetes/reverse_proxy/factory/main.tf
@@ -22,14 +22,6 @@ variable "max_body_size" {
type = string
default = "50m"
}
-variable "use_proxy_protocol" {
- type = bool
- default = true
-}
-variable "proxy_timeout" {
- type = number
- default = 60
-}
variable "extra_annotations" {
default = {}
}
@@ -37,8 +29,8 @@ variable "rybbit_site_id" {
default = null
type = string
}
-variable "additional_configuration_snippet" {
- default = ""
+variable "custom_content_security_policy" {
+ default = null
type = string
}
@@ -70,94 +62,87 @@ resource "kubernetes_ingress_v1" "proxied-ingress" {
name = var.name
namespace = var.namespace
annotations = merge({
- "nginx.ingress.kubernetes.io/backend-protocol" = "${var.backend_protocol}"
- "kubernetes.io/ingress.class" = "nginx"
- # "nginx.ingress.kubernetes.io/auth-url" : var.protected ? "https://oauth2.viktorbarzin.me/oauth2/auth" : null
- # "nginx.ingress.kubernetes.io/auth-signin" : var.protected ? "https://oauth2.viktorbarzin.me/oauth2/start?rd=/redirect/$http_host$escaped_request_uri" : null
- # Do not do hairpinning
- # "nginx.ingress.kubernetes.io/auth-url" : var.protected ? "http://oauth2.oauth2.svc.cluster.local/oauth2/auth" : null
- # "nginx.ingress.kubernetes.io/auth-signin" : var.protected ? "http://oauth2.oauth2.svc.cluster.local/oauth2/start?rd=/redirect/$http_host$escaped_request_uri" : null
-
- "nginx.ingress.kubernetes.io/auth-url" : var.protected ? "http://ak-outpost-authentik-embedded-outpost.authentik.svc.cluster.local:9000/outpost.goauthentik.io/auth/nginx" : null
- # "nginx.ingress.kubernetes.io/auth-signin" : var.protected ? "https://authentik.viktorbarzin.me/outpost.goauthentik.io/start?rd=$scheme%3A%2F%2F$host$escaped_request_uri" : null
- # "nginx.ingress.kubernetes.io/auth-signin" : var.protected ? "https://authentik.viktorbarzin.me/outpost.goauthentik.io/start?rd=$scheme://$http_host$escaped_request_uri" : null
- "nginx.ingress.kubernetes.io/auth-signin" : var.protected ? "https://authentik.viktorbarzin.me/outpost.goauthentik.io/start?rd=$escaped_request_uri" : null
- "nginx.ingress.kubernetes.io/auth-response-headers" : var.protected ? "Set-Cookie,X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid" : null
- "nginx.ingress.kubernetes.io/auth-snippet" : var.protected ? "proxy_set_header X-Forwarded-Host $http_host;" : null
-
- # # 2. Local Basic Auth Config
- # nginx.ingress.kubernetes.io/auth-type: basic
- # nginx.ingress.kubernetes.io/auth-secret: emergency-basic-auth
- # nginx.ingress.kubernetes.io/auth-realm: "Authentik Down - Use Emergency Login"
-
- # # 3. The Fallback Magic
- # nginx.ingress.kubernetes.io/configuration-snippet: |
- # satisfy any;
- # allow all;
-
- "nginx.ingress.kubernetes.io/proxy-body-size" : var.max_body_size
- "nginx.ingress.kubernetes.io/use-proxy-protocol" : var.use_proxy_protocol
- "nginx.ingress.kubernetes.io/proxy-connect-timeout" : var.proxy_timeout
- "nginx.ingress.kubernetes.io/proxy-send-timeout" : var.proxy_timeout
- "nginx.ingress.kubernetes.io/proxy-read-timeout" : var.proxy_timeout
-
- "nginx.ingress.kubernetes.io/configuration-snippet" = <<-EOF
- limit_req_status 429;
- limit_conn_status 429;
- ${var.additional_configuration_snippet}
- ${var.rybbit_site_id != null ? <<-JS
- # Rybbit Analytics
- # Only modify HTML
- sub_filter_types text/html;
- sub_filter_once off;
-
- # Disable compression so sub_filter works
- proxy_set_header Accept-Encoding "";
-
- # Inject analytics before
- sub_filter '' '
-
- ';
- JS
- : ""
- }
- EOF
-
- }, var.extra_annotations)
-}
-
-spec {
- tls {
- hosts = ["${var.name}.viktorbarzin.me"]
- secret_name = var.tls_secret_name
+ "traefik.ingress.kubernetes.io/router.middlewares" = join(",", compact([
+ "traefik-rate-limit@kubernetescrd",
+ var.custom_content_security_policy == null ? "traefik-csp-headers@kubernetescrd" : null,
+ "traefik-crowdsec@kubernetescrd",
+ var.protected ? "traefik-authentik-forward-auth@kubernetescrd" : null,
+ var.rybbit_site_id != null ? "${var.namespace}-rybbit-analytics-${var.name}@kubernetescrd" : null,
+ var.custom_content_security_policy != null ? "${var.namespace}-custom-csp-${var.name}@kubernetescrd" : null,
+ ]))
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
+ }, var.extra_annotations)
}
- rule {
- host = "${var.name}.viktorbarzin.me"
- http {
- dynamic "path" {
- # for_each = { for pr in var.ingress_path : pr => pr }
- for_each = var.ingress_path
- content {
- path = path.value
- backend {
- service {
+ spec {
+ ingress_class_name = "traefik"
+ tls {
+ hosts = ["${var.name}.viktorbarzin.me"]
+ secret_name = var.tls_secret_name
+ }
+ rule {
+ host = "${var.name}.viktorbarzin.me"
+ http {
+ dynamic "path" {
+ for_each = var.ingress_path
- name = var.name
- port {
- number = var.port
+ content {
+ path = path.value
+ backend {
+ service {
+
+ name = var.name
+ port {
+ number = var.port
+ }
}
}
}
}
}
- # path {
- # # path = var.ingress_path
- # path = each.value
- # }
}
}
}
+
+# Rybbit analytics middleware (rewritebody plugin) - created per service when rybbit_site_id is set
+resource "kubernetes_manifest" "rybbit_analytics" {
+ count = var.rybbit_site_id != null ? 1 : 0
+
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "Middleware"
+ metadata = {
+ name = "rybbit-analytics-${var.name}"
+ namespace = var.namespace
+ }
+ spec = {
+ plugin = {
+ rewritebody = {
+ rewrites = [{
+ regex = ""
+ replacement = ""
+ }]
+ }
+ }
+ }
+ }
+}
+
+# Custom CSP headers middleware - created per service when custom_content_security_policy is set
+resource "kubernetes_manifest" "custom_csp" {
+ count = var.custom_content_security_policy != null ? 1 : 0
+
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "Middleware"
+ metadata = {
+ name = "custom-csp-${var.name}"
+ namespace = var.namespace
+ }
+ spec = {
+ headers = {
+ contentSecurityPolicy = var.custom_content_security_policy
+ }
+ }
+ }
}
diff --git a/modules/kubernetes/reverse_proxy/main.tf b/modules/kubernetes/reverse_proxy/main.tf
index dd5fd4ef..7969b215 100644
--- a/modules/kubernetes/reverse_proxy/main.tf
+++ b/modules/kubernetes/reverse_proxy/main.tf
@@ -76,34 +76,28 @@ module "nas-files" {
# https://idrac.viktorbarzin.me/
module "idrac" {
- source = "./factory"
- name = "idrac"
- external_name = "idrac.viktorbarzin.lan"
- port = 443
- tls_secret_name = var.tls_secret_name
- backend_protocol = "HTTPS"
- extra_annotations = {
- # authentik causes 413; we don't need the header below
- "nginx.ingress.kubernetes.io/auth-response-headers" : null
- }
- depends_on = [kubernetes_namespace.reverse-proxy]
+ source = "./factory"
+ name = "idrac"
+ external_name = "idrac.viktorbarzin.lan"
+ port = 443
+ tls_secret_name = var.tls_secret_name
+ backend_protocol = "HTTPS"
+ extra_annotations = {}
+ depends_on = [kubernetes_namespace.reverse-proxy]
}
# Can either listen on https or http; can't do both :/
# TODO: Not working yet
module "tp-link-gateway" {
- source = "./factory"
- name = "gw"
- external_name = "gw.viktorbarzin.lan"
- port = 443
- tls_secret_name = var.tls_secret_name
- backend_protocol = "HTTPS"
- depends_on = [kubernetes_namespace.reverse-proxy]
- protected = true
- extra_annotations = {
- # authentik causes 413; we don't need the header below
- "nginx.ingress.kubernetes.io/auth-response-headers" : null
- }
+ source = "./factory"
+ name = "gw"
+ external_name = "gw.viktorbarzin.lan"
+ port = 443
+ tls_secret_name = var.tls_secret_name
+ backend_protocol = "HTTPS"
+ depends_on = [kubernetes_namespace.reverse-proxy]
+ protected = true
+ extra_annotations = {}
}
# https://truenas.viktorbarzin.me/
diff --git a/modules/kubernetes/rybbit/main.tf b/modules/kubernetes/rybbit/main.tf
index 8e30f113..ddce51ba 100644
--- a/modules/kubernetes/rybbit/main.tf
+++ b/modules/kubernetes/rybbit/main.tf
@@ -293,35 +293,13 @@ resource "kubernetes_ingress_v1" "rybbit" {
namespace = kubernetes_namespace.rybbit.metadata[0].name
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
- "nginx.ingress.kubernetes.io/use-regex" = "true"
- # Optional: enable SSL redirect
- #"nginx.ingress.kubernetes.io/force-ssl-redirect" = "true"
-
- "nginx.ingress.kubernetes.io/configuration-snippet" = <<-EOF
- limit_req_status 429;
- limit_conn_status 429;
-
- # Rybbit Analytics
- # Only modify HTML
- sub_filter_types text/html;
- sub_filter_once off;
-
- # Disable compression so sub_filter works
- proxy_set_header Accept-Encoding "";
-
- # Inject analytics before
- sub_filter '' '
-
- ';
- EOF
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd,rybbit-rybbit-analytics@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
}
spec {
- ingress_class_name = "nginx"
+ ingress_class_name = "traefik"
tls {
hosts = ["rybbit.viktorbarzin.me"]
secret_name = var.tls_secret_name
@@ -332,7 +310,8 @@ resource "kubernetes_ingress_v1" "rybbit" {
http {
# API backend
path {
- path = "/api(/|$)(.*)"
+ path = "/api"
+ path_type = "Prefix"
backend {
service {
name = "rybbit"
@@ -361,3 +340,25 @@ resource "kubernetes_ingress_v1" "rybbit" {
}
}
}
+
+# Rybbit analytics middleware for self-tracking
+resource "kubernetes_manifest" "rybbit_analytics" {
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "Middleware"
+ metadata = {
+ name = "rybbit-analytics"
+ namespace = kubernetes_namespace.rybbit.metadata[0].name
+ }
+ spec = {
+ plugin = {
+ rewritebody = {
+ rewrites = [{
+ regex = ""
+ replacement = ""
+ }]
+ }
+ }
+ }
+ }
+}
diff --git a/modules/kubernetes/send/main.tf b/modules/kubernetes/send/main.tf
index 6469688b..6f4e0eb7 100644
--- a/modules/kubernetes/send/main.tf
+++ b/modules/kubernetes/send/main.tf
@@ -114,9 +114,5 @@ module "ingress" {
name = "send"
tls_secret_name = var.tls_secret_name
port = 1443
- extra_annotations = {
- "nginx.ingress.kubernetes.io/client-max-body-size" : "0"
- "nginx.ingress.kubernetes.io/proxy-body-size" : "0",
- }
- rybbit_site_id = "c1b8f8aa831b"
+ rybbit_site_id = "c1b8f8aa831b"
}
diff --git a/modules/kubernetes/servarr/qbittorrent/main.tf b/modules/kubernetes/servarr/qbittorrent/main.tf
index 2a473a3e..ed156773 100644
--- a/modules/kubernetes/servarr/qbittorrent/main.tf
+++ b/modules/kubernetes/servarr/qbittorrent/main.tf
@@ -140,7 +140,4 @@ module "ingress" {
name = "qbittorrent"
tls_secret_name = var.tls_secret_name
protected = true
- extra_annotations = {
- "nginx.ingress.kubernetes.io/proxy-body-size" : "1G" // allow uploading .torrent files
- }
}
diff --git a/modules/kubernetes/servarr/readarr/main.tf b/modules/kubernetes/servarr/readarr/main.tf
index 68369b06..23f8844e 100644
--- a/modules/kubernetes/servarr/readarr/main.tf
+++ b/modules/kubernetes/servarr/readarr/main.tf
@@ -123,13 +123,13 @@ resource "kubernetes_ingress_v1" "readarr" {
name = "readarr"
namespace = "readarr"
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
- "nginx.ingress.kubernetes.io/auth-url" : "https://oauth2.viktorbarzin.me/oauth2/auth"
- "nginx.ingress.kubernetes.io/auth-signin" : "https://oauth2.viktorbarzin.me/oauth2/start?rd=/redirect/$http_host$escaped_request_uri"
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd,traefik-authentik-forward-auth@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["readarr.viktorbarzin.me"]
secret_name = var.tls_secret_name
diff --git a/modules/kubernetes/traefik/middleware.tf b/modules/kubernetes/traefik/middleware.tf
new file mode 100644
index 00000000..4b3d9185
--- /dev/null
+++ b/modules/kubernetes/traefik/middleware.tf
@@ -0,0 +1,177 @@
+# Shared Traefik Middleware CRDs
+# These are referenced by ingress resources via annotations like:
+# "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd"
+
+# Rate limiting middleware
+resource "kubernetes_manifest" "middleware_rate_limit" {
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "Middleware"
+ metadata = {
+ name = "rate-limit"
+ namespace = kubernetes_namespace.traefik.metadata[0].name
+ }
+ spec = {
+ rateLimit = {
+ average = 5
+ burst = 250
+ }
+ }
+ }
+
+ depends_on = [helm_release.traefik]
+}
+
+# Authentik forward auth middleware
+resource "kubernetes_manifest" "middleware_authentik_forward_auth" {
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "Middleware"
+ metadata = {
+ name = "authentik-forward-auth"
+ namespace = kubernetes_namespace.traefik.metadata[0].name
+ }
+ spec = {
+ forwardAuth = {
+ address = "http://ak-outpost-authentik-embedded-outpost.authentik.svc.cluster.local:9000/outpost.goauthentik.io/auth/traefik"
+ trustForwardHeader = true
+ authResponseHeaders = [
+ "X-authentik-username",
+ "X-authentik-uid",
+ "X-authentik-email",
+ "X-authentik-name",
+ "X-authentik-groups",
+ "Set-Cookie",
+ ]
+ }
+ }
+ }
+
+ depends_on = [helm_release.traefik]
+}
+
+# IP allowlist for local-only access
+resource "kubernetes_manifest" "middleware_local_only" {
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "Middleware"
+ metadata = {
+ name = "local-only"
+ namespace = kubernetes_namespace.traefik.metadata[0].name
+ }
+ spec = {
+ ipAllowList = {
+ sourceRange = [
+ "192.168.1.0/24",
+ "10.0.0.0/8",
+ "fc00::/7",
+ "fe80::/10",
+ ]
+ }
+ }
+ }
+
+ depends_on = [helm_release.traefik]
+}
+
+# HTTPS redirect middleware
+resource "kubernetes_manifest" "middleware_redirect_https" {
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "Middleware"
+ metadata = {
+ name = "redirect-https"
+ namespace = kubernetes_namespace.traefik.metadata[0].name
+ }
+ spec = {
+ redirectScheme = {
+ scheme = "https"
+ permanent = true
+ }
+ }
+ }
+
+ depends_on = [helm_release.traefik]
+}
+
+# CSP headers middleware (default)
+resource "kubernetes_manifest" "middleware_csp_headers" {
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "Middleware"
+ metadata = {
+ name = "csp-headers"
+ namespace = kubernetes_namespace.traefik.metadata[0].name
+ }
+ spec = {
+ headers = {
+ contentSecurityPolicy = "frame-ancestors 'self' *.viktorbarzin.me viktorbarzin.me"
+ }
+ }
+ }
+
+ depends_on = [helm_release.traefik]
+}
+
+# CrowdSec bouncer plugin middleware
+resource "kubernetes_manifest" "middleware_crowdsec" {
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "Middleware"
+ metadata = {
+ name = "crowdsec"
+ namespace = kubernetes_namespace.traefik.metadata[0].name
+ }
+ spec = {
+ plugin = {
+ crowdsec-bouncer = {
+ crowdsecLapiKey = var.crowdsec_api_key
+ crowdsecLapiHost = "crowdsec-service.crowdsec.svc.cluster.local:8080"
+ crowdsecMode = "stream"
+ }
+ }
+ }
+ }
+
+ depends_on = [helm_release.traefik]
+}
+
+# TLS option for mTLS (client certificate auth)
+resource "kubernetes_manifest" "tls_option_mtls" {
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "TLSOption"
+ metadata = {
+ name = "mtls"
+ namespace = kubernetes_namespace.traefik.metadata[0].name
+ }
+ spec = {
+ clientAuth = {
+ secretNames = ["ca-secret"]
+ clientAuthType = "RequireAndVerifyClientCert"
+ }
+ }
+ }
+
+ depends_on = [helm_release.traefik]
+}
+
+# Immich-specific rate limit (higher limits for photo uploads)
+resource "kubernetes_manifest" "middleware_immich_rate_limit" {
+ manifest = {
+ apiVersion = "traefik.io/v1alpha1"
+ kind = "Middleware"
+ metadata = {
+ name = "immich-rate-limit"
+ namespace = kubernetes_namespace.traefik.metadata[0].name
+ }
+ spec = {
+ rateLimit = {
+ average = 100
+ burst = 1000
+ }
+ }
+ }
+
+ depends_on = [helm_release.traefik]
+}
diff --git a/modules/kubernetes/tuya-bridge/main.tf b/modules/kubernetes/tuya-bridge/main.tf
index 9ed60b19..bc154638 100644
--- a/modules/kubernetes/tuya-bridge/main.tf
+++ b/modules/kubernetes/tuya-bridge/main.tf
@@ -97,13 +97,4 @@ module "ingress" {
namespace = kubernetes_namespace.tuya-bridge.metadata[0].name
name = "tuya-bridge"
tls_secret_name = var.tls_secret_name
-
- extra_annotations = {
- "nginx.ingress.kubernetes.io/server-snippet" : <<-EOF
- location /metrics {
- deny all;
- return 403;
- }
- EOF
- }
}
diff --git a/modules/kubernetes/uptime-kuma/main.tf b/modules/kubernetes/uptime-kuma/main.tf
index 2c5e410b..2a5a3402 100644
--- a/modules/kubernetes/uptime-kuma/main.tf
+++ b/modules/kubernetes/uptime-kuma/main.tf
@@ -98,9 +98,8 @@ module "ingress" {
tls_secret_name = var.tls_secret_name
service_name = "uptime-kuma"
extra_annotations = {
- "nginx.org/websocket-services" = "uptime-kuma"
- "gethomepage.dev/enabled" = "true"
- "gethomepage.dev/description" = "Uptime monitor"
+ "gethomepage.dev/enabled" = "true"
+ "gethomepage.dev/description" = "Uptime monitor"
# gethomepage.dev/group: Media
"gethomepage.dev/icon" : "uptime-kuma.png"
"gethomepage.dev/name" = "Uptime Kuma"
diff --git a/modules/kubernetes/vikunja/main.tf b/modules/kubernetes/vikunja/main.tf
index 15360429..b0b1664e 100644
--- a/modules/kubernetes/vikunja/main.tf
+++ b/modules/kubernetes/vikunja/main.tf
@@ -200,11 +200,13 @@ resource "kubernetes_ingress_v1" "vikunja" {
name = "vikunja"
namespace = kubernetes_namespace.vikunja.metadata[0].name
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["todo.viktorbarzin.me"]
secret_name = var.tls_secret_name
diff --git a/modules/kubernetes/webhook_handler/main.tf b/modules/kubernetes/webhook_handler/main.tf
index fc33c938..c449e7f6 100644
--- a/modules/kubernetes/webhook_handler/main.tf
+++ b/modules/kubernetes/webhook_handler/main.tf
@@ -194,11 +194,13 @@ resource "kubernetes_ingress_v1" "webhook_handler" {
name = "webhook-handler-ingress"
namespace = kubernetes_namespace.webhook-handler.metadata[0].name
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["webhook.viktorbarzin.me"]
secret_name = var.tls_secret_name
diff --git a/modules/kubernetes/xray/main.tf b/modules/kubernetes/xray/main.tf
index b2538ddc..10c666e1 100644
--- a/modules/kubernetes/xray/main.tf
+++ b/modules/kubernetes/xray/main.tf
@@ -188,14 +188,13 @@ resource "kubernetes_ingress_v1" "ingress" {
namespace = kubernetes_namespace.xray.metadata[0].name
name = "xray"
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
- "nginx.ingress.kubernetes.io/backend-protocol" = "HTTP"
- "nginx.org/websocket-services" : "xray"
- "nginx.ingress.kubernetes.io/enable-access-log" = "false"
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["xray-ws.viktorbarzin.me"]
secret_name = var.tls_secret_name
@@ -224,15 +223,14 @@ resource "kubernetes_ingress_v1" "ingress-grpc" {
namespace = kubernetes_namespace.xray.metadata[0].name
name = "xray-grpc"
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
- "nginx.ingress.kubernetes.io/enable-access-log" = "false"
- "nginx.ingress.kubernetes.io/backend-protocol" = "GRPC"
- "nginx.ingress.kubernetes.io/proxy-read-timeout" = "3600"
- "nginx.ingress.kubernetes.io/proxy-send-timeout" = "3600"
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
+ "traefik.ingress.kubernetes.io/service.serversscheme" = "h2c"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["xray-grpc.viktorbarzin.me"]
secret_name = var.tls_secret_name
@@ -262,11 +260,13 @@ resource "kubernetes_ingress_v1" "ingress-vless" {
namespace = kubernetes_namespace.xray.metadata[0].name
name = "xray-vless"
annotations = {
- "kubernetes.io/ingress.class" = "nginx"
+ "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-rate-limit@kubernetescrd,traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd"
+ "traefik.ingress.kubernetes.io/router.entrypoints" = "websecure"
}
}
spec {
+ ingress_class_name = "traefik"
tls {
hosts = ["xray-vless.viktorbarzin.me"]
secret_name = var.tls_secret_name
diff --git a/modules/kubernetes/youtube_dl/main.tf b/modules/kubernetes/youtube_dl/main.tf
index 1721421c..156cc66a 100644
--- a/modules/kubernetes/youtube_dl/main.tf
+++ b/modules/kubernetes/youtube_dl/main.tf
@@ -126,10 +126,6 @@ module "ingress" {
name = "ytdlp"
tls_secret_name = var.tls_secret_name
host = "yt"
- extra_annotations = {
- "nginx.ingress.kubernetes.io/client-max-body-size" : "0"
- "nginx.ingress.kubernetes.io/proxy-body-size" : "0",
- }
}
# ----------------------
@@ -321,8 +317,4 @@ module "highlights_ingress" {
tls_secret_name = var.tls_secret_name
host = "yt-highlights"
protected = true
- extra_annotations = {
- "nginx.ingress.kubernetes.io/proxy-read-timeout" : "300"
- "nginx.ingress.kubernetes.io/proxy-send-timeout" : "300"
- }
}