Machines virtuelles et contenaires : comment créer ses propres serveurs from scratch?
Durant mon temps libre, j’ai eu besoin de mettre en place des machines linux pour différentes raisons :
- Faire des essais de nouvelles technologies pour ma satisfaction personnelle.
- Exposer des services qui me sont utiles (serveur web, serveur de jeu, VPN1, stockage de fichiers, etc)
- Avoir un environnement de tests pour ensuite déployer des services chez d’autres personnes.
À mes débuts, je ne pouvais pas juste louer un serveur pour des raisons de moyens de paiement. Du coup, passionné, je récupérais du matériel (et je continue).
Je prenais donc un vieil ordinateur récupéré et remis en état, le reformatais pour ensuite installer un OS2 et faire mes tests. Mais au fur et à mesure du temps j’ai trouvé cette “logistique” fatigante :
- Chercher une machine
- Vérifier le fonctionnement et la compatibilité des composants
- Chercher un écran et un clavier
- Tendre un câble ethernet
- Réinstaller un OS
- Installer et configurer l’OS et les services dessus
Aussi en cas de besoin d’exposer plusieurs services, je n’avais pas envie de tout mettre sur un même serveur, sans rien diviser pour des raisons de sécurité entre autres.
Seulement, n’ayant pas un énorme switch3 – ni de câbles ethernet, ni beaucoup de prises électriques, ni une volonté de consommer beaucoup trop de courant – maintenir en fonctionnement beaucoup de matériels de récupération, anciens, pas forcément le plus fiable, n’était pas une option.
Me retrouvant limité, j’ai pensé à plusieurs solutions : la virtualisation et les containers. J’ai opté pour les deux solutions à des niveaux différents, comme nous le verrons par la suite.
Architecture
Donc voici les spécifications de la machine de récupération utilisée que j’avais sous la main :
- CPU4 :
Athlon II x2 255 3.1GHz
de 2010 - RAM5 :
8GB
- Carte Réseau : gigabit ethernet intégrée à la carte mère
- Stockage : HDD6 de
512GB
.
Ce n’est pas très puissant, mais je n’avais pas besoin de beaucoup de puissance et surtout, c’est ce que j’avais sous la main. Mais si vous souhaitez le refaire avec du meilleur matériel c’est parfait. Vérifiez toujours que votre CPU supporte la virtualisation et qu’elle est activée dans le bios7.
Voici donc comment j’ai architecturé mon projet :
Au départ je souhaitais me servir des services de ma box8 comme le NAT, le firewall9 intégré, etc. J’ai donc configuré l’hôte pour que les machines virtuelles soient sur le réseau local, pour expérimenter et faire mes services privés (envie de domotique dans le futur) accessibles en LAN10.
L’idée était donc de me servir du NAT11 de la box pour les rares services exposés.
Sauf que ma box actuelle est si mauvaise qu’elle arrive pas à enregistrer les paramètres nat entre autres. Heureusement la fonctionnalité DMZ “marche”, enfin comme le font certains ISP avec leur box, pas une vraie DMZ avec un second sous réseau, ni même un vlan12, mais juste de quoi refiler tout le trafic à une machine du réseau locale – une vraie passoire si la machine exposée n’a pas un firewall bien configuré.
Comme on peut le voir sur l’image, j’ai donc mis une machine virtuelle dédiée au firewall et c’est elle qui est en front (sur l’extérieur) avec un firewall et qui fait les bonnes redirections vers les autres machines virtuelles du LAN qui leur permettront d’exposer leurs services. En fait c’est du NAT également.
Cette architecture possède des défauts que j’exposerai de façon non exhaustive à la fin de cet article. Je parlerai également de ce que je souhaite changer ou améliorer.
Choix du système hôte
J’ai donc eu besoin d’un hyperviseur13. Étant assez porté sur l’OpenSource, j’ai écarté VMWare. Par ailleurs, je ne suis même pas sûr que mon matériel l’aurait supporté. J’ai fini par chercher une solution faite sur une distribution linux.
Proxmox est vraiment une solution pratique, mais j’avais aussi envie de tout faire par moi-même. J’avais certes un besoin, mais aussi une envie d’apprendre des choses.
J’ai fini par choisir centos 8 dont l’annonce du raccourcissement de la durée de vie n’avait pas encore été faite au moment où j’ai monté ce serveur. Je compte m’occuper de ce problème, comme vous le verrez à la fin avec les améliorations à venir.
Du coup voici les raisons pour lesquelles j’avais choisis centos :
- Clone de RHEL
- Stabilité
- Fiabilité
- Support 10 ans avec mises à jour de sécurités régulières
- Groupes de paquets, fonctionnalité équivalente aux méta-paquets debian
- Documentation redhat parfaitement adaptée.
Pour la virtualisation, j’ai donc choisi qemu-kvm avec libvirt. C’est performant, c’est très utilisé, parmi les mieux intégrés à linux, et cette solution étant conseillée et détaillée dans la documentation redhat, c’est un plus.
Configuration de l’hôte
Dans un premier temps, nous devons installer la solution de virtualisation. Si vous installez centos spécifiquement pour ça, vous pouvez selectionner le groupe de virtualisation dans la selection des paquets à installer. Si vous avez déjà installé centos, vous pouvez installer le groupe de virtualisation avec
1
dnf groupinstall "Virtualization Host"
Ça installe libvirt, qemu, et tous les outils dont nous aurons besoin.
J’ai également installé ovmf pour pouvoir faire des vm en uefi14.
1
dnf install edk2-ovmf
Ensuite, normalement tuned-adm est préinstallé sur centos, sinon vous pouvez l’installer avec
1
dnf install tuned && systemctl enable --now tuned
Enfin selectionnant le profil de performance dédié à la virtualisation avec
1
tuned-adm profile virtual-host && tuned-adm active
Pour être sur que le service libvirt est activé, nous allons activer et lancer le service
1
systemctl enable --now libvirtd
Ensuite nous allons configurer le réseau de l’hôte. Pour que les machines virtuelles soient sur le LAN, nous allons créer un bridge15 virtuel. Pour ça il y a deux méthodes : la méthode avec le xml de libvirt et la méthode externe. Pour ma part, j’ai choisi la méthode externe avec networkmanager et en particulier, nmcli. J’ai donc créé un bridge virtuel :
1
nmcli con add ifname br0 type bridge con-name br0
Et ensuite pour que mon hôte puisse être considéré comme connecté au bridge et avoir accès à internet par la même interface, je passe l’interface hôte, ici eth016, en slave de ce bridge :
1
nmcli con add type bridge-slave ifname eth0 master br0
Enfin, avoir une addresse ip statique pour la connection à l’hôte est quand même très pratique, du coup, pour l’ipv417 :
1
2
3
nmcli con mod br0 ipv4.addresses 192.168.0.100/24
nmcli con mod br0 ipv4.gateway 192.168.0.1
nmcli con mod br0 ipv4.method manual
Vous pouvez faire la même chose pour l’ipv618, pour ma part, ma box ne supporte pas l’ipv6. Enfin, nous pouvons activer cette interface :
1
nmcli con up br0
Enfin nous allons configurer le firewall de l’hôte. Étant sous centos, avec firewalld d’installé, c’est plutôt simple et fiable de suivre le principe des zones. Vous pouvez lister les zones et leurs détails avec :
1
firewall-cmd --list-all-zones
N’ayant besoin que du ssh19, la zone publique me convenait, et j’ai donc choisi public comme zone par défaut. La zone libvirt convient aussi.
1
firewall-cmd --set-default-zone=public
Vous pouvez aussi spécifier l’addon’ que vous souhaitez par interface, et également modifier ou ajouter des zones custom, et ajouter des règles. En fait firewalld est un frontend simplifié de nftables, un firewall très utilisé sous linux. Ici je n’en parlerais pas plus car ce n’est pas l’objet de cet article. Normalement, maintenant, nous avons une centos prête à héberger des vm.
Firewall et DMZ
Nous allons maintenant monter la machine virtuelle qui va me servir de firewall. J’ai choisi alpine linux20 pour sa légèreté, sa sécurité et sa faible consommation de ressources. J’ai donc téléchargé la dernière iso21 sur le site officiel de alpine linux sur le disque du serveur, et avec
1
osinfo-query os
j’ai pu récupérer le short-id de l’OS que je veux installer.
Cela va me permettre de créer une machine virtuelle avec la commande suivante.
1
virt-install --boot uefi --machine q35 --name fw-1 --memory 128 --vcpus 1 --disk bus=virtio,size=16 --network bridge=br0,model=virtio --OS-variant alpinelinux3.13 --cdrom alpine-virt-3.13.5-x86_64.iso --graphics none
Cette commande est pour lancer la procédure d’installation dans un terminal, sans interface graphique, il est aussi possible de passer par du vnc22, pour des distributions avec installateurs graphiques. D’autres détails peuvent varier selon l’OS à virtualiser, et la façon de l’installer.
Par exemple ici j’ai choisis 128MB
de ram car j’ai pas besoin de beaucoup de RAM pour la gestion du réseau, et que alpine fonctionne très bien avec 128MB
.
Également ça me permet de garder la majorité de la RAM restante pour mes services. Aussi remarquer bridge=br0
, pour que les machines virtuelles se connectent au bridge établi préalablement.
Ensuite, j’ai lancé la procédure d’installation de alpine linux avec un serveur openssh23 et une ip fixe, ici 192.168.0.101
, puis je me suis connecté en ssh, car c’est plus pratique. La configuration du serveur ssh est hors de la portée de cet article.
Pour que la machine virtuelle redémarre toute seule si l’hyperviseur redémarre :
1
virsh autostart fw-1
La suite de l’article porte sur l’architecture pour des services publiques, créer des machines virtuelles suffisent en LAN pour des services privés. La suite de l’article est faite pour l’exposition de services externes. Les commandes suivantes se feront sur la machine virtuelle qui sera utilisée comme firewall.
Configuration basique du firewall sur la machine virtuelle
Pour gérer le firewall, j’ai décidé d’utiliser nftables.
Il a donc fallu l’installer.
1
apk add nftables
Pour pouvoir le configurer il faut lancer le service. Ces étapes sont relativement “critiques”, dans le sens où il y a un risque de s’enfermer dehors.
C’est pourquoi dans un premier temps on va directement autoriser le ssh dès le lancement du service. La configuration alpine par default est plutôt bien faite avec les bonnes règles pour l’ICMP (également cette configuration par défault fait que si on n’autorise pas de nouvelles connexions, les connexions existantes – comme celles utilisées pour la configuration – ne sont pas éjectées, donc on a la possibilité d’autoriser le ssh pour éviter de s’enfermer dehors)
1
2
rc-service nftables start
nft add rule inet filter input tcp dport 22 accept
Pour voir la configuration vous pouvez faire
1
nft -a list ruleset
Tant que vous ne sauvegardez pas, relancer le service suffit à revenir aux paramètres précédents. Evidemment, il est possible d’enlever des règles facilement, mais cet article n’a pas vocation à être une documentation de nftables. Quand vous sauvegardez soyez bien sûr que votre configuration ne vous enferme pas dehors. Pour sauvegarder :
1
rc-service nftables save
Et enfin pour faire en sorte que le service puisse se relancer au redémarrage de la machine virtuelle.
1
rc-update add nftables default
Configuration plus avancée du firewall sur la machine virtuelle
IP forwarding
Pour autoriser le forwarding24, il faut le permettre au niveau du kernel, c’est possible via des réglages avec sysctl25.
Éditez /etc/sysctl.conf, et ajoutez à la fin
1
2
3
4
5
# Pour l'ipv4
net.ipv4.ip_forward=1
# Pour l'ipv6
net.ipv6.conf.default.forwarding=1
net.ipv6.conf.all.forwarding=1
Et ensuite
1
sysctl -p
Pour vérifier que c’est bien appliqué, (par exemple pour l’ipv4) n’hésitez pas à faire
1
sysctl net.ipv4.ip_forward
et à regarder si le retour est bien 1
.
Maintenant nous allons activer le forwarding dans nftables en modifiant la chaine forwarding
1
nft 'add chain inet filter forward { policy accept; }'
Mise en place du NAT
Nous allons créer la table et les chaînes du NAT.
1
2
3
nft add table nat
nft 'add chain nat prerouting { type nat hook prerouting priority -100; }'
nft 'add chain nat postrouting { type nat hook postrouting priority 100; }'
En postrouting, nous allons faire du masquerading
1
nft add rule nat postrouting masquerade
Scénario d’exposition d’un service public
Maintenant que nous avons notre firewall prêt, supposons que nous avons sur une machine du réseau local en 192.168.0.102
avec un serveur web à exposer en 80
et 443
.
Le prerouting va permettre de rediriger le trafic avant de se frotter aux barrières de la chaine input. Pour plus d’informations, voir la documentation nftables par rapport aux priorités. Pour cela on prend l’interface d’entrée du trafic, ici eth0, et
1
2
nft add rule nat prerouting iif "eth0" tcp dport 80 dnat to 192.168.0.102
nft add rule nat prerouting iif "eth0" tcp dport 443 dnat to 192.168.0.102
Ça redirige tout le trafic qui arrive sur l’interface eth0 en tcp et sur les ports et 80
et 443
, vers la machine virtuelle hébergeant le serveur web qui lui répond au firewall qui va faire le masquerading pour pouvoir répondre à la requête à la vraie personne.
Evidemment une fois toutes les actions faites, ne pas oublier de sauvegarder la configuration
1
rc-service nftables save
Configuration de la box pour la DMZ
Je ne peux pas vous aider la dessus car ça dépend des box et opérateurs, mais l’idée c’est de mettre la machine virtuelle faisant office de firewall (dans l’exemple ici, son ip était 192.168.0.101
) en DMZ via les paramètres de votre box.
Conclusion et améliorations à venir
Voici les principaux défauts de cette configuration :
- Tout est dans mon LAN, donc si un service se fait pirater, il a accès à tout le LAN.
- J’aurais besoin du contrôle de ma box pour faire une vraie DMZ isolée par vlan ou second réseau
Voici des réflexions un peu fouillies que j’ai eues pour parvenir à une meilleure solution:
- Je ne peux pas changer ma box, pour des raisons de résidences.
- Je souhaitais me servir des services de la box mais c’est une erreur vu sa médiocrité.
- Autre possibilité, configurer le bridge virtuel sur l’hôte pour faire un vlan (aucune idée pour le moment de comment m’y prendre), pour que les vm exposées n’aient le droit de communiquer qu’entre elles et avec le monde extérieur mais pas avec le reste du LAN.
- Ajouter une carte réseau pour conserver un bridge actuel pour les services du réseau local uniquement
- Faire du routage sur une seconde carte réseau vers un réseau de vms (aucune idée de comment m’y prendre)
- Faire un réseau entre les vm depuis la vm firewall, mais ce n’est pas trivial car c’est virtuel
Je continue d’apprendre et tout n’est pas forcement juste, et serai heureux d’avoir des suggestions pertinentes par des personnes plus compétantes.
Ce sont des sujets relativement compliqués en réseau si on est pas technicien réseau de formation.
C’est loin d’être aussi trivial que ça puisse paraître, en pratique, les fournisseurs cloud, de serveur et de vps, vous donnent des machines “déjà câblées”.
Mon envie à l’avenir avec ce serveur :
- Mettre un nouvel OS car le support de centos 8 a été raccourci (debian 11 ? rocky linux 8 ?)
- Mieux repenser son architecture.
- M’améliorer en réseau grâce à ça.
Références
-
VPN, Virtual Private Network ↩
-
OS, Operating System ↩
-
Switch, Commutateur réseau ↩
-
CPU, Central Process Unit ↩
-
RAM, Random Access Memory ↩
-
HDD, Hard Drive Disk ↩
-
Basic Input Output System ↩
-
Box Internet ↩
-
Pare feu ↩
-
LAN, Local Area Network ↩
-
NAT, Network Adress Transcripteur ↩
-
VLAN, Virtual Local Area Network ↩
-
Micrologiciel ou matériel qui crée et exécute machines virtuelles ↩
-
Unified Extensible Firmware Interface ↩
-
Passerelle entre deux réseaux informatiques ↩
-
Nom traditionnellement donné au pilote de la carte physique réseau sur un système Linux ↩
-
IPV4, Internet Protocol Version 4 ↩
-
IPV6, Internet Protocol Version 6 ↩
-
Secure Shell (SSH) est à la fois un programme informatique et un protocole de communication sécurisé ↩
-
Alpine Linux ↩
-
ISO, une image ISO est la représentation binaire exacte d’un CD ou DVD-ROM afin de la stocker sur votre disque dur en vue de la duplication ultérieure du média original ↩
-
VNC, Virtual Network Computing, système de visualisation et de contrôle de l’environnement de bureau d’un ordinateur distant. ↩
-
Ensemble d’outils informatiques libres permettant des communications sécurisées sur un réseau informatique en utilisant le protocole SSH ↩
-
le forwarding consiste à rediriger des paquets réseaux reçus sur un port donné d’un ordinateur ou un équipement réseau vers un autre ordinateur ↩
-
Interface qui permet d’examiner et de modifier dynamiquement les paramètres des systèmes d’exploitation BSD et Linux ↩