Automatisation du déploiement de VM Virtualbox
Jusqu’ici j’ai montĂ© mes laboratoires en accès par pont dans Virtualbox mais je n’ai pas le mĂŞme adressage entre mon bureau et ma maison et c’Ă©tait casse bonbon : je devais ajouter une seconde interface rĂ©seau Ă mes VM d’une part mais aussi modifier mon hosts
selon mon lieu de travail.
Il y avait pas mal d’effet de bord comme le simple fait qu’Ă la maison, mes VM n’accĂ©daient pas Ă Internet tant que je ne modifiais pas leur passerelle par dĂ©faut ; nia nia nia modifier ma configuration rĂ©seau, nia nia nia Ă©teindre et rallumer les interfaces… une tannĂ©e !
Pour Ă©viter ça, on va crĂ©er un rĂ©seau NAT dans Virtualbox. En mode clic-o-drome, tu peux faire Fichier –> Outils –> Network Manager (ou Ctrl + H) et cliquer sur CrĂ©er (ou Ctrl + Shift + C) mais c’est plus amusant de se familiariser avec la ligne de commande pour bricoler.
# Création du réseau NAT
VBoxManage natnetwork add --netname ansible_lab --network "10.0.0.0/24" --enable --dhcp on
# Liste des réseaux NAT
VBoxManage natnetwork list
NAT Networks:
Name: ansible_lab
Enabled: Yes
Network: 10.0.0.0/24
Gateway: 10.0.0.1
DHCP Server: Yes
IPv6: No
IPv6 Prefix: fd17:625c:f037:2::/64
IPv6 Default: No
loopback mappings (ipv4)
127.0.0.1=2
1 network found
Les commandes sont accessibles Ă qui comprend 3 ou 4 mots d’anglais technique alors je te les dĂ©tailles pas plus que ça. Je suis sĂ»r que tu auras mĂŞme pas Ă faire un effort.
Un truc cool lorsqu’on liste les rĂ©seaux NAT, Virtualbox affiche les informations du rĂ©seau dont la passerelle. J’avais lu vite fait que la passerelle Ă©tait la première adresse du rĂ©seau mais j’avais pas pris le temps de chercher une source officielle. LĂ y a plus de doute, j’ai son IP !
Modèle de machine et déploiement automatique
Comme je te l’Ă©crivais au dĂ©but de mon prĂ©cĂ©dent laboratoire, je me suis montĂ© une Debian 12 de base que je clonais au besoin (de mĂ©moire, j’ai juste installĂ© ssh
et vim
dessus). C’Ă©tait pas spĂ©cialement long mais c’Ă©tait pas non plus hyper fun. Du coup j’ai ajoutĂ© ma clĂ© SSH et ai exportĂ© la machine en ova puis, j’ai Ă©cris un petit script pour gĂ©nĂ©rer mes VMs avec les objectifs suivant :
- CrĂ©er une nouvelle VM Ă l’aide d’un template avec la quantitĂ© de mĂ©moire et le nombre de vCPU dĂ©sirĂ©,
- L’affecter au groupe dĂ©signĂ©,
- Affecter à la carte réseau du système parent en cas de connexion par pont ou à un réseau NAT et créer une réservation DHCP,
- VĂ©rifier que si l’on souhaite crĂ©er une rĂ©servation DHCP, l’adresse demandĂ©e ne soit pas dĂ©jĂ attribuĂ©e et supprimer l’existante si nĂ©cessaire,
- DĂ©marrer la machine et,
- Mettre Ă jour mon fichier ~/.ssh/config selon que ce soit la machine qui me servira de rebond (via une redirection de port) ou celles vers lesquelles je rebondirai.
L’intĂ©rĂŞt du script est qu’en très peu de temps j’ai une VM prĂŞte sur laquelle je peux me connecter.
D’une manière gĂ©nĂ©rale, si on le chatouille trop le serveur DHCP de Virtualbox est fragile alors au pire prends la suite de cet article comme un projet artistique…
#!/bin/bash
programname=$0
function usage {
echo "Create a new virtual machine from an OVA template"
echo "Usage:"
echo "$programname --distro DISTRO \\"
echo " --vmname VMNAME \\"
echo " --group VMGROUP \\"
echo " --mem MEMORY \\"
echo " --vcpu CPU \\"
echo " --network (nat|bridged) \\"
echo " [--bridgeadapter BRIDGE_ADAPTER_NAME] \\"
echo " [--natname NAT_NETWORK_NAME] \\"
echo " [--ip IP_ADDRESS] \\"
echo " [--keep-existing-lease] \\"
echo " [--ssh-config] \\"
echo " [--ssh-ip 127.0.0.1] \\"
echo " [--ssh-port 22] \\"
echo " [--ssh-proxy PROXY_JUMP_HOST] \\"
echo " [--no-start]"
exit 1
}
# Default values
DISTRO=""
VMNAME=""
VMGROUP=""
MEM=1024
VCPU=2
NETWORK="nat"
NATNAME="natnetwork"
BRIDGEADAPTER=""
IP_ADDRESS=""
KEEP_EXISTING_LEASE=false
SSH_CONFIG=false
SSH_IP_ADDRESS=""
SSH_PORT="22"
PROXYJUMP_HOST=""
START_VM=true
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--distro)
DISTRO=$2
shift 2
;;
--vmname)
VMNAME=$2
shift 2
;;
--group)
VMGROUP=$2
shift 2
;;
--mem)
MEM=$2
shift 2
;;
--vcpu)
VCPU=$2
shift 2
;;
--network)
NETWORK=$2
shift 2
;;
--natname)
NATNAME=$2
shift 2
;;
--bridgeadapter)
BRIDGEADAPTER=$2
shift 2
;;
--ip)
IP_ADDRESS=$2
shift 2
;;
--keep-existing-lease)
KEEP_EXISTING_LEASE=true
shift
;;
--ssh-config)
SSH_CONFIG=true
shift
;;
--ssh-ip)
SSH_IP_ADDRESS=$2
shift 2
;;
--ssh-port)
SSH_PORT=$2
shift 2
;;
--ssh-proxy)
PROXYJUMP_HOST=$2
shift 2
;;
--no-start)
START_VM=false
shift
;;
*)
echo "Unknown option: $1"
usage
;;
esac
done
# Validate arguments
if [[ -z $DISTRO || -z $VMNAME || -z $VMGROUP ]]; then
echo "Missing required arguments!"
usage
fi
if [[ $NETWORK != "nat" && $NETWORK != "bridged" ]]; then
echo "Invalid network type! Choose 'nat' or 'bridged'."
usage
fi
TEMPLATE_PATH="$HOME/Documents/Tools/VMTemplates/${DISTRO}.ova"
if [[ ! -f $TEMPLATE_PATH ]]; then
echo "Template file $TEMPLATE_PATH does not exist."
exit 1
fi
echo "Checking if IP is already reserved"
EXISTING_LEASE=$(VBoxManage list dhcpservers | awk '
/Individual Config:.*MAC/ { mac = $NF }
/Fixed Address:/ { print mac, $3 }
' | grep $IP_ADDRESS)
if [[ ! -z $EXISTING_LEASE ]]; then
if $KEEP_EXISTING_LEASE; then
echo "Error : $(echo $EXISTING_LEASE | cut -d ' ' -f2 ) is already leased by $(echo $EXISTING_LEASE | cut -d ' ' -f1)"
exit 1
else
echo "Removing existing lease : $EXISTING_LEASE"
VBoxManage dhcpserver modify --network=$NATNAME --mac-address=$(echo $EXISTING_LEASE | cut -d ' ' -f1) --remove-config
fi
fi
echo "Importing VM from template..."
VBoxManage import "$TEMPLATE_PATH" --vsys 0 --vmname "$VMNAME" --memory "$MEM" --cpus "$VCPU"
echo "Setting VM group..."
VBoxManage modifyvm "$VMNAME" --groups "/$VMGROUP"
echo "Configuring network..."
if [[ $NETWORK == "nat" ]]; then
VBoxManage modifyvm "$VMNAME" --nic1 natnetwork --nat-network1 "$NATNAME"
if [[ ! -z $IP_ADDRESS ]]; then
# Obtain the MAC address of the VM
MAC=$(VBoxManage showvminfo "$VMNAME" --machinereadable | grep "macaddress1" | cut -d '=' -f2 | tr -d '"')
# Assign the specified IP to the VM in the NAT network
VBoxManage dhcpserver modify --network=$NATNAME --mac-address=$MAC --fixed-address=$IP_ADDRESS
VBoxManage dhcpserver restart --network=$NATNAME
echo "IP $IP_ADDRESS reserved for VM $VMNAME with MAC address $MAC in NAT network $NATNAME."
fi
else
if [[ -z $BRIDGEADAPTER ]]; then
BRIDGEADAPTER=$(VBoxManage list bridgedifs | grep '^Name:' | head -n 1 | awk '{ print $2 }')
fi
VBoxManage modifyvm "$VMNAME" --nic1 bridged --bridgeadapter1 "$BRIDGEADAPTER"
fi
if $START_VM; then
echo "Starting VM in headless mode..."
VBoxManage startvm "$VMNAME" --type headless
echo "VM $VMNAME started successfully."
else
echo "VM created but not started (opt-out selected)."
fi
if $SSH_CONFIG; then
HOST_TO_ADD="\nHost $VMNAME"
HOST_TO_ADD+=" $(echo $VMNAME | awk '{print tolower($0)}')"
if [[ -z $SSH_IP_ADDRESS ]]; then
SSH_IP=$IP_ADDRESS
fi
HOST_TO_ADD+="\n\tHostname $SSH_IP"
if [[ ! -z $SSH_PORT ]]; then
HOST_TO_ADD+="\n\tPort $SSH_PORT"
fi
if [[ ! -z $PROXYJUMP_HOST ]]; then
HOST_TO_ADD+="\n\tProxyJump $PROXYJUMP_HOST"
fi
printf "$HOST_TO_ADD" >> ~/.ssh/config
fi
echo "VM $VMNAME created successfully with the following settings:"
echo " - Distribution: $DISTRO"
echo " - Memory: $MEM MB"
echo " - vCPUs: $VCPU"
echo " - Group: $VMGROUP"
echo " - Network: $NETWORK"
if [[ $NETWORK == "nat" ]]; then
echo " NAT Network Name: $NATNAME"
[[ ! -z $IP_ADDRESS ]] && echo " Reserved IP Address: $IP_ADDRESS"
else
echo " Bridged Adapter: $BRIDGEADAPTER"
fi
Première machine et rebond vers le réseau NAT
J’ai testĂ© le script avec la crĂ©ation de la première machine de mon prochain laboratoire, LAB-BASTION-01
.
./newvm.sh --distro debian12 --vmname LAB-BASTION-01 --group LABANSIBLE --mem 512 --vcpu 1 --network nat --natname ansible_lab --ip 10.0.0.254 --ssh-config --ssh-ip 127.0.0.1 --ssh-port 5555
Importing VM from template...
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Interpreting /home/antoine/Documents/Tools/VMTemplates/debian12.ova...
OK.
Disks:
vmdisk1 21474836480 -1 http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized debian12-disk001.vmdk -1 -1
Virtual system 0:
0: Suggested OS type: "Debian_64"
(change with "--vsys 0 --ostype <type>"; use "list ostypes" to list all possible values)
1: VM name specified with --vmname: "LAB-BASTION-01"
2: Suggested VM group "/Templates"
(change with "--vsys 0 --group <group>")
3: Suggested VM settings file name "/home/antoine/VirtualBox VMs/Templates/TemplateDebian12 1/TemplateDebian12 1.vbox"
(change with "--vsys 0 --settingsfile <filename>")
4: Suggested VM base folder "/home/antoine/VirtualBox VMs"
(change with "--vsys 0 --basefolder <path>")
5: No. of CPUs specified with --cpus: 1
6: Guest memory specified with --memory: 512 MB
7: USB controller
(disable with "--vsys 0 --unit 7 --ignore")
8: Network adapter: orig Bridged, config 3, extra slot=0;type=Bridged
9: CD-ROM
(disable with "--vsys 0 --unit 9 --ignore")
10: IDE controller, type PIIX4
(disable with "--vsys 0 --unit 10 --ignore")
11: IDE controller, type PIIX4
(disable with "--vsys 0 --unit 11 --ignore")
12: SATA controller, type AHCI
(disable with "--vsys 0 --unit 12 --ignore")
13: Hard disk image: source image=debian12-disk001.vmdk, target path=debian12-disk001.vmdk, controller=12;port=0
(change target path with "--vsys 0 --unit 13 --disk path";
change controller with "--vsys 0 --unit 13 --controller <index>";
change controller port with "--vsys 0 --unit 13 --port <n>";
disable with "--vsys 0 --unit 13 --ignore")
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Successfully imported the appliance.
Setting VM group...
Configuring network...
IP 10.0.0.254 reserved for VM LAB-BASTION-01 with MAC address 080027D39E1D in NAT network ansible_lab.
Starting VM in headless mode...
Waiting for VM "LAB-BASTION-01" to power on...
VM "LAB-BASTION-01" has been successfully started.
VM LAB-BASTION-01 started successfully.
VM LAB-BASTION-01 created successfully with the following settings:
- Distribution: debian12
- Memory: 512 MB
- vCPUs: 1
- Group: LABANSIBLE
- Network: nat
NAT Network Name: ansible_lab
Reserved IP Address: 10.0.0.254
La machine crĂ©Ă©e, j’ai redirigĂ© le port 5555 de mon système hĂ´te vers le SSH de LAB-BASTION-01.
VBoxManage natnetwork modify --netname ansible_lab --port-forward-4 "ssh_to_vm:tcp:[]:5555:[10.0.0.254]:22"
Ça y est j’ai accès Ă mon bastion et par rebond aux autres machines du rĂ©seau après crĂ©ation de celles-ci.
Afin de nettoyer le serveur DHCP de Virtualbox et mon fichier de config SSH, on va créer un second script pour décommissionner proprement les VMs.
#!/bin/bash
VMNAME="$1"
# Check if vmname is provided
if [ -z "$VMNAME" ]; then
echo "Usage: $0 <VMNAME>"
exit 1
fi
# Get mac address and network infos
echo "Getting VM informations"
VM_INFO=$(VBoxManage showvminfo "$VMNAME" --machinereadable)
MAC=$(echo "$VM_INFO" | grep "macaddress1" | cut -d '=' -f2 | tr -d '"'i | sed 's/../&:/g; s/:$//' | tr 'A-F' 'a-f')
echo "Found MAC $MAC"
NETWORK_TYPE=$(echo "$VM_INFO" | grep nic1 | cut -d '=' -f2 | tr -d '"')
# If VM is in nat network
if [[ $NETWORK_TYPE == "natnetwork" ]]; then
NAT_NETWORK=$(echo "$VM_INFO" | grep "nat-network1" | cut -d '=' -f2 | tr -d '"')
echo "VM belongs to the NAT network $NAT_NETWORK"
DHCP_RESERVATION=$(VBoxManage list dhcpservers | grep $MAC | cut -d " " -f7)
#Â If there is a fixed address in the scope
if [[ ! -z $DHCP_RESERVATION ]]; then
echo "Removing DHCP fixed address for $MAC"
VBoxManage dhcpserver modify --network=$NAT_NETWORK --mac-address=$MAC --remove-config
fi
fi
# Poweroff and delete VM
echo "Shutting down the VM $VMNAME"
VBoxManage controlvm "$VMNAME" poweroff 2>/dev/null
echo "Deleting the VM $VMNAME"
VBoxManage unregistervm "$VMNAME" --delete
# Removing ssh config record
echo "Cleaning ssh config file"
cp ~/.ssh/config ~/.ssh/config.bak
awk -v host="$VMNAME" '
$1 == "Host" && $2 == host {skip=1; next}
skip && /^\t/ {next}
skip && !/^\t/ {skip=0}
{print}
' ~/.ssh/config.bak > ~/.ssh/config
Le script rmvm.sh conserve un léger défaut : il laisse des lignes vides. Au moins il ne défonce pas le reste de mon fichier de configuration donc je vais en rester là .
D’une manière plus gĂ©nĂ©rale, une fonction d’aide sur les paramètres serait la bienvenue si je partageais le script Ă plus grande Ă©chelle.
Bon on est d’accord que le DHCP c’est pas glop mais pour un premier jet ça fait le job. Je trouverai bien une techno plus tard pour rĂ©gler ce problème.
Toujours est-il, voici les scripts en action.
Fin de la bande