From 824bd2d112c73676cc06743434b9db7982bfccd3 Mon Sep 17 00:00:00 2001 From: sirius0xdev Date: Sat, 3 Jan 2026 03:47:18 +0000 Subject: [PATCH] Setup gatewayapi, ssl certs managed by gcloud, n8n. Terraform is also set up --- apps/base/n8n/apigateway.yaml | 18 ++++++++ apps/base/n8n/configmap.yaml | 21 ++++++++++ apps/base/n8n/deployment.yaml | 68 +++++++++++++++++++++++++++++++ apps/base/n8n/healthcheck.yaml | 16 ++++++++ apps/base/n8n/http-route.yaml | 15 +++++++ apps/base/n8n/kustomization.yaml | 10 +++++ apps/base/n8n/n8n-secret.yaml | 8 ++++ apps/base/n8n/namespace.yaml | 4 ++ apps/base/n8n/pg-cluster-n8n.yaml | 9 ++++ apps/base/n8n/service.yaml | 15 +++++++ apps/base/n8n/storage.yaml | 13 ++++++ helm.tf | 21 ++++++++++ main.tf | 60 --------------------------- mise.toml | 1 + modules/.terraform.lock.hcl | 59 +++++++++++++++++++++++++++ modules/gke.tf | 29 +++++++++++++ modules/nodepool.tf | 26 ++++++++++++ modules/postgress.tf | 68 +++++++++++++++++++++++++++++++ modules/providers.tf | 16 ++++++++ modules/variables.tf | 0 modules/vpc.tf | 32 +++++++++++++++ 21 files changed, 449 insertions(+), 60 deletions(-) create mode 100644 apps/base/n8n/apigateway.yaml create mode 100644 apps/base/n8n/configmap.yaml create mode 100644 apps/base/n8n/deployment.yaml create mode 100644 apps/base/n8n/healthcheck.yaml create mode 100644 apps/base/n8n/http-route.yaml create mode 100644 apps/base/n8n/kustomization.yaml create mode 100644 apps/base/n8n/n8n-secret.yaml create mode 100644 apps/base/n8n/namespace.yaml create mode 100644 apps/base/n8n/pg-cluster-n8n.yaml create mode 100644 apps/base/n8n/service.yaml create mode 100644 apps/base/n8n/storage.yaml create mode 100644 helm.tf delete mode 100644 main.tf create mode 100644 modules/.terraform.lock.hcl create mode 100644 modules/gke.tf create mode 100644 modules/nodepool.tf create mode 100644 modules/postgress.tf create mode 100644 modules/providers.tf create mode 100644 modules/variables.tf create mode 100644 modules/vpc.tf diff --git a/apps/base/n8n/apigateway.yaml b/apps/base/n8n/apigateway.yaml new file mode 100644 index 0000000..d22aeda --- /dev/null +++ b/apps/base/n8n/apigateway.yaml @@ -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 diff --git a/apps/base/n8n/configmap.yaml b/apps/base/n8n/configmap.yaml new file mode 100644 index 0000000..6fdf096 --- /dev/null +++ b/apps/base/n8n/configmap.yaml @@ -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" + + diff --git a/apps/base/n8n/deployment.yaml b/apps/base/n8n/deployment.yaml new file mode 100644 index 0000000..279140a --- /dev/null +++ b/apps/base/n8n/deployment.yaml @@ -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 + + diff --git a/apps/base/n8n/healthcheck.yaml b/apps/base/n8n/healthcheck.yaml new file mode 100644 index 0000000..80ccb06 --- /dev/null +++ b/apps/base/n8n/healthcheck.yaml @@ -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 diff --git a/apps/base/n8n/http-route.yaml b/apps/base/n8n/http-route.yaml new file mode 100644 index 0000000..542084a --- /dev/null +++ b/apps/base/n8n/http-route.yaml @@ -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 + diff --git a/apps/base/n8n/kustomization.yaml b/apps/base/n8n/kustomization.yaml new file mode 100644 index 0000000..12830d1 --- /dev/null +++ b/apps/base/n8n/kustomization.yaml @@ -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 + diff --git a/apps/base/n8n/n8n-secret.yaml b/apps/base/n8n/n8n-secret.yaml new file mode 100644 index 0000000..53b8521 --- /dev/null +++ b/apps/base/n8n/n8n-secret.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +data: + DB_POSTGRESDB_PASSWORD: RzIzPWFRKjhpcnRmKnhLTA== +kind: Secret +metadata: + creationTimestamp: null + name: n8n-secret + namespace: n8n diff --git a/apps/base/n8n/namespace.yaml b/apps/base/n8n/namespace.yaml new file mode 100644 index 0000000..8206fe4 --- /dev/null +++ b/apps/base/n8n/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: n8n diff --git a/apps/base/n8n/pg-cluster-n8n.yaml b/apps/base/n8n/pg-cluster-n8n.yaml new file mode 100644 index 0000000..22349cf --- /dev/null +++ b/apps/base/n8n/pg-cluster-n8n.yaml @@ -0,0 +1,9 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: cluster-n8n +spec: + instances: 1 + + storage: + size: 1G diff --git a/apps/base/n8n/service.yaml b/apps/base/n8n/service.yaml new file mode 100644 index 0000000..fdc3067 --- /dev/null +++ b/apps/base/n8n/service.yaml @@ -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 + diff --git a/apps/base/n8n/storage.yaml b/apps/base/n8n/storage.yaml new file mode 100644 index 0000000..c03a1f7 --- /dev/null +++ b/apps/base/n8n/storage.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: n8n-data + namespace: n8n +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + + diff --git a/helm.tf b/helm.tf new file mode 100644 index 0000000..08d6eca --- /dev/null +++ b/helm.tf @@ -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 } + } + }) + ] +} + diff --git a/main.tf b/main.tf deleted file mode 100644 index 458eebb..0000000 --- a/main.tf +++ /dev/null @@ -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 -} diff --git a/mise.toml b/mise.toml index 030b0e6..af05b47 100644 --- a/mise.toml +++ b/mise.toml @@ -1,4 +1,5 @@ [tools] +cilium-cli = "latest" gcloud = "latest" k9s = "latest" terraform = "latest" diff --git a/modules/.terraform.lock.hcl b/modules/.terraform.lock.hcl new file mode 100644 index 0000000..ac5c1d7 --- /dev/null +++ b/modules/.terraform.lock.hcl @@ -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", + ] +} diff --git a/modules/gke.tf b/modules/gke.tf new file mode 100644 index 0000000..2a330d0 --- /dev/null +++ b/modules/gke.tf @@ -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 +} diff --git a/modules/nodepool.tf b/modules/nodepool.tf new file mode 100644 index 0000000..0488bfc --- /dev/null +++ b/modules/nodepool.tf @@ -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] + +} diff --git a/modules/postgress.tf b/modules/postgress.tf new file mode 100644 index 0000000..07d3f21 --- /dev/null +++ b/modules/postgress.tf @@ -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 +} diff --git a/modules/providers.tf b/modules/providers.tf new file mode 100644 index 0000000..5839940 --- /dev/null +++ b/modules/providers.tf @@ -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) + } +} diff --git a/modules/variables.tf b/modules/variables.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules/vpc.tf b/modules/vpc.tf new file mode 100644 index 0000000..c29f322 --- /dev/null +++ b/modules/vpc.tf @@ -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" + } +}