KVM (under)VDI med engÄngs virtuella maskiner med hjÀlp av bash

Vem Àr denna artikel avsedd för

Denna artikel kan vara intressant för systemadministratörer som stÄr inför uppgiften att skapa en tjÀnst för "engÄngs" arbetsplatser.

Prolog

IT-supportavdelningen pÄ ett ungt, dynamiskt vÀxande företag med ett litet regionalt nÀtverk vÀnde sig till oss för att organizera "sjÀlvbetjÀningstationer" för anvÀndning av deras externa kunder. Dessa stationer skulle anvÀndas för registrering pÄ företagets externa portaler, för att ladda upp data frÄn externa enheter och för att arbeta med statliga portaler.

En viktig aspekt var att en stor del av programvaran Àr "anpassad" för MS Windows (till exempel "Deklaration"), och trots rörelsen mot öppna format, förblir MS Office den dominerande standarden vid utbyte av elektroniska dokument. DÀrför kunde vi inte avstÄ frÄn MS Windows nÀr vi skulle lösa denna uppgift.

Det största problemet var möjligheten att olika data frÄn anvÀndarsessioner kan samlas, vilket skulle kunna leda till lÀckor till tredje part. En sÄdan situation har redan drabbat MFC. Men till skillnad frÄn quasi-statliga (statliga autonoma institutioner) MFC, kommer icke-statliga organisationer att straffas mycket hÄrdare för liknande brister. NÀsta mest kritiska problem var kravet pÄ att arbeta med externa datalagringsenheter, vilket utan tvekan har en massa skadlig programvara. Risken för att fÄ in skadlig programvara frÄn internet ansÄgs som mindre möjlig, pÄ grund av begrÀnsad internetÄtkomst via en vit lista av adresser. Medarbetare frÄn andra avdelningar deltog i utvecklingen av kraven och bidrog med sina krav och önskemÄl. De slutgiltiga kraven blev följande:

Krav pÄ informationssÀkerhet

  • Efter anvĂ€ndning ska alla anvĂ€ndardata (inklusive temporĂ€ra filer och registernycklar) raderas.
  • Alla processer som anvĂ€ndaren har startat ska avslutas efter att de har avslutat arbetet.
  • InternetĂ„tkomst endast via en vit lista av adresser.
  • BegrĂ€nsningar för möjligheten att köra tredjeparts kod.
  • Om sessionen Ă€r inaktiv i mer Ă€n 5 minuter ska sessionen automatiskt avslutas, och stationen ska rensa.

Krav frÄn kunden

  • Antalet klientstationer per filial – högst 4.
  • Minimalt vĂ€ntetid för systemets beredskap, frĂ„n det ögonblick man "sĂ€tter sig pĂ„ stolen" till man börjar arbeta med klientprogramvaran.
  • Möjlighet att ansluta perifera enheter (skanners, usb-minnen) direkt vid installationen av "sjĂ€lvbetjĂ€ningsstationen".
  • Kundens önskemĂ„l
  • Visning av reklammaterial (bilder) under stillestĂ„nd i komplexet.

Kreativa plÄgor

Efter att ha lekt med Windows livecd kom vi fram till en enhÀllig slutsats: den lösning som vi fick fram uppfyller inte minst tre kritiska punkter. Antingen laddar de lÄngsamt, eller sÄ Àr de inte helt live, eller sÄ var anpassningen förknippad med stora svÄrigheter. Kanske letade vi inte tillrÀckligt bra och du kan rekommendera en uppsÀttning verktyg, jag skulle vara tacksam.

Vidare började vi titta pÄ VDI, men för den hÀr uppgiften Àr de flesta lösningar antingen för dyra eller sÄ krÀver de noggrant övervakande. Vi ville ha ett enkelt verktyg, med minsta mÀngd magi, dÀr de flesta problem kunde lösas med en enkel omstart/omstart av tjÀnsten. Lyckligtvis hade vi serverutrustning av lÄgklass i filialerna, frÄn en avvecklad tjÀnst, som vi kunde anvÀnda som teknologisk bas.

Vad blev resultatet? Jo, vad resultatet blev kan jag inte berÀtta för er, eftersom det Àr under NDA, men i sökprocessen utvecklade vi ett intressant schema som visat sig fungera bra i laboratorietester, Àven om det inte gick i produktion.

Lite disclaimer: författaren pÄstÄr inte att den föreslagna lösningen helt löser alla uppsatta uppgifter och gör det frivilligt och med sÄng. Författaren Àr pÄ förhand överens med pÄstÄendet att Sein Englishe sprache is zehr schlecht. Eftersom lösningen inte lÀngre utvecklas kan man inte rÀkna med buggfixar eller funktionella Àndringar, allt ligger i era hÀnder. Författaren antar att ni Àr Ätminstone lite bekanta med KVM och har lÀst en översiktsartikel om Spice-protokollet och har arbetat lite med Centos eller nÄgon annan GNU Linux-distribution.

I denna artikel skulle jag vilja diskutera kÀrnan i den resulterande lösningen, nÀmligen interaktionen mellan klient och server samt processen för livscykeln för virtuella maskiner inom den övervÀgda lösningen. Om artikeln visar sig intressant för publiken kommer jag att beskriva detaljerna kring implementeringen av live-bilder för att skapa tunna klienter baserade pÄ Fedora och berÀtta om detaljerna kring konfigureringen av virtuella maskiner. KVM-servrar för att optimera prestanda och sÀkerhet.

Om vi tar fÀrgat papper,
FĂ€rger, penslar och lim,
Och lite mer skicklighet...
Kan man göra hundra kronor!

Schema och beskrivning av testmiljön

KVM (under)VDI med engÄngs virtuella maskiner med hjÀlp av bash

All utrustning finns inuti filialens nÀtverk, utÄt gÄr endast internetkanalen. En proxyserver har historiskt redan funnits, den Àr inget extraordinÀrt. Men just pÄ den kommer, bland annat, filtreringen av trafik frÄn de virtuella maskinerna (förkortat VM i texten) att ske. Det finns inget som hindrar att denna tjÀnst placeras pÄ KVM-servern, det enda som mÄste övervakas Àr hur belastningen frÄn den pÄverkar lagringssystemet.

Client Station – sjĂ€lva "sjĂ€lvbetjĂ€ningsstationerna", "frontend" för vĂ„r tjĂ€nst. De bestĂ„r av nettops frĂ„n Lenovo IdeaCentre. Vad Ă€r bra med denna enhet? Ja, nĂ€stan allt, sĂ€rskilt det stora antalet USB-portar och kortlĂ€saren pĂ„ framsidan. I vĂ„r konfiguration Ă€r en SD-kort insatt i kortlĂ€saren med aktiverad hĂ„rdvaruskrivskydd, pĂ„ vilket den modifierade live-bilden av Fedora 28 Ă€r lagrad. Naturligtvis Ă€r en skĂ€rm, ett tangentbord och en mus anslutna till nettopen.

Switch – en helt vanlig hĂ„rdvaruswitch av andra nivĂ„n, stĂ„r i serverrummet och blinkar med lampor. Den Ă€r inte ansluten till nĂ„gra nĂ€tverk, förutom nĂ€tverket för "sjĂ€lvbetjĂ€ningsstationerna".

KVM_Server – kĂ€rnan i schemat, under test stĂ„r Core 2 Quad Q9650 med 8 GB RAM stabilt ut för 3 virtuella maskiner med Windows 10. Lagringssystemet – Adaptec 3405 med 2 diskar i Raid 1 + SSD. Under fĂ€lttester kunde Xeon 1220 med en mer robust LSI 9260 + SSD lĂ€tt hantera 5-6 VM. Servrarna vi fick skulle komma frĂ„n en borttagen tjĂ€nst, och kapitalkostnaderna skulle inte vara stora. PĂ„ dessa servrar Ă€r KVM-virtualiseringssystemet utbyggt med en pool av virtuella maskiner pool_Vm.

VM – den virtuella maskinen, backend för vĂ„r tjĂ€nst. HĂ€r sker anvĂ€ndarens arbete.

Enp5s0 – nĂ€tverksgrĂ€nssnittet som pekar mot nĂ€tverket av "sjĂ€lvbetjĂ€ningsstationer", dĂ€r dhcpd, ntpd, httpd och xinetd lyssnar pĂ„ "signal"-porten.

Lo0 – pseudogrĂ€nssnittet för loopback. Standard.

Spice_console – En mycket intressant funktion, skillnaden Ă€r att vid implementering av KVM + Spice-protokoll skapas en extra entitet – porten för den virtuella maskinens konsol. Faktiskt, genom att ansluta till denna TCP-port fĂ„r vi konsolen för Vm, utan behov av att ansluta till Vm via dess nĂ€tverksgrĂ€nssnitt. All interaktion med Vm sker genom signalöverföring, serven hanterar detta. Den nĂ€rmaste funktionen som liknar detta Ă€r IPKVM. Det vill sĂ€ga, pĂ„ denna port överförs den virtuella maskinens skĂ€rmbild, detaljer om musens rörelse överförs, och (det viktigaste) interaktionen via Spice-protokollet möjliggör sömlös omdirigering av USB-enheter till den virtuella maskinen, som om enheten var ansluten direkt till Vm. Testat med flash-enheter, skannrar, webbkameror.

Vnet0, virbr0 och virtuella nÀtverkskort i Vm skapar ett nÀtverk av virtuella maskiner.

Hur DETTA fungerar

FrÄn sidan av Klientstationen

Klientstationen startar i grafiskt lÀge frÄn en modifierad live-image av Fedora 28, fÄr en IP-adress via DHCP frÄn nÀtverksutrymmet 169.254.24.0/24. Under uppstarten skapas brandvÀggsregler som möjliggör anslutningar till serverns "signal" och "spice"-portar. Efter att ha slutfört starten vÀntar stationen pÄ godkÀnnande av anvÀndaren "Client". Efter att anvÀndaren godkÀnts startar skrivbordschefen "openbox" och kör skriptet autostart frÄn den inloggade anvÀndarens namn. Bland annat startar autostart-skriptet skriptet 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

Beskrivning av variablerna i filen client.conf
server_ip – adressen till KVM_Server
vdi_signal_port – porten pĂ„ KVM_Server dĂ€r xinetd "sitter"
vdi_spice_port – nĂ€tverksporten pĂ„ KVM_Server, frĂ„n vilken begĂ€ran om anslutning frĂ„n remote-viewer-klienten omdirigeras till spice-porten för den dedikerade Vm (mer information nedan)
animation_folder – katalogen frĂ„n vilken bilder tas för att visa bullshit-anime
background_folder – katalogen frĂ„n vilken bilder tas för att visa presentationer i vĂ€ntelĂ€ge. Mer om animation i nĂ€sta del av artikeln.

Scriptet remote.sh hÀmtar instÀllningar frÄn konfigurationsfilen /etc/client.conf och gör en anslutning till "vdi_signal_port" pÄ KVM-servern med nc, och fÄr en datastream frÄn servern, dÀr den vÀntar pÄ raderna "RULE ADDED, CONNECT NOW". NÀr den sökta raden mottas startas processen remote-viewer i kiosklÀge och skapar en anslutning till "vdi_spice_port" pÄ servern. Scriptets körning pausas tills remote-viewer-processen har avslutats.

Remote-viewer ansluter till "vdi_spice_port"-porten, och genom en omdirigering pÄ serversidan kommer den till "spice_console"-porten pÄ lo0-grÀnssnittet, dvs. till konsolen för den virtuella maskinen, och anvÀndarens arbete pÄbörjas. Under vÀntan pÄ anslutning visas en bullshit-animation för anvÀndaren i form av ett bildspel av jpeg-filer, vars sökvÀg definieras av variabeln animation_folder i konfigurationsfilen.

Vid förlust av anslutningen till "spice_console"-porten för den virtuella maskinen, vilket signalerar att den virtuella maskinen har stÀngts av eller startats om (dvs. det faktiska slutet pÄ anvÀndarsessionen), avslutas alla processer som körs i namnet pÄ den autentiserade anvÀndaren, vilket leder till att lightdm startas om och anvÀndaren Äterförs till inloggningsskÀrmen.

FrÄn KVM-serverns sida

PÄ "signal"-porten av nÀtverkskortet enp5s0 vÀntar xinetd pÄ anslutningar. Efter att ha kopplat upp sig pÄ "signal"-porten kör xinetd scriptet vm_manager.sh utan att ge det nÄgra indata-parametrar och omdirigerar resultatet av scriptets utförande till nc-sessionen 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

Beskrivning av variablerna i konfigurationsfilen vm_manager.conf
srv_scripts_dir — mappen dĂ€r skripten vm_manager.sh, vm_connect.sh, vm_delete.sh, vm_create.sh, vm_clear.sh finns
srv_pool_size — storleken pĂ„ Vm-poolen
srv_start_port_pool — den initiala porten efter vilken placeringen av spice-konsolportarna för de virtuella maskinerna börjar
srv_tmp_dir — mappen för temporĂ€ra filer
base_host — grundlĂ€ggande Vm (guldimage) frĂ„n vilken klonade Vm ska göras i poolen
input_iface — nĂ€tverksgrĂ€nssnittet pĂ„ servern, som vetter mot Client Stations
vdi_spice_port — nĂ€tverksporten pĂ„ servern, frĂ„n vilken begĂ€ran om anslutning frĂ„n remote-viewer-klienten kommer att omdirigeras till spice-porten för den dedikerade Vm
count_conn_tryes — en tidsinstĂ€llning för att vĂ€nta, efter vilken det anses att anslutningen till Vm inte har skett (se detaljer om fungerar i vm_connect.sh)

Skriptet vm_manager.sh lÀser konfigurationsfilen frÄn vm_manager.conf och bedömer tillstÄndet för virtuella maskiner i poolen utifrÄn flera parametrar, nÀmligen: hur mÄnga VM som Àr distribuerade, om det finns lediga, rena VM. För detta lÀser den filen clear.list som innehÄller nummer pÄ "spice_console"-portar för "nyss skapade" virtuella maskiner och kontrollerar om det finns en etablerad anslutning till dem. NÀr en port med en etablerad nÀtverksanslutning upptÀcks (vilket absolut inte borde vara fallet) visas en varning och porten flyttas till waste.list. NÀr den första porten frÄn clear.list upptÀcks som för nÀrvarande inte har nÄgon anslutning anropar vm_manager.sh skriptet vm_connect.sh och överför numret pÄ denna 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

Skriptet vm_connect.sh lÀgger in brandvÀggregler som skapar en omdirigering av "vdi_spice_port" porten frÄn servergrÀnssnittet enp5s0 till "spice console port" VM, belÀgen pÄ lo0-grÀnssnittet av servern, som överförs som en startparameter. Porten flyttas till conn_wait.list, VM anses vÀnta pÄ anslutning. I sessionen Client Station överförs en strÀng "RULE ADDED, CONNECT NOW" till "signal"-porten pÄ servern, vilken vÀntas av det körda skriptet remote.sh. En vÀntescykel pÄbörjas med ett antal försök som bestÀms av vÀrdet av variabeln "count_conn_tryes" frÄn konfigurationsfilen. Varje sekund kommer strÀngen "RULE ADDED, CONNECT NOW" att skickas till nc-sessionen och nÀrvaron av en etablerad anslutning till "spice_console"-porten kontrolleras.

Om det inte har skett nÄgon anslutning under det angivna antalet försök flyttas "spice_console"-porten tillbaka till clear.list. Utförandet av vm_connect.sh avslutas, och vm_manager.sh Äterupptar sitt arbete, som startar rensningscykeln.

Om anslutningen av Client Station till "spice_console"-porten pÄ lo0-grÀnssnittet registreras, tas brandvÀggsreglerna som skapar en omdirigering mellan serverns "spice"-port och "spice_console"-porten bort, och vidare upprÀtthÄllande av anslutningen sker genom brandvÀggens mekanism för tillstÄndsdetektering. Vid avbrott i anslutningen kan anslutningen till "spice_console"-porten inte ÄterstÀllas. "spice_console"-porten flyttas till waste.list, VM:en anses vara "smutsig" och kan inte ÄtergÄ till "rena" virtuella maskiner utan att genomgÄ en rensningsprocess. Utförandet av vm_connect.sh avslutas, och vm_manager.sh fortsÀtter att köras, vilket startar rensningscykeln.

Rensningscykeln inleds med en genomgÄng av filen waste.list dÀr numren pÄ "spice_console"-portarna för virtuella maskiner som haft en anslutning flyttas. Det faststÀlls om det finns en aktiv anslutning pÄ varje "spice_console"-port i listan. Om det inte finns nÄgon anslutning, anses den virtuella maskinen vara oanvÀnd och porten flyttas till recycle.list och processen för att ta bort den virtuella maskinen (se nedan) startas. Om en aktiv nÀtverksanslutning upptÀcks pÄ porten, anses den virtuella maskinen vara i bruk och inga ÄtgÀrder vidtas för den. Om porten inte övervakas, anses VM:n vara avstÀngd och inte lÀngre nödvÀndig. Porten flyttas till recycle.list och processen för att ta bort den virtuella maskinen startas. För detta anropas skriptet vm_delete.sh, dÀr numret pÄ "spice_console"-porten för den VM som ska tas bort skickas 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

Att ta bort en virtuell maskin Àr en ganska trivial operation, skriptet vm_delete.sh identifierar namnet pÄ den virtuella maskinen som porten, som skickas som startparameter, tillhör. Den virtuella maskinen stoppas tvingat, den tas bort frÄn hypervisorn, och den virtuella hÄrddisken för denna VM tas bort. "spice_console"-porten tas bort frÄn recycle.list. Utförandet av vm_delete.sh avslutas, och utförandet av vm_manager.sh Äterupptas.

Skriptet vm_manager.sh, efter att ha avslutat ÄtgÀrderna för att rensa onödiga virtuella maskiner frÄn waste.list, börjar en cykel för att skapa virtuella maskiner i poolen.

Processen börjar med att identifiera tillgÀngliga portar för "spice_console". För detta syfte i enlighet med parametern i konfigurationsfilen "srv_start_port_pool" som anger startporten för poolen "spice_console" virtuella maskiner och parametern "srv_pool_size", som definierar det maximala antalet virtuella maskiner, görs en sekventiell genomgÄng av alla möjliga portalternativ. För varje bestÀmd port görs en sökning i clear.list, waste.list, conn_wait.list, recycle.list. Om porten hittas i nÄgon av dessa filer anses porten vara upptagen och hoppar över. Om porten inte hittas i angivna filer, lÀggs den till i recycle.list och processen för att skapa en ny virtuell maskin startar. För detta anropas skriptet vm_create.sh, dÀr portnumret "spice_console" för vilket VM ska skapas överförs 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 för att skapa en ny virtuell maskin

Skriptet vm_create.sh lÀser vÀrdet av variabeln "base_host" frÄn konfigurationsfilen, vilket definierar mallen för den virtuella maskinen som en klon ska baseras pÄ. Det hÀmtar xml-konfigurationen för VM frÄn hypervisor-databasen, gör en rad kontroller av qcow-bildfilen för VM och vid framgÄngsrika kontroller skapas en xml-konfigurationsfil för den nya VM och en "linked clone" bild av den nya VMs disk. DÀrefter laddas xml-konfigurationsfilen för den nya VM in i hypervisor-databasen och VM startas. Porten "spice_console" flyttas frÄn recycle.list till clear.list. Utförandet av vm_create.sh avslutas och utförandet av vm_manager.sh avslutas.
Vid nÀsta anslutning börjar allt om frÄn början.

För nödsituationer finns ett skript vm_clear.sh som tvingar en genomgÄng av alla VM i poolen och tar bort dem med ÄterstÀllande av vÀrdena i listorna. Att anropa det i uppladdningssteget möjliggör starten av (under)VDI frÄn ett rent blad.

/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="

Jag skulle vilja avsluta första delen av min berÀttelse hÀr. Det som har presenterats bör vara tillrÀckligt för systemadministratörer att prova (under)VDI i praktiken. Om gemenskapen finner detta Àmne intressant kommer jag i andra delen att berÀtta om modifieringen av livecd Fedora och hur den omvandlas till en kiosk.

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster