19/11/2021

Le cloud gaming : Le PC RETRO GAMING !

Vous connaissez Shadow ? C’est un service de cloud gaming français développé par Blade. J’ai utilisé cette solution pendant 1 ans, il faut dire que l’offre à 15€/mois était vraiment intéressante pour les utilisateurs. Un Ordinateur assez puissant pour faire tourner mes jeux et accessible depuis mon pc et mon téléphone. Cependant ils se sont cassé la gueule et ont été racheté par OVH. Le problème c’est qu’après, le prix à doubler et ça c’est relou ! Du coup je me suis m’y dans l’idée de monter mon propre système. Et voici comment est né le projet PC RETRO GAMING !

Présentation

Alors le principe est d’installer des VM sur un PC fixe, leur dédier des cartes graphiques, mutualiser le processeur et se connecter à distance dessus.

Ce système présente plusieurs avantages :

Voilà ce que donne le projet fini :

photo_pc

Le pc se présente comme un ordinateur normal comme vous pouvez le voir sur cette capture (c’est le pc du milieu). Le pc de gauche est connecté en remote à celui du milieu.

photo_phone

Ici je me connecte en remote depuis mon telephone pour jouer sur le PC

Architecture

Pour mener à bien ce projet j’ai à ma disposition un PC fixe de pur gaming avec

La carte graphique dédié servira pour la VM Windows (pour le GAMING !) et la carte graphique AMD pour un Linux desktop classique.

Sur la machine hôte je vais installer de quoi virtualiser les deux machines.

Je vais aussi dédier quelques ports USB pour les utiliser directement dans les VM.

La virtualisation

La virtualisation est une grosse partie du projet. ça a été beaucoup de recherche pour comprendre les différents types de virtualisation pour trouver la solution la plus adaptée. Pour mener à bien ce projet j’ai choisi d’utiliser KVM.

KVM (Kernel-based Virtual Machine) est une technologie de virtualisation Open Source intégrée à Linux®. Avec KVM, vous pouvez transformer Linux en un hyperviseur qui permet à une machine hôte d’exécuter plusieurs environnements virtuels isolés, appelés invités ou machines virtuelles.

Ici kvm nous permet de répondre à tous ces besoins :

Il n’existe pas de conteneur windows. Je ne peux pas utiliser lxc ni docker. Pour faire ce genre de machine je dois utiliser une machine virtuelle.

Par défaut pour créer une VM il faut donner tailles mémoires et un espace disque.

Le fait de dédier des composants à une machine virtuelle s’appelle un PCI Passthrough. Pour faire ce genre de manipulation on ne peut pas utiliser de la virtualisation en conteneur avec lxc ou docker. Je dois obligatoirement passer par des machines virtuelles. De plus des conteneurs Windows ça n’existe pas. A l’école j’avais pour habitude de créer des VM avec VirtualBox. Cependant cet outil n’est pas assez bas niveau, il fait de la virtualisation de niveau 2 et pour réaliser un PCI Passthrough nous devons obligatoirement faire de la virtualisation de niveau 1.

Kvm est depuis la version 2.6.201 directement intégré dans le noyaux Linux. L’avantage c’est que je vais pouvoir construire mon infra sur un Dedian 11 et ainsi pouvoir gérer mon réseau de VM avec ce dernier.

Dédier des composant : PCI Passthrough

L’intérêt du passthrough est par exemple de pouvoir bénéficier de l’accélération 3D d’une carte graphique sur la machine virtuelle, ou encore la meilleur gestion d’une souris ou un clavier spécifique sur cette machine virtuelle.

Le PCI PASSTHROUGH consiste à décharger le pilote d’un matériel (Carte graphique , usb , souris …) pour ensuite l’assigner à une machine virtuelle par exemple.

Le principe est donc de mettre les GPU et les ports USB sur VFIO. Et de les assigner aux VM. Cependant ce n’est pas aussi simple. Les composants sont relié à un iommu groups qui permet de faire le lien entre les addresse physique et virtuelle.

IOMMU – autrement appelé input/output memory management unit, est un MMU c’est à dire une unité de gestion de mémoire qui fait le lien entre les adresses physique (IOVA) des composant vers des adresses virtuelles.

Pour dédier un composant il faut obligatoirement que tout le groupe soit sur vfio et les iommu group ne sont pas modifiable.

Dédier un composant comme son nom l’indique c’est le rendre exclusivement utilisable par la machine virtuelle donc inutilisable par la machine hôte alors si comme moi votre carte graphique se trouve dans le même iommu group que votre carte réseau vous êtes bloqué, car sans carte réseau sur la machine hôte, le projet est baisé. Il va falloir séparer ces deux éléments.

Visualisation IOMMU Group

#!/bin/bash

echo "Un instant s'il vous plait... Spyfox au poste de commandement mobile"

# Initialisation des iommu group
GROUP=`find /sys/kernel/iommu_groups/ -type l | cut -d '/' -f 5,7 --output-delimiter='-'`

for i in $GROUP; do

        # numero du group
        groupNumber=`echo $i | cut -d '-' -f 1`

        # address normale
        address=`echo $i | cut -d '-' -f 2`

        # address du pci 000:00:0:0
        pciAddress=`echo $i | cut -d ':' -f 2,3,4`

        # driver (in use)
        m=`lspci -k -s $pciAddress | grep "Kernel driver in use"`

        echo -n  "Group: "

        # S'il fait parti du group
        if [ $groupNumber -lt 10 ]
                then
                        echo -n " $groupNumber  "
                else
                        echo -n " $groupNumber "
        fi

        ## OUTPUT ##

        # adresse
        echo -n " $address "

        # name and id
        echo -n "`lspci -nn | grep $pciAddress | cut -d ' ' -f 2-`"

        # driver
        if ! [ -z "$driver" ]
                then
                        echo -n "   Driver:"
        fi

        # driver (in use)
        echo "$driver" | cut -d ':' -f 2

# sort
done | sort -nk2        

Exemple de sortie du script

Group:  9   0000:30:00.0 VGA compatible controller [0300]: Advanced Micro Devices, Inc. [AMD/ATI] Renoir [1002:1636] (rev d9)   Driver: vfio-pci
Group:  9   0000:30:00.1 Audio device [0403]: Advanced Micro Devices, Inc. [AMD/ATI] Device [1002:1637]   Driver: vfio-pci
Group:  9   0000:30:00.2 Encryption controller [1080]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 10h-1fh) Platform Security Processor [1022:15df]   Driver: vfio-pci
Group:  9   0000:30:00.3 USB controller [0c03]: Advanced Micro Devices, Inc. [AMD] Renoir USB 3.1 [1022:1639]   Driver: xhci_hcd
Group:  9   0000:30:00.4 USB controller [0c03]: Advanced Micro Devices, Inc. [AMD] Renoir USB 3.1 [1022:1639]   Driver: xhci_hcd
Group:  9   0000:30:00.6 Audio device [0403]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 10h-1fh) HD Audio Controller [1022:15e3]   Driver: vfio-pci

On voit bien ici que l’identifiant du composant est 1002:1636 et que l’identifiant PCI est 0000:30:00.0

ACS Override

Si comme moi vos composant sont relou vous allez devoir les séparer avec ce que l’on appelle un ACS Override. Sinon vous pouvez passer cette partie.

Alors tout d’abord il faut savoir que cette solution doit être utilisé en dernier recours car et présente quelques problèmes de sécurité. En effet le fait d’attacher l’un de ces périphériques (mais pas l’autre) à une VM permettrait à un logiciel malveillant s’exécutant dans la VM d’effectuer des écritures sur l’autre périphérique (celui qui est attaché à l’hôte !). C’est une fuite de la VM.

Imaginez que vous utilisiez le patch ACS Override pour attacher votre carte graphique à une VM pour jouer. Un virus infecte la VM. Il utilise la carte son de la carte mère qui se trouvait dans le même groupe IOMMU que votre carte graphique pour s’échapper de la VM (en écrivant dans la mémoire du noyau hôte) et prendre le contrôle du système d’exploitation hôte.

Le patch ACS Override ne fait pas partie du noyau Linux pour ces raisons. De plus, si vous l’utilisez, je vous encourage fortement à vous arrêter et à trouver une carte mère qui soit correctement isolé. Tant que vous l’utilisez pour attacher une partie, mais pas la totalité, d’un groupe IOMMU à un invité, votre système n’est ni sécurisé ni fiable. Outre les virus une écriture accidentelle par l’invité à la mauvaise adresse pourrait écraser toute mémoire sur votre hôte.

Maintenant que vous avez bien compris les risques voici un site qui répertorie des kernel linux patché que vous pouvez installer. Prenez la même version que vous avez actuellement.

Pour voir le kernel utilisé actuellement tapez la commande

uname -r

Vous téléchargez les 2 fichiers .deb (image + header) et de les installer

apt install ./linux-*

Et de redémarrer

reboot

Sélectionner le bon noyau au démarrage de la machine

Si vous changer votre noyau par défaut au démarrage il faut lui dire de démarrer le bon kernel. Il y a un paramètre qu’on peut envoyer au grub.

/etc/default/grub

GRUB_DEFAULT="1>2"

Pour que la modification face effet, il faut recompiler le grub

update-grub

Le 1>2 c’est le numéro de l’option qui doit être sélectionné par défaut au lancement de la machine hôte. Attention la liste commence par zéro. Concrètement ça veut dire que par défaut au démarrage mon système va sélectionner le deuxième puis le troisième choix.

Dédier le composant

Pour récupérer ces identifiants lancer le script de la partie Visualisation IOMMU Group

/etc/modprobe.d/vfio.conf

options vfio-pci ids=1002:1636,1002:1637
options vfio-pci ids=1022:15df,1022:1639,1022:15e3

/etc/modules

pci_stub
vfio
vfio_iommu_type1
vfio_pci
kvm
#xhci_hcd
kvm_amd
#kvm_intel
update-initramfs -u

/etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash amd_iommu=on pcie_acs_override=downstream,multifunction video=efifb:off"
update-grub

KVM

Installation

apt install qemu-kvm libvirt-clients libvirt-daemon-system bridge-utils virt-manager ovmf

Pour créer un disque, on le fait avec cette commande :

qemu-img create -f [format] [image name] [size]

qemu-img create -f raw win.img 30G

Format

Il existe plusieurs format :

ISO windows

Ensuite munissez-vous d’un ISO. Ici moi ça va être Windows.

Petite particularité : Si vous allez sur le site de microsoft depuis un linux, le site vous proposera uniquement de télécharger un utilitaire pour créer votre iso Windows #MICROSOFT2MERDE. La technique est de changer l'user agent de votre navigateur pour apparaitre sur linux auprès du site.

Pour chrome demerdez vous ! Fallait pas utiliser cette bouse aussi là.

La partie réseau

Avant de créer les VM il faut faire en sorte qu’elles puissent accéder au réseau. Nous allons mettre en place un bridge qui va permettre de les interconnecter.

[routeur physique (la box)]
  [   192.168.0.1  ]
           |
         eth0 (interface du PC)
      192.168.0.2
           |
         kvm0 (notre bridge)
       10.0.30.1
       /      \
   vm1          vm2
10.0.30.2    10.0.30.3
Ip forwarding

Pour arriver à ce résultat il faut que ton pc réagisse comme un routeur pour router les ip. Pour faire ça sous linux on active l’option ip forwarding. Pour savoir s’il est déjà, tape cette commande si ça renvoie … = 1 c’est bon

sysctl net.ipv4.ip_forward

Sinon lancez cette commande

sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/g' /etc/sysctl.conf

et lance la commande :

sysctl -p /etc/sysctl.conf
Configurer votre brige

Pour créer une interface, il faut créer un fichier avec la configuration de l’interface dans le répertoire /etc/network/interfaces.d. Normalement le fichier /etc/network/interfaces fait le lien avec ce répertoire (include). Vérifier au cas où par exemple sur certaines version le fichier doit porter l’extension .cfg

Créer un fichier /etc/network/interfaces.d/kvm0 qui contient :

auto kvm0
iface kvm0 inet static
    bridge_ports none
    address 10.0.30.1
    netmask 255.255.255.0

Si vous n’avez pas de répertoire interfaces.d, installez net-tools

apt install ifupdown net-tools

On peut ensuite contrôler l’interface avec les scripts ifup et ifdown:

# monter l'interface
ifup kvm0

# demonter l'interface
ifdown kvm0

Pour voir état du bridge, tapez la commande

brctl show

Installation VM

Pour créer et manager les VM j’utilise virsh manager. En commande line voici comment on l’utilise

# Sans le PCI passthrough (classique)
virt-install -n win --description "windows10" --os-type=windows --ram 4096 --vcpus=4 --graphics none --cdrom Win10_21H1_French_x64.iso --network bridge:kvm0 --graphics vnc,listen=0.0.0.0 --noautoconsole --disk win.img,format=raw

# Avec le PCI passthrough (composant dédier)
virt-install -n win --description "windows10" --os-type=windows --ram 4096 --vcpus=4 --graphics none --cdrom Win10_21H1_French_x64.iso --network bridge:kvm0 --graphics vnc,listen=0.0.0.0 --noautoconsole --disk win.img,format=raw --host-device=pci_0000_30_00_0
# Lancer
virsh start win10

# Stopper
virsh destroy win10 

Je vous ai filé les ligne de commande au cas où vous seriez attaché au CLI. Cependant je n’ai pas l’habitude de conseiller des outils avec une GUI mais je vous encourage à utiliser la GUI de virsh manager. Elle permet de se connecter à plusieurs instances, organiser ses VM, les paramétrer, ajouter/supprimer des composant PCI, gérer les snapshot et beaucoup encore… très très facilement. De plus il embarque aussi un client VNC, spice (pratique pour l’installation des machines en mode graphique)

manager1 manager2 manager3

Camoufler la VM

virsh edit win10
<hyperv>
<feature>
    ...
        <vendor_id state='on' value='123456789ab'/>
    ...
</hyperv>
        <kvm>
        <hidden state='on'/>
    </kvm>

<cpu mode='host-passthrough' check='none'>
    ...
    <feature policy='disable' name='hypervisor'/>
</cpu>

relancer la VM et c’est bon.

RDP ?

Le RDP est un protocole de bureau à distance développé par Microsoft. Il est très utile pour prendre la main sur un PC à distance et est utilisé dans beaucoup d’entreprise. Ce protocole est natif sur Windows (version pro) et installable très facilement sur Linux via le paquet xrdp.

Il tourne sur le port 3389.

J’ai fait un petit tuto pour installer RDP sur des conteneur lxc, c’est presque pareil pour une VM.

RDP Alternatives

Le RDP c’est cool pour se connecter à distance mais pour jouer ce n’est pas le plus performant. Du coup j’ai commencé à chercher comment se passer de RDP et avoir un protocole qui permette d’obtenir une fluidité et une faible latence.

D’abord je me suis tourné vers Steam link qui permet de jouer à distance à ses jeux Steam dans le même réseau. Le problème vous l’aurez compris c’est que c’est dispo que pour les jeux Steam

Ensuite je suis tombé sur Moonlight une implémentation open source du GameStream protocole d’Nvidia. Le problème c’est que s’est uniquement pour les cartes Nvidia et moi je veux que mon système ne dépendant pas de mon architecture.

Pour finir je suis tombé sur Parsec. Un outil open source qui permet de partager sa machine avec d’autres personnes. Sa version gratuite permet de partager sa machine avec autant de personne que l’on souhaite, il prend en charge le son, la gestion des manettes, la gestion des personnes connecté.

Parsec Virtual Display Workaround

La version gratuite de Parsec ne permet pas de partager plusieurs écrans ni de partager un Virtual display. C’est à dire que pour partager mon écran je dois obligatoirement brancher un écran sur la carte graphique. Cependant pour palier à ce problème je simule un écran avec un émulateur d’affichage. https://www.amazon.fr/gp/product/B07BCCTWMX?psc=1/. Que je branche au niveau de la carte que j’ai dédier sur la VM en question.

Je garde Le RDP pour de la bureautique car il est beaucoup plus pratique à lancer et à utiliser. Et j’utilise Parsec pour les jeux vidéo ainsi que pour partager l’accès avec d’autre personne.

Le mode Arcade de parsec.

Petite aparté sur le mode arcade. Je vous conseille vivement de jeter à œil à cet outil car il permet à n’importe quel utilisateur de rendre son accès public et ainsi jouer avec n’importe quel utilisateur de parsec. En gros si une personne partage sa partie de Street fighter vous pouvez rejoindre et jouer avec (sans avoir le jeu). De la pure folie.

VPN

Pour se connecter depuis une machine extérieure à une VM je ne vais pas ouvrir le port 3389 sur ma box. Ce n’est pas une très sécurisé. De plus je n’ai pas forcément une IP Fixe sur auprès de mon operateur. Du coup je vais rendre intégrer mon système dans mon VPN Wireguard. Vous pouvez très bien vous passez de cette partie si vous ne souhaitez pas vous connecter à distance.

Voici un petit tuto pour installer WireGuard.

Si vous n’avez pas de VPN vous pouvez aussi très bien ouvrir un tunnel de la VM au client de votre choix. Sur ce tutoriel j’avais écrit une petite partie sur le tunnel SSH si ça intéresse

Conclusion

Chaque configuration est différente, j’ai mis pas mal de temps à obtenir ce que je voulais et c’est encore améliorable. Cet article ne va surement pas correspondre exactement à votre cas. (Par exemple comme ma machine hôte n’utilise pas de GPU je n’ai pas besoin de blacklister les drivers alors que vous aurez surement un linux desktop sur votre machine hôte). Mais en cas de problème je vous conseille de prendre le temps de lire en détails les différents projets que vous allez trouver sur internet. Et n’oubliez pas que “FUCK LE CODE 43 !!!” et “FUCK Windows !”. Je vous déteste tous… La bise <3.

Sources

Voici les sources les plus intéressantes qui m’ont aidé à réaliser ce projet :