Setup gatewayapi, ssl certs managed by gcloud, n8n. Terraform is also set up

This commit is contained in:
sirius0xdev 2026-01-03 03:47:18 +00:00
parent cae4e350b3
commit 824bd2d112
21 changed files with 449 additions and 60 deletions

View file

@ -0,0 +1,18 @@
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: external-http-gateway
namespace: n8n
spec:
gatewayClassName: gke-l7-global-external-managed # Use gke-l7-global-external-http for external, or gke-l7-rilb for internal
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
options:
networking.gke.io/pre-shared-certs: n8n-sirius-sec-com
allowedRoutes:
namespaces:
from: All

View file

@ -0,0 +1,21 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: n8n-config
namespace: n8n
data:
N8N_PORT: "5678"
WEBHOOK_URL: "n8n.sirius-sec.com"
N8N_LISTEN_ADDRESS: "0.0.0.0"
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: "true"
N8N_SECURE_COOKIE: "true"
N8N_METRICS: "true"
QUEUE_HEALTH_CHECK_ACTIVE: "true"
DB_TYPE: "postgresdb"
DB_POSTGRESDB_HOST: "35.188.139.22"
DB_POSTGRESDB_PORT: "5432"
DB_POSTGRESDB_DATABASE: "n8ndb"
DB_POSTGRESDB_USER: "sirius"

View file

@ -0,0 +1,68 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: n8n
namespace: n8n
labels:
app: n8n
spec:
selector:
matchLabels:
app: n8n
replicas: 1
strategy:
rollingUpdate:
type: RollingUpdate
template:
metadata:
labels:
app: n8n
spec:
# initContainers:
# Init containers are exactly like regular containers, except:
# - Init containers always run to completion.
# - Each init container must complete successfully before the next one starts.
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
containers:
- name: n8n
image: docker.n8n.io/n8nio/n8n:2.1.4
imagePullPolicy: IfNotPresent
resources:
requests:
memory: "500Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500"
securityContext:
allowPrivilegeEscalation: false
envFrom:
- configMapRef:
name: n8n-config
- secretRef:
name: n8n-secret
ports:
- containerPort: 5678
name: n8n-port
volumeMounts:
- name: n8n-data
mountPath: /home/node/.n8n
volumes:
- name: n8n-data
persistentVolumeClaim:
claimName: n8n-data
restartPolicy: Always

View file

@ -0,0 +1,16 @@
apiVersion: networking.gke.io/v1
kind: HealthCheckPolicy
metadata:
name: n8n-healthcheck
namespace: n8n
spec:
default:
config:
type: HTTP
httpHealthCheck:
port: 5678 # The port n8n is listening on
requestPath: /healthz # Ensure this returns a 200 OK
targetRef:
group: ""
kind: Service
name: n8n-service # Must match your n8n Service name

View file

@ -0,0 +1,15 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: n8n-route
namespace: n8n
spec:
parentRefs:
- name: external-http-gateway
hostnames:
- "n8n.sirius-sec.com"
rules:
- backendRefs:
- name: n8n-service # The name of your Kubernetes Service
port: 80

View file

@ -0,0 +1,10 @@
apiVersion: kustomize.config.k8s.io/v1beta
kind: Kustomization
namespace: n8n
resources:
- namespace.yaml
- deployment.yaml
- configmap.yaml
- service.yaml
- storage.yaml

View file

@ -0,0 +1,8 @@
apiVersion: v1
data:
DB_POSTGRESDB_PASSWORD: RzIzPWFRKjhpcnRmKnhLTA==
kind: Secret
metadata:
creationTimestamp: null
name: n8n-secret
namespace: n8n

View file

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: n8n

View file

@ -0,0 +1,9 @@
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-n8n
spec:
instances: 1
storage:
size: 1G

View file

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: n8n-service
namespace: n8n
annotations:
cloud.google.com: '{"ingress": true}'
spec:
selector:
app: n8n
ports:
- port: 80
targetPort: 5678
name: http

View file

@ -0,0 +1,13 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: n8n-data
namespace: n8n
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi

21
helm.tf Normal file
View file

@ -0,0 +1,21 @@
resource "helm_release" "cilium" {
name = "cilium"
repository = "https://helm.cilium.io"
chart = "cilium"
namespace = "kube-system"
version = "1.18.5"
values = [
yamlencode({
ingressController = {
enabled = true
default = true
}
hubble = {
enabled = true
relay = { enabled = true }
ui = { enabled = true }
}
})
]
}

60
main.tf
View file

@ -1,60 +0,0 @@
# Configure the Google Cloud provider
provider "google" {
credentials = file("~/.config/gcloud/application_default_credentials.json")
project = "devops-lab-cluster"
region = "us-central1" # Or your desired region/location
}
resource "google_compute_network" "default" {
name = "devops-lab-network"
auto_create_subnetworks = false
enable_ula_internal_ipv6 = true
}
resource "google_compute_subnetwork" "default" {
name = "devops-lab-subnetwork"
ip_cidr_range = "10.0.0.0/16"
region = "us-central1"
stack_type = "IPV4_IPV6"
ipv6_access_type = "INTERNAL" # Change to "EXTERNAL" if creating an external loadbalancer
network = google_compute_network.default.id
secondary_ip_range {
range_name = "services-range"
ip_cidr_range = "192.168.0.0/24"
}
secondary_ip_range {
range_name = "pod-ranges"
ip_cidr_range = "192.168.1.0/24"
}
}
resource "google_container_cluster" "default" {
name = "devops-lab-cluster"
location = "us-central1-a"
# 1. Enable Cilium via Dataplane V2
datapath_provider = "ADVANCED_DATAPATH"
initial_node_count = 1
# 2. REQUIRED: Remove the network_policy block.
# Dataplane V2 handles policies natively.
# 3. Optional: Enable Cilium-specific cluster-wide policies (GKE 1.28+)
enable_cilium_clusterwide_network_policy = true
network = google_compute_network.default.id
subnetwork = google_compute_subnetwork.default.id
ip_allocation_policy {
stack_type = "IPV4_IPV6"
services_secondary_range_name = google_compute_subnetwork.default.secondary_ip_range[0].range_name
cluster_secondary_range_name = google_compute_subnetwork.default.secondary_ip_range[1].range_name
}
enable_l4_ilb_subsetting = true
deletion_protection = false
}

View file

@ -1,4 +1,5 @@
[tools] [tools]
cilium-cli = "latest"
gcloud = "latest" gcloud = "latest"
k9s = "latest" k9s = "latest"
terraform = "latest" terraform = "latest"

59
modules/.terraform.lock.hcl generated Normal file
View file

@ -0,0 +1,59 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/google" {
version = "7.14.1"
hashes = [
"h1:PWld5LsERFpdHnINaCgXebs2oQtm2T09Ls/0OUASk0A=",
"zh:0006182db112098af8514fc38d9cd4e816da4145a2a0b9fb62cc9e281eb2b2a1",
"zh:60311d9770ca26c549af9a964ee6cb60ce7541b52fedfaf5f112b0931e6bcce1",
"zh:65b400c0718f6b7c5cd0fba1b2e3696d5f4f69868229627b11b0b2b94b613ade",
"zh:9ec00812dc750687610140f9a97c374492ef320eddcb669b154e1d2e8714f7f3",
"zh:adaf0486d68da121886992a3762cedffa86b611fa43294359b2a569044c462a7",
"zh:ba95c0d8279dd8e7b9294e521e461d4adaa7c171b00502be197b6c7ff4f07d65",
"zh:c216ca4b350a90c4e74e3f502ef3f35617cdd5c278e2b04ecba2bca980fb5e96",
"zh:dd7991a71477dee46c7c57f60775341524271c425ab04e66d8f2762f9b4763eb",
"zh:dd7b63b40e67b073d2acb32ee60099d884ce75bf1152a307422c47358054d170",
"zh:e5d601ca4ab813c51d897e4c2e80bf3e3565c0dd4f37f85bb91964e90ca92dfe",
"zh:f12d8f91ed783ffac9ed8d6c331e0cbe5189455fe352ba633b171b366f52e2cd",
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
]
}
provider "registry.terraform.io/hashicorp/helm" {
version = "3.1.1"
hashes = [
"h1:5b2ojWKT0noujHiweCds37ZreRFRQLNaErdJLusJN88=",
"zh:1a6d5ce931708aec29d1f3d9e360c2a0c35ba5a54d03eeaff0ce3ca597cd0275",
"zh:3411919ba2a5941801e677f0fea08bdd0ae22ba3c9ce3309f55554699e06524a",
"zh:81b36138b8f2320dc7f877b50f9e38f4bc614affe68de885d322629dd0d16a29",
"zh:95a2a0a497a6082ee06f95b38bd0f0d6924a65722892a856cfd914c0d117f104",
"zh:9d3e78c2d1bb46508b972210ad706dd8c8b106f8b206ecf096cd211c54f46990",
"zh:a79139abf687387a6efdbbb04289a0a8e7eaca2bd91cdc0ce68ea4f3286c2c34",
"zh:aaa8784be125fbd50c48d84d6e171d3fb6ef84a221dbc5165c067ce05faab4c8",
"zh:afecd301f469975c9d8f350cc482fe656e082b6ab0f677d1a816c3c615837cc1",
"zh:c54c22b18d48ff9053d899d178d9ffef7d9d19785d9bf310a07d648b7aac075b",
"zh:db2eefd55aea48e73384a555c72bac3f7d428e24147bedb64e1a039398e5b903",
"zh:ee61666a233533fd2be971091cecc01650561f1585783c381b6f6e8a390198a4",
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.7.2"
hashes = [
"h1:356j/3XnXEKr9nyicLUufzoF4Yr6hRy481KIxRVpK0c=",
"zh:14829603a32e4bc4d05062f059e545a91e27ff033756b48afbae6b3c835f508f",
"zh:1527fb07d9fea400d70e9e6eb4a2b918d5060d604749b6f1c361518e7da546dc",
"zh:1e86bcd7ebec85ba336b423ba1db046aeaa3c0e5f921039b3f1a6fc2f978feab",
"zh:24536dec8bde66753f4b4030b8f3ef43c196d69cccbea1c382d01b222478c7a3",
"zh:29f1786486759fad9b0ce4fdfbbfece9343ad47cd50119045075e05afe49d212",
"zh:4d701e978c2dd8604ba1ce962b047607701e65c078cb22e97171513e9e57491f",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:7b8434212eef0f8c83f5a90c6d76feaf850f6502b61b53c329e85b3b281cba34",
"zh:ac8a23c212258b7976e1621275e3af7099e7e4a3d4478cf8d5d2a27f3bc3e967",
"zh:b516ca74431f3df4c6cf90ddcdb4042c626e026317a33c53f0b445a3d93b720d",
"zh:dc76e4326aec2490c1600d6871a95e78f9050f9ce427c71707ea412a2f2f1a62",
"zh:eac7b63e86c749c7d48f527671c7aee5b4e26c10be6ad7232d6860167f99dbb0",
]
}

29
modules/gke.tf Normal file
View file

@ -0,0 +1,29 @@
resource "google_container_cluster" "default" {
name = "devops-lab-cluster"
location = "us-central1-a"
initial_node_count = 1
datapath_provider = "ADVANCED_DATAPATH"
enable_cilium_clusterwide_network_policy = true
remove_default_node_pool = true
network = google_compute_network.default.id
subnetwork = google_compute_subnetwork.default.id
node_config {
taint {
key = "node.cilium.io/agent-not-ready"
value = "true"
effect = "NO_EXECUTE"
}
}
ip_allocation_policy {
stack_type = "IPV4_IPV6"
services_secondary_range_name = google_compute_subnetwork.default.secondary_ip_range[0].range_name
cluster_secondary_range_name = google_compute_subnetwork.default.secondary_ip_range[1].range_name
}
enable_l4_ilb_subsetting = true
deletion_protection = false
}

26
modules/nodepool.tf Normal file
View file

@ -0,0 +1,26 @@
resource "google_container_node_pool" "cluster" {
name = "devops-lab-nodepool"
location = "us-central1-a"
cluster = google_container_cluster.default.name
network_config {
pod_range = "pod-ranges-ext"
}
initial_node_count = 2
autoscaling {
min_node_count = 1
max_node_count = 16
}
node_config {
machine_type = "e2-standard-2"
disk_size_gb = 50
disk_type = "pd-ssd"
oauth_scopes = [
"https://www.googleapis.com/auth/cloud-platform"
]
}
depends_on = [google_container_cluster.default]
}

68
modules/postgress.tf Normal file
View file

@ -0,0 +1,68 @@
resource "google_project_service" "sqladmin_api" {
service = "sqladmin.googleapis.com"
disable_on_destroy = false
}
# Create a Cloud SQL instance for PostgreSQL
resource "google_sql_database_instance" "postgres_instance" {
name = "pg-instance-via-tf"
database_version = "POSTGRES_15" # Specify the desired PostgreSQL version
region = "us-central1"
settings {
# The tier for the Cloud SQL instance, determines CPU and memory
tier = "db-f1-micro"
# Set disk size and enable disk auto-resize
disk_size = 10
disk_autoresize = true
# Network configuration for private IP
# If you need public IP, adjust authorized_networks
ip_configuration {
authorized_networks {
value = "0.0.0.0/0"
}
}
}
# Prevents accidental deletion of the instance
deletion_protection = false
depends_on = [google_project_service.sqladmin_api]
}
# Generate a random password for the default user
resource "random_password" "db_password" {
length = 16
special = true
override_special = "!#$%*()_-+="
keepers = {
rotatation_version = "2"
}
}
# Create a database user
resource "google_sql_user" "db_user" {
name = "sirius"
instance = google_sql_database_instance.postgres_instance.name
password = random_password.db_password.result
}
# Create the initial database
resource "google_sql_database" "database" {
name = "n8ndb"
instance = google_sql_database_instance.postgres_instance.name
}
# Output the connection name and generated password
output "instance_connection_name" {
value = google_sql_database_instance.postgres_instance.connection_name
description = "The connection name of the instance, used for the Cloud SQL Proxy"
}
output "database_user_password" {
value = random_password.db_password.result
sensitive = true
}

16
modules/providers.tf Normal file
View file

@ -0,0 +1,16 @@
provider "google" {
credentials = file("~/.config/gcloud/application_default_credentials.json")
project = "devops-lab-cluster"
region = "us-central1" # Or your desired region/location
}
data "google_client_config" "default" {}
provider "helm" {
kubernetes = {
host = "https://${google_container_cluster.default.endpoint}"
token = data.google_client_config.default.access_token
cluster_ca_certificate = base64decode(google_container_cluster.default.master_auth[0].cluster_ca_certificate)
}
}

0
modules/variables.tf Normal file
View file

32
modules/vpc.tf Normal file
View file

@ -0,0 +1,32 @@
resource "google_compute_network" "default" {
name = "devops-lab-network"
auto_create_subnetworks = false
enable_ula_internal_ipv6 = true
}
resource "google_compute_subnetwork" "default" {
name = "devops-lab-subnetwork"
ip_cidr_range = "10.0.0.0/16"
region = "us-central1"
stack_type = "IPV4_IPV6"
ipv6_access_type = "INTERNAL" # Change to "EXTERNAL" if creating an external loadbalancer
network = google_compute_network.default.id
secondary_ip_range {
range_name = "services-range"
ip_cidr_range = "192.168.16.0/24"
}
secondary_ip_range {
range_name = "pod-ranges"
ip_cidr_range = "192.168.32.0/20"
}
secondary_ip_range {
range_name = "pod-ranges-ext"
ip_cidr_range = "172.16.0.0/16"
}
}