Vous souhaitez auto-héberger RustDesk (alternative libre à AnyDesk/TeamViewer) dans un cluster Kubernetes avec interface Web, WebSocket WSS et intégration complète ? Voici une solution prête à l’emploi avec MetalLB, Let’s Encrypt, liveness/readiness probes, et un déploiement clef en main du web client.
Table of Contents
Prérequis
- Un cluster Kubernetes fonctionnel
- MetalLB configuré (pour attribuer une IP publique au service LoadBalancer)
- Cert-manager installé avec un issuer
letsencrypt-prod
- Un nom de domaine configuré vers l’IP fournie par MetalLB (ex.
rustdesk.test.local
)
Comment récupérer la clé (KEY) ?
Lors du premier démarrage de votre serveur RustDesk, une paire de clés est générée automatiquement. Vous pouvez extraire la clé publique (à injecter côté client) via la commande suivante :
kubectl exec -n rustdesk deploy/rustdesk-server -c hbbs -- cat /root/id_ed25519.pub
Cette clé est à renseigner dans la variable d’environnement KEY
du web client pour établir des connexions sécurisées.
Client Web accessible
Vous pouvez tester l’interface Web ici : https://rustdesk-web-client.pascal-mietlicki.fr
Adresse IP externe à utiliser dans le client lourd
Pour connaître l’IP publique exposée par MetalLB sur laquelle pointer les ports 21116
(registry/heartbeat) et 21117
(relay), utilisez :
kubectl get svc -n rustdesk rustdesk-server
Exemple de sortie :
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
rustdesk-server LoadBalancer 10.43.120.151 192.168.100.5 21115:... 21116:... 21117:...
Dans le client RustDesk natif, configurez :
- ID Server :
192.168.100.5:21116
- Relay Server :
192.168.100.5:21117
Vous aurez aussi besoin de la clef que vous avez récupéré dans l’étape précédente, elle sera à utiliser à la fois dans le client lourd mais aussi dans le client Web (env KEY).
Namespace RustDesk
apiVersion: v1
kind: Namespace
metadata:
name: rustdesk
Volume persistant pour les clés et les données
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: rustdesk-data
namespace: rustdesk
labels:
app: rustdesk-server
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 5Gi
Déploiement du serveur RustDesk (hbbs + hbbr)
apiVersion: apps/v1
kind: Deployment
metadata:
name: rustdesk-server
namespace: rustdesk
labels:
app: rustdesk-server
spec:
replicas: 1
selector:
matchLabels: { app: rustdesk-server }
template:
metadata:
labels: { app: rustdesk-server }
spec:
containers:
- name: hbbs
image: docker.io/rustdesk/rustdesk-server:latest
imagePullPolicy: IfNotPresent
command: ["hbbs"]
args: ["-k","_"]
ports:
- name: nat-port
containerPort: 21115
protocol: TCP
- name: registry-port
containerPort: 21116
protocol: TCP
- name: heartbeat-port
containerPort: 21116
protocol: UDP
- name: web-port
containerPort: 21118
protocol: TCP
livenessProbe:
tcpSocket: { port: 21115 }
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
tcpSocket: { port: 21115 }
initialDelaySeconds: 5
periodSeconds: 10
volumeMounts:
- name: rustdesk-data
mountPath: /root
- name: hbbr
image: docker.io/rustdesk/rustdesk-server:latest
imagePullPolicy: IfNotPresent
command: ["hbbr"]
args: ["-k","_"]
ports:
- name: relay-port
containerPort: 21117
protocol: TCP
- name: client-port
containerPort: 21119
protocol: TCP
livenessProbe:
tcpSocket: { port: 21117 }
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
tcpSocket: { port: 21117 }
initialDelaySeconds: 5
periodSeconds: 10
volumeMounts:
- name: rustdesk-data
mountPath: /root
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels: { app: rustdesk-server }
topologyKey: kubernetes.io/hostname
volumes:
- name: rustdesk-data
persistentVolumeClaim:
claimName: rustdesk-data
Service LoadBalancer exposé via MetalLB
apiVersion: v1
kind: Service
metadata:
name: rustdesk-server
namespace: rustdesk
labels:
app: rustdesk-server
spec:
type: LoadBalancer
externalTrafficPolicy: Cluster
selector: { app: rustdesk-server }
ports:
- name: nat-port
port: 21115
targetPort: 21115
protocol: TCP
- name: registry-port
port: 21116
targetPort: 21116
protocol: TCP
- name: heartbeat-port
port: 21116
targetPort: 21116
protocol: UDP
- name: web-port
port: 21118
targetPort: 21118
protocol: TCP
- name: relay-port
port: 21117
targetPort: 21117
protocol: TCP
- name: client-port
port: 21119
targetPort: 21119
protocol: TCP
Déploiement du Web Client
apiVersion: apps/v1
kind: Deployment
metadata:
name: rustdesk-web-client
namespace: rustdesk
labels:
app: rustdesk-web-client
spec:
replicas: 1
selector:
matchLabels: { app: rustdesk-web-client }
template:
metadata:
labels: { app: rustdesk-web-client }
spec:
containers:
- name: web-client
image: pmietlicki/rustdesk-web-client:v1
imagePullPolicy: Always
ports:
- containerPort: 5000
env:
- name: CUSTOM_RENDEZVOUS_SERVER
value: "rustdesk.test.local"
- name: RELAY_SERVER
value: "rustdesk.test.local"
- name: KEY
value: "xxxxxxxxxxxxxxxxxxxxxxx"
livenessProbe:
httpGet: { path: "/", port: 5000 }
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet: { path: "/", port: 5000 }
initialDelaySeconds: 5
periodSeconds: 10
Service interne Web Client
apiVersion: v1
kind: Service
metadata:
name: rustdesk-web-client
namespace: rustdesk
labels:
app: rustdesk-web-client
spec:
type: ClusterIP
selector: { app: rustdesk-web-client }
ports:
- port: 5000
targetPort: 5000
protocol: TCP
Ingress unique avec WebSocket + HTTPS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rustdesk
namespace: rustdesk
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
spec:
tls:
- hosts: [rustdesk.test.local]
secretName: rustdesk-server-tls
rules:
- host: rustdesk.test.local
http:
paths:
- path: /ws/id
pathType: Prefix
backend:
service: { name: rustdesk-server, port: { name: web-port } }
- path: /ws/relay
pathType: Prefix
backend:
service: { name: rustdesk-server, port: { name: client-port } }
- path: /
pathType: Prefix
backend:
service: { name: rustdesk-web-client, port: { number: 5000 } }
Pourquoi cette solution ?
- Elle repose uniquement sur les images officielles RustDesk et une version web-client personnalisée disponible ici : docker-rustdesk-web-client.
- Le système est sécurisé par TLS, supporte WSS, et peut être intégré dans un environnement Zero Trust.
- Le tout est optimisé pour Kubernetes avec un déploiement industrialisé et facilement scalable.
✅ Compatible avec un usage interne ou externe.
🔐 Pas besoin de se connecter aux serveurs publics.
📡 Possibilité d’utiliser le web client dans un navigateur avec relais WebSocket sécurisé.