From e8b25a9a99999e84f56f0214c69b16755c585da3 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Wed, 18 Feb 2026 12:32:57 +0100 Subject: [PATCH] added upsnap and guacamole --- admin-system/upsnap.yaml | 209 ++++++++++++++++++++ kisfenyo-system/guacamole.yaml | 341 +++++++++++++++++++++++++++++++++ 2 files changed, 550 insertions(+) create mode 100644 admin-system/upsnap.yaml create mode 100644 kisfenyo-system/guacamole.yaml diff --git a/admin-system/upsnap.yaml b/admin-system/upsnap.yaml new file mode 100644 index 0000000..f4fd88f --- /dev/null +++ b/admin-system/upsnap.yaml @@ -0,0 +1,209 @@ +# ============================================ +# UpSnap - Wake on LAN Web App +# ============================================ +# https://github.com/seriousm4x/UpSnap +# Image: ghcr.io/seriousm4x/upsnap +# +# Access: https://upsnap.dooplex.hu (Authentik OIDC login) +# https://upsnap.home (internal) +# +# IMPORTANT: UpSnap needs hostNetwork to send magic packets (WoL) +# on the local LAN. This means it binds directly to the host's +# network stack on port 8090. +# +# SSO Setup (Native OIDC via PocketBase): +# UpSnap uses PocketBase under the hood, which supports OIDC natively. +# After first deployment: +# 1. Open https://upsnap.dooplex.hu/_/#/settings/auth-providers +# 2. Select "OpenID Connect (oidc)" and fill in: +# - Client ID: (from Authentik provider) +# - Client Secret: (from Authentik provider) +# - Auth URL: https://authentik.dooplex.hu/application/o/authorize/ +# - Token URL: https://authentik.dooplex.hu/application/o/token/ +# - User API URL: https://authentik.dooplex.hu/application/o/userinfo/ +# 3. Create an OAuth2/OIDC Provider in Authentik: +# - Name: UpSnap +# - Authorization flow: default-provider-authorization-implicit-consent +# - Redirect URI: https://upsnap.dooplex.hu/api/oauth2-redirect +# - Scopes: openid email profile +# 4. Create an Application in Authentik: +# - Name: UpSnap +# - Slug: upsnap +# - Provider: UpSnap +# +# CAUTION: Do not enable/disable auth methods in PocketBase settings +# (other than OIDC). This can cause DB migrations that conflict with updates. +# +# Post-deploy: +# 1. Visit https://upsnap.dooplex.hu and create admin account +# 2. Configure OIDC as above +# 3. Add workstation device (IP + MAC address) +# 4. Optionally configure shutdown command: +# sshpass -p "$PASSWORD" ssh -o "StrictHostKeyChecking=no" user@IP "shutdown /s /t 0" +# ============================================ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: upsnap-data + namespace: admin-system + labels: + app.kubernetes.io/instance: upsnap + app.kubernetes.io/name: upsnap + recurring-job-group.longhorn.io/backup: enabled +spec: + accessModes: + - ReadWriteOnce + storageClassName: longhorn + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: upsnap + namespace: admin-system + labels: + app.kubernetes.io/instance: upsnap + app.kubernetes.io/name: upsnap + app.kubernetes.io/version: "5" +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/instance: upsnap + app.kubernetes.io/name: upsnap + template: + metadata: + labels: + app.kubernetes.io/instance: upsnap + app.kubernetes.io/name: upsnap + app.kubernetes.io/version: "5" + annotations: + match-regex.version-checker.io/upsnap: '^\d+$' + spec: + # hostNetwork required for sending WoL magic packets on LAN + hostNetwork: true + dnsPolicy: ClusterFirstWithHostNet + containers: + - name: upsnap + image: ghcr.io/seriousm4x/upsnap:5 + imagePullPolicy: IfNotPresent + env: + - name: TZ + value: Europe/Budapest + - name: UPSNAP_INTERVAL + value: "@every 30s" + - name: UPSNAP_SCAN_RANGE + value: "192.168.0.0/24" + # Privileged ping (required for hostNetwork WoL) + - name: UPSNAP_PING_PRIVILEGED + value: "true" + ports: + - containerPort: 8090 + name: http + protocol: TCP + livenessProbe: + httpGet: + path: /api/health + port: http + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /api/health + port: http + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 500m + memory: 256Mi + volumeMounts: + - name: data + mountPath: /app/pb_data + volumes: + - name: data + persistentVolumeClaim: + claimName: upsnap-data +--- +# Service still needed for ingress even with hostNetwork +apiVersion: v1 +kind: Service +metadata: + name: upsnap + namespace: admin-system + labels: + app.kubernetes.io/instance: upsnap + app.kubernetes.io/name: upsnap + app.kubernetes.io/version: "5" +spec: + type: ClusterIP + ports: + - name: http + port: 8090 + protocol: TCP + targetPort: http + selector: + app.kubernetes.io/instance: upsnap + app.kubernetes.io/name: upsnap +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: upsnap + namespace: admin-system + labels: + app.kubernetes.io/instance: upsnap + app.kubernetes.io/name: upsnap + app.kubernetes.io/version: "5" + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + external-dns.alpha.kubernetes.io/hostname: upsnap.dooplex.hu,upsnap.home + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-body-size: 10m + nginx.ingress.kubernetes.io/configuration-snippet: | + set $geo_allowed 0; + if ($remote_addr ~ "^192\.168\.") { set $geo_allowed 1; } + if ($remote_addr ~ "^10\.") { set $geo_allowed 1; } + if ($geoip2_country_code = "HU") { set $geo_allowed 1; } + if ($geo_allowed = 0) { + return 403 "Access restricted to Hungary"; + } +spec: + ingressClassName: nginx-internal + rules: + - host: upsnap.dooplex.hu + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: upsnap + port: + number: 8090 + - host: upsnap.home + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: upsnap + port: + number: 8090 + tls: + - hosts: + - upsnap.dooplex.hu + secretName: upsnap-tls \ No newline at end of file diff --git a/kisfenyo-system/guacamole.yaml b/kisfenyo-system/guacamole.yaml new file mode 100644 index 0000000..d946257 --- /dev/null +++ b/kisfenyo-system/guacamole.yaml @@ -0,0 +1,341 @@ +# ============================================ +# Apache Guacamole - Clientless Remote Desktop Gateway +# ============================================ +# https://guacamole.apache.org/ +# Images: guacamole/guacamole:1.6.0 (web frontend) +# guacamole/guacd:1.6.0 (connection proxy daemon) +# +# Access: https://remote.dooplex.hu (Authentik OIDC + DB login) +# https://remote.home (internal) +# +# Architecture: +# Browser → [Ingress] → guacamole (Tomcat/8080) → guacd (4822) → RDP/VNC/SSH target +# guacamole ↔ PostgreSQL (shared CloudNativePG cluster) +# +# Database Setup (ONE-TIME, run before deploying): +# 1. Create the database and user in CloudNativePG: +# kubectl exec -it -n database-system postgresql-1 -- psql -U postgres +# CREATE USER guacamole WITH PASSWORD ''; +# CREATE DATABASE guacamole_db OWNER guacamole; +# \q +# +# 2. Generate and apply the Guacamole schema: +# kubectl run guac-initdb --rm -it --restart=Never \ +# --image=guacamole/guacamole:1.6.0 \ +# -- /opt/guacamole/bin/initdb.sh --postgresql > /tmp/guac-initdb.sql +# +# kubectl cp /tmp/guac-initdb.sql database-system/postgresql-1:/tmp/initdb.sql +# kubectl exec -it -n database-system postgresql-1 -- \ +# psql -U guacamole -d guacamole_db -f /tmp/initdb.sql +# +# 3. Create the secret: +# kubectl create secret generic guacamole-secrets \ +# -n kisfenyo-system \ +# --from-literal=postgres-password='' \ +# --from-literal=openid-client-id='' +# +# SSO Setup (Native OIDC): +# Guacamole has built-in OpenID Connect support via environment variables. +# 1. Create an OAuth2/OIDC Provider in Authentik: +# - Name: Guacamole +# - Authorization flow: default-provider-authorization-implicit-consent +# - Client type: Public +# - Redirect URI: https://remote.dooplex.hu/ +# - Scopes: openid email profile +# - Subject mode: Based on the User's Email +# - Signing Key: Select your Authentik signing key +# 2. Create an Application in Authentik: +# - Name: Guacamole +# - Slug: guacamole +# - Provider: Guacamole +# +# Post-deploy: +# 1. Login with default credentials: guacadmin / guacadmin +# 2. Go to Settings → Connections → New Connection: +# - Name: Workstation +# - Protocol: RDP +# - Hostname: 192.168.0.XXX (workstation LAN IP) +# - Port: 3389 +# - Username: +# - Password: (NOT the PIN) +# - Security mode: Any +# - Ignore server certificate: checked +# 3. Change guacadmin password! +# 4. (Optional) Assign connections to OIDC users after they first log in +# +# Windows Prep: +# - Enable Remote Desktop: Settings → System → Remote Desktop → On +# - Ensure account has a password set (PIN won't work over RDP) +# - Firewall: allow port 3389 from 192.168.0.0/24 +# ============================================ +--- +# guacd - The Guacamole proxy daemon (handles RDP/VNC/SSH connections) +apiVersion: apps/v1 +kind: Deployment +metadata: + name: guacd + namespace: kisfenyo-system + labels: + app.kubernetes.io/instance: guacamole + app.kubernetes.io/name: guacd + app.kubernetes.io/version: "1.6.0" +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/instance: guacamole + app.kubernetes.io/name: guacd + template: + metadata: + labels: + app.kubernetes.io/instance: guacamole + app.kubernetes.io/name: guacd + app.kubernetes.io/version: "1.6.0" + annotations: + match-regex.version-checker.io/guacd: '^\d+\.\d+\.\d+$' + spec: + containers: + - name: guacd + image: guacamole/guacd:1.6.0 + imagePullPolicy: IfNotPresent + env: + - name: TZ + value: Europe/Budapest + ports: + - containerPort: 4822 + name: guacd + protocol: TCP + livenessProbe: + tcpSocket: + port: guacd + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + readinessProbe: + tcpSocket: + port: guacd + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 2000m + memory: 1Gi + # Optional: mount for file transfer / drive mapping + volumeMounts: + - name: drive + mountPath: /drive + volumes: + - name: drive + emptyDir: {} +--- +apiVersion: v1 +kind: Service +metadata: + name: guacd + namespace: kisfenyo-system + labels: + app.kubernetes.io/instance: guacamole + app.kubernetes.io/name: guacd + app.kubernetes.io/version: "1.6.0" +spec: + type: ClusterIP + ports: + - name: guacd + port: 4822 + protocol: TCP + targetPort: guacd + selector: + app.kubernetes.io/instance: guacamole + app.kubernetes.io/name: guacd +--- +# guacamole - The web frontend (Tomcat) +apiVersion: apps/v1 +kind: Deployment +metadata: + name: guacamole + namespace: kisfenyo-system + labels: + app.kubernetes.io/instance: guacamole + app.kubernetes.io/name: guacamole + app.kubernetes.io/version: "1.6.0" +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/instance: guacamole + app.kubernetes.io/name: guacamole + template: + metadata: + labels: + app.kubernetes.io/instance: guacamole + app.kubernetes.io/name: guacamole + app.kubernetes.io/version: "1.6.0" + annotations: + match-regex.version-checker.io/guacamole: '^\d+\.\d+\.\d+$' + spec: + containers: + - name: guacamole + image: guacamole/guacamole:1.6.0 + imagePullPolicy: IfNotPresent + env: + - name: TZ + value: Europe/Budapest + # --- guacd connection --- + - name: GUACD_HOSTNAME + value: guacd.kisfenyo-system.svc.cluster.local + - name: GUACD_PORT + value: "4822" + # --- PostgreSQL (shared CloudNativePG) --- + - name: POSTGRES_HOSTNAME + value: postgresql-rw.database-system.svc.cluster.local + - name: POSTGRES_PORT + value: "5432" + - name: POSTGRES_DATABASE + value: guacamole_db + - name: POSTGRES_USER + value: guacamole + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: guacamole-secrets + key: postgres-password + # --- Serve at / instead of /guacamole --- + - name: WEBAPP_CONTEXT + value: "ROOT" + # --- OpenID Connect (Authentik) --- + - name: OPENID_AUTHORIZATION_ENDPOINT + value: "https://authentik.dooplex.hu/application/o/authorize/" + - name: OPENID_JWKS_ENDPOINT + value: "https://authentik.dooplex.hu/application/o/guacamole/jwks/" + - name: OPENID_ISSUER + value: "https://authentik.dooplex.hu/application/o/guacamole/" + - name: OPENID_CLIENT_ID + valueFrom: + secretKeyRef: + name: guacamole-secrets + key: openid-client-id + - name: OPENID_REDIRECT_URI + value: "https://remote.dooplex.hu/" + - name: OPENID_USERNAME_CLAIM_TYPE + value: "preferred_username" + - name: OPENID_GROUPS_CLAIM_TYPE + value: "groups" + - name: OPENID_SCOPE + value: "openid email profile" + # Show both DB login form AND OIDC button on login page + - name: EXTENSION_PRIORITY + value: "*, openid" + # Auto-create Guacamole accounts for OIDC users + - name: POSTGRESQL_AUTO_CREATE_ACCOUNTS + value: "true" + ports: + - containerPort: 8080 + name: http + protocol: TCP + livenessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + readinessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + resources: + requests: + cpu: 100m + memory: 512Mi + limits: + cpu: 1000m + memory: 1Gi +--- +apiVersion: v1 +kind: Service +metadata: + name: guacamole + namespace: kisfenyo-system + labels: + app.kubernetes.io/instance: guacamole + app.kubernetes.io/name: guacamole + app.kubernetes.io/version: "1.6.0" +spec: + type: ClusterIP + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: http + selector: + app.kubernetes.io/instance: guacamole + app.kubernetes.io/name: guacamole +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: guacamole + namespace: kisfenyo-system + labels: + app.kubernetes.io/instance: guacamole + app.kubernetes.io/name: guacamole + app.kubernetes.io/version: "1.6.0" + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + external-dns.alpha.kubernetes.io/hostname: remote.dooplex.hu,remote.home + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-body-size: 100m + # WebSocket support (required for Guacamole RDP/VNC sessions) + nginx.ingress.kubernetes.io/proxy-buffering: "off" + nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" + nginx.ingress.kubernetes.io/configuration-snippet: | + set $geo_allowed 0; + if ($remote_addr ~ "^192\.168\.") { set $geo_allowed 1; } + if ($remote_addr ~ "^10\.") { set $geo_allowed 1; } + if ($geoip2_country_code = "HU") { set $geo_allowed 1; } + if ($geo_allowed = 0) { + return 403 "Access restricted to Hungary"; + } +spec: + ingressClassName: nginx-internal + rules: + - host: remote.dooplex.hu + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: guacamole + port: + number: 8080 + - host: remote.home + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: guacamole + port: + number: 8080 + tls: + - hosts: + - remote.dooplex.hu + secretName: guacamole-tls \ No newline at end of file