# 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] volumeMode: Filesystem 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: serviceAccountName: guacd-exec containers: - name: guacd image: guacamole/guacd:latest ports: - containerPort: 4822 name: guacd env: - name: LOG_LEVEL value: debug resources: requests: memory: 128Mi cpu: 100m limits: memory: 512Mi cpu: 500m livenessProbe: tcpSocket: port: 4822 initialDelaySeconds: 15 periodSeconds: 10 volumeMounts: - name: recordings mountPath: /var/lib/guacamole/recordings - name: kubectl-proxy image: bitnami/kubectl:latest args: - proxy - "--port=8001" - "--address=127.0.0.1" - "--accept-hosts=.*" - "--accept-paths=.*" - "--disable-filter=true" - "--v=2" resources: requests: memory: 32Mi cpu: 10m limits: memory: 64Mi cpu: 50m volumes: - name: recordings nfs: server: 10.0.58.3 path: /volume1/kubernetes/guacamole/recordings --- 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 # RECORDING_* presence triggers history-recording-storage extension load # per Guacamole Docker convention (/opt/guacamole/environment/RECORDING_/). # Recordings are written by guacd and read by guacamole web (history UI). - name: RECORDING_SEARCH_PATH value: /var/lib/guacamole/recordings 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 volumeMounts: - name: guac-properties mountPath: /etc/guacamole/guacamole.properties subPath: guacamole.properties - name: bluejay-branding mountPath: /etc/guacamole/extensions/bluejay-branding-1.0.0.jar subPath: bluejay-branding-1.0.0.jar - name: recordings mountPath: /var/lib/guacamole/recordings volumes: - name: guac-properties configMap: name: guacamole-properties - name: bluejay-branding configMap: name: guacamole-branding - name: recordings nfs: server: 10.0.58.3 path: /volume1/kubernetes/guacamole/recordings --- 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 --- # Blue Jay Branding Extension (CSS + translations) apiVersion: v1 kind: ConfigMap metadata: name: guacamole-branding namespace: guacamole binaryData: bluejay-branding-1.0.0.jar: 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 --- # Guacamole custom properties apiVersion: v1 kind: ConfigMap metadata: name: guacamole-properties namespace: guacamole data: guacamole.properties: | # Blue Jay Remote Access — Guacamole Configuration # MySQL/guacd settings provided via env vars — do NOT duplicate here # Extension Priority extension-priority: mysql, ban, bluejay, * # Ban (brute force) ban-max-invalid-attempts: 5 ban-address-duration: 300000 ban-max-addresses: 1000 # TOTP totp-issuer: Blue Jay Remote Access totp-digits: 6 totp-period: 30 totp-mode: sha256 # Session api-session-timeout: 60 --- # guacd ServiceAccount for K8s exec apiVersion: v1 kind: ServiceAccount metadata: name: guacd-exec namespace: guacamole --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: guacd-pod-exec labels: app.kubernetes.io/component: proxy app.kubernetes.io/name: guacd rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list"] - apiGroups: [""] resources: ["pods/exec", "pods/attach"] verbs: ["create", "get"] - apiGroups: [""] resources: ["namespaces"] verbs: ["list", "get"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: guacd-pod-exec labels: app.kubernetes.io/component: proxy app.kubernetes.io/name: guacd roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: guacd-pod-exec subjects: - kind: ServiceAccount name: guacd-exec namespace: guacamole --- # K8s pod sync CronJob — auto-updates Guacamole connections when pods restart apiVersion: batch/v1 kind: CronJob metadata: name: guac-k8s-sync namespace: guacamole labels: app.kubernetes.io/name: guac-k8s-sync app.kubernetes.io/component: sync spec: schedule: "*/2 * * * *" concurrencyPolicy: Forbid successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 3 jobTemplate: spec: template: spec: serviceAccountName: guacd-exec restartPolicy: OnFailure containers: - name: sync image: bitnami/kubectl:latest command: - /bin/bash - /scripts/sync-k8s-connections.sh env: - name: GUAC_URL value: "http://guacamole.guacamole.svc.cluster.local:8080" - name: GUAC_ADMIN_USER value: "guacadmin" - name: GUAC_ADMIN_PASSWORD valueFrom: secretKeyRef: name: guacamole-credentials key: password - name: TARGET_NAMESPACES value: "argocd,gitea,telephony,traefik-system,zabbix,matrix,irc,mail,selenium" volumeMounts: - name: scripts mountPath: /scripts volumes: - name: scripts configMap: name: guac-k8s-sync-scripts defaultMode: 0755 --- apiVersion: v1 kind: ConfigMap metadata: name: guac-k8s-sync-scripts namespace: guacamole labels: app.kubernetes.io/name: guac-k8s-sync app.kubernetes.io/component: sync data: sync-k8s-connections.sh: | #!/bin/bash set -euo pipefail GUAC_API="${GUAC_URL}/guacamole/api" DS="mysql" echo "[k8s-sync] Starting pod connection sync" # Auth using grep (no python3/jq in bitnami/kubectl) AUTH_RESP=$(curl -sf -d "username=${GUAC_ADMIN_USER}&password=${GUAC_ADMIN_PASSWORD}" "${GUAC_API}/tokens") TOKEN=$(echo "$AUTH_RESP" | grep -o '"authToken":"[^"]*"' | cut -d'"' -f4) if [ -z "$TOKEN" ]; then echo "[k8s-sync] ERROR: Auth failed"; exit 1; fi echo "[k8s-sync] Authenticated" CONNS=$(curl -sf "${GUAC_API}/session/data/${DS}/connections?token=${TOKEN}") IFS=',' read -ra NAMESPACES <<< "$TARGET_NAMESPACES" UPDATED=0 for NS in "${NAMESPACES[@]}"; do PODS=$(kubectl get pods -n "$NS" --field-selector=status.phase=Running \ -o jsonpath='{range .items[*]}{.metadata.name} {.spec.containers[0].name}{"\n"}{end}' 2>/dev/null || echo "") while read -r POD CONTAINER; do [ -z "$POD" ] && continue CONN_IDS=$(echo "$CONNS" | grep -o "\"[0-9]*\":{\"name\":\"k8s: ${CONTAINER}\"" | grep -o '"[0-9]*"' | tr -d '"' || echo "") for CID in $CONN_IDS; do PARAMS=$(curl -sf "${GUAC_API}/session/data/${DS}/connections/${CID}/parameters?token=${TOKEN}") CURR_NS=$(echo "$PARAMS" | grep -o '"namespace":"[^"]*"' | cut -d'"' -f4) CURR_POD=$(echo "$PARAMS" | grep -o '"pod":"[^"]*"' | cut -d'"' -f4) if [ "$CURR_NS" = "$NS" ] && [ "$CURR_POD" != "$POD" ]; then echo "[k8s-sync] Updating $NS/$CONTAINER: $CURR_POD -> $POD" curl -sf -X PUT -H "Content-Type: application/json" \ -d "{\"name\":\"k8s: $CONTAINER\",\"parentIdentifier\":\"ROOT\",\"protocol\":\"kubernetes\",\"parameters\":{\"hostname\":\"localhost\",\"port\":\"8001\",\"namespace\":\"$NS\",\"pod\":\"$POD\",\"container\":\"$CONTAINER\",\"use-ssl\":\"false\",\"exec-command\":\"/bin/sh\",\"font-size\":\"14\",\"color-scheme\":\"gray-black\",\"scrollback-size\":\"5000\"},\"attributes\":{\"max-connections\":\"2\",\"max-connections-per-user\":\"1\"}}" \ "${GUAC_API}/session/data/${DS}/connections/${CID}?token=${TOKEN}" > /dev/null UPDATED=$((UPDATED + 1)) fi done done <<< "$PODS" done echo "[k8s-sync] Done: ${UPDATED} updated"