Initial commit

This commit is contained in:
Scooby Husky
2026-03-09 20:21:35 -05:00
commit aacb8eebbe
314 changed files with 21766 additions and 0 deletions
@@ -0,0 +1,50 @@
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: pg-guacamole
namespace: guacamole
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:16
instances: 2
# Resource limits to prevent OOM
resources:
requests:
memory: "256Mi"
cpu: "50m"
limits:
memory: "1Gi"
# Spread replicas across different nodes
affinity:
topologyKey: kubernetes.io/hostname
storage:
size: 10Gi
storageClass: rook-ceph-block
primaryUpdateStrategy: unsupervised
# PostgreSQL configuration for Guacamole
postgresql:
parameters:
max_connections: "100"
shared_buffers: "256MB"
effective_cache_size: "768MB"
maintenance_work_mem: "64MB"
checkpoint_completion_target: "0.9"
wal_buffers: "8MB"
default_statistics_target: "100"
random_page_cost: "1.1"
effective_io_concurrency: "200"
work_mem: "1310kB"
min_wal_size: "1GB"
max_wal_size: "4GB"
bootstrap:
initdb:
database: guacamole
owner: guacamole
monitoring:
enablePodMonitor: true
@@ -0,0 +1,114 @@
# Job to initialize Guacamole database schema
# This should run once after the PostgreSQL cluster is ready
apiVersion: batch/v1
kind: Job
metadata:
name: guacamole-init-schema
namespace: guacamole
spec:
ttlSecondsAfterFinished: 300
template:
spec:
restartPolicy: OnFailure
initContainers:
- name: wait-for-postgres
image: postgres:16-alpine
securityContext:
runAsUser: 10000
runAsGroup: 10000
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumeMounts:
- name: tmp
mountPath: /tmp
command:
- sh
- -c
- |
echo "Waiting for PostgreSQL to be ready..."
until pg_isready -h pg-guacamole-rw -p 5432 -U guacamole; do
echo "PostgreSQL not ready, waiting..."
sleep 5
done
echo "PostgreSQL is ready!"
env:
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: pg-guacamole-app
key: password
- name: generate-initdb
image: guacamole/guacamole:1.6.0
securityContext:
runAsUser: 1000
runAsGroup: 1000
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
command:
- sh
- -c
- |
set -e
/opt/guacamole/bin/initdb.sh --postgresql > /initdb/initdb.sql
test -s /initdb/initdb.sql
volumeMounts:
- name: initdb
mountPath: /initdb
containers:
- name: init-schema
image: postgres:16-alpine
securityContext:
runAsUser: 10000
runAsGroup: 10000
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
command:
- sh
- -c
- |
set -e
echo "Checking if schema already exists..."
TABLES=$(PGPASSWORD="$PGPASSWORD" psql -h pg-guacamole-rw -U guacamole -d guacamole -tA -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_name = 'guacamole_user';" 2>/dev/null | tr -d '[:space:]' || printf '0')
TABLES=${TABLES:-0}
if [ "$TABLES" -gt "0" ]; then
echo "Schema already exists."
else
echo "Initializing Guacamole database schema..."
PGPASSWORD="$PGPASSWORD" psql -v ON_ERROR_STOP=1 -h pg-guacamole-rw -U guacamole -d guacamole -f /initdb/initdb.sql
echo "Schema initialization complete!"
fi
echo "Setting up Authentik Admins group permissions..."
PGPASSWORD="$PGPASSWORD" psql -v ON_ERROR_STOP=1 -h pg-guacamole-rw -U guacamole -d guacamole -f /schema/permissions.sql
echo "Authentik Admins group permissions configured!"
env:
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: pg-guacamole-app
key: password
volumeMounts:
- name: initdb
mountPath: /initdb
- name: schema
mountPath: /schema
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
- name: initdb
emptyDir: {}
- name: schema
configMap:
name: guacamole-schema
@@ -0,0 +1,256 @@
# Guacamole complete deployment with OpenID/Authentik support
# This deployment includes:
# - Environment-based OpenID configuration
# - PostgreSQL backend for connections/users
# - Authentik uses Let's Encrypt (no custom cert import needed)
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: guacamole
namespace: guacamole
labels:
app: guacamole
component: client
spec:
replicas: 1
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
selector:
matchLabels:
app: guacamole
component: client
template:
metadata:
labels:
app: guacamole
component: client
spec:
automountServiceAccountToken: false
containers:
- name: guacamole
image: guacamole/guacamole:1.6.0
securityContext:
runAsUser: 10000
runAsGroup: 10000
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
volumeMounts:
- name: tmp
mountPath: /tmp
- name: tomcat-work
mountPath: /usr/local/tomcat/work
- name: tomcat-logs
mountPath: /usr/local/tomcat/logs
ports:
- name: http
containerPort: 8080
protocol: TCP
env:
- name: GUACD_HOSTNAME
value: "guacd"
- name: GUACD_PORT
value: "4822"
- name: POSTGRESQL_HOSTNAME
value: "pg-guacamole-rw"
- name: POSTGRESQL_PORT
value: "5432"
- name: POSTGRESQL_DATABASE
value: "guacamole"
- name: POSTGRESQL_ENABLED
value: "true"
- name: POSTGRESQL_USERNAME
value: "guacamole"
- name: POSTGRESQL_PASSWORD
valueFrom:
secretKeyRef:
name: pg-guacamole-app
key: password
- name: POSTGRESQL_AUTO_CREATE_ACCOUNTS
value: "true"
- name: ENABLE_ENVIRONMENT_PROPERTIES
value: "true"
- name: LOG_LEVEL
value: "debug"
- name: WEBAPP_CONTEXT
value: "ROOT"
# FIX: Enabled WebSockets to stop the 10-second tunnel timeout
- name: ENABLE_WEBSOCKET
value: "true"
- name: EXTENSION_PRIORITY
value: "*,openid"
- name: OPENID_RESPONSE_TYPE
value: "code"
- name: OPENID_AUTHORIZATION_ENDPOINT
value: "https://auth.kube.huskypup.net/application/o/authorize/"
- name: OPENID_JWKS_ENDPOINT
value: "https://auth.kube.huskypup.net/application/o/guacamole/jwks/"
- name: OPENID_ISSUER
value: "https://auth.kube.huskypup.net/application/o/guacamole/"
- name: OPENID_TOKEN_ENDPOINT
value: "https://auth.kube.huskypup.net/application/o/token/"
- name: OPENID_REDIRECT_URI
value: "https://guacamole.kube.huskypup.net/"
- name: OPENID_USERNAME_CLAIM_TYPE
value: "preferred_username"
- name: OPENID_GROUPS_CLAIM_TYPE
value: "groups"
- name: OPENID_SCOPE
value: "openid email profile groups"
- name: OPENID_ALLOWED_CLOCK_SKEW
value: "30"
- name: OPENID_MAX_TOKEN_VALIDITY
value: "300"
- name: OPENID_MAX_NONCE_VALIDITY
value: "60"
- name: OPENID_CLIENT_ID
valueFrom:
secretKeyRef:
name: guacamole-oauth-secret
key: client-id
- name: OPENID_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: guacamole-oauth-secret
key: client-secret
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /api/languages
port: http
initialDelaySeconds: 60
periodSeconds: 30
# FIX: Relaxed readiness probe so minor DB lags don't kill the Endpoint
readinessProbe:
httpGet:
path: /api/languages
port: http
initialDelaySeconds: 30
periodSeconds: 15
failureThreshold: 5
volumes:
- name: tmp
emptyDir: {}
- name: tomcat-work
emptyDir: {}
- name: tomcat-logs
emptyDir: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: guacd
namespace: guacamole
labels:
app: guacamole
component: guacd
spec:
replicas: 1
selector:
matchLabels:
app: guacamole
component: guacd
template:
metadata:
labels:
app: guacamole
component: guacd
spec:
automountServiceAccountToken: false
containers:
- name: guacd
image: guacamole/guacd:1.6.0
securityContext:
runAsUser: 10000
runAsGroup: 10000
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
volumeMounts:
- name: tmp
mountPath: /tmp
- name: home
mountPath: /home
ports:
- name: guacd
containerPort: 4822
protocol: TCP
env:
- name: LOG_LEVEL
value: "debug"
resources:
requests:
memory: "256Mi"
cpu: "10m"
limits:
memory: "512Mi"
cpu: "500m"
readinessProbe:
tcpSocket:
port: 4822
initialDelaySeconds: 2
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
volumes:
- name: tmp
emptyDir: {}
- name: home
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: guacamole
namespace: guacamole
labels:
app: guacamole
component: client
spec:
type: ClusterIP
ports:
- port: 8080
targetPort: http
protocol: TCP
name: http
selector:
app: guacamole
component: client
---
apiVersion: v1
kind: Service
metadata:
name: guacd
namespace: guacamole
labels:
app: guacamole
component: guacd
spec:
type: ClusterIP
ports:
- port: 4822
targetPort: guacd
protocol: TCP
name: guacd
selector:
app: guacamole
component: guacd
@@ -0,0 +1,67 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: guacamole-envoy-tls
namespace: guacamole
spec:
secretName: guacamole-envoy-tls
issuerRef:
kind: ClusterIssuer
name: letsencrypt-production
dnsNames:
- guacamole.kube.huskypup.net
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: guacamole
namespace: guacamole
spec:
gatewayClassName: envoy-gateway
listeners:
- name: https
hostname: guacamole.kube.huskypup.net
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: guacamole-envoy-tls
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: guacamole
namespace: guacamole
spec:
parentRefs:
- name: guacamole
sectionName: https
hostnames:
- guacamole.kube.huskypup.net
rules:
# Legacy path support: https://host/guacamole/* -> https://host/*
- matches:
- path:
type: PathPrefix
value: /guacamole
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: guacamole
port: 8080
# Standard root routing
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: guacamole
port: 8080
+33
View File
@@ -0,0 +1,33 @@
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: guacamole
namespace: guacamole
spec:
parentRefs:
- name: edge
namespace: gateway
sectionName: https
hostnames:
- guacamole.kube.huskypup.net
rules:
- matches:
- path:
type: PathPrefix
value: /guacamole
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: guacamole
port: 8080
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: guacamole
port: 8080
@@ -0,0 +1,29 @@
# kubernetes/apps/guacamole/external-secret.yaml
# ExternalSecrets for Guacamole - pulls credentials from Vault
# Note: Database password is managed by CNPG cluster (pg-guacamole-app secret)
# We reference it directly in the guacamole values.yaml
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: guacamole-oauth
namespace: guacamole
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: vault-backend
target:
name: guacamole-oauth-secret
creationPolicy: Owner
data:
- secretKey: client-id
remoteRef:
key: guacamole-oauth
property: client-id
- secretKey: client-secret
remoteRef:
key: guacamole-oauth
property: client-secret
@@ -0,0 +1,16 @@
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: guacamole
namespace: guacamole
spec:
hosts:
- guacamole.kube.huskypup.net
gateways:
- istio-system/edge
http:
- route:
- destination:
host: guacamole.guacamole.svc.cluster.local
port:
number: 8080