Voor wie is dit artikel bedoeld
Dit artikel kan interessant zijn voor systeembeheerders die de taak hebben om een service voor 'eenmalige' werkplekken te creƫren.
Proloog
De IT-afdeling van een jong, dynamisch groeiend bedrijf met een klein regionaal netwerk heeft de vraag gesteld om 'self-service stations' in te richten voor hun externe klanten. Deze stations zouden worden gebruikt voor registratie op externe portalen van het bedrijf, het uploaden van gegevens vanaf externe apparaten en het werken met overheidsportalen.
Een belangrijk aspect was het feit dat een groot deel van de software is 'afgestemd' op MS Windows (bijvoorbeeld 'Declaratie'), en hoewel er een beweging naar open formaten is, blijft MS Office de dominante standaard voor het uitwisselen van elektronische documenten. Daarom konden we niet ontsnappen aan MS Windows bij het oplossen van deze taak.
Het belangrijkste probleem was de mogelijkheid van accumulatie van verschillende gegevens van gebruikerssessies, die zouden kunnen leiden tot lekken naar derden. . Maar in tegenstelling tot quasi-overheidsinstanties (autonome staatsinstellingen) zullen niet-gouvernementele organisaties voor dergelijke tekortkomingen veel zwaarder worden bestraft. Het volgende kritieke probleem was de eis om te werken met externe opslagmedia, waarop onvermijdelijk veel kwaadaardige software zal zijn. De kans op het binnenbrengen van malware via internet werd als minder waarschijnlijk beschouwd, vanwege de beperkte internettoegang via een witte lijst van adressen. Medewerkers van andere afdelingen werden betrokken bij de uitwerking van de eisen en brachten hun eigen wensen en eisen in, de uiteindelijke eisen zagen er als volgt uit:
Eisen voor Informatiebeveiliging
- Na gebruik moeten alle gebruikersgegevens (inclusief tijdelijke bestanden en registersleutels) worden verwijderd.
- Alle processen die door de gebruiker zijn gestart, moeten na het einde van hun werk worden afgesloten.
- Toegang tot internet alleen via een witte lijst van adressen.
- Beperkingen op de mogelijkheid om externe code uit te voeren.
- In het geval van een inactiviteit van meer dan 5 minuten, moet de sessie automatisch worden beƫindigd en moet het station opruimen.
Eisen van de klant
- Aantal klantstations per filiaal ā niet meer dan 4.
- De minimale wachttijd voor de gereedheid van het systeem, van het moment van 'gaan zitten' tot het begin van het werken met de klantsoftware.
- De mogelijkheid om randapparatuur (scanners, USB-sticks) direct vanaf de locatie van de 'zelfbedieningsstation' aan te sluiten.
- Wensen van de klant
- Demonstratie van reclame-materiaal (afbeeldingen) tijdens de stilstand van het complex.
Creatieve worstelingen
Na genoeg gespeeld te hebben met Windows livecd's, kwamen we tot de unanieme conclusie dat de resulterende oplossing niet voldeed aan minstens 3 kritieke punten. Ze laden ofwel langzaam, of zijn niet echt live, of de aanpassing ging gepaard met enorme pijn. Misschien hebben we slecht gezocht, en kunt u een set hulpmiddelen aanbevelen; ik zou dankbaar zijn.
Daarna begonnen we naar VDI te kijken, maar voor deze taak zijn de meeste oplossingen ofwel te duur, of vereisen ze veel aandacht. We wilden een eenvoudig hulpmiddel, met een minimum aan magie, waarvan de meeste problemen opgelost konden worden met een simpele herstart of herstart van de service. Gelukkig hadden we serverhardware, low-end klasse in de vestigingen, van een buiten gebruik gesteld service, die we konden gebruiken als technologische basis.
Wat is er uiteindelijk gebeurd? Wat er uiteindelijk is gebeurd, kan ik je niet vertellen, omdat NDA, maar tijdens het zoeken hebben we een interessant schema ontwikkeld dat goed presteerde in laboratoriumtesten, hoewel het niet in productie is gegaan.
Een paar disclaimers: de auteur pretendeert niet dat de voorgestelde oplossing volledig alle gestelde taken oplost en dit vrijwillig en zingend doet. De auteur gaat akkoord met de bewering dat Sein Englishe sprache is zehr slecht. Aangezien de oplossing niet verder wordt ontwikkeld, hoeft men niet te rekenen op bugfixes of wijzigingen in functionaliteit; alles ligt in jouw handen. De auteur gaat ervan uit dat je enigszins bekend bent met KVM en de inleidende artikel over het Spice-protocol hebt gelezen, en dat je enige ervaring hebt met Centos of een andere GNU Linux-distributie.
In dit artikel wil ik de kern van de uiteindelijke oplossing bespreken, namelijk de interactie tussen de cliƫnt en de server en de essentie van de levenscyclusprocessen van virtuele machines binnen de beschreven oplossing. Als het artikel het publiek interesseert, zal ik de details van de implementatie van live afbeeldingen voor het creƫren van dunne cliƫnten op basis van Fedora beschrijven en vertellen over de details van het configureren van virtuele machines. KVM servers voor prestatieoptimalisatie en beveiliging.
Als je gekleurde papieren neemt,
verf, kwasten en lijm,
en nog een beetje handigheidā¦
kan je honderd roebel maken!
Schema en beschrijving van de testopstelling

Alle apparatuur bevindt zich binnen het netwerk van het filiaal, alleen de internetverbinding gaat naar buiten. De proxyserver was er historisch al, hij is niets bijzonders. Maar juist daar zal, naast andere dingen, de filtering van het verkeer van virtuele machines (in de tekst verder afgekort als VM) plaatsvinden. Er staat niets in de weg om deze service op de KVM-server te plaatsen, het enige waar we naar moeten kijken is hoe de belasting op het opslagsysteem verandert.
Client Station ā eigenlijk de 'zelfbedieningsstations', 'frontend' van onze service. Het zijn nettopcomputers van Lenovo IdeaCentre. Wat is goed aan dit apparaat? Vrijwel alles, vooral het grote aantal USB-poorten en de kaartlezer aan de voorkant. In ons schema is er een SD-kaart met ingeschakelde hardwarebescherming tegen schrijven in de kaartlezer gestoken, waarop een aangepaste live afbeelding van Fedora 28 is opgenomen. Uiteraard is de nettop aangesloten op een monitor, toetsenbord en muis.
Switch ā een volstrekt onopvallende hardware-switch van de tweede laag, staat in de serverruimte en knippert met lampjes. Hij is met geen enkel netwerk verbonden, behalve het netwerk van de 'zelfbedieningsstations'.
KVM_Server ā de kern van het schema, tijdens de test met de Core 2 Quad Q9650 met 8 GB RAM wist hij drie virtuele machines met Windows 10 soepel te draaien. De opslagsystemen ā Adaptec 3405 met 2 schijven in Raid 1 + SSD. In veldtests waren de Xeon 1220 en de serieuzere LSI 9260 + SSD gemakkelijk in staat om 5-6 VM's te draaien. De servers zouden van een afgeschreven service komen, de kapitaalsinvesteringen zouden niet hoog zijn. Op deze server(s) is het KVM virtualisatiesysteem opgezet met een pool van virtuele machines pool_Vm.
Vm ā de virtuele machine, de backend van onze service. Hier vindt het werk van de gebruiker plaats.
Enp5s0 ā netwerkinterface gericht op het netwerk van de 'zelfbedieningsstations', waarop dhcpd, ntpd, httpd en xinetd de 'signal' poort afluisteren.
Lo0 ā pseudo-interface voor de lus terug. Standaard.
Spice_console ā Een zeer interessante zaak, het is zo dat in tegenstelling tot de klassieke RDP, bij de implementatie van de KVM+Spice protocol bundel, een extra entiteit verschijnt ā de consolepoort van de virtuele machine. Door verbinding te maken met deze TCP-poort, krijgen we de console van de VM, zonder dat we verbinding hoeven te maken met de VM via zijn netwerkinterface. Alle interactie met de VM voor signaaloverdracht wordt door de server afgehandeld. De dichtstbijzijnde analogie qua functie is IPKVM. Dat wil zeggen, op deze poort wordt het beeld van de VM-monitor verzonden, evenals gegevens over de muisbeweging, en (het belangrijkste) de interactie via het Spice-protocol maakt naadloos doorsturen van USB-apparaten naar de virtuele machine mogelijk, alsof dit apparaat rechtstreeks op de VM is aangesloten. Bevestigd voor flash-drives, scanners, webcams.
Vnet0, virbr0 en virtuele netwerken van Vm vormen een netwerk van virtuele machines.
Hoe DIT werkt
Vanuit de Client Station
De clientstation boot vanaf een gemodificeerd live-image van Fedora 28 in grafische modus, ontvangt een ip-adres via dhcp uit het adresbereik van netwerk 169.254.24.0/24. Tijdens het opstarten worden firewallregels aangemaakt die verbindingen met de 'signal' en 'spice' poorten van de server mogelijk maken. Na de opstart wacht het station op de autorisatie van de gebruiker 'Client'. Na autorisatie van de gebruiker start de desktopmanager 'openbox' en wordt het opstartscript autostart uitgevoerd namens de ingelogde gebruiker. Onder andere start het opstartscript het script remote.sh.
$HOME/.config/openbox/scripts/remote.sh
#!/bin/sh
server_ip=$(/usr/bin/cat /etc/client.conf |/usr/bin/grep "server_ip"
|/usr/bin/cut -d "=" -f2)
vdi_signal_port=$(/usr/bin/cat /etc/client.conf |/usr/bin/grep "vdi_signal_port"
|/usr/bin/cut -d "=" -f2)
vdi_spice_port=$(/usr/bin/cat /etc/client.conf |/usr/bin/grep "vdi_spice_port"
|/usr/bin/cut -d "=" -f2)
animation_folder=$(/usr/bin/cat /etc/client.conf |/usr/bin/grep "animation_folder"
|/usr/bin/cut -d "=" -f2)
process=/usr/bin/remote-viewer
while true
do
if [ -z `/usr/bin/pidof feh` ]
then
/usr/bin/echo $animation_folder
/usr/bin/feh -N -x -D1 $animation_folder &
else
/usr/bin/echo
fi
/usr/bin/nc -i 1 $server_ip $vdi_signal_port |while read line
do
if /usr/bin/echo "$line" |/usr/bin/grep "RULE ADDED, CONNECT NOW!"
then
/usr/bin/killall feh
pid_process=$($process "spice://$server_ip:$vdi_spice_port"
"--spice-disable-audio" "--spice-disable-effects=animation"
"--spice-preferred-compression=auto-glz" "-k"
"--kiosk-quit=on-disconnect" | /bin/echo $!)
/usr/bin/wait $pid_process
/usr/bin/killall -u $USER
exit
else
/usr/bin/echo $line >> /var/log/remote.log
fi
done
done
/etc/client.conf
server_ip=169.254.24.1
vdi_signal_port=5905
vdi_spice_port=5906
animation_folder=/usr/share/backgrounds/animation
background_folder=/usr/share/backgrounds2/fedora-workstation
Beschrijving van de variabelen in het bestand client.conf
server_ip ā adres van KVM_Server
vdi_signal_port ā poort van KVM_Server waar xinetd 'zit'
vdi_spice_port ā netwerkpoort van KVM_Server vanwaar de verzoeken van de remote-viewer-client naar de spice-poort van de toegewezen VM worden doorgestuurd (details hieronder)
animation_folder ā map waar de afbeeldingen voor het tonen van bullshit animation vandaan komen
background_folder ā map waar de afbeeldingen voor de presentatie in de rustmodus vandaan komen. Meer over animatie in het volgende deel van het artikel.
Het script remote.sh haalt instellingen op uit het configuratiebestand /etc/client.conf en maakt via nc verbinding met de "vdi_signal_port" poort van de KVM-server, waarbij het datastroom van de server ontvangt, waaronder het wacht op de tekst "RULE ADDED, CONNECT NOW". Bij ontvangst van de gewenste tekst wordt het proces remote-viewer in kiosk-modus gestart met verbinding op de "vdi_spice_port" poort van de server. De uitvoering van het script wordt gepauzeerd tot het proces remote-viewer is voltooid.
Remote-viewer maakt verbinding met de "vdi_spice_port" poort en via redirect aan de serverzijde komt het op de "spice_console" poort van de interface lo0, dat wil zeggen op de console van de virtuele machine, en de gebruiker kan daar direct mee werken. Tijdens het wachten op verbinding wordt de gebruiker een bullshit-animatie getoond, in de vorm van een diavoorstelling van jpeg-bestanden, waarbij de pad naar de map met afbeeldingen wordt bepaald door de variabele animation_folder uit het configuratiebestand.
Bij verlies van de verbinding met de "spice_console" poort van de virtuele machine, wat signaleert dat de virtuele machine is uitgeschakeld/herstart (dat wil zeggen het feitelijke einde van de gebruikerssessie), worden alle processen die zijn gestart onder de naam van de geautoriseerde gebruiker afgesloten, wat leidt tot de herstart van lightdm en terugkeer naar het inlogscherm.
Vanuit de KVM Server
Op de "signal" poort van de netwerkaansluiting enp5s0 wacht xinetd op verbindingen. Na de verbinding met de "signal" poort start xinetd het script vm_manager.sh zonder enige invoerparameters door te geven en leidt het resultaat van de scriptuitvoering naar de nc-sessie van de Client Station.
/etc/xinetd.d/test-server
service vdi_signal
{
port = 5905
socket_type = stream
protocol = tcp
wait = no
user = root
server = /home/admin/scripts_vdi_new/vm_manager.sh
}
/home/admin/scripts_vdi_new/vm_manager.sh
#!/usr/bin/sh
#<SET LOCAL VARIABLES FOR SCRIPT>#
SRV_SCRIPTS_DIR=$(/usr/bin/cat /etc/vm_manager.conf
|/usr/bin/grep "srv_scripts_dir" |/usr/bin/cut -d "=" -f2)
/usr/bin/echo "SRV_SCRIPTS_DIR=$SRV_SCRIPTS_DIR"
export SRV_SCRIPTS_DIR=$SRV_SCRIPTS_DIR
SRV_POOL_SIZE=$(/usr/bin/cat /etc/vm_manager.conf
|/usr/bin/grep "srv_pool_size" |/usr/bin/cut -d "=" -f2)
/usr/bin/echo "SRV_POOL_SIZE=$SRV_POOL_SIZE"
export "SRV_POOL_SIZE=$SRV_POOL_SIZE"
SRV_START_PORT_POOL=$(/usr/bin/cat /etc/vm_manager.conf
|/usr/bin/grep "srv_start_port_pool" |/usr/bin/cut -d "=" -f2)
/usr/bin/echo SRV_START_PORT_POOL=$SRV_START_PORT_POOL
export SRV_START_PORT_POOL=$SRV_START_PORT_POOL
SRV_TMP_DIR=$(/usr/bin/cat /etc/vm_manager.conf
|/usr/bin/grep "srv_tmp_dir" |/usr/bin/cut -d "=" -f2)
/usr/bin/echo "SRV_TMP_DIR=$SRV_TMP_DIR"
export SRV_TMP_DIR=$SRV_TMP_DIR
date=$(/usr/bin/date)
#</SET LOCAL VARIABLES FOR SCRIPT>#
/usr/bin/echo "# $date START EXECUTE VM_MANAGER.SH #"
make_connect_to_vm() {
#<READING CLEAR.LIST AND CHECK PORT FOR NETWORK STATE>#
/usr/bin/echo "READING CLEAN.LIST AND CHECK PORT STATE"
#<CHECK FOR NO ONE PORT IN CLEAR.LIST>#
if [ -z `/usr/bin/cat $SRV_TMP_DIR/clear.list` ]
then
/usr/bin/echo "NO AVALIBLE PORTS IN CLEAN.LIST FOUND"
/usr/bin/echo "Will try to make housekeeper, and create new vm"
make_housekeeper
else
#<MINIMUN ONE PORT IN CLEAR.LIST FOUND>#
/usr/bin/cat $SRV_TMP_DIR/clear.list |while read line
do
clear_vm_port=$(($line))
/bin/echo "FOUND PORT $clear_vm_port IN CLEAN.LIST. TRY NETSTAT"
"CHECK FOR PORT=$clear_vm_port"
#<NETSTAT LISTEN CHECK FOR PORT FROM CLEAN.LIST>#
if /usr/bin/netstat -lnt |/usr/bin/grep ":$clear_vm_port" > /dev/null
then
/bin/echo "$clear_vm_port IS LISTEN"
#<PORT IS LISTEN. CHECK FOR IS CONNECTED NOW>#
if /usr/bin/netstat -nt |/usr/bin/grep ":$clear_vm_port"
|/usr/bin/grep "ESTABLISHED" > /dev/null
then
#<PORT LISTEN AND ALREADY CONNECTED! MOVE PORT FROM CLEAR.LIST
# TO WASTE.LIST>#
/bin/echo "$clear_vm_port IS ALREADY CONNECTED, MOVE PORT TO WASTE.LIST"
/usr/bin/sed -i "/$clear_vm_port/d" $SRV_TMP_DIR/clear.list
/usr/bin/echo $clear_vm_port >> $SRV_TMP_DIR/waste.list
else
#<PORT LISTEN AND NO ONE CONNECT NOW. MOVE PORT FROM CLEAR.LIST TO
# CONN_WAIT.LIST AND CREATE IPTABLES RULES>##
/usr/bin/echo "OK, $clear_vm_port IS NOT ALREADY CONNECTED"
/usr/bin/sed -i "/$clear_vm_port/d" $SRV_TMP_DIR/clear.list
/usr/bin/echo $clear_vm_port >> $SRV_TMP_DIR/conn_wait.list
$SRV_SCRIPTS_DIR/vm_connect.sh $clear_vm_port
#<TRY TO CLEAN VM IN WASTE.LIST AND CREATE NEW WM>#
/bin/echo "TRY TO CLEAN VM IN WASTE.LIST AND CREATE NEW VM"
make_housekeeper
/usr/bin/echo "# $date STOP EXECUTE VM_MANAGER.SH#"
exit
fi
else
#<PORT IS NOT A LISTEN. MOVE PORT FROM CLEAR.LIST TO WASTE.LIST>#
/bin/echo " "$clear_vm_port" is NOT LISTEN. REMOVE PORT FROM CLEAR.LIST"
/usr/bin/sed -i "/$clear_vm_port/d" $SRV_TMP_DIR/clear.list
/usr/bin/echo $clear_vm_port >> $SRV_TMP_DIR/waste.list
make_housekeeper
fi
done
fi
}
make_housekeeper() {
/usr/bin/echo "=Execute housekeeper="
/usr/bin/cat $SRV_TMP_DIR/waste.list |while read line
do
/usr/bin/echo "$line"
if /usr/bin/netstat -lnt |/usr/bin/grep ":$line" > /dev/null
then
/bin/echo "port_alive, vm is running"
if /usr/bin/netstat -nt |/usr/bin/grep ":$line"
|/usr/bin/grep "ESTABLISHED" > /dev/null
then
/bin/echo "port_in_use can't delete vm!!!"
else
/bin/echo "port_not in use. Deleting vm"
/usr/bin/sed -i "/$line/d" $SRV_TMP_DIR/waste.list
/usr/bin/echo $line >> $SRV_TMP_DIR/recycle.list
$SRV_SCRIPTS_DIR/vm_delete.sh $line
fi
else
/usr/bin/echo "posible vm is already off. Deleting vm"
/usr/bin/echo "MOVE VM IN OFF STATE $line FROM WASTE.LIST TO"
"RECYCLE.LIST AND DELETE VM"
/usr/bin/sed -i "/$line/d" $SRV_TMP_DIR/waste.list
/usr/bin/echo $line >> $SRV_TMP_DIR/recycle.list
$SRV_SCRIPTS_DIR/vm_delete.sh "$line"
fi
done
create_clear_vm
}
create_clear_vm() {
/usr/bin/echo "=Create new VM="
while [ $SRV_POOL_SIZE -gt 0 ]
do
new_vm_port=$(($SRV_START_PORT_POOL+$SRV_POOL_SIZE))
/usr/bin/echo "new_vm_port=$new_vm_port"
if /usr/bin/grep "$new_vm_port" $SRV_TMP_DIR/clear.list > /dev/null
then
/usr/bin/echo "$new_vm_port port is already defined in clear.list"
else
if /usr/bin/grep "$new_vm_port" $SRV_TMP_DIR/waste.list > /dev/null
then
/usr/bin/echo "$new_vm_port port is already defined in waste.list"
else
if /usr/bin/grep "$new_vm_port" $SRV_TMP_DIR/recycle.list > /dev/null
then
/usr/bin/echo "$new_vm_port PORT IS ALREADY DEFINED IN RECYCLE LIST"
else
if /usr/bin/grep "$new_vm_port" $SRV_TMP_DIR/conn_wait.list > /dev/null
then
/usr/bin/echo "$new_vm_port PORT IS ALREADY DEFINED IN CONN_WAIT LIST"
else
/usr/bin/echo "PORT IN NOT DEFINED IN NO ONE LIST WILL CREATE"
"VM ON PORT $new_vm_port"
/usr/bin/echo $new_vm_port >> $SRV_TMP_DIR/recycle.list
$SRV_SCRIPTS_DIR/vm_create.sh $new_vm_port
fi
fi
fi
fi
SRV_POOL_SIZE=$(($SRV_POOL_SIZE-1))
done
/usr/bin/echo "# $date STOP EXECUTE VM_MANAGER.SH #"
}
make_connect_to_vm |/usr/bin/tee -a /var/log/vm_manager.log
/etc/vm_manager.confsrv_scripts_dir=/home/admin/scripts_vdi_new
srv_pool_size=4
srv_start_port_pool=5920
srv_tmp_dir=/tmp/vm_state
base_host=win10_2
input_iface=enp5s0
vdi_spice_port=5906
count_conn_tryes=10
Beschrijving van de configuratiebestandsvariabelen vm_manager.conf
srv_scripts_dir ā map met scripts vm_manager.sh, vm_connect.sh, vm_delete.sh, vm_create.sh, vm_clear.sh
srv_pool_size ā grootte van de VM-pool
srv_start_port_pool ā beginpoort, waar de plaatsing van de spice-consolepoorten van virtuele machines begint
srv_tmp_dir ā map voor tijdelijke bestanden
base_host ā basis VM (golden image) waarvan klonen van VM's in de pool zullen worden gemaakt
input_iface ā netwerkinterface van de server, gericht op Client Stations
vdi_spice_port ā netwerpoort van de server, van waaruit het verzoek voor verbinding van de remote-viewer-client naar de spice-poort van de toegewezen VM wordt geleid
count_conn_tryes ā de wachttimer, waarna wordt aangenomen dat de verbinding met de VM niet is gelukt (zie vm_connect.sh voor details).
Het script vm_manager.sh leest de configuratie uit het bestand vm_manager.conf, beoordeelt de status van de virtuele machines in de pool op verschillende parameters, namelijk: hoeveel VM's zijn uitgerold, zijn er beschikbare schone VM's. Daartoe leest het script het bestand clear.list waarin de nummers van de 'spice_console' poorten van de 'vers aangemaakte' virtuele machines zijn opgeslagen (zie hieronder de cyclus voor het creƫren van V.M.) en controleert het op een actieve verbinding met deze poorten. Bij ontdekking van een poort met een actieve netwerkverbinding (wat categorisch niet zou moeten gebeuren) wordt er een waarschuwing weergegeven en wordt de poort verplaatst naar waste.list. Zodra de eerste poort uit het bestand clear.list wordt aangetroffen zonder actieve verbinding, roept vm_manager.sh het script vm_connect.sh aan en geeft het nummer van deze poort als parameter door.
/home/admin/scripts_vdi_new/vm_connect.sh
#!/bin/sh
date=$(/usr/bin/date)
/usr/bin/echo "#" "$date" "START EXECUTE VM_CONNECT.SH#"
#<SET LOCAL VARIABLES FOR SCRIPT>#
free_port="$1"
input_iface=$(/usr/bin/cat /etc/vm_manager.conf |/usr/bin/grep "input_iface"
|/usr/bin/cut -d "=" -f2)
/usr/bin/echo "input_iface=$input_iface"
vdi_spice_port=$(/usr/bin/cat /etc/vm_manager.conf
|/usr/bin/grep "vdi_spice_port" |/usr/bin/cut -d "=" -f2)
/usr/bin/echo "vdi_spice_port=$vdi_spice_port"
count_conn_tryes=$(/usr/bin/cat /etc/vm_manager.conf
|/usr/bin/grep "count_conn_tryes" |/usr/bin/cut -d "=" -f2)
/usr/bin/echo "count_conn_tryes=$count_conn_tryes"
#</SET LOCAL VARIABLES FOR SCRIPT>#
#<CREATE IPTABLES RULES AND SEND SIGNAL TO CONNECT>#
/usr/bin/echo "create rule for port" $free_port
/usr/sbin/iptables -I INPUT -i $input_iface -p tcp -m tcp --dport
$free_port -j ACCEPT
/usr/sbin/iptables -I OUTPUT -o $input_iface -p tcp -m tcp --sport
$free_port -j ACCEPT
/usr/sbin/iptables -t nat -I PREROUTING -p tcp -i $input_iface --dport
$vdi_spice_port -j DNAT --to-destination 127.0.0.1:$free_port
/usr/bin/echo "RULE ADDED, CONNECT NOW!"
#</CREATE IPTABLES RULES AND SEND SIGNAL TO CONNECT>#
#<WAIT CONNECT ESTABLISHED AND ACTIVATE CONNECT TIMER>#
while [ $count_conn_tryes -gt 0 ]
do
if /usr/bin/netstat -nt |/usr/bin/grep ":$free_port"
|/usr/bin/grep "ESTABLISHED" > /dev/null
then
/bin/echo "$free_port NOW in use!!!"
/usr/bin/sleep 1s
/usr/sbin/iptables -t nat -D PREROUTING -p tcp -i $input_iface --dport
$vdi_spice_port -j DNAT --to-destination 127.0.0.1:$free_port
/usr/sbin/iptables -D INPUT -i $input_iface -p tcp -m tcp --dport
$free_port -j ACCEPT
/usr/sbin/iptables -D OUTPUT -o $input_iface -p tcp -m tcp --sport
$free_port -j ACCEPT
/usr/bin/sed -i "/$free_port/d" $SRV_TMP_DIR/conn_wait.list
/usr/bin/echo $free_port >> $SRV_TMP_DIR/waste.list
return
else
/usr/bin/echo "$free_port NOT IN USE"
/usr/bin/echo "RULE ADDED, CONNECT NOW!"
/usr/bin/sleep 1s
fi
count_conn_tryes=$((count_conn_tryes-1))
done
#</WAIT CONNECT ESTABLISED AND ACTIVATE CONNECT TIMER>#
#<IF COUNT HAS EXPIRED. REMOVE IPTABLES RULE AND REVERT
# VM TO CLEAR.LIST>#
/usr/bin/echo "REVERT IPTABLES RULE AND REVERT VM TO CLEAN
LIST $free_port"
/usr/sbin/iptables -t nat -D PREROUTING -p tcp -i $input_iface --dport
$vdi_spice_port -j DNAT --to-destination 127.0.0.1:$free_port
/usr/sbin/iptables -D INPUT -i $input_iface -p tcp -m tcp --dport $free_port
-j ACCEPT
/usr/sbin/iptables -D OUTPUT -o $input_iface -p tcp -m tcp --sport
$free_port -j ACCEPT
/usr/bin/sed -i "/$free_port/d" $SRV_TMP_DIR/conn_wait.list
/usr/bin/echo $free_port >> $SRV_TMP_DIR/clear.list
#</COUNT HAS EXPIRED. REMOVE IPTABLES RULE AND REVERT VM
#TO CLEAR.LIST>#
/usr/bin/echo "#" "$date" "END EXECUTE VM_CONNECT.SH#"
# Attention! Must Be! sysctl net.ipv4.conf.all.route_localnet=1
Het script vm_connect.sh voegt regels toe aan de firewall die de redirect van de 'vdi_spice_port' van de serverinterface enp5s0 naar de 'spice console port' van de VM op de lo0 interface van de server tot stand brengt, dit wordt als opstartparameter doorgegeven. De poort wordt verplaatst naar conn_wait.list, de VM wordt als wachtend op verbinding beschouwd. In de Client Station sessie wordt op de 'signal' poort van de server de regel 'RULE ADDED, CONNECT NOW' verzonden, die door het actieve script remote.sh wordt verwacht. Er begint een cyclus van verbinding wachten met een aantal pogingen, bepaald door de waarde van de variabele 'count_conn_tryes' uit het configuratiebestand. Elke seconde wordt de regel 'RULE ADDED, CONNECT NOW' in de nc-sessie verzonden en wordt gecontroleerd op een actieve verbinding met de 'spice_console' poort.
Als er binnen het vastgestelde aantal pogingen geen verbinding is tot stand gekomen, wordt de 'spice_console' poort teruggeplaatst naar clear.list. De uitvoering van vm_connect.sh eindigt, en de uitvoering van vm_manager.sh wordt hervat, die de cyclus van het opruimen start.
Als de Client Station verbinding maakt met de "spice_console" poort op de lo0 interface, worden de firewallregels die een omleiding tussen de "spice" poort van de server en de "spice_console" poort creƫren verwijderd, en het verdere onderhoud van de verbinding gebeurt via het mechanisme voor statusdetectie van de firewall. In geval van een verbroken verbinding kan de verbinding met de "spice_console" poort niet opnieuw worden vastgesteld. De "spice_console" poort wordt overgebracht naar waste.list, de VM wordt als "vies" beschouwd en kan niet terugkeren naar de pool van "schone" virtuele machines zonder een schoonmaakprocedure te doorlopen. De uitvoering van vm_connect.sh eindigt, de uitvoering van vm_manager.sh wordt hervat, die de schoonmaakcyclus start.
De schoonmaakcyclus begint met het bekijken van het waste.list bestand, waarin de nummers van de "spice_console" poorten van virtuele machines die verbinding hebben gemaakt, worden overgebracht. Er wordt gecontroleerd of er een actieve verbinding is op elke "spice_console" poort uit de lijst. Als de verbinding ontbreekt, wordt de virtuele machine als niet meer gebruikt beschouwd en wordt de poort overgebracht naar recycle.list en start het proces voor het verwijderen van de virtuele machine (zie hieronder) waartoe deze poort behoorde. Bij het aantreffen van een actieve netwerkverbinding op de poort, wordt de virtuele machine als in gebruik beschouwd, er worden geen acties ondernomen. Als de poort niet wordt beluisterd, wordt de VM als uitgeschakeld en niet meer nodig beschouwd. De poort wordt overgebracht naar recycle.list en het proces voor het verwijderen van de virtuele machine wordt gestart. Hiervoor wordt het script vm_delete.sh aangeroepen, waaraan het nummer van de "spice_console" poort van de VM die moet worden verwijderd, als parameter wordt doorgegeven.
/home/admin/scripts_vdi_new/vm_delete.sh
#!/bin/sh
#<Set local VARIABLES>#
port_to_delete="$1"
date=$(/usr/bin/date)
#</Set local VARIABLES>#
/usr/bin/echo "# $date START EXECUTE VM_DELETE.SH#"
/usr/bin/echo "TRY DELETE VM ON PORT: $vm_port"
#<VM NAME SETUP>#
vm_name_part1=$(/usr/bin/cat /etc/vm_manager.conf |/usr/bin/grep 'base_host'
|/usr/bin/cut -d'=' -f2)
vm_name=$(/usr/bin/echo "$vm_name_part1""-""$port_to_delete")
#</VM NAME SETUP>#
#<SHUTDOWN AND DELETE VM>#
/usr/bin/virsh destroy $vm_name
/usr/bin/virsh undefine $vm_name
/usr/bin/rm -f /var/lib/libvirt/images_write/$vm_name.qcow2
/usr/bin/sed -i "/$port_to_delete/d" $SRV_TMP_DIR/recycle.list
#</SHUTDOWN AND DELETE VM>#
/usr/bin/echo "VM ON PORT $vm_port HAS BEEN DELETE AND REMOVE"
"FROM RECYCLE.LIST. EXIT FROM VM_DELETE.SH"
/usr/bin/echo "# $date STOP EXECUTE VM_DELETE.SH#"
exit
Het verwijderen van een virtuele machine is een vrij triviale operatie; het script vm_delete.sh bepaalt de naam van de virtuele machine waartoe de poort behoort die als opstartparameter is doorgegeven. De VM wordt geforceerd gestopt, de VM wordt uit de hypervisor verwijderd en de virtuele harde schijf van deze VM wordt verwijderd. De "spice_console" poort wordt uit recycle.list verwijderd. De uitvoering van vm_delete.sh eindigt, de uitvoering van vm_manager.sh wordt hervat.
Het script vm_manager.sh start, na het uitvoeren van de schoonmaakoperaties voor overtollige virtuele machines uit de waste.list, de cyclus voor het creƫren van virtuele machines in de pool.
Het proces begint met het bepalen van de beschikbare poorten voor de "spice_console". Hiervoor wordt, op basis van de parameter in het configuratiebestand "srv_start_port_pool" die de startpoort voor de pool van "spice_console" virtuele machines specificeert, en de parameter "srv_pool_size" die het maximale aantal virtuele machines aangeeft, een opeenvolgende doorloop van alle mogelijke poorten uitgevoerd. Voor elke gedefinieerde poort wordt gezocht in clear.list, waste.list, conn_wait.list, recycle.list. Als de poort in een van deze bestanden wordt aangetroffen, wordt deze beschouwd als bezet en overgeslagen. Als de poort in de genoemde bestanden niet wordt gevonden, wordt deze aan het bestand recycle.list toegevoegd en begint het proces van het creƫren van een nieuwe virtuele machine. Hiervoor wordt het script vm_create.sh aangeroepen, waarbij het nummer van de "spice_console" poort die voor de VM moet worden aangemaakt als parameter wordt doorgegeven.
/home/admin/scripts_vdi_new/vm_create.sh
#!/bin/sh
/usr/bin/echo "#" "$date" "START RUNNING VM_CREATE.SH#"
new_vm_port=$1
date=$(/usr/bin/date)
a=0
/usr/bin/echo SRV_TMP_DIR=$SRV_TMP_DIR
#<SET LOCAL VARIABLES FOR SCRIPT>#
base_host=$(/usr/bin/cat /etc/vm_manager.conf |/usr/bin/grep "base_host"
|/usr/bin/cut -d "=" -f2)
/usr/bin/echo "base_host=$base_host"
#</SET LOCAL VARIABLES FOR SCRIPT>#
hdd_image_locate() {
/bin/echo "Run STEP 1 - hdd_image_locate"
hdd_base_image=$(/usr/bin/virsh dumpxml $base_host
|/usr/bin/grep "source file" |/usr/bin/grep "qcow2" |/usr/bin/head -n 1
|/usr/bin/cut -d "'" -f2)
if [ -z "$hdd_base_image" ]
then
/bin/echo "base hdd image not found!"
else
/usr/bin/echo "hdd_base_image found is a $hdd_base_image. Run next step 2"
#< CHECK FOR SNAPSHOT ON BASE HDD >#
if [ 0 -eq `/usr/bin/qemu-img info "$hdd_base_image" | /usr/bin/grep -c "Snapshot"` ]
then
/usr/bin/echo "base image haven't snapshot, run NEXT STEP 3"
else
/usr/bin/echo "base hdd image have a snapshot, can't use this image"
exit
fi
#</ CHECK FOR SNAPSHOT ON BASE HDD >#
#< CHECK FOR HDD IMAGE IS LINK CLONE >#
if [ 0 -eq `/usr/bin/qemu-img info "$hdd_base_image" |/usr/bin/grep -c "backing file"
then
/usr/bin/echo "base image is not a linked clone, NEXT STEP 4"
/usr/bin/echo "Base image check complete!"
else
/usr/bin/echo "base hdd image is a linked clone, can't use this image"
exit
fi
fi
#</ CHECK FOR HDD IMAGE IS LINK CLONE >#
cloning
}
cloning() {
# <Step_1 turn the base VM off >#
/usr/bin/virsh shutdown $base_host > /dev/null 2>&1
# </Step_1 turn the base VM off >#
#<Create_vm_config>#
/usr/bin/echo "Free port for Spice VM is $new_vm_port"
#<Setup_name_for_new_VM>#
new_vm_name=$(/bin/echo $base_host"-"$new_vm_port)
#</Setup_name_for_new_VM>#
#<Make_base_config_as_clone_base_VM>#
/usr/bin/virsh dumpxml $base_host > $SRV_TMP_DIR/$new_vm_name.xml
#<Make_base_config_as_clone_base_VM>#
##<Setup_New_VM_Name_in_config>##
/usr/bin/sed -i "s%<name>$base_host</name>%<name>$new_vm_name</name>%g" $SRV_TMP_DIR/$new_vm_name.xml
#</Setup_New_VM_Name_in_config>#
#<UUID Changing>#
old_uuid=$(/usr/bin/cat $SRV_TMP_DIR/$new_vm_name.xml |/usr/bin/grep "<uuid>")
/usr/bin/echo old UUID $old_uuid
new_uuid_part1=$(/usr/bin/echo "$old_uuid" |/usr/bin/cut -d "-" -f 1,2)
new_uuid_part2=$(/usr/bin/echo "$old_uuid" |/usr/bin/cut -d "-" -f 4,5)
new_uuid=$(/bin/echo $new_uuid_part1"-"$new_vm_port"-"$new_uuid_part2)
/usr/bin/echo $new_uuid
/usr/bin/sed -i "s%$old_uuid%$new_uuid%g" $SRV_TMP_DIR/$new_vm_name.xml
#</UUID Changing>#
#<Spice port replace>#
old_spice_port=$(/usr/bin/cat $SRV_TMP_DIR/$new_vm_name.xml
|/usr/bin/grep "graphics type='spice' port=")
/bin/echo old spice port $old_spice_port
new_spice_port=$(/usr/bin/echo "<graphics type='spice' port='$new_vm_port' autoport='no' listen='127.0.0.1'>")
/bin/echo $new_spice_port
/usr/bin/sed -i "s%$old_spice_port%$new_spice_port%g" $SRV_TMP_DIR/$new_vm_name.xml
#</Spice port replace>#
#<MAC_ADDR_GENERATE>#
mac_new=$(/usr/bin/hexdump -n6 -e '/1 ":%02X"' /dev/random|/usr/bin/sed s/^://g)
/usr/bin/echo New Mac is $mac_new
#</MAC_ADDR_GENERATE>#
#<GET OLD MAC AND REPLACE>#
mac_old=$(/usr/bin/cat $SRV_TMP_DIR/$new_vm_name.xml |/usr/bin/grep "mac address=")
/usr/bin/echo old mac is $mac_old
/usr/bin/sed -i "s%$mac_old%$mac_new%g" $SRV_TMP_DIR/$new_vm_name.xml
#<GET OLD MAC AND REPLACE>#
#<new_disk_create>#
/usr/bin/qemu-img create -f qcow2 -b $hdd_base_image /var/lib/libvirt/images_write/$new_vm_name.qcow2
#</new_disk_create>#
#<attach_new_disk_in_confiig>#
/usr/bin/echo hdd base image is $hdd_base_image
/usr/bin/sed -i "s%<source file='$hdd_base_image'/>%<source file='/var/lib/libvirt/images_write/$new_vm_name.qcow2'/>%g" $SRV_TMP_DIR/$new_vm_name.xml
#</attach_new_disk_in_confiig>#
starting_vm
#</Create_vm config>#
}
starting_vm() {
/usr/bin/virsh define $SRV_TMP_DIR/$new_vm_name.xml
/usr/bin/virsh start $new_vm_name
while [ $a -ne 1 ]
do
if /usr/bin/virsh list --all |/usr/bin/grep "$new_vm_name" |/usr/bin/grep "running" > /dev/null 2>&1
then
a=1
/usr/bin/sed -i "/$new_vm_port/d" $SRV_TMP_DIR/recycle.list
/usr/bin/echo $new_vm_port >> $SRV_TMP_DIR/clear.list
/usr/bin/echo "#" "$date" "VM $new_vm_name IS STARTED #"
else
/usr/bin/echo "#VM $new_vm_name is not ready#"
a=0
/usr/bin/sleep 2s
fi
done
/usr/bin/echo "#$date EXIT FROM VM_CREATE.SH#"
exit
}
hdd_image_locate
Het proces van het creƫren van een nieuwe virtuele machine
Het script vm_create.sh leest de waarde van de variabele "base_host" uit het configuratiebestand, wat het voorbeeld van de virtuele machine bepaalt waarvan een kloon zal worden gemaakt. Het haalt de XML-configuratie van de VM uit de hypervisor-database, voert verschillende controles uit op de qcow-schijf afbeelding van de VM en, als deze succesvol zijn, maakt het een XML-configuratiebestand voor de nieuwe VM en een "linked clone" afbeelding van de disk van de nieuwe VM. Vervolgens wordt de XML-configuratie van de nieuwe VM in de hypervisor-database geladen en wordt de VM gestart. De poort "spice_console" wordt overgebracht van recycle.list naar clear.list. Het uitvoeren van vm_create.sh eindigt en het uitvoeren van vm_manager.sh wordt afgesloten.
Bij de volgende verbinding begint alles weer opnieuw.
Voor noodgevallen is er een script vm_clear.sh dat geforceerd door alle VM's in de pool loopt en deze verwijdert, waarbij de waarden van de lijsten worden gereset. Het aanroepen ervan tijdens de opstartfase maakt het mogelijk om (onder)VDI vanaf een schone lei te starten.
/home/admin/scripts_vdi_new/vm_clear.sh
#!/usr/bin/sh
#set VARIABLES#
SRV_SCRIPTS_DIR=$(/usr/bin/cat /etc/vm_manager.conf
|/usr/bin/grep "srv_scripts_dir" |/usr/bin/cut -d "=" -f2)
/usr/bin/echo "SRV_SCRIPTS_DIR=$SRV_SCRIPTS_DIR"
export SRV_SCRIPTS_DIR=$SRV_SCRIPTS_DIR
SRV_TMP_DIR=$(/usr/bin/cat /etc/vm_manager.conf
|/usr/bin/grep "srv_tmp_dir" |/usr/bin/cut -d "=" -f2)
/usr/bin/echo "SRV_TMP_DIR=$SRV_TMP_DIR"
export SRV_TMP_DIR=$SRV_TMP_DIR
SRV_POOL_SIZE=$(/usr/bin/cat /etc/vm_manager.conf
|/usr/bin/grep "srv_pool_size" |/usr/bin/cut -d "=" -f2)
/usr/bin/echo "SRV_POOL_SIZE=$SRV_POOL_SIZE"
SRV_START_PORT_POOL=$(/usr/bin/cat /etc/vm_manager.conf
|/usr/bin/grep "srv_start_port_pool" |/usr/bin/cut -d "=" -f2)
/usr/bin/echo SRV_START_PORT_POOL=$SRV_START_PORT_POOL
#Set VARIABLES#
/usr/bin/echo "= Cleanup ALL VM="
/usr/bin/mkdir $SRV_TMP_DIR
/usr/sbin/service iptables restart
/usr/bin/cat /dev/null > $SRV_TMP_DIR/clear.list
/usr/bin/cat /dev/null > $SRV_TMP_DIR/waste.list
/usr/bin/cat /dev/null > $SRV_TMP_DIR/recycle.list
/usr/bin/cat /dev/null > $SRV_TMP_DIR/conn_wait.list
port_to_delete=$(($SRV_START_PORT_POOL+$SRV_POOL_SIZE))
while [ "$port_to_delete" -gt "$SRV_START_PORT_POOL" ]
do
$SRV_SCRIPTS_DIR/vm_delete.sh $port_to_delete
port_to_delete=$(($port_to_delete-1))
done
/usr/bin/echo "= EXIT FROM VM_CLEAR.SH="
Hiermee zou ik het eerste deel van mijn verhaal willen afsluiten. Wat ik heb uiteengezet, zou voldoende moeten zijn voor systeembeheerders om (onder)VDI in de praktijk uit te proberen. Als de gemeenschap deze kwestie interessant vindt, zal ik in het tweede deel vertellen over de aanpassing van livecd Fedora en hoe deze in een kiosk kan worden omgevormd.
Bron: habr.com
