Kinek szól ez a cikk
Ez a cikk érdekes lehet rendszergazdák számára, akiknek az a feladata, hogy létrehozzanak 'egyszer használatos' munkahelyi szolgáltatásokat.
Prológus
A fiatal, dinamikusan fejlődő vállalat IT támogatási osztályához kérés érkezett, hogy szervezzenek 'önkiszolgáló állomásokat', amelyeket külső ügyfeleik használnának. Ezeket az állomásokat a vállalat külső portáljaira való regisztrációra, külső eszközökről származó adatok feltöltésére és állami portálokkal való munkára szánták.
Fontos szempont volt, hogy a szoftverek többsége 'kizárólag' MS Windows-ra van optimalizálva (például a 'Deklaráció'), és a nyílt formátumok felé való elmozdulás ellenére az MS Office továbbra is a domináló szabvány az elektronikus dokumentumok cseréjében. Így tehát nem tudtunk lemondani az MS Windows-ról ennek a feladatnak a megoldásához.
Fő problémaként merült fel, hogy különféle felhasználói session-adatai halmozódhatnak fel, ami harmadik fél részére való kiszivárgáshoz vezethet. . De ellentétben a kvázi állami (állami autonóm intézmény) MFC-vel, az ilyen hiányosságok miatt nem állami szervezeteket sokkal súlyosabban fognak megbüntetni. A következő legkritikusabb probléma a külső adathordozók kezelésére vonatkozó követelmény volt, amelyeken bizonyára sok rosszindulatú szoftver található. Az interneten való rosszindulatú programok beszerzésének valószínűségét kevesebbnek tartották, mivel a fehérlistás címek megválasztása korlátozza az internethez való hozzáférést. A követelmények kidolgozásához más részlegek munkatársai is csatlakoztak, akik hozzájárultak saját igényeikkel és kívánságaikkal; a végső követelmények a következőképpen alakultak:
Az IB követelményei
- A felhasználási időszak után minden felhasználói adatnak (ideértve a átmeneti fájlokat és a rendszerleíró kulcsokat) törlődnie kell.
- Minden felhasználó által elindított folyamatot a munka befejezésekor le kell állítani.
- Internet hozzáférés csak fehérlistás címek révén.
- A harmadik féltől származó kód futtatásának korlátozása.
- Ha a session több mint 5 percig inaktív, akkor automatikusan be kell zárni, és az állomásnak tisztítania kell.
A megrendelő követelményei
- A fiókonkénti kliensállomások száma – legfeljebb 4.
- A minimális várakozási idő a rendszer készenlétének eléréséig, a 'székbe ülés' pillanatától kezdve a kliens szoftverrel való munka kezdetéig.
- A perifériás eszközök (szkennerek, USB flash meghajtók) közvetlen csatlakoztatásának lehetősége az 'önkiszolgáló állomás' telepítési helyén.
- Megrendelői kívánságok
- Reklámanyagok (képek) bemutatása a rendszer leállása alatt.
A kreativitás fájdalmai
Miután alaposan játszottunk a Windows live CD-kkel, egyhangúlag arra a következtetésre jutottunk, hogy a megoldásunk nem elégíti ki legalább 3 kritikus szempontot. Vagy lassan töltődnek be, vagy nem igazán élőek, vagy a testreszabásuk őrült nehézségekkel járt. Lehet, hogy rosszul kerestünk, és talán tud ajánlani egy eszközkészletet, ezért hálás lennék.
Ezután a VDI irányába kezdtünk el nézni, de a legtöbb megoldás vagy túl drága, vagy sok figyelmet igényel. Olyan egyszerű eszközt szerettünk volna, minimális varázslattal, ahol a legtöbb probléma egy egyszerű újraindítással vagy a szolgáltatás újraindításával megoldható. Szerencsére volt szerverhardverünk, az alacsony kategóriás, a kivont szolgáltatásból, amit felhasználhattunk a technológiához.
Mi lett az eredmény? Nos, hogy mi lett az eredmény, arról nem tudok beszámolni, mert NDA, de a keresés során kifejlesztettünk egy érdekes sémát, amely jól teljesített a laboratóriumi teszteken, bár nem került sorozatgyártásra.
Néhány diszklaimer: a szerző nem állítja, hogy a javasolt megoldás teljes mértékben megoldja az összes feladatot, és ezt önkéntesen, dalolva végzi. A szerző előre egyetért azzal az állítással, hogy Sein Englishe sprache is zehr schlecht. Mivel a megoldás már nem fejlődik, a hibajavításra vagy funkcióváltoztatásra nem számíthat, minden a saját kezében van. A szerző feltételezi, hogy Ön legalább egy kicsit ismerős a KVM-mel, olvasta a Spice protokollról szóló áttekintő cikket, és dolgozott CentOS-szal vagy más GNU Linux disztribúcióval.
Ebben a cikkben a megoldás vázlatát szeretném áttekinteni, nevezetesen a kliens és a szerver közötti kölcsönhatást, valamint a virtuális gépek életciklusának folyamatait a vizsgált megoldás keretein belül. Ha a cikk érdekesnek bizonyul a közönség számára, leírom a live képek részleteit a Fedora alapú vékony kliensek létrehozásához, és beszámolok a virtuális gépek beállításának részleteiről. KVM szerver a teljesítmény és a biztonság optimalizálásához.
Ha színes papírt veszünk,
Festékek, ecsetek és ragasztó,
És még egy kis ügyesség…
Lehet belőle száz forintot csinálni!
A tesztpult terve és leírása

Minden berendezés a fiók hálózatán belül helyezkedik el, kívül csak az internetes csatorna érhető el. A proxy szerver történelmileg már létezett, semmi rendkívüli nincs benne. De éppen ezen a szerveren, többek között, a virtuális gépek (a továbbiakban VMs) forgalmának szűrése fog történni. Semmi nem akadályozza meg, hogy ezt a szolgáltatást a KVM szerveren telepítsük, az egyetlen, amit figyelembe kell vennünk, az a megnövekedett terhelés a tárolórendszeren.
Client Station – lényegében a „önkiszolgáló állomások”, a szolgáltatásunk „frontendje”. A Lenovo IdeaCentre nettopok. Miért jó ez az eszköz? Szinte minden szempontból, különösen öröm a sok USB csatlakozó és a kártyaolvasó az elülső panelen. A rendszerünkben a kártyaolvasóba egy, az írás védelmével ellátott SD kártya van behelyezve, amelyen a módosított Fedora 28 live kép található. Természetesen a nettophoz monitor, billentyűzet és egér is csatlakozik.
Switch – egy semmi különöset elmondani nem tudó második szintű hardver switch, a szerverteremben helyezkedik el, és villog a lámpákkal. Nem kapcsolódik semmilyen hálózathoz, kivéve az „önkiszolgáló állomások” hálózatát.
KVM_Server – a rendszer szíve, a tesztelések során a Core 2 Quad Q9650 8 GB RAM-mal magabiztosan futtatott 3 virtuális gépet Windows 10-tel. A tárolórendszer – adaptec 3405, 2 lemez Raid 1 + SSD. A tereppróbák során a Xeon 1220 egy komolyabb LSI 9260 + SSD könnyedén kezelte az 5-6 VMs-et. A szervereket az elavult szolgáltatásból kaptuk, a tőkeköltségek nem voltak magasak. Ezen a szerveren(ek) van telepítve a KVM virtualizációs rendszer a pool_Vm virtuális gépekkel.
Vm – a virtuális gép, a szolgáltatásunk háttérfolyamatainak motorja. Itt történik a felhasználó munkája.
Enp5s0 – hálózati interfész, amely a "önkiszolgáló állomások" hálózatába néz, itt él a dhcpd, ntpd, httpd, és az xinetd hallgat a "signal" porton.
Lo0 – a visszacsatoló hurok álkör interfész. Szabványos.
Spice_console – Nagyon érdekes dolog, a lényeg, hogy ellentétben a klasszikus RDP-vel, a KVM+Spice protokoll kombinálásánál megjelenik egy plusz entitás – a virtuális gép konzolának portja. Valójában, csatlakozva ehhez a TCP porthoz, konzolt kapunk a Vm-hez, anélkül, hogy a Vm hálózati interfésze felé kellene csatlakoznunk. A Vm-hez való kapcsolódás során a jel továbbítását a szerver vállalja. A legközelebbi funkcionális analóg – IPKVM. Tehát erre a portra a Vm monitorának képe kerül, ide érkeznek az egérmozgás adatai, és (ami a legfontosabb) a Spice protokollon keresztüli interakció lehetővé teszi az USB eszközök zökkenőmentes átirányítását a virtuális gépbe, mintha ez az eszköz a Vm-hez lenne csatlakoztatva. Tesztelve USB flash meghajtókkal, szkennelőkkel, webkamerákkal.
A vnet0, virbr0 és a virtuális Vm hálózati kártyák létrehozzák a virtuális gépek hálózatát.
Hogyan működik EZ?
A kliens állomás oldaláról
A kliens állomás grafikus módban bootol a módosított Fedora 28 live képből, ip címet kap a dhcp-ből a 169.254.24.0/24 hálózati területből. A betöltési folyamat során tűzfal szabályok jönnek létre, amelyek lehetővé teszik a kapcsolatokat a szerver "signal" és "spice" portjaira. A betöltés befejezése után az állomás vár a "Client" felhasználó hitelesítésére. A felhasználó hitelesítése után elindul az "openbox" asztalkezelő és a felhasználónév alatt az autostart script futtatása. Egyebek mellett az autostart script elindítja a remote.sh scriptet.
$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
A client.conf fájl változóinak leírása
server_ip — a KVM_Server címe
vdi_signal_port — a KVM_Server portja, ahol az xinetd fut
vdi_spice_port — a KVM_Server hálózati portja, ahonnan a remote-viewer kliens kapcsolódási kérése a virtuális gép spice portjára kerül átirányításra (további részletek alább)
animation_folder — a mappája, ahonnan a bullshit animációk megjelenítéséhez szükséges képek érkeznek
background_folder — a mappája, ahonnan a várakozási állapotban a bemutatók megjelenítéséhez szükséges képek érkeznek. Részletek az animációról a cikk következő részében.
A remote.sh script takes settings from the configuration file /etc/client.conf and, using nc, connects to the 'vdi_signal_port' port of the KVM server to receive a data stream from the server, among which it waits for the string 'RULE ADDED, CONNECT NOW'. Upon receiving the desired string, the remote-viewer process starts in kiosk mode, establishing a connection to the 'vdi_spice_port' port of the server. The script execution is paused until the remote-viewer process completes.
When remote-viewer connects to the 'vdi_spice_port', it is redirected on the server side to the 'spice_console' port of the lo0 interface, meaning the virtual machine console, where the user can directly interact. During the connection wait, a bullshit animation in the form of a slideshow of jpeg files is displayed to the user, with the path to the image folder defined by the animation_folder variable from the configuration file.
When the connection to the 'spice_console' port of the virtual machine is lost, signaling the shutdown/reboot of the virtual machine (i.e., the actual end of the user session), all processes started under the logged-in user's account are terminated, which leads to a restart of lightdm and a return to the login screen.
From the KVM Server side
The xinetd waits for connections on the 'signal' port of network card enp5s0. After connecting to the 'signal' port, xinetd launches the vm_manager.sh script without passing any input parameters and redirects the script's execution results to the nc session of the 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
Description of the variables in the configuration file vm_manager.conf
srv_scripts_dir — the folder where the scripts vm_manager.sh, vm_connect.sh, vm_delete.sh, vm_create.sh, vm_clear.sh are located
srv_pool_size — the size of the VM pool
srv_start_port_pool — the initial port after which the spice console ports of virtual machines will be assigned
srv_tmp_dir — the folder for temporary files
base_host — the base VM (golden image) from which clones of VMs in the pool will be created
input_iface — the network interface of the server facing the Client Stations
vdi_spice_port — the network port of the server from which connection requests from the remote-viewer client to the spice port of the designated VM will be redirected
count_conn_tryes — várakozási időzítő, amelynek letelte után a kapcsolatot a Vm-hez nem jött létre (a működés részletei megtalálhatók a vm_connect.sh fájlban)
A vm_manager.sh szkript beolvassa a konfigurációs fájlt a vm_manager.conf fájlból, és kiértékeli a virtuális gépek állapotát a poolban több paraméter alapján, mint például: hány VM van telepítve, van-e szabad tiszta VM. Ehhez beolvassa a clear.list fájlt, amely tartalmazza a „spice_console” portok számát a „frissen létrehozott” virtuális gépekről (lásd az alábbi V.M. létrehozási ciklust), és ellenőrzi a kapcsolat meglétét velük. Amikor egy portot talál, amelyhez aktív hálózati kapcsolat van (amely nem lenne szabad, hogy létezzen), figyelmeztetést ad ki, és a port átkerül a waste.list fájlba. Ha a clear.list fájl első portját találja, amelyhez éppen nincs kapcsolat, a vm_manager.sh meghívja a vm_connect.sh szkriptet, és átadja neki ezt a portszámot paraméterként.
/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
A vm_connect.sh szkript tűzfal szabályokat ad hozzá, amelyek átirányítják a „vdi_spice_port” portot az enp5s0 interfész szerverén a VM „spice console portjára”, amely a szerver lo0 interfészén található, és amelyet a futtatáshoz paraméterként adtak meg. A port átkerül a conn_wait.list fájlba, a VM a kapcsolat várakozó állapotába kerül. Az ügyfélszolgálati állomásra a szerver „signal” portján a „RULE ADDED, CONNECT NOW” üzenet kerül továbbításra, amelyet a rajta futó remote.sh szkript vár. Elkezdődik a kapcsolat várakozási ciklusa, amelynek próbálkozásait a konfigurációs fájl „count_conn_tryes” változója határozza meg. Minden egyes másodpercben a nc session-hoz a „RULE ADDED, CONNECT NOW” üzenet kerül átadásra, és ellenőrzésre kerül a kapcsolat megléte a „spice_console” portra.
Ha a megadott próbálkozások számán belül nem jött létre a kapcsolat, a „spice_console” port visszakerül a clear.list fájlba. A vm_connect.sh futása befejeződik, és a vm_manager.sh végrehajtása folytatódik, amely elindítja a tisztítási ciklust.
Ha a Client Station csatlakozása a „spice_console” porthoz a lo0 interfészen rögzítve van, a tűzfal szabályai, amelyek irányítják a „spice” portot a szerver és a „spice_console” port között, eltávolításra kerülnek, és a kapcsolattartás a tűzfal állapotának meghatározó mechanizmusa által történik. Ha a kapcsolat megszakad, a „spice_console” porttal való kapcsolódás nem állítható vissza. A „spice_console” port átkerül a waste.list-be, a VM „piszkosnak” minősül, és nem tud visszatérni a „tiszta” virtuális gépek medencéjébe anélkül, hogy megtisztítanák. A vm_connect.sh végrehajtása befejeződik, és folytatódik a vm_manager.sh végrehajtása, amely elindítja a tisztítási ciklust.
A tisztítási ciklus a waste.list fájl megtekintésével kezdődik, amelybe a virtuális gépekhez tartozó „spice_console” portok számai kerülnek. Ellenőrizni kell, hogy van-e aktív kapcsolat minden egyes „spice_console” porton a listában. Ha a kapcsolat hiányzik, akkor a virtuális gépet már nem használják, és a port áthelyezésre kerül a recycle.list-be, majd elindul a virtuális gép törlési folyamata (lásd alább), amelyhez ez a port tartozott. Ha van aktív hálózati kapcsolat a porton, azt jelenti, hogy a virtuális gép használatban van, és semmilyen intézkedést nem hoznak vele kapcsolatban. Ha a port nem figyelhető meg, akkor a VM leállt, és már nincs szükség rá. A port átkerül a recycle.list-be, és elindul a virtuális gép törlési folyamata. Ehhez a vm_delete.sh scriptet hívják meg, amelynek bemeneti paramétereként a VM „spice_console” portjának számát adják meg, amelyet törölni kell.
/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
A virtuális gép törlése – meglehetősen egyszerű folyamat, a vm_delete.sh script meghatározza a virtuális gép nevét, amelyhez a paraméterként megadott port tartozik. A VM kényszerített leállítása, a VM törlése a hypervisorból, a virtuális HDD eltávolítása történik. A „spice_console” port eltávolításra kerül a recycle.list-ből. A vm_delete.sh végrehajtása befejeződik, és a vm_manager.sh végrehajtásának folytatása kezdődik.
A vm_manager.sh script, a waste.list-ből a felesleges virtuális gépek tisztítási műveleteinek befejezésével, elindítja a virtuális gépek létrehozásának ciklusát a poolban.
A folyamat azzal kezdődik, hogy meghatározzák a rendelkezésre álló „spice_console” portokat. Ehhez a konfigurációs fájl „srv_start_port_pool” paraméteréből, amely a „spice_console” virtuális gépek portpoolszámát határozza meg, valamint az „srv_pool_size” paraméterből, amely a virtuális gépek maximális számát határozza meg, következik a lehetséges portvariációk sorozatos ellenőrzése. Minden egyes meghatározott port esetén megkeresik azt a clear.list, waste.list, conn_wait.list, recycle.list listákban. Ha a port bármelyik fájlban megtalálható, akkor az foglalt, és átugrásra kerül. Ha a port a megadott fájlokban nem található, akkor a recycle.list fájlba kerül, és elkezdődik egy új virtuális gép létrehozásának folyamata. Ehhez a vm_create.sh szkriptet hívják meg, amelynek paramétereként a „spice_console” port számát adják meg, amelyhez virtuális gépet kell létrehozni.
/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
Új virtuális gép létrehozásának folyamata
A vm_create.sh szkript beolvassa a konfigurációs fájl „base_host” változójának értékét, amely alapján a virtuális gép mintáját határozzák meg, aminek a klónja készül. Kinyeri az XML konfigurációt a VM-ből a hypervisor adatbázisból, számos ellenőrzést hajt végre a VM qcow lemezképén, és ha ezek sikeresen befejeződnek, létrehozza az új VM számára az XML konfigurációs fájlt és a „linked clone” lemezképet. Ezután az új VM XML konfigurációját betölti a hypervisor adatbázisába, és elindítja a VM-et. A „spice_console” portot áthelyezik a recycle.list-ból a clear.list-be. A vm_create.sh végrehajtása befejeződik, és a vm_manager.sh végrehajtása is.
A következő csatlakozáskor minden kezdődik elölről.
Vészhelyzetekre van egy vm_clear.sh szkript is, amely kényszerítőleg átfut az összes VM-en a poolból és eltávolítja azokat, nullázva a listák értékeit. Ennek hívása a betöltési szakaszban lehetővé teszi, hogy a (félkész)VDI tiszta lappal kezdje el a működést.
/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="
Ezzel szeretném befejezni a történetem első részét. Az eddigiek elegendőek kell legyenek a rendszergazdák számára, hogy kipróbálhassák a félkész VDI működését. Ha a közösség érdekesnek találja ezt a témát, a második részben bemutatom a Fedora livecd módosítását és annak átalakítását egy kioszká.
Forrás: habr.com
