Hvem er denne artikel beregnet til
Denne artikel kan være interessant for systemadministratorer, der står over for opgaven at oprette 'engangs' arbejdspladser.
Prolog
IT-supportafdelingen i en ung, dynamisk vækstvirksomhed med et lille regionalt netværk henvendte sig med en anmodning om at organisere 'selvbetjeningsstationer' til brug for deres eksterne kunder. Disse stationer var tænkt til registrering på virksomhedens eksterne portaler, overførsel af data fra eksterne enheder og arbejde med statslige portaler.
En vigtig faktor var, at størstedelen af softwareløsningerne var 'tilpasset' MS Windows (for eksempel 'Deklaration'), og på trods af en bevægelse mod åbne formater forbliver MS Office den dominerende standard ved udveksling af elektroniske dokumenter. Derfor kunne vi ikke undgå at bruge MS Windows til denne opgave.
Hovedproblemet blev betragtet som muligheden for at akkumulere forskellige data fra brugerens sessioner, som kunne føre til lækage til tredjepart. . Men i modsætning til det quasiforvaltningsmæssige (statsligt autonomt institut) MFC, vil private organisationer blive straffet meget hårdere for sådanne fejl. Næste kritiske problem var kravet om at arbejde med eksterne datamedier, som utvivlsomt vil indeholde mange skadelige programmer. Sandsynligheden for indsmitte skadelig software fra internettet blev vurderet som mindre sandsynlig på grund af begrænsningen af adgang til internettet via en hvidliste af adresser. Andre afdelinger deltog i arbejdet med kravene, hvilket medførte deres ønsker og krav, og de endelige krav så således ud:
Sikkerhedskrav
- Efter brug skal alle brugerdata (inklusive midlertidige filer og registreringsnøgler) blive slettet.
- Alle processer, der er startet af brugeren, skal afsluttes efter afslutningen af arbejdet.
- Adgang til internettet via en hvidliste af adresser.
- Begrænsninger for muligheden for at køre tredjeparts kode.
- Hvis sessionen er inaktiv i mere end 5 minutter, skal sessionen automatisk afsluttes, og stationen skal rydde op.
Kundens krav
- Antallet af klientstationer pr. filial - højst 4.
- Den minimale ventetid for systemets klarhed, fra det øjeblik du sætter dig ned, til du begynder at arbejde med klientsoftwaren.
- Muligheden for at tilslutte perifere enheder (scanner, USB-nøgler) direkte fra installationsstedet for "selvbetjeningsstationen".
- Kundeønsker
- Visning af reklame-materialer (billeder) i perioder med nedetid for systemet.
Kreativitetens kvaler
Efter at have leget en del med Windows livecd'er kom vi til den enstemmige konklusion, at den løsning, vi kom frem til, ikke levede op til minimum tre kritiske punkter. De enten tager lang tid at starte, er ikke helt live, eller tilpasning af dem var forbundet med betydelige problemer. Måske søgte vi dårligt, og du kan rådgive os om et sæt værktøjer, hvilket jeg ville sætte pris på.
Derefter begyndte vi at kigge i retning af VDI, men de fleste løsninger til denne opgave er enten for dyre eller kræver stor opmærksomhed. Vi ønskede et simpelt værktøj med minimal magi, hvor de fleste problemer kunne løses med en simpel genstart af systemet/service. Heldigvis havde vi serverhardware af lavere klasse i filialerne, fra en service, der var ved at blive udfaset, som vi kunne bruge til vores teknologiske grundlag.
Hvad kom der ud af det? Jeg kan desværre ikke fortælle dig, hvad vi endte med, da der er en NDA, men i løbet af vores søgen udviklede vi et interessant setup, som viste sig at fungere godt i laboratorietests, selvom det ikke gik i produktion.
Et par disclaimers: Forfatteren hævder ikke, at den foreslåede løsning fuldstændig løser alle de stillede opgaver og gør det frivilligt og med sang. Forfatteren er på forhånd enig i påstanden om, at Sein Englishe sprache is zehr schlecht. Da løsningen ikke længere udvikles, kan der ikke forventes bug-fixes eller funktionsændringer; alt er i jeres hænder. Forfatteren antager, at du er lidt bekendt med KVM og har læst en oversigtsartikel om Spice-protokollen samt har arbejdet lidt med Centos eller en anden GNU Linux distribution.
I denne artikel vil jeg gå i dybden med kernen i den løsning, vi har opnået, nemlig interaktionen mellem klient og server samt essensen af processerne i livscyklussen for virtuelle maskiner inden for den diskuterede løsning. Hvis artiklen viser sig at være interessant for læserne, vil jeg beskrive detaljerne omkring implementeringen af live billeder til oprettelse af tynde klienter baseret på Fedora og fortælle om de specifikke indstillinger for virtuelle maskiner. KVM-servere for at optimere ydeevne og sikkerhed.
Hvis man tager farvet papir,
Maling, pensler og lim,
Og lidt mere fingerfærdighed…
Kan man lave hundrede kroner!
Diagram og beskrivelse af testmiljøet

Alt udstyr er placeret inden for filialens netværk, og kun internetkanalen går udad. En proxyserver har historisk set allerede været til stede, og den repræsenterer ikke noget ekstraordinært. Men netop på den vil der blandt andet blive udført trafikfiltrering fra de virtuelle maskiner (forkortet VM i teksten). Intet forhindrer os i at placere denne service på en KVM-server, men vi skal bare holde øje med, hvordan belastningen fra den vil påvirke det diskbaserede system.
Klientstationen - selve "selvbetjeningsstationerne", "frontend" til vores service. De består af nettoppe fra Lenovo IdeaCentre. Hvad er godt ved denne enhed? Næsten alt, især er der glæde over det store antal USB-porte og kortlæseren på frontpanelet. I vores opsætning er der indsat et SD-kort med aktiveret hardwarebeskyttelse mod skrivning i kortlæseren, hvor der er gemt et modificeret live billede af Fedora 28. Selvfølgelig er der tilsluttet en skærm, tastatur og mus til nettoppen.
Switch - en helt almindelig hardware switch af anden niveau, som står i serverrummet og blinker med lys. Den er ikke tilsluttet nogen netværk, undtagen netværket for "selvbetjeningsstationerne".
KVM_Server - kernen i opsætningen, i testforsøgene kunne Core 2 Quad Q9650 med 8 GB RAM uden problemer håndtere 3 virtuelle maskiner med Windows 10. Det diskbaserede system - adaptec 3405 2 diske Raid 1 + SSD. I feltforsøgene kunne Xeon 1220 med en mere seriøs LSI 9260 + SSD let håndtere 5-6 VMer. Serverne blev givet til os fra en udfaset service, så de kapitaludgifter ville være lave. På denne server(e) er KVM-virtualisering systemet implementeret med et pool af virtuelle maskiner, pool_Vm.
VM - den virtuelle maskine, backend til vores service. Her foregår brugerens arbejde.
Enp5s0 – netværksgrænsefladen, der peger mod netværket "selvbetjeningsstationer", hvor dhcpd, ntpd, httpd og xinetd lytter på "signal" porten.
Lo0 – pseudo-grænseflade for loopback. Standard.
Spice_console – En meget interessant funktion; forskellen fra klassisk RDP er, at når KVM+Spice protokol bruges, oprettes der en ekstra enhed – porten til den virtuelle maskines konsol. Faktisk, når vi tilslutter os denne TCP-port, får vi adgang til Vm-konsollen uden at skulle forbinde til Vm via dens netværksgrænseflade. Al signaloverførsel sker via serveren. Den nærmeste analog er IPKVM. Det vil sige, at billederne fra Vm's skærm overføres til denne port, og data om musens bevægelser sendes også dertil. (Det mest betydningsfulde) interaktionen gennem Spice-protokollen gør det muligt at seamless viderekobling af USB-enheder til den virtuelle maskine, som om enheden var tilsluttet Vm'en. Det er testet med flashdrev, scannere og webkameraer.
Vnet0, virbr0 og virtuelle netværkskort i Vm danner et netværk for virtuelle maskiner.
Hvordan DET fungerer
Fra Client Station's side
Client Station starter op i grafisk tilstand med et modificeret live-billede af Fedora 28, modtager en IP-adresse via DHCP fra netværksområdets adresse 169.254.24.0/24. Under opstart oprettes firewall-regler, der muliggør forbindelser til serverens "signal" og "spice" porte. Efter opstart venter stationen på brugerautorisering af "Client". Efter brugerautorisering starter skrivebordshåndteringsprogrammet "openbox" og kører scriptet autostart fra den autoriserede brugers navn. Blandt andet starter autostart-scriptet scriptet 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
Beskrivelse af variabler i filen client.conf
server_ip — adressen på KVM_Server
vdi_signal_port — porten på KVM_Server, hvor xinetd er til stede
vdi_spice_port — netværksporten på KVM_Server, som vil håndtere anmodninger om forbindelse fra remote-viewer klienten til spice-porten på den dedikerede Vm (nærmere oplysninger nedenfor)
animation_folder — mappen, hvor billeder til demonstration af bullshit-animation hentes fra
background_folder — mappen, hvor billeder til demonstration af præsentationer i standby-tilstand hentes fra. Mere om animation i næste del af artiklen.
Scriptet remote.sh henter indstillinger fra konfigurationsfilen /etc/client.conf og opretter forbindelse til "vdi_signal_port" porten på KVM-serveren via nc for at modtage datastream fra serveren, hvor den venter på strengen "RULE ADDED, CONNECT NOW". Når den ønskede streng modtages, startes processsen remote-viewer i kiosk-tilstand, der opretter forbindelse til "vdi_spice_port" porten på serveren. Udførelsen af skriptet pauses, indtil remote-viewer processen er afsluttet.
Remote-viewer, ved at oprette forbindelse til "vdi_spice_port" porten, dirigeres på serversiden til "spice_console" porten på lo0-grænsefladen, dvs. til konsollen af den virtuelle maskine, hvor brugeren arbejder. Under ventetiden på forbindelsen vises der en bullshit-animation i form af et slideshow af jpeg-filer, hvor stien til mappen med billeder bestemmes af variablen animation_folder fra konfigurationsfilen.
Ved tab af forbindelse til "spice_console" porten på den virtuelle maskine, som signalerer, at den virtuelle maskine er slukket/genstartet (dvs. det faktiske slut på brugerens session), afsluttes alle processer, der er kørt under navnet på den autoriserede bruger, hvilket fører til en genstart af lightdm og returnering til login-skærmen.
Fra KVM-serverens side
På "signal" porten på netværkskortet enp5s0 venter xinetd på forbindelser. Efter en forbindelse til "signal" porten starter xinetd skriptet vm_manager.sh uden at give det nogen indgående parametre og omdirigerer resultatet af skriptets udførelse til nc-sessionen på klientstationen.
/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
Beskrivelse af variablerne i konfigurationsfilen vm_manager.conf
srv_scripts_dir — mappen, hvor skripterne vm_manager.sh, vm_connect.sh, vm_delete.sh, vm_create.sh, vm_clear.sh er placeret
srv_pool_size — størrelsen på VM-poolen
srv_start_port_pool — den startport, hvorefter portene til spice-konsollerne for de virtuelle maskiner vil blive placeret
srv_tmp_dir — mappen til opbevaring af midlertidige filer
base_host — den grundlæggende VM (guld billede), hvorfra klonerne af VM vil blive lavet i poolen
input_iface — netværksinterfacet på serveren, der ser mod klientstationerne
vdi_spice_port — netværksporten på serveren, hvorigennem anmodningen om forbindelse fra remote-viewer klienten til spice-porten på den tildelte VM vil blive omdirigeret
count_conn_tryes — en timer, hvorefter det vurderes, at der ikke er opnået forbindelse til Vm (se detaljer om funktionaliteten i vm_connect.sh)
Scriptet vm_manager.sh læser konfigurationsfilen fra vm_manager.conf, vurderer tilstanden af de virtuelle maskiner i puljen baseret på flere parametre, nemlig: hvor mange VM'er der er blevet implementeret, og om der er ledige, friske VM'er. For dette formål læser det filen clear.list, som indeholder numrene på 'spice_console' porte for de 'nyoprettede' (se nedenfor cyklus for oprettelse af V.M.) virtuelle maskiner, og tjekker for etablerede forbindelser til dem. Når en port med en etableret netværkforbindelse registreres (hvilket absolut ikke bør ske), vises en advarsel, og porten flyttes til waste.list. Når den første port fra clear.list opdages, som i øjeblikket ikke har forbindelse, kalder vm_manager.sh scriptet vm_connect.sh og overfører nummeret på denne port som parameter.
/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
Scriptet vm_connect.sh tilføjer firewall-regler, der opretter en omdirigering af 'vdi_spice_port' fra serverens enhed enp5s0 til 'spice console port' for VM'en, der ligger på serverens lo0-interface, og som blev overført som startparameter. Porten flyttes til conn_wait.list, og VM'en anses for at vente på forbindelse. Til Client Station-sessionen på serverens 'signal'-port sendes strengen 'RULE ADDED, CONNECT NOW', som scriptet remote.sh forventer at modtage. En cyklus afventer forbindelsen med et antal forsøg, bestemmes af værdien af variablen 'count_conn_tryes' fra konfigurationsfilen. Hver sekund vil teksten 'RULE ADDED, CONNECT NOW' blive sendt til nc-sessionen, og der vil blive kontrolleret for en etableret forbindelse til 'spice_console' porten.
Hvis der ikke er opnået forbindelse inden for det angivne antal forsøg, flyttes 'spice_console' porten tilbage til clear.list. Udførelsen af vm_connect.sh afsluttes, og udførelsen af vm_manager.sh genoptages, som så starter en oprydningscyklus.
Hvis forbindelsen fra Client Station til "spice_console"-porten på grænsefladen lo0 registreres, fjernes firewall-reglerne, der skaber en omdirigering mellem serverens "spice"-port og "spice_console"-porten, og den efterfølgende opretholdelse af forbindelsen sker ved hjælp af firewallens tilstandsbestemmelsesmekanisme. I tilfælde af, at forbindelsen går tabt, vil det ikke være muligt at genoprette forbindelsen til "spice_console"-porten. "Spice_console"-porten flyttes til waste.list, VM'en betragtes som "snavset", og den kan ikke vende tilbage til puljen med "rene" virtuelle maskiner uden at gennemgå en rensning. Udførelsen af vm_connect.sh afsluttes, og udførelsen af vm_manager.sh genoptages, hvilket starter rensningssløjfen.
Rensningssløjfen begynder med at gennemse filen waste.list, hvor numrene på "spice_console"-portene fra virtuelle maskiner, som der er oprettet forbindelse til, flyttes. Der bestemmes tilstedeværelsen af en aktiv forbindelse på hver "spice_console"-port fra listen. Hvis forbindelsen mangler, betragtes den virtuelle maskine som ikke længere i brug, og porten flyttes til recycle.list, hvorefter processen med at slette den virtuelle maskine (se nedenfor) påbegyndes. Hvis der opdages en aktiv netværksforbindelse på porten, betragtes den virtuelle maskine som i brug, og der træffes ingen foranstaltninger for den. Hvis porten ikke bliver overvåget, betragtes VM som slukket og ikke længere nødvendig. Porten flyttes til recycle.list, og processen med at slette den virtuelle maskine påbegyndes. For at gøre dette kaldes scriptet vm_delete.sh, hvor nummeret på "spice_console"-porten for den VM, der skal slettes, sendes som parameter.
/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
Sletning af den virtuelle maskine er en ret triviel opgave; scriptet vm_delete.sh identificerer navnet på den virtuelle maskine, som porten, der blev givet som startparameter, tilhører. Der udføres en tvungen stop af VM, VM'en slettes fra hypervisoren, og den virtuelle harddisk for denne VM slettes. "Spice_console"-porten fjernes fra recycle.list. Udførelsen af vm_delete.sh afsluttes, og udførelsen af vm_manager.sh genoptages.
Scriptet vm_manager.sh, efter afslutningen af rensningsoperationerne for unødvendige virtuelle maskiner fra listen waste.list, begynder cyklussen med at oprette virtuelle maskiner i puljen.
Processen begynder med, at der identificeres tilgængelige porte "spice_console" til hosting. For dette formål, baseret på parameteren i konfigurationsfilen "srv_start_port_pool" der angiver startporten for poolen af "spice_console" virtuelle maskiner og parameteren "srv_pool_size", der definerer det maksimale antal virtuelle maskiner, gennemgås alle mulige portkombinationer sekventielt. For hver bestemt port søges den i clear.list, waste.list, conn_wait.list, recycle.list. Hvis porten findes i nogen af disse filer, betragtes den som optaget og springes over. Hvis porten ikke findes i de angivne filer, tilføjes den til recycle.list, og processen med at oprette en ny virtuel maskine påbegyndes. Til dette formål kaldes scriptet vm_create.sh, som får portnummeret "spice_console" for den virtuelle maskine, der skal oprettes, som parameter.
/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
Processen med at oprette en ny virtuel maskine
Scriptet vm_create.sh læser værdien af variablen "base_host" fra konfigurationsfilen, som bestemmer skabelonen for den virtuelle maskine, som klonen skal laves fra. Det udtrækker XML-konfigurationen for VM fra hypervisorens database, udfører en række kontroller af diskens VM qcow-billede, og ved vellykket gennemførelse oprettes XML-konfigurationsfilen for den nye VM og et "linked clone" billede af diskens nye VM. Herefter indlæses XML-konfigurationen for den nye VM i hypervisorens database, og VM'en startes. Porten "spice_console" flyttes fra recycle.list til clear.list. Udførelsen af vm_create.sh afsluttes, og udførelsen af vm_manager.sh afsluttes.
Ved næste forbindelse begynder det hele forfra.
Til nødsituationer er der et script, vm_clear.sh, der tvunget gennemgår alle VM'er i puljen og fjerner dem med nulstilling af listenes værdier. At kalde det i opstartstrinnet gør det muligt at starte (under)VDI med et rent ark.
/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="
På dette punkt vil jeg afslutte første del af min fortælling. Det præsenterede materiale burde være tilstrækkeligt for systemadministratorer til at prøve underVDI i praksis. Hvis samfundet finder dette emne interessant, vil jeg i anden del tale om modificeringen af livecd Fedora og omdannelsen til en kiosk.
Kilde: habr.com
