Addes support for reverse proxy to home server
This commit is contained in:
@@ -3,7 +3,7 @@ terraform {
|
||||
required_providers {
|
||||
kubernetes = {
|
||||
source = "hashicorp/kubernetes"
|
||||
version = "~>2.18"
|
||||
version = "2.36.0"
|
||||
}
|
||||
scaleway = {
|
||||
source = "scaleway/scaleway"
|
||||
|
||||
@@ -3,7 +3,7 @@ terraform {
|
||||
required_providers {
|
||||
kubernetes = {
|
||||
source = "hashicorp/kubernetes"
|
||||
version = "~>2.18"
|
||||
version = "2.36.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ terraform {
|
||||
required_providers {
|
||||
kubernetes = {
|
||||
source = "hashicorp/kubernetes"
|
||||
version = "~>2.18"
|
||||
version = "2.36.0"
|
||||
}
|
||||
scaleway = {
|
||||
source = "scaleway/scaleway"
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
locals {
|
||||
wireguard_version = "1.0.20210914-r4-ls54"
|
||||
wireguard_config = try(templatefile("${path.module}/resources/wireguard/wireguard.conf.tpl", {
|
||||
wireguard_private_key = var.wireguard_config.private_key
|
||||
wireguard_public_key = var.wireguard_config.public_key
|
||||
wireguard_pre_shared_key = var.wireguard_config.pre_shared_key
|
||||
wireguard_address = var.wireguard_config.address
|
||||
wireguard_dns = var.wireguard_config.dns
|
||||
wireguard_allowed_ips = join(", ", var.wireguard_config.allowed_ips)
|
||||
wireguard_endpoint = "${var.wireguard_config.endpoint}:${var.wireguard_config.endpoint_port}"
|
||||
}), "null")
|
||||
wireguard_config_checksum = nonsensitive(sha256(local.wireguard_config))
|
||||
|
||||
default_headers = {
|
||||
"Host" : "$host",
|
||||
"X-Real-IP" : "$remote_addr",
|
||||
"X-Forwarded-For" : "$proxy_add_x_forwarded_for",
|
||||
"X-Forwarded-Proto" : "$scheme",
|
||||
"X-Forwarded-Host" : "$host",
|
||||
"X-Forwarded-Port" : "$server_port",
|
||||
}
|
||||
nginx_config = file("${path.module}/resources/nginx/nginx.conf")
|
||||
nginx_config_checksum = sha256(local.nginx_config)
|
||||
|
||||
services_config = {
|
||||
for k, v in var.services : "${k}.conf" => coalesce(v.config, templatefile("${path.module}/resources/nginx/conf.d/default.conf.tpl", {
|
||||
http_port = v.http_port
|
||||
https_port = v.https_port
|
||||
upstream = v.upstream
|
||||
tls = v.tls
|
||||
fqdn = join(" ", v.fqdn)
|
||||
locations = [for l in v.locations : {
|
||||
path = l.path
|
||||
headers = merge(l.headers, coalesce(v.default_headers, local.default_headers))
|
||||
}]
|
||||
custom_snippet = v.custom_snippet
|
||||
}))
|
||||
}
|
||||
services_config_checksum = sha256(jsonencode(local.services_config))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
resource "kubernetes_namespace" "reverse_proxy" {
|
||||
metadata {
|
||||
annotations = {
|
||||
name = "reverse-proxy"
|
||||
}
|
||||
|
||||
labels = {
|
||||
name = "reverse-proxy"
|
||||
}
|
||||
|
||||
name = "reverse-proxy"
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_service_account" "reverse-proxy_service_account" {
|
||||
|
||||
metadata {
|
||||
name = "reverse-proxy-app-sa"
|
||||
namespace = kubernetes_namespace.reverse_proxy.metadata[0].name
|
||||
}
|
||||
|
||||
automount_service_account_token = false
|
||||
}
|
||||
|
||||
resource "kubernetes_service" "reverse-proxy" {
|
||||
for_each = var.services
|
||||
metadata {
|
||||
name = format("reverse-proxy-%s", replace(each.key, ".", "-"))
|
||||
namespace = kubernetes_namespace.reverse_proxy.metadata[0].name
|
||||
}
|
||||
spec {
|
||||
port {
|
||||
name = "http"
|
||||
port = 80
|
||||
target_port = substr(format("http-%s", replace(each.key, ".", "-")), 0, 15)
|
||||
}
|
||||
dynamic "port" {
|
||||
for_each = each.value.tls ? [1] : []
|
||||
content {
|
||||
name = "https"
|
||||
port = 443
|
||||
target_port = substr(format("https-%s", replace(each.key, ".", "-")), 0, 15)
|
||||
}
|
||||
}
|
||||
selector = {
|
||||
app = "reverse-proxy"
|
||||
}
|
||||
cluster_ip = "None"
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_manifest" "internal_cert" {
|
||||
|
||||
manifest = {
|
||||
apiVersion = "cert-manager.io/v1"
|
||||
kind = "Certificate"
|
||||
metadata = {
|
||||
name = "internal-cert"
|
||||
namespace = kubernetes_namespace.reverse_proxy.metadata[0].name
|
||||
}
|
||||
spec = {
|
||||
secretName = "internal-cert"
|
||||
issuerRef = {
|
||||
name = var.issuer_name
|
||||
kind = "ClusterIssuer"
|
||||
}
|
||||
commonName = "reverse-proxy.internal"
|
||||
dnsNames = flatten([for _, v in var.services : v.fqdn if v.tls == true])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_config_map" "wireguard_config" {
|
||||
count = local.wireguard_config != "null" ? 1 : 0
|
||||
metadata {
|
||||
name = "wireguard-config"
|
||||
namespace = kubernetes_namespace.reverse_proxy.metadata[0].name
|
||||
}
|
||||
|
||||
data = {
|
||||
"wireguard.conf" = local.wireguard_config
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_config_map" "nginx_config" {
|
||||
metadata {
|
||||
name = "nginx-config"
|
||||
namespace = kubernetes_namespace.reverse_proxy.metadata[0].name
|
||||
}
|
||||
|
||||
data = {
|
||||
"nginx.conf" = local.nginx_config
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_config_map" "nginx_default_config" {
|
||||
metadata {
|
||||
name = "nginx-default-config"
|
||||
namespace = kubernetes_namespace.reverse_proxy.metadata[0].name
|
||||
}
|
||||
|
||||
data = local.services_config
|
||||
}
|
||||
|
||||
resource "kubernetes_ingress_v1" "reverse-proxy_ingress" {
|
||||
for_each = var.services
|
||||
metadata {
|
||||
name = "reverse-proxy-ingress"
|
||||
namespace = kubernetes_namespace.reverse_proxy.metadata[0].name
|
||||
|
||||
annotations = merge({
|
||||
"kubernetes.io/ingress.class" = "public"
|
||||
"cert-manager.io/cluster-issuer" = "letsencrypt-prod"
|
||||
},
|
||||
each.value.tls ? {
|
||||
"nginx.org/ssl-services" = format("reverse-proxy-%s", replace(each.key, ".", "-"))
|
||||
"nginx.org/ssl-redirect" = "true"
|
||||
"nginx.org/redirect-to-https" = "true"
|
||||
"nginx.ingress.kubernetes.io/backend-protocol" = "HTTPS"
|
||||
} : {})
|
||||
}
|
||||
spec {
|
||||
tls {
|
||||
hosts = each.value.fqdn
|
||||
secret_name = format("reverse-proxy-%s-tls", replace(each.key, ".", "-"))
|
||||
}
|
||||
dynamic "rule" {
|
||||
for_each = each.value.fqdn
|
||||
content {
|
||||
host = rule.value
|
||||
http {
|
||||
path {
|
||||
backend {
|
||||
service {
|
||||
name = format("reverse-proxy-%s", replace(each.key, ".", "-"))
|
||||
port {
|
||||
name = each.value.tls ? "https" : "http"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_deployment" "reverse_proxy" {
|
||||
|
||||
metadata {
|
||||
name = "reverse-proxy"
|
||||
namespace = kubernetes_namespace.reverse_proxy.metadata[0].name
|
||||
|
||||
annotations = {
|
||||
"security.alpha.kubernetes.io/unsafe-sysctls" = "net.ipv4.conf.all.src_valid_mark=1"
|
||||
}
|
||||
}
|
||||
|
||||
spec {
|
||||
replicas = 1
|
||||
|
||||
selector {
|
||||
match_labels = {
|
||||
app = "reverse-proxy"
|
||||
}
|
||||
}
|
||||
|
||||
strategy {
|
||||
type = "Recreate"
|
||||
}
|
||||
|
||||
template {
|
||||
metadata {
|
||||
labels = {
|
||||
app = "reverse-proxy"
|
||||
}
|
||||
annotations = {
|
||||
"checksum/nginx" = local.nginx_config_checksum
|
||||
"checksum/services" = local.services_config_checksum
|
||||
"checksum/wireguard" = local.wireguard_config_checksum
|
||||
}
|
||||
}
|
||||
spec {
|
||||
dynamic "container" {
|
||||
for_each = local.wireguard_config != "null" ? [1] : []
|
||||
content {
|
||||
|
||||
name = "wireguard"
|
||||
image = "linuxserver/wireguard:${local.wireguard_version}"
|
||||
|
||||
resources {
|
||||
requests = {
|
||||
memory = "64Mi"
|
||||
}
|
||||
}
|
||||
|
||||
liveness_probe {
|
||||
exec {
|
||||
command = [
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
"wg show | grep -q transfer"
|
||||
]
|
||||
}
|
||||
initial_delay_seconds = 90
|
||||
period_seconds = 90
|
||||
}
|
||||
|
||||
security_context {
|
||||
privileged = true
|
||||
capabilities {
|
||||
add = ["NET_ADMIN"]
|
||||
}
|
||||
}
|
||||
|
||||
env {
|
||||
name = "PUID"
|
||||
value = "1000"
|
||||
}
|
||||
|
||||
env {
|
||||
name = "PGID"
|
||||
value = "1000"
|
||||
}
|
||||
|
||||
env {
|
||||
name = "TZ"
|
||||
value = "Europe/Berlin"
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
name = "wireguard-config"
|
||||
mount_path = "/config/wg_confs"
|
||||
read_only = true
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
name = "lib-modules"
|
||||
mount_path = "/lib/modules"
|
||||
read_only = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
container {
|
||||
name = "reverse-proxy"
|
||||
image = "nginx:${var.tag}"
|
||||
image_pull_policy = "Always"
|
||||
|
||||
resources {
|
||||
requests = {
|
||||
memory = "100Mi"
|
||||
}
|
||||
}
|
||||
|
||||
readiness_probe {
|
||||
http_get {
|
||||
path = "/"
|
||||
port = 8080
|
||||
}
|
||||
initial_delay_seconds = 5
|
||||
period_seconds = 10
|
||||
}
|
||||
|
||||
liveness_probe {
|
||||
http_get {
|
||||
path = "/"
|
||||
port = 8080
|
||||
}
|
||||
initial_delay_seconds = 5
|
||||
period_seconds = 10
|
||||
}
|
||||
|
||||
security_context {
|
||||
allow_privilege_escalation = false
|
||||
}
|
||||
|
||||
dynamic "port" {
|
||||
for_each = { for k, v in var.services : k => v.http_port }
|
||||
content {
|
||||
container_port = port.value
|
||||
name = substr(format("http-%s", replace(port.key, ".", "-")), 0, 15)
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "port" {
|
||||
for_each = { for k, v in var.services : k => v.https_port if v.tls }
|
||||
content {
|
||||
container_port = port.value
|
||||
name = substr(format("https-%s", replace(port.key, ".", "-")), 0, 15)
|
||||
}
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
name = "nginx"
|
||||
mount_path = "/etc/nginx/nginx.conf"
|
||||
sub_path = "nginx.conf"
|
||||
read_only = true
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
name = "services"
|
||||
mount_path = "/etc/nginx/conf.d/"
|
||||
read_only = true
|
||||
}
|
||||
|
||||
dynamic "volume_mount" {
|
||||
for_each = length([for v in var.services : v.tls if v.tls == true]) > 0 ? [1] : []
|
||||
content {
|
||||
name = "tls"
|
||||
mount_path = "/etc/ssl/certs/private"
|
||||
read_only = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "volume" {
|
||||
for_each = local.wireguard_config != "null" ? [1] : []
|
||||
content {
|
||||
name = "wireguard-config"
|
||||
config_map {
|
||||
name = kubernetes_config_map.wireguard_config[0].metadata[0].name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "volume" {
|
||||
for_each = local.wireguard_config != "null" ? [1] : []
|
||||
content {
|
||||
name = "lib-modules"
|
||||
host_path {
|
||||
path = "/lib/modules"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
volume {
|
||||
name = "nginx"
|
||||
config_map {
|
||||
name = kubernetes_config_map.nginx_config.metadata[0].name
|
||||
}
|
||||
}
|
||||
|
||||
volume {
|
||||
name = "services"
|
||||
config_map {
|
||||
name = kubernetes_config_map.nginx_default_config.metadata[0].name
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "volume" {
|
||||
for_each = length([for v in var.services : v.tls if v.tls == true]) > 0 ? [1] : []
|
||||
content {
|
||||
name = "tls"
|
||||
secret {
|
||||
secret_name = kubernetes_manifest.internal_cert.manifest["metadata"]["name"]
|
||||
items {
|
||||
key = "tls.crt"
|
||||
path = "tls.crt"
|
||||
}
|
||||
items {
|
||||
key = "tls.key"
|
||||
path = "tls.key"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
ignore_changes = [spec[0].template[0].metadata[0].annotations["kubectl.kubernetes.io/restartedAt"]]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
terraform {
|
||||
required_version = "~>1.8"
|
||||
required_providers {
|
||||
kubernetes = {
|
||||
source = "hashicorp/kubernetes"
|
||||
version = "2.36.0"
|
||||
}
|
||||
scaleway = {
|
||||
source = "scaleway/scaleway"
|
||||
version = "~>2.13"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
server {
|
||||
listen ${http_port};
|
||||
%{ if tls ~}
|
||||
listen ${https_port} ssl;
|
||||
ssl_certificate /etc/ssl/certs/private/tls.crt;
|
||||
ssl_certificate_key /etc/ssl/certs/private/tls.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
%{ endif ~}
|
||||
server_name ${fqdn};
|
||||
%{ if custom_snippet != "" ~}
|
||||
${custom_snippet}
|
||||
%{ endif ~}
|
||||
%{ for l in locations ~}
|
||||
location ${l.path} {
|
||||
proxy_pass ${upstream};
|
||||
proxy_http_version 1.1;
|
||||
%{ for k,v in l.headers ~}
|
||||
proxy_set_header ${k} ${v};
|
||||
%{ endfor ~}
|
||||
}
|
||||
%{ endfor ~}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log notice;
|
||||
pid /tmp/nginx.pid;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
proxy_temp_path /tmp/proxy_temp;
|
||||
client_body_temp_path /tmp/client_temp;
|
||||
fastcgi_temp_path /tmp/fastcgi_temp;
|
||||
uwsgi_temp_path /tmp/uwsgi_temp;
|
||||
scgi_temp_path /tmp/scgi_temp;
|
||||
|
||||
client_max_body_size 10M;
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
[Interface]
|
||||
PrivateKey = ${wireguard_private_key}
|
||||
Address = ${wireguard_address}
|
||||
|
||||
[Peer]
|
||||
PublicKey = ${wireguard_public_key}
|
||||
PresharedKey = ${wireguard_pre_shared_key}
|
||||
AllowedIPs = ${wireguard_allowed_ips}
|
||||
Endpoint = ${wireguard_endpoint}
|
||||
PersistentKeepalive = 25
|
||||
@@ -0,0 +1,45 @@
|
||||
variable "tag" {
|
||||
description = "The version of the nginx server to install"
|
||||
type = string
|
||||
default = "stable-alpine"
|
||||
}
|
||||
|
||||
variable "wireguard_config" {
|
||||
description = "The Wireguard configuration file"
|
||||
type = object({
|
||||
private_key = string
|
||||
public_key = string
|
||||
pre_shared_key = string
|
||||
endpoint = string
|
||||
endpoint_port = number
|
||||
address = string
|
||||
dns = string
|
||||
allowed_ips = list(string)
|
||||
})
|
||||
default = null
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "issuer_name" {
|
||||
description = "The name of the issuer to use for TLS certificates, internal certificates"
|
||||
type = string
|
||||
default = "internal-ca"
|
||||
}
|
||||
|
||||
variable "services" {
|
||||
description = "A list of services to be deployed"
|
||||
type = map(object({
|
||||
fqdn = list(string)
|
||||
upstream = string
|
||||
default_headers = optional(map(string), {}),
|
||||
http_port = optional(number, 80)
|
||||
https_port = optional(number, 443)
|
||||
tls = optional(bool, false)
|
||||
locations = optional(list(object({
|
||||
path = string,
|
||||
headers = optional(map(string), {}),
|
||||
})), [])
|
||||
custom_snippet = optional(string, "")
|
||||
config = optional(string, "")
|
||||
}))
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
locals {
|
||||
persistent_folder = var.persistent_folder
|
||||
websites_path = "${local.persistent_folder}/web"
|
||||
websites = var.websites
|
||||
|
||||
nginx_config = file("${path.module}/resources/nginx.conf")
|
||||
nginx_config_checksum = sha256(local.nginx_config)
|
||||
|
||||
@@ -23,7 +23,7 @@ resource "kubernetes_service_account" "website_service_account" {
|
||||
}
|
||||
|
||||
resource "kubernetes_service" "website" {
|
||||
for_each = local.websites
|
||||
for_each = var.websites
|
||||
metadata {
|
||||
name = format("website-%s", replace(each.key, ".", "-"))
|
||||
namespace = kubernetes_namespace.website.metadata[0].name
|
||||
@@ -35,7 +35,7 @@ resource "kubernetes_service" "website" {
|
||||
target_port = "http"
|
||||
}
|
||||
dynamic "port" {
|
||||
for_each = each.value.tls ? [443] : []
|
||||
for_each = each.value.tls ? [1] : []
|
||||
content {
|
||||
name = "https"
|
||||
port = 443
|
||||
@@ -51,7 +51,7 @@ resource "kubernetes_service" "website" {
|
||||
|
||||
resource "kubernetes_manifest" "internal_cert" {
|
||||
for_each = {
|
||||
for k, v in local.websites : k => v if v.tls == true
|
||||
for k, v in var.websites : k => v if v.tls == true
|
||||
}
|
||||
manifest = {
|
||||
apiVersion = "cert-manager.io/v1"
|
||||
@@ -84,7 +84,7 @@ resource "kubernetes_config_map" "nginx_config" {
|
||||
}
|
||||
|
||||
resource "kubernetes_config_map" "nginx_default_config" {
|
||||
for_each = local.websites
|
||||
for_each = var.websites
|
||||
metadata {
|
||||
name = format("website-%s-default", replace(each.key, ".", "-"))
|
||||
namespace = kubernetes_namespace.website.metadata[0].name
|
||||
@@ -100,7 +100,7 @@ resource "kubernetes_config_map" "nginx_default_config" {
|
||||
}
|
||||
|
||||
resource "kubernetes_ingress_v1" "website_ingress" {
|
||||
for_each = local.websites
|
||||
for_each = var.websites
|
||||
metadata {
|
||||
name = "website-ingress"
|
||||
namespace = kubernetes_namespace.website.metadata[0].name
|
||||
@@ -144,7 +144,7 @@ resource "kubernetes_ingress_v1" "website_ingress" {
|
||||
}
|
||||
|
||||
resource "kubernetes_deployment" "website" {
|
||||
for_each = local.websites
|
||||
for_each = var.websites
|
||||
|
||||
metadata {
|
||||
name = "website-app"
|
||||
|
||||
@@ -3,7 +3,7 @@ terraform {
|
||||
required_providers {
|
||||
kubernetes = {
|
||||
source = "hashicorp/kubernetes"
|
||||
version = "~>2.18"
|
||||
version = "2.36.0"
|
||||
}
|
||||
scaleway = {
|
||||
source = "scaleway/scaleway"
|
||||
|
||||
@@ -3,7 +3,7 @@ terraform {
|
||||
required_providers {
|
||||
kubernetes = {
|
||||
source = "hashicorp/kubernetes"
|
||||
version = "~>2.18"
|
||||
version = "2.36.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ terraform {
|
||||
required_providers {
|
||||
kubernetes = {
|
||||
source = "hashicorp/kubernetes"
|
||||
version = "~>2.18"
|
||||
version = "2.36.0"
|
||||
}
|
||||
scaleway = {
|
||||
source = "scaleway/scaleway"
|
||||
|
||||
@@ -3,7 +3,7 @@ terraform {
|
||||
required_providers {
|
||||
kubernetes = {
|
||||
source = "hashicorp/kubernetes"
|
||||
version = "~>2.18"
|
||||
version = "2.36.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user