mirror of
https://gitlab.kube.huskypup.net/Scooby/Homelabv4.git
synced 2026-08-20 23:16:49 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Cluster
|
||||
metadata:
|
||||
name: pg-n8n
|
||||
namespace: n8n
|
||||
spec:
|
||||
imageName: ghcr.io/cloudnative-pg/postgresql:16
|
||||
instances: 2
|
||||
|
||||
# Resource limits to prevent OOM
|
||||
resources:
|
||||
requests:
|
||||
memory: "512Mi"
|
||||
cpu: "25m"
|
||||
limits:
|
||||
memory: "2Gi"
|
||||
cpu: "250m"
|
||||
|
||||
# Spread replicas across different nodes
|
||||
affinity:
|
||||
topologyKey: kubernetes.io/hostname
|
||||
|
||||
storage:
|
||||
size: 10Gi
|
||||
storageClass: rook-ceph-block
|
||||
|
||||
primaryUpdateStrategy: unsupervised
|
||||
|
||||
# PostgreSQL configuration for better performance
|
||||
postgresql:
|
||||
parameters:
|
||||
max_connections: "200"
|
||||
shared_buffers: "512MB"
|
||||
effective_cache_size: "1536MB"
|
||||
maintenance_work_mem: "128MB"
|
||||
checkpoint_completion_target: "0.9"
|
||||
wal_buffers: "16MB"
|
||||
default_statistics_target: "100"
|
||||
random_page_cost: "1.1"
|
||||
effective_io_concurrency: "200"
|
||||
work_mem: "2621kB"
|
||||
min_wal_size: "1GB"
|
||||
max_wal_size: "4GB"
|
||||
|
||||
bootstrap:
|
||||
initdb:
|
||||
database: n8n
|
||||
owner: n8n
|
||||
|
||||
monitoring:
|
||||
enablePodMonitor: true
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
apiVersion: generators.external-secrets.io/v1alpha1
|
||||
kind: Password
|
||||
metadata:
|
||||
name: n8n-cnpg-secret
|
||||
namespace: n8n
|
||||
spec:
|
||||
length: 42
|
||||
digits: 5
|
||||
symbols: 5
|
||||
symbolCharacters: "-_$@"
|
||||
noUpper: false
|
||||
allowRepeat: true
|
||||
|
||||
---
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: n8n-cnpg-secret
|
||||
namespace: n8n
|
||||
spec:
|
||||
# Rotate database password every 24 hours
|
||||
refreshInterval: "24h"
|
||||
target:
|
||||
# This will merge the generated password into the existing pg-n8n-app secret
|
||||
name: pg-n8n-app
|
||||
creationPolicy: Merge
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
cnpg.io/reload: "true"
|
||||
data:
|
||||
# Override the password field with our ESO-generated password
|
||||
password: "{{ .password }}"
|
||||
dataFrom:
|
||||
- sourceRef:
|
||||
generatorRef:
|
||||
apiVersion: generators.external-secrets.io/v1alpha1
|
||||
kind: Password
|
||||
name: n8n-cnpg-secret
|
||||
@@ -0,0 +1,20 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: n8n
|
||||
namespace: n8n
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: edge
|
||||
namespace: gateway
|
||||
sectionName: https
|
||||
hostnames:
|
||||
- n8n.kube.huskypup.net
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- name: n8n
|
||||
port: 80
|
||||
@@ -0,0 +1,49 @@
|
||||
# kubernetes/apps/n8n/external-secret.yaml
|
||||
# ExternalSecrets for n8n - pulls credentials from Vault
|
||||
|
||||
# Note: Database password is managed by CNPG cluster (pg-n8n-app secret)
|
||||
# We reference it directly in the n8n values.yaml extraEnv section
|
||||
|
||||
---
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: n8n-config
|
||||
namespace: n8n
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: vault-backend
|
||||
target:
|
||||
name: n8n-config-secret
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: encryption-key
|
||||
remoteRef:
|
||||
key: n8n-config
|
||||
property: encryption-key
|
||||
|
||||
---
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: n8n-oauth
|
||||
namespace: n8n
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: vault-backend
|
||||
target:
|
||||
name: n8n-oauth-secret
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: client-id
|
||||
remoteRef:
|
||||
key: n8n-oauth
|
||||
property: client-id
|
||||
- secretKey: client-secret
|
||||
remoteRef:
|
||||
key: n8n-oauth
|
||||
property: client-secret
|
||||
@@ -0,0 +1,81 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: n8n-hooks
|
||||
namespace: n8n
|
||||
data:
|
||||
hooks.js: |
|
||||
// n8n v2.0.3 compatible hooks for Authentik forward auth integration
|
||||
const { resolve, dirname } = require('path');
|
||||
|
||||
module.exports = {
|
||||
credentials: {
|
||||
create: [],
|
||||
delete: [],
|
||||
update: []
|
||||
},
|
||||
workflow: {
|
||||
create: [],
|
||||
delete: [],
|
||||
update: []
|
||||
},
|
||||
server: {
|
||||
started: [
|
||||
async function (app) {
|
||||
console.log('[n8n-hooks] Initializing forward auth middleware');
|
||||
|
||||
// Get Express app
|
||||
const expressApp = app?.app;
|
||||
if (!expressApp) {
|
||||
console.error('[n8n-hooks] Express app not available');
|
||||
return;
|
||||
}
|
||||
|
||||
let issueCookie, UserRepository, Container;
|
||||
try {
|
||||
const n8nPath = dirname(require.resolve('n8n'));
|
||||
issueCookie = require(resolve(n8nPath, 'dist/auth/jwt')).issueCookie;
|
||||
UserRepository = require(resolve(n8nPath, 'dist/databases/repositories/user.repository')).UserRepository;
|
||||
Container = require('typedi').Container;
|
||||
} catch (error) {
|
||||
console.error('[n8n-hooks] Failed to load dependencies:', error.message);
|
||||
return;
|
||||
}
|
||||
|
||||
const ignoreAuthRegexp = /^\/(assets|healthz|webhook|rest\/oauth2-credential|rest\/settings|static|icons|types)/;
|
||||
|
||||
// Add middleware for forward auth
|
||||
expressApp.use(async (req, res, next) => {
|
||||
try {
|
||||
if (ignoreAuthRegexp.test(req.url)) return next();
|
||||
if (req.cookies?.['n8n-auth']) return next();
|
||||
if (!process.env.N8N_FORWARD_AUTH_HEADER) return next();
|
||||
|
||||
const headerName = process.env.N8N_FORWARD_AUTH_HEADER.toLowerCase().replace(/_/g, '-');
|
||||
const email = req.headers[headerName];
|
||||
if (!email) return next();
|
||||
|
||||
const userRepo = Container.get(UserRepository);
|
||||
const user = await userRepo.findOne({ where: { email } });
|
||||
|
||||
if (!user) {
|
||||
console.warn(`[n8n-hooks] User not found: ${email}`);
|
||||
res.statusCode = 401;
|
||||
res.end(`User ${email} not found. Please contact an admin.`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`[n8n-hooks] Auto-login: ${email}`);
|
||||
issueCookie(res, user);
|
||||
next();
|
||||
} catch (error) {
|
||||
console.error('[n8n-hooks] Middleware error:', error.message);
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
console.log('[n8n-hooks] Forward auth middleware active');
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: networking.istio.io/v1beta1
|
||||
kind: VirtualService
|
||||
metadata:
|
||||
name: n8n
|
||||
namespace: n8n
|
||||
spec:
|
||||
hosts:
|
||||
- n8n.kube.huskypup.net
|
||||
gateways:
|
||||
- istio-system/edge
|
||||
http:
|
||||
- route:
|
||||
- destination:
|
||||
host: n8n.n8n.svc.cluster.local
|
||||
port:
|
||||
number: 80
|
||||
@@ -0,0 +1,161 @@
|
||||
# kubernetes/apps/n8n/values.yaml
|
||||
# n8n workflow automation platform
|
||||
# Using chart: https://riatlas.github.io/chart__n8n
|
||||
|
||||
# --- n8n image ---
|
||||
image:
|
||||
repository: n8nio/n8n
|
||||
tag: "2.0.3"
|
||||
|
||||
# --- n8n encryption key (loaded from secret) ---
|
||||
n8n:
|
||||
encryption_key: # Will be set via extraEnv from n8n-config-secret
|
||||
|
||||
# --- n8n configuration ---
|
||||
config:
|
||||
database:
|
||||
type: postgresdb
|
||||
postgresdb:
|
||||
host: pg-n8n-rw # CNPG read-write service
|
||||
port: 5432
|
||||
database: n8n
|
||||
user: n8n
|
||||
schema: public
|
||||
generic:
|
||||
timezone: America/New_York
|
||||
path: /
|
||||
host: n8n.kube.huskypup.net
|
||||
port: 5678
|
||||
protocol: https
|
||||
executions:
|
||||
mode: regular
|
||||
saveDataOnError: all
|
||||
saveDataOnSuccess: all
|
||||
saveDataManualExecutions: true
|
||||
pruneData: true
|
||||
pruneDataMaxAge: 3760 # 1 year in hours
|
||||
|
||||
# --- Secret values (passwords, etc.) ---
|
||||
# Note: password is injected via extraEnvSecrets below
|
||||
secret: {}
|
||||
|
||||
# --- Deployment replicas ---
|
||||
# Increased to 2 for high availability and faster response times
|
||||
replicaCount: 2
|
||||
|
||||
# --- Service configuration ---
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
|
||||
# --- Security context ---
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
|
||||
# --- Ingress disabled - Istio VirtualService handles routing ---
|
||||
ingress:
|
||||
enabled: false
|
||||
|
||||
# --- Persistence for workflows and data ---
|
||||
persistence:
|
||||
enabled: true
|
||||
type: existing
|
||||
existingClaim: n8n-main-persistence
|
||||
storageClass: rook-ceph-block
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
size: 10Gi
|
||||
|
||||
# --- Resources ---
|
||||
# No CPU limits (burst allowed). Keep requests minimal for scheduling.
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 512Mi
|
||||
|
||||
# --- Startup probe ---
|
||||
# Allows app to start without being killed by liveness probe
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 12 # 60 seconds total startup time
|
||||
|
||||
# --- Readiness probe ---
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: http
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 2
|
||||
|
||||
# --- Liveness probe ---
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
|
||||
# --- Extra environment variables (from secrets) ---
|
||||
# Secrets are loaded from Kubernetes secrets
|
||||
extraEnvSecrets:
|
||||
# Database password from CNPG-managed secret
|
||||
DB_POSTGRESDB_PASSWORD:
|
||||
name: pg-n8n-app
|
||||
key: password
|
||||
|
||||
# n8n encryption key
|
||||
N8N_ENCRYPTION_KEY:
|
||||
name: n8n-config-secret
|
||||
key: encryption-key
|
||||
|
||||
# --- Extra environment variables (plain values) ---
|
||||
extraEnv:
|
||||
# Webhook & Editor URLs
|
||||
WEBHOOK_URL: https://n8n.kube.huskypup.net/
|
||||
N8N_EDITOR_BASE_URL: https://n8n.kube.huskypup.net
|
||||
|
||||
# Logging
|
||||
N8N_LOG_LEVEL: error
|
||||
|
||||
# --- Disable built-in PostgreSQL (we'll deploy it separately with Bitnami) ---
|
||||
postgresql:
|
||||
enabled: false
|
||||
|
||||
# --- Disable Redis (not needed for single instance) ---
|
||||
redis:
|
||||
enabled: false
|
||||
|
||||
# --- Scaling (disabled for single instance) ---
|
||||
scaling:
|
||||
enabled: false
|
||||
|
||||
# --- Extra volumes ---
|
||||
extraVolumes: []
|
||||
|
||||
# --- Extra volume mounts ---
|
||||
extraVolumeMounts: []
|
||||
|
||||
|
||||
|
||||
# --- Node selector ---
|
||||
nodeSelector: {}
|
||||
|
||||
# --- Tolerations ---
|
||||
tolerations: []
|
||||
|
||||
# --- Affinity ---
|
||||
affinity: {}
|
||||
Reference in New Issue
Block a user