Description
Je note ici l'ensemble des actions que je mène pour mettre en place un
hyperviseur sous CentOS 8.
En ces temps de confinement, cela permet de continuer à travailler sur
certaines applications, garder la main et faire de la veille technologique.
Globalement, beaucoup de chose se font aisément, les divers fournisseurs de
distributions font ce qu'il faut pour avoir des solutions utilisables, même
avec les versions Open Source.
Aussi, ayant un PC dans un coin avec 4 CPU et 8GB de RAM, c'est suffisant pour
manipuler les bases. Cela permet de faire tourner 3 machines en parallèles.
Points d'attention sur la sécurité
Il est facile de monter rapidement ces solutions. De mon côté, je m'attache
à étudier la solution technique mais aussi la sécurité de ce qui est mis en
place. Aussi, les règles de base que j'applique sont :
- Réduction de la surface d'attaque:
- je n'installe que ce qui est nécessaire et suffisant
- Le pare-feu est activé et seuls les flux nécessaires et suffisants sont ouverts
- moindre privilèges: les applications installées doivent tourner avec des id
différents
- Je n'installe que
- les paquets venant de chez CentOS/RHEL
- des paquets signés
- des logiciels recompilés depuis les sources si pas dans le référentiel
De manière globale, les recos de l'ANSSI
RedHat est un acteur fort dans ce périmètre et s'attache à fournir des
solutions sécurisées de base. C'est notamment le cas avec l'utilisation de
selinux. Point que beaucoup ont trop souvent l'habitude de désactiver :(
Installation du serveur
Création d'une clé usb bootable
Ici on réalise une action assez standard récupération de l'iso et copie sur une
clé USB
curl -O http://mirror.in2p3.fr/linux/CentOS/8.1.1911/isos/x86_64/CentOS-8.1.1911-x86_64-dvd1.iso
sudo dd if=CentOS-8.1.1911-x86_64-dvd1.iso of=/dev/sdXXX bs=512
/!\ - bien choisir le /dev/sdxxx qui correspond à sa clef USB (dmesg ou
journalctl)
Installation du système de base
En appliquant le principe d'installer ce qui est juste nécessaire, la démarche
est :
* installer le système avec le rôle de serveur de virtualisation
* ajouter des service
* faire les configurations spécifiques
Installation de base
Là encore pas de chose très spéciales, on boote sur la clé USB et on suis les
instructions :
* Nom de machine
* choix du partitionnement
* choix du rôle du serveur
* Configuration du réseau
* Mot de passe root
* Utilisateur supplémentaire administrateur
Service cockpit
Cockpit est une application qui permet de configurer le serveur au travers
d'une interface https. J'ai tendance à éviter d'installer des applications qui
font tout toutes seules pour favoriser l'apprentissage en profondeur de ce qui
est installé. Cela évite aussi d'augmenter la surface d'attaque du serveur.
Ici, le risque est réduit parce que je suis sur une infra locale avec des
systèmes de tests. Donc je l'installe pour découvrir.
Cockpit est installé par défaut mais le service n'est pas lancé.
systemctl enable cockpit.service
systemctl start cockpit.service
Ensuite, on accède à l'interface via l'url https://:9090/ et on se
connecte avec un des comptes du système. Etant en phase de découverte, je me
connecte root.
Cockpit est évolutif et peut être étoffé à l'aide de plugins.
Dans mon cas, voulant faire un serveur de virtu, j'ai ajouté les paquets
suivants:
* cockpit-machines: gestion de libvirt
* cockpit-storaged: Gestion du stockage
* cockpit-podman: gestion docker
* cockpit-dashboard: état du serveur.
On retrouve assez naturellement les éléments de configuration d'un système
d'hyper convergence. C'est la direction qu'à prise RedHat il y a plusieurs
années et c'est la raison pour laquelle IBM l'a acheté !:)
Après un tour rapide de l'interface, on peut faire pas mal de choses simples
mais on est vite bloqué dès qu'on sort des sentiers balisés. Or dans mon cas,
je sors de ces sentiers.
Configuration du réseau
C'est ici que je sors des sentiers battus pour la raison suivante.
Partie d'une configuration du réseau des VM par défaut, c'est du NAT qui est
configuré. Pour pouvoir accéder directement à ces machines, j'ai plusieurs
solutions :
* passer le réseau en bridge: alors les vm seront vue sur mon réseau local
* passer le réseau en mode routé
La solution bridge est séduisante mais je n'ai pas trop envie que des VM, qui
servent de tests, aient accès au reste de mon infra. Aussi, il faut pouvoir
isoler au maximum le réseau des VM tout en y ayant accès. Les flux peuvent
entrer mais pas sortir. Mais, si on bloque tout en sortie, on se heurte à
des soucis pour faire des mises à jour ou installer de nouvelles choses.
La solution routée est pas mal mais passerelle par défaut est ma box sur
laquelle je ne peux ajouter de route. Il faudrait alors que j'ajoute la
route à chacun des postes depuis lesquels je souhaite me connecter aux VM.
De plus, je partage cette infra avec des personnes qui sont elles aussi
confinées dont l'accès se fait forcément par la box... qui ne sait pas faire
le routage.
Doc
Libvirt
Pour avancer sur le sujet, j'ai pris la décision d'opter pour une première
solution qui est :
* garder le NAT et filtrer les flux sortants et entrants
* Créer un VM "bastion" qui est le point d'entrée pour les connexions
depuis l'extérieur
[BOX G]--.--[BOX P]
|
|
(tcp/xxxx)
|
|
-----------
| BOX |
-----------
|
|
(tcp/yyyy)
|
|
-------------|---------------------------------
| hyper \ |
| viser \ ---------------------- |
| | | | |
| [Bastion:22] | | |
| [VM2] [VM3] |
| |
-----------------------------------------------
Avec cette solution, il faut jouer avec le pare feu de l'hyper viseur pour
ajouter le pNAT pour router le flux entrant vers le bastion.
C'est là que cockpit atteint ses limites car on ne peut faire ce genre de
configurations aisément. C'est aussi là que je rouve un nouveau challenge
car je découvre nftable et l'instrumentation qui
en est faite par RedHat au travers de firewalld
Je suis en plein dans le truc des solutions qui font "papa/maman" mais qui sont
restrictives dès qu'on veut faire "plus" ou "différemment". Donc cela demande
un effort d'apprentissage des 2 technos ainsi que le modèle mis en place par
RedHat.
Parce que ça ne fait que 3j que j'ai commencé à regardé tout ça entre d'autres
choses, j'ai réussi à atteindre l'objectif de faire le pNAT pour donner les
accès mais ce n'est pas satisfaisant. En effet, je n'ai pas encore réussi
à tout configurer avec "firewalld". Je n'arrive pas à positionner, de manière
permanente 2 règles accept au bon endroit.
Lorsque que je relance le pare-feu, j'ai une règle qui est mal placée car elle
est automatiquement placée APRÈS les règles mises par "libvirtd" quand il lance
le réseau virtuel. J'en ai une autre qui ne reste pas.
Çà ressemble à ce
soucis
Pour que ça fonctionne, il faut :
* autoriser le flux tcp/yyyy entrant sur l'hyperviseur
* forwarder ce flux vers le bastion tcp/22
* autoriser ce forward à traverser le pare-feu.
Si j'utilise les outils mis à disposition par CentOS, je dois utiliser
firewall-cmd (je ne détaille pas ici la gestion des zones public/libvirt...
peut-être pus tard ou dans un autre doc car ce n'est pas le sujet du moment)
# On autorise le flux à entrer sur l'hyper viseur.
firewall-cmd --zone=public --add-port=yyyy/tcp
# Ajout du forward
firewall-cmd --zone=public --add-forward-port=port=yyyy:proto=tcp:toport=22:toaddr=<@ip_bastion>
Je suis ensuite obligé de faire les actions manuelles suivantes
# Lister la chaine qui m'initéresse avec les numéro de handle.
# Util pour le insert d'après
$ nft list table filter -a
# Dans la chain FORWARD, je repère le handle de la première règle
# et j'insère la règle qui me convient pour autoriser le forward des flux
# établis depuis réseau local vers le réseau virtuel.
# \/ ici c'est 12 mais ça peut-être autre chose
$ nft insert rule filter FORWARD handle 12 iifname enp2s0 oifname virbr0 ct state new,untracked accept
# Enfin j'ajoute la règle qui permet au ssh d'arriver sur le bastion.
$ nft add rule filter OUTPUT oifname "virbr0" meta l4proto tcp tcp dport 22 counter accept
En écrivant ces lignes, j'ai chercher des informations et je suis tombé sur
virsh nwfilter-list ... Ciel, encore un truc intéressant à creuser !!:P
Cela sera tout pour aujourd'hui, je continue sur le sujet plus tard...
Annexes
Kickstart serveur virtualisation
Ci-dessous le fichier kickstart généré par l'installation du rôle serveur
virtualisation.
#version=RHEL8
ignoredisk --only-use=sda
autopart --type=lvm
# Partition clearing information
clearpart --none --initlabel
# Use graphical install
graphical
# Use CDROM installation media
cdrom
# Keyboard layouts
keyboard --vckeymap=fr-oss --xlayouts='fr (oss)'
# System language
lang fr_FR.UTF-8
# Network information
network --bootproto=dhcp --device=enp2s0 --onboot=off --ipv6=auto --no-activate
network --hostname=localhost.localdomain
repo --name="AppStream" --baseurl=file:///run/install/repo/AppStream
# Root password
rootpw --iscrypted *
# Run the Setup Agent on first boot
firstboot --enable
# Do not configure the X Window System
skipx
# System services
services --enabled="chronyd"
# System timezone
timezone America/New_York --isUtc
%packages
@^virtualization-host-environment
@remote-system-management
kexec-tools
%end
%addon com_redhat_kdump --enable --reserve-mb='auto'
%end
%anaconda
pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty
pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok
pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty