# Apache Guacamole - Remote Desktop Gateway # MySQL 8 + guacd + guacamole web # ArgoCD managed - BlueJay Lab # ALL credentials sourced from 1Password via OnePasswordItem CRD (guacamole-credentials) # Fields: username, password, DB-User, DB-Password, DB-Root-Password, DB-Name, URL --- apiVersion: v1 kind: Namespace metadata: name: guacamole labels: app.kubernetes.io/part-of: bluejay-infra --- # MySQL 8 StatefulSet apiVersion: apps/v1 kind: StatefulSet metadata: name: guac-mysql namespace: guacamole labels: app: guac-mysql spec: serviceName: guac-mysql replicas: 1 selector: matchLabels: app: guac-mysql template: metadata: labels: app: guac-mysql spec: containers: - name: mysql image: mysql:8.0 ports: - containerPort: 3306 name: mysql env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: guacamole-credentials key: DB-Root-Password - name: MYSQL_DATABASE valueFrom: secretKeyRef: name: guacamole-credentials key: DB-Name - name: MYSQL_USER valueFrom: secretKeyRef: name: guacamole-credentials key: DB-User - name: MYSQL_PASSWORD valueFrom: secretKeyRef: name: guacamole-credentials key: DB-Password volumeMounts: - name: guac-mysql-data mountPath: /var/lib/mysql resources: requests: memory: 256Mi cpu: 100m limits: memory: 1Gi cpu: 500m livenessProbe: exec: command: - mysqladmin - ping - -h - localhost initialDelaySeconds: 60 periodSeconds: 10 readinessProbe: exec: command: - mysqladmin - ping - -h - localhost initialDelaySeconds: 30 periodSeconds: 5 volumeClaimTemplates: - metadata: name: guac-mysql-data spec: accessModes: [ReadWriteOnce] resources: requests: storage: 5Gi --- apiVersion: v1 kind: Service metadata: name: guac-mysql namespace: guacamole spec: selector: app: guac-mysql ports: - port: 3306 targetPort: 3306 name: mysql clusterIP: None --- # DB schema init Job # Generates the MySQL schema and pipes it into the database apiVersion: batch/v1 kind: Job metadata: name: guacamole-initdb namespace: guacamole annotations: argocd.argoproj.io/hook: PostSync argocd.argoproj.io/hook-delete-policy: BeforeHookCreation spec: ttlSecondsAfterFinished: 300 template: spec: restartPolicy: OnFailure initContainers: - name: wait-for-mysql image: mysql:8.0 command: - sh - -c - | until mysqladmin ping -h guac-mysql --silent; do echo "Waiting for MySQL..." sleep 5 done containers: - name: initdb image: guacamole/guacamole:latest command: - sh - -c - | # Generate schema SQL /opt/guacamole/bin/initdb.sh --mysql > /tmp/initdb.sql # Apply schema (ignore errors if tables already exist) mysql -h guac-mysql -u root -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" < /tmp/initdb.sql || true env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: guacamole-credentials key: DB-Root-Password - name: MYSQL_DATABASE valueFrom: secretKeyRef: name: guacamole-credentials key: DB-Name --- # guacd (Guacamole daemon) apiVersion: apps/v1 kind: Deployment metadata: name: guacd namespace: guacamole labels: app: guacd spec: replicas: 1 selector: matchLabels: app: guacd template: metadata: labels: app: guacd spec: containers: - name: guacd image: guacamole/guacd:latest ports: - containerPort: 4822 name: guacd resources: requests: memory: 128Mi cpu: 100m limits: memory: 512Mi cpu: 500m livenessProbe: tcpSocket: port: 4822 initialDelaySeconds: 15 periodSeconds: 10 --- apiVersion: v1 kind: Service metadata: name: guacd namespace: guacamole spec: selector: app: guacd ports: - port: 4822 targetPort: 4822 name: guacd --- # Guacamole Web Application apiVersion: apps/v1 kind: Deployment metadata: name: guacamole namespace: guacamole labels: app: guacamole spec: replicas: 1 selector: matchLabels: app: guacamole template: metadata: labels: app: guacamole spec: containers: - name: guacamole image: guacamole/guacamole:latest ports: - containerPort: 8080 name: http env: - name: GUACD_HOSTNAME value: guacd - name: GUACD_PORT value: "4822" - name: MYSQL_HOSTNAME value: guac-mysql - name: MYSQL_PORT value: "3306" - name: MYSQL_DATABASE valueFrom: secretKeyRef: name: guacamole-credentials key: DB-Name - name: MYSQL_USER valueFrom: secretKeyRef: name: guacamole-credentials key: DB-User - name: MYSQL_PASSWORD valueFrom: secretKeyRef: name: guacamole-credentials key: DB-Password resources: requests: memory: 256Mi cpu: 100m limits: memory: 1Gi cpu: 500m livenessProbe: httpGet: path: /guacamole/ port: 8080 initialDelaySeconds: 120 periodSeconds: 10 readinessProbe: httpGet: path: /guacamole/ port: 8080 initialDelaySeconds: 60 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: guacamole namespace: guacamole spec: selector: app: guacamole ports: - port: 8080 targetPort: 8080 name: http --- # Traefik addPrefix middleware # External URL guac.iamworkin.lan/ gets prefix /guacamole added apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: guac-add-prefix namespace: guacamole spec: addPrefix: prefix: /guacamole --- # TLS Certificate via cert-manager apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: guacamole-tls namespace: guacamole spec: secretName: guacamole-tls issuerRef: name: step-ca-acme kind: ClusterIssuer dnsNames: - guac.iamworkin.lan --- # Traefik IngressRoute apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: guacamole namespace: guacamole spec: entryPoints: - websecure routes: - match: Host(`guac.iamworkin.lan`) kind: Rule middlewares: - name: guac-add-prefix services: - name: guacamole port: 8080 tls: secretName: guacamole-tls --- # 1Password secret sync — creates guacamole-credentials K8s Secret # Fields: username, password, DB-User, DB-Password, DB-Root-Password, DB-Name, URL apiVersion: onepassword.com/v1 kind: OnePasswordItem metadata: name: guacamole-credentials namespace: guacamole spec: itemPath: vaults/IAmWorkin/items/Guacamole