From 6e03863016842e0f5d9108d4a9f57eb9e58af1c4 Mon Sep 17 00:00:00 2001 From: Raphael Dannecker Date: Wed, 2 Jul 2025 13:54:19 +0200 Subject: [PATCH] Refactor VM volume mounting - Replace bind-mounts on /lmn/media/$USER with separate mounting for Home and Share SMB shares in the VM. - Update vm-run to start virtiofsd with /lmn/media/$USER (/home/$USER on localhome machines). - Use vm-vminfo to generate a JSON file containing user information, including Username, Groups, printer list krb5-ticket and some more - Configure vminfo.service (systemd-timer) to periodically call vm-vminfo. - Ensure krb5-ticket (TGT) is injected into the Windows VM. - Mount SMB-Home and SMB-Share shares as part of the new structure. --- misc/vm/Netzlaufwerke neu verbinden.lnk | Bin 0 -> 1244 bytes misc/vm/injector.ps1 | 155 ++++++++++++++++++++++++ misc/vm/vm-prepare-sys.ps1 | 73 +++++++++++ misc/vm/vm-prepare-sys.xml | Bin 0 -> 3452 bytes misc/vm/vm-prepare-user.ps1 | 102 ++++++++++++++++ misc/vm/vm-prepare-user.xml | Bin 0 -> 3468 bytes misc/vm/vm-update-user.ps1 | 30 +++++ misc/vm/vm-update-user.xml | Bin 0 -> 3958 bytes roles/lmn_vm/files/vm-link-images | 5 +- roles/lmn_vm/files/vm-run | 81 +++++++++---- roles/lmn_vm/files/vm-vminfo | 113 +++++++++++++++++ roles/lmn_vm/tasks/main.yml | 25 ++++ 12 files changed, 560 insertions(+), 24 deletions(-) create mode 100755 misc/vm/Netzlaufwerke neu verbinden.lnk create mode 100644 misc/vm/injector.ps1 create mode 100644 misc/vm/vm-prepare-sys.ps1 create mode 100755 misc/vm/vm-prepare-sys.xml create mode 100644 misc/vm/vm-prepare-user.ps1 create mode 100755 misc/vm/vm-prepare-user.xml create mode 100644 misc/vm/vm-update-user.ps1 create mode 100755 misc/vm/vm-update-user.xml create mode 100755 roles/lmn_vm/files/vm-vminfo diff --git a/misc/vm/Netzlaufwerke neu verbinden.lnk b/misc/vm/Netzlaufwerke neu verbinden.lnk new file mode 100755 index 0000000000000000000000000000000000000000..e2fe26b9576e281887f759e2d9824147eac6449f GIT binary patch literal 1244 zcma)6T}V@57=C88l#sR%$?RuEUYVzhI+-ADHh*HBpwg2YZJyzoZr(*u-qeNB zg%<@QjEE@kChJCuqOhyzN{}EH5oD1U2|+hK@0mMtRH|>h-~abM&-=d5SqA_|MG1T0 zx!I%bH%j5*{Ih#<`?JJ?{z1h9fa8kwCYFFxO6WlX=p;5yH#Tx3Du0n-ORjCUT6Z3I4z$ zbXqy8?QvnWUhx8c8%`lZLmT2O2C5yS^q_#xP(h!JgvD!$9_scWCDaX}u4q{e#42Q7 zPV>>2$$#m!lP?SM_$8s3t=hAUl*QPv7%OC+r2%_sG1_jL)J?`ZnzTWh(A`9vyPEHI zaReo@q5!p6v*N~Tid9K~x|a6|#-~8F_4e$l>*}fgNB&h5bEp0_+A_4TCMx}mNd-^7DGu``S>cQ3y|CPm;JNlN~ zx2hfv&YxMn{jc)?ee&-m=i^u(2}hR;9kgiP3C}C I^ki#)0AA1p4gdfE literal 0 HcmV?d00001 diff --git a/misc/vm/injector.ps1 b/misc/vm/injector.ps1 new file mode 100644 index 0000000..4c785cf --- /dev/null +++ b/misc/vm/injector.ps1 @@ -0,0 +1,155 @@ +param( + [string]$ticketb64 +) +# BASE64 +$ticket = New-Object System.Byte +#reading from b64 +$ticket = [System.Convert]::FromBase64String($ticketb64) +if ($ticket -eq $null){ + write-host "[-] Be Sure entering the correct mode" + write-host "[-] Cannot receive ticket from file or b64" + exit; +} + + +# ------------------- FUNCTIONS -----------------------# +$ptt = @" +[StructLayout(LayoutKind.Sequential)] +public struct LUID +{ + public UInt32 LowPart; + public Int32 HighPart; +} +public enum KERB_PROTOCOL_MESSAGE_TYPE +{ + KerbDebugRequestMessage, + KerbQueryTicketCacheMessage, + KerbChangeMachinePasswordMessage, + KerbVerifyPacMessage, + KerbRetrieveTicketMessage, + KerbUpdateAddressesMessage, + KerbPurgeTicketCacheMessage, + KerbChangePasswordMessage, + KerbRetrieveEncodedTicketMessage, + KerbDecryptDataMessage, + KerbAddBindingCacheEntryMessage, + KerbSetPasswordMessage, + KerbSetPasswordExMessage, + KerbVerifyCredentialMessage, + KerbQueryTicketCacheExMessage, + KerbPurgeTicketCacheExMessage, + KerbRefreshSmartcardCredentialsMessage, + KerbAddExtraCredentialsMessage, + KerbQuerySupplementalCredentialsMessage, + KerbTransferCredentialsMessage, + KerbQueryTicketCacheEx2Message, + KerbSubmitTicketMessage, + KerbAddExtraCredentialsExMessage +} +[StructLayout(LayoutKind.Sequential)] +public struct KERB_CRYPTO_KEY32 +{ + public int KeyType; + public int Length; + public int Offset; +} +[StructLayout(LayoutKind.Sequential)] +public struct KERB_SUBMIT_TKT_REQUEST +{ + public KERB_PROTOCOL_MESSAGE_TYPE MessageType; + public LUID LogonId; + public int Flags; + public KERB_CRYPTO_KEY32 Key; + public int KerbCredSize; + public int KerbCredOffset; +} +[StructLayout(LayoutKind.Sequential)] +public struct LSA_STRING_IN +{ + public ushort Length; + public ushort MaximumLength; + public IntPtr buffer; +} +[DllImport("secur32.dll", SetLastError=false)] +public static extern int LsaLookupAuthenticationPackage([In] IntPtr LsaHandle,[In] ref LSA_STRING_IN PackageName,[Out] out UInt32 AuthenticationPackage); +[DllImport("Secur32.dll", SetLastError = true)] +public static extern int LsaCallAuthenticationPackage(IntPtr LsaHandle,uint AuthenticationPackage,IntPtr ProtocolSubmitBuffer,int SubmitBufferLength,out IntPtr ProtocolReturnBuffer,out ulong ReturnBufferLength,out int ProtocolStatus); +[DllImport("secur32.dll", SetLastError=false)] +public static extern int LsaConnectUntrusted([Out] out IntPtr LsaHandle); +[DllImport("secur32.dll", SetLastError=false)] +public static extern int LsaDeregisterLogonProcess([In] IntPtr LsaHandle); +[DllImport("advapi32.dll", SetLastError=true)] +public static extern uint LsaNtStatusToWinError(uint status); +"@ + + +Function ConnectToLsa() +{ +$lsahandle = New-Object System.IntPtr +[int]$retcode = [KRB.PTT]::LsaConnectUntrusted([ref]$lsahandle) +if ($retcode -ne 0){ + write-host "[-] LsaConnectUntrusted Error (NTSTATUS): ", $retcode -ForegroundColor Red + exit; +} +return $lsahandle +} + +#-------------------------------- ENTRY POINT ----------------------------# + +$assemblies = [System.Reflection.Assembly]::LoadWithPartialName("System.Security.Principal") +Add-Type -MemberDefinition $ptt -Namespace "KRB" -Name "PTT" -ReferencedAssemblies $assemblies.location -UsingNamespace System.Security.Principal +# CONNECTING TO LSA +$LsaHandle = ConnectToLsa +write-host "[?] LSA HANDLE: ", $LsaHandle +# EXTRACTING KERBEROS AP +$retcode = New-Object System.Int32 +$authPackage = New-Object System.Int32 +$name = "kerberos" +$importnantlsastring = New-Object KRB.PTT+LSA_STRING_IN +$importnantlsastring.Length = [uint16]$name.Length +$importnantlsastring.MaximumLength = [uint16]($name.Length + 1) +$importnantlsastring.buffer = [System.Runtime.InteropServices.Marshal]::StringToHGlobalAnsi($name) +$retcode = [KRB.PTT]::LsaLookupAuthenticationPackage($lsaHandle,[ref]$importnantlsastring,[ref]$authPackage) +if ($retcode -ne 0){ +write-host "[-] Error LsaLookupAuthPckg (NTSTATUS): ", $retcode -ForegroundColor Red +exit; +} +write-host "[?] Kerberos Package: ", $authPackage +# GETTING CURRENT LUID (INJECT PURPOSES) +$output = klist +$CurrLuid = $output.split("`n")[1].split(":")[1] +$sysIntCurrLuid = [convert]::ToInt32($CurrLuid,16) +$luidFinally = New-Object KRB.PTT+LUID +$luidFinally.LowPart = $sysIntCurrLuid + +# TICKET INJECTING +$protocolReturnBuffer = New-Object System.IntPtr +$ReturnBufferLength = New-Object System.Int32 +$ProtocolStatus = New-Object System.Int32 +$KrbRequestInfo = New-Object KRB.PTT+KERB_SUBMIT_TKT_REQUEST +$KrbRequestInfoType = $KrbRequestInfo.getType() +$KrbRequestInfo.MessageType = [KRB.PTT+KERB_PROTOCOL_MESSAGE_TYPE]::KerbSubmitTicketMessage +$KrbRequestInfo.KerbCredSize = $ticket.Length +$KrbRequestInfo.KerbCredOffset = [System.Runtime.InteropServices.Marshal]::SizeOf([type]$KrbRequestInfoType) +$KrbRequestInfo.LogonId = $luidFinally +$inputBufferSize = [System.Runtime.InteropServices.Marshal]::SizeOf([type]$KrbRequestInfoType) + $ticket.Length +$inputBuffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($inputBufferSize) +[System.Runtime.InteropServices.Marshal]::StructureToPtr($KrbRequestInfo,$inputBuffer,$false) +[System.IntPtr]$PtrToCred = $inputBuffer.ToInt64() + $KrbRequestInfo.KerbCredOffset +[System.Runtime.InteropServices.Marshal]::Copy($ticket,0,$PtrToCred,$ticket.Length) +$ntstatus = [KRB.PTT]::LsaCallAuthenticationPackage($lsaHandle,$authPackage,$inputBuffer,$inputBufferSize,[ref]$protocolReturnBuffer,[ref]$ReturnBufferLength,[ref]$ProtocolStatus) +if(($ProtocolStatus -ne 0) -or ($ntstatus -ne 0)) +{ + Write-Host "[!] Error in LsaCallAuthenticationPackage" -ForegroundColor Red + write-host " NTSTATUS: ", $ntstatus, " Protocol Status: ", $ProtocolStatus + if ($ProtocolStatus -eq -1073741517){ + " Ticket may be out of date" + } + exit; +} +if($inputBuffer -ne [System.IntPtr]::Zero) +{ + [System.Runtime.InteropServices.Marshal]::FreeHGlobal($inputBuffer) + [System.Object]$ticket = $null +} +klist diff --git a/misc/vm/vm-prepare-sys.ps1 b/misc/vm/vm-prepare-sys.ps1 new file mode 100644 index 0000000..ad8f16f --- /dev/null +++ b/misc/vm/vm-prepare-sys.ps1 @@ -0,0 +1,73 @@ +# Installiere alle Mounts aus target.csv +# Geprüft wird, ob das Laufwerk bereits vorhanden +# 11.05.2025 da + +function Mount-Drive { + param ( + [string]$DriveLetter, + [string]$TargetPath + ) + + try { + & "C:\Program Files (x86)\WinFsp\bin\launchctl-x64.exe" start virtiofs viofs$DriveLetter $TargetPath \\.\${DriveLetter}: + Write-Verbose "Laufwerk hinzugefügt: $DriveLetter" + } catch { + Write-Error "Fehler beim Hinzufügen des Laufwerks ${DriveLetter}: $_" + } +} + +function Import-VMInfo { + param ( + [string]$Path + ) + + if (Test-Path $Path) { + return Get-Content -Path $Path -Raw | ConvertFrom-Json + } else { + Write-Error "Fehler beim Einlesen der VMInfo Datei ($Path nicht gefunden)." + Write-Error "Tipp: Beim Neustart der VM wird diese Datei neu angelegt." + Pause + exit + } +} + +# Laufwerk Y: mit weiteren Mountpoint-Infos mounten +& "C:\Program Files\Virtio-Win\VioFS\virtiofs.exe" -m Y: +#Mount-Drive -DriveLetter "Y" -TargetPath "VM-Data" + +# VMInfo aus JSON File einlesen +$VMInfoPath = "Y:\.vminfo.json" +# Schleife, die auf das Laufwerk wartet +while (-not (Test-Path $VMInfoPath)) { + Write-Host "Warte auf $VMInfoPath..." + Start-Sleep -Seconds 1 +} +$VMInfo = Import-VMInfo -Path $VMInfoPath + +# Weitere Laufwerke einbinden +#foreach ($virtiofs in $VMInfo.VirtioFS) { +# $targetDrive = $virtiofs.Drive +# if (-not (Get-PSDrive -Name $targetDrive -ErrorAction SilentlyContinue)) { +# Mount-Drive -DriveLetter $targetDrive -TargetPath $virtiofs.Target +# } else { +# Write-Error "Laufwerk bereits vorhanden: $targetDrive" +# } +#} + +# Drucker installieren +foreach ($drucker in $VMInfo.Printers) { + # Überprüfen, ob der Drucker bereits vorhanden ist + $druckerName = $drucker.Name + $druckerVorhanden = Get-Printer | Where-Object { $_.Name -eq $druckerName } + + # Umwandlung in HTTP-URL + $httpUrl = $drucker.IppURL -replace "ipp://", "http://" -replace "122.1", "122.1:631" + + if (-not $druckerVorhanden) { + # Drucker hinzufügen, wenn er nicht vorhanden ist + Add-Printer -PortName $httpUrl -Name $druckerName -DriverName "Microsoft IPP Class Driver" + Write-Host "Drucker hinzugefuegt: $druckerName" + } else { + Write-Host "Drucker bereits vorhanden: $druckerName" + } +} diff --git a/misc/vm/vm-prepare-sys.xml b/misc/vm/vm-prepare-sys.xml new file mode 100755 index 0000000000000000000000000000000000000000..b00a30e041ee65f27e81e0312472335c4f141ebf GIT binary patch literal 3452 zcmbuCTT>cA5QY0WRrwE;?;sKrV~SwmB}z=9r5Im$A+jPRpv7WL{(O?}%sA}MqJ)^* z+6yy1r@K#IX88T9Zd>-lMmDsPeYeCWmfFB>Y;5ms#n!B1Ro*3L#&*GYkJ+*H-RzmQ z?Sqw(dV|cCHSDFmusWk%P_CauTz80G>ljICy0R(Pt-Z5Vt`uEY=o~qu3hx0U6ZEIt zmq=EeH6vv2okcyy@0ne-8WvY=ov$k2t8VR-_w>Q4l?OXZ*jKj`wDdtpnV%prd=;ap|eqYy7X`f7yMv;i85QReQy=h7}dtaB`dW);5u@V@ZY~zB25l z-J$;qtrPHfZO=~a5H^qO82m%qv3-7>*;o5$9p0&nmk{HXG3psUw!rHU-w`;speH=L z?p+4uC$~_7WLU4U<@r(EC$QJ&@_AI`ZX3^T@GzE(r98`fu-~1B0uMO`;6T~H-GD5t16SdUB|aD8{}=D8P!8#U%2lb zJO7Fjd$|jGipC+Add8{KKjGP;;*Zd`&8)mo77uvJk4v{gQ4tT%!-(yW-WxJZTKD<# zoIRyq-6>SSzpae5HfMex;L(H(Y&$>IVO4YTIWE|PS0H*A3u_jMNXGn6YKGH zAkK#5irfjEzW(P{~>a+IIL4YEB3fWa(V3@RXsYzYsGWw zGB{utIbI~0$5oC8jKzM{;ds81AoSx6rB;7s~T+58E#!prNq{sNs4;^7cI8U?@DLbF`$mr_j>5} z=dhmZ+rC4IZJn6LNW`7uc?hu;=VZ(^$ZdG=ycWHFOd`hE<3yWaxlU7_xi;?+W1rl6 zz<7h5rp?)`#?Gm1Uv5pT$M2TvG(yyVwc)Vl$HO{ldb*xeOBXYnQ%;6EurOvM!K3M4 zPZ#x>7ul|zof(8R%2nhJC)AFeW4kKUpPoBdr_-P`jyS{C<|MQ~-!nTwQl|~ozD}0@ zpF)S64fE-2zT)inJ7>{~z)WUm6YM^*Q@rH6cHi;?LO!O_6ix~y&WbD=aAQ+sV2Tm`z$(K&IX7S9nY zGxQhSXGpf(nhCPkZqb1CYj!uRgT-yzeO>#4mLUiQ`!giw zyvzFo?lazkPm&GtyDpQlEcI+*G9ofJ3&iXtHkCzbBlPT&EJ|2MAC%zMK zrl4p1p7^tg&JrieMg(E6&yXTNgTEnH$fP=ZAMkDH{HBtrTwr>S2%Ok|WM)RzZFzjY z@-Ca|d(qx-a>E|hBb3yJ0v!>j3*+)cIX`l)#;{cG2R+L&4WtL2&((-jGp>yGTQpq4 z{*>s*ze8g4j9VG?oZQjd<|1S2veC{G?F&dberI0AWAOB@I-D-Rn$&B()^`>B4b>XX-L<*j!j)lzbL>|H&lUtNK& ze2f)d=XAX@sh&^+;%rR5D1OS4IZ;*4T&$t>F$k#*s%2H#>f8_cA0sD=<2iM9%Q*Q`x}#cIfJ2Tk}(nMA|7HhmcxvOvYS*+=GX}Yu)R+BvOn$=GzR* zHCqMddORnreemaq^)0?MJtnn|?JwqwbP7M^>|Kob$>mnmQHVN zE|?Q{U?FEE!=w3wQ%095DR@zSwaa-ySd&Uc?l7tv@JP|R%bKdql38XwoUFz&L`3WHf2A$^ E9~=)fO8@`> literal 0 HcmV?d00001 diff --git a/misc/vm/vm-update-user.ps1 b/misc/vm/vm-update-user.ps1 new file mode 100644 index 0000000..c3036d5 --- /dev/null +++ b/misc/vm/vm-update-user.ps1 @@ -0,0 +1,30 @@ +# Injects krb5-credential from .vminfo.json if available +# 02.07.2025 da + +function Import-VMInfo { + param ( + [string]$Path + ) + + if (Test-Path $Path) { + return Get-Content -Path $Path -Raw | ConvertFrom-Json + } else { + Write-Error "Fehler beim Einlesen der VMInfo Datei ($Path nicht gefunden)." + Write-Error "Tipp: Beim Neustart der VM wird diese Datei neu angelegt." + Pause + exit + } +} + +$VMInfoPath = "Y:\.vminfo.json" + +# Schleife, die auf das Laufwerk wartet +if (-not (Test-Path $VMInfoPath)) { + Write-Host "$VMInfoPath nicht gefunden. Skript beenden." + exit +} + +# VMInfo aus JSON File einlesen +$VMInfo = Import-VMInfo -Path $VMInfoPath + +& $PSScriptRoot\injector.ps1 $VMInfo.krb5.cred diff --git a/misc/vm/vm-update-user.xml b/misc/vm/vm-update-user.xml new file mode 100755 index 0000000000000000000000000000000000000000..b6500e8d23b2a0dfcc40a32b841e505cbc0a0d15 GIT binary patch literal 3958 zcmbuC-BKGz5QY0XRe1-M?-2r%5T`bF$%q))CU$`cH@YFL02Pv;vXJ-HXC$l&9#l|+WhCSNArdHU{?rdVe*$dmTrnR^>kWB27=Q+}e zT|4QS_3U@sLhEOA-q>sV(SERurx#ecU1@R8S@d2fXv))#m3;2)SKH=O;OhpTV`r(! zb;y$`{tLcWXf_?1F}gE{DChYM>9*~HxMh3XwYcAQYfG->9My|C&Ib50JHg8}77FB3 zG)nFjeV6YQcRnWRdi{>m8Sq?wcI_wPZxR2NyYIopE-|$1CBM6%XxhHhdu6}aE3`9^ zENqCch3&RI;Qt1%Q|x!_&`#|GY<{xO*#BS$_KtsN_NTqKKG(wKOBU0Wsns)L>|(D^ ze#h8(faQE1h5jw91kJEsmN(}|d7r}GH6LF`RqlF7$H-E>RLiq0ulIbFjgXPLp4U~T zO6T#Bs76Hk84o*lxOm-+*$Jl{z{_dSQRT;=o0$o_LtYTb0#U zpRiU;-I3)Et4916TAz<&$J#2L*qU&Et5&?us(Q=tDVb*0kgTdM3Xr5;>N*Xyb2pFT zj*2HnsG5vk)I3r*bsblycUqy==B3_2N4~jE$%k@u(Up$*C&lcB#*FB0tAMib0*M9$R1e>5d`9VO`iM zV#Y&$m+8gc!yHiG1is@n)vDDVxk@v(t4aj2(728BV>TxT)A5601Lwj#w- z-AQ75Rav_7h?>j!3_w!lBbBA=>Xi`~sajd5nc7@EHN=>*Gp2IYfo=APdskBh8T04M zs^9->Eyd=~hG*ay(np$lIsW~bI(Jjs&nL01)9(b0w1b~#Aw_Xc#oS`K2M?atYSha# zQX7Z7oeV&(ld)$moD4qlUvsVmxlS1S?79BTx7nK1kKZdbX=G8m)xO)E>d(VE_0{Tr zQZHS8#awX8KY#^)QvJenz4NR)V`QBEedLQhTNA=h3E3J)9eKdHuW#pI_vbIY3H3qu z`u|t+m?D?ZwdabuTRYedGS@=L{GRBeHBGuW`VPf?Q3$UQ@>)@ z8DeX0AyoK^XC)3b@mZ&TS;7+OHb$ykQ}>seHHx8liX>m-o4zjB0rjwVr>08 gP!q2O(JQuP$m95=Q_qlJ8Ry++PtSWUqFWgF51Uqfz5oCK literal 0 HcmV?d00001 diff --git a/roles/lmn_vm/files/vm-link-images b/roles/lmn_vm/files/vm-link-images index e4c8618..0eccd04 100755 --- a/roles/lmn_vm/files/vm-link-images +++ b/roles/lmn_vm/files/vm-link-images @@ -19,8 +19,9 @@ done shift "$((OPTIND -1))" # link system-VM-Images to User VM Directory -for i in *.qcow2; do - [[ -f "${VM_DIR}/${i}" ]] || ln "${i}" "${VM_DIR}/${i}" +for filename in "$@"; do + filename="$(basename ${filename})" + [[ -f "${VM_DIR}/${filename}" ]] || ln "${filename}" "${VM_DIR}/${filename}" done # allow lmnsynci to remove old vm images diff --git a/roles/lmn_vm/files/vm-run b/roles/lmn_vm/files/vm-run index 5307c68..fd58fca 100755 --- a/roles/lmn_vm/files/vm-run +++ b/roles/lmn_vm/files/vm-run @@ -90,17 +90,21 @@ create_clone() { local VM_NAME="$1" if ! [[ -f "${VM_SYSDIR}/${VM_NAME}.qcow2" || -f "${VM_DIR}/${VM_NAME}.qcow2" ]]; then - echo "qcow2 File does not exists." >&2 - exit 1 + echo "qcow2 File does not exists." >&2 + exit 1 fi # Create User-VM-Dir and link system VM-Images [[ -d "${VM_DIR}" ]] || mkdir -p "${VM_DIR}" - if [[ "${PERSISTENT}" -eq 1 ]]; then - sudo /usr/local/bin/vm-link-images -p - else - sudo /usr/local/bin/vm-link-images - fi + IMAGE="${VM_NAME}.qcow2" + while [[ -n ${IMAGE} ]]; do + if [[ "${PERSISTENT}" -eq 1 ]]; then + sudo /usr/local/bin/vm-link-images -p "${IMAGE}" + else + sudo /usr/local/bin/vm-link-images "${IMAGE}" + fi + IMAGE="$(qemu-img info -U "${VM_DIR}/${IMAGE}" | grep "^backing file:" | cut -d ' ' -f 3)" + done # Create backing file cd "${VM_DIR}" @@ -130,18 +134,30 @@ create_printerlist() { } create_mountlist() { - if id | grep -q teachers; then - NETHOME=/srv/samba/schools/default-school/teachers/$USER - else - NETHOME=(/srv/samba/schools/default-school/students/*/"$USER") - fi - NETHOME="${NETHOME#/srv/samba/schools}" - cat << EOF > "/lmn/media/${USER}/.mounts.csv" + NETHOMEPART="${NETHOME#/srv/samba/schools}" + cat << EOF > "${VMINFO_DIR}/.mounts.csv" Drive;Remotepath -H;\\\\10.190.1.1${NETHOME//\//\\} -T;\\\\10.190.1.1\default-school\share +H;\\\\server.pn.steinbeis.schule${NETHOMEPART//\//\\} +T;\\\\server.pn.steinbeis.schule\\default-school\\share EOF - echo "${USER}" > "/lmn/media/${USER}/.user" + echo "${USER}" > "/${VMINFO_DIR}/.user" +} + +start_virtiofs_service() { + local target_name=$1 + local shared_dir=$2 + local drive_letter=$3 + local socket="/run/user/${UID}/virtiofs-${VM_NAME}-${target_name,,}.sock" + + systemd-run --user /usr/local/bin/virtiofsd --uid-map=":${GUEST_UID}:${UID}:1:" --gid-map=":${GUEST_GID}:$(id -g):1:" \ + --socket-path "${socket}" --shared-dir "${shared_dir}" --syslog + + if [[ $? -ne 0 ]]; then + echo "Error starting virtiofsd for ${target_name}." >&2 + return 1 + fi + + LIBVIRTOPTS="${LIBVIRTOPTS} --filesystem driver.type=virtiofs,accessmode=passthrough,target.dir=${target_name},xpath1.set=./source/@socket=${socket}" } start_virtiofsd() { @@ -151,9 +167,16 @@ start_virtiofsd() { [[ "$GUEST_GID" == 0 ]] && GUEST_GID=1010 fi # END temporary fix - socket="/run/user/$(id -u $USER)/virtiofs-${VM_NAME}.sock" - systemd-run --user /usr/local/bin/virtiofsd --uid-map=:${GUEST_UID}:${UID}:1: --gid-map=:${GUEST_GID}:$(id -g):1: \ - --socket-path "$socket" --shared-dir "/lmn/media/${USER}" --syslog + + # start_virtiofs_service "VM-Data" "/lmn/media/${USER}" "Y" + # start_virtiofs_service "default-school" "/srv/samba/schools/default-school" "Y" + + # Home@PC / VM-Data + if [[ "${HOME}" != "${NETHOME}" ]]; then + start_virtiofs_service "Home_Linux" "${HOME}" "Y" + else + start_virtiofs_service "VM-Data" "/lmn/media/${USER}" "Y" + fi } ask_really_persistent() { @@ -316,18 +339,33 @@ if ! virsh --connect="${QEMU}" list | grep "${VM_NAME}-clone"; then check_images fi if [[ "${NEWCLONE}" = 1 ]] || [[ ! -f "${VM_DIR}/${VM_NAME}-clone.qcow2" ]]; then - create_clone "${VM_NAME}" + create_clone "${VM_NAME}" fi # delete the old vm virsh --connect=qemu:///session undefine --nvram "${VM_NAME}-clone" || echo "${VM_NAME}-clone did not exist" #trap exit_script SIGHUP SIGINT SIGTERM + if id | grep -q teachers; then + NETHOME=/srv/samba/schools/default-school/teachers/$USER + else + NETHOME=(/srv/samba/schools/default-school/students/*/"$USER") + fi + if [[ "${HOME}" != "${NETHOME}" ]]; then + VMINFO_DIR="${HOME}" + else + VMINFO_DIR="/lmn/media/${USER}" + fi create_printerlist create_mountlist # start virtiofsd-service [[ "${QEMU}" = 'qemu:///session' ]] && start_virtiofsd + # Create VMInfo Json file + #( umask 027; ./vm-create-vminfo > "${VMINFO_DIR}/.vminfo.json" ) + # Start vminfo.timer + systemctl --user start vminfo.timer + uuid=$(openssl rand -hex 16) uuid="${uuid:0:8}-${uuid:8:4}-${uuid:12:4}-${uuid:16:4}-${uuid:20:12}" @@ -349,7 +387,6 @@ if ! virsh --connect="${QEMU}" list | grep "${VM_NAME}-clone"; then --memorybacking source.type=memfd,access.mode=shared \ --disk "${VM_DIR}/${VM_NAME}-clone.qcow2",driver.discard=unmap,target.bus=scsi,cache=writeback \ --network=bridge=virbr0,model.type=virtio \ - --filesystem driver.type=virtiofs,accessmode=passthrough,target.dir=virtiofs,xpath1.set=./source/@socket="/run/user/${UID}/virtiofs-${VM_NAME}.sock" \ --controller type=scsi,model=virtio-scsi \ --check path_in_use=off \ --connect="${QEMU}" \ diff --git a/roles/lmn_vm/files/vm-vminfo b/roles/lmn_vm/files/vm-vminfo new file mode 100755 index 0000000..f19b4ce --- /dev/null +++ b/roles/lmn_vm/files/vm-vminfo @@ -0,0 +1,113 @@ +#!/usr/bin/python3 + +import argparse +import struct +import subprocess +import json + +from os import environ,path +from impacket.krb5.ccache import CCache +from base64 import b64encode + +home = "" +nethome = "" +vminfo = {} + +def get_printers(): + printers = [] + try: + result = subprocess.run(['lpstat', '-v'], capture_output=True, text=True, check=True) + for line in result.stdout.splitlines(): + # Extrahiere den Druckernamen + printer_name = line.split()[2].rstrip(':') + ipp_url = f"ipp://192.168.122.1/printers/{printer_name}" + printer = { 'Name': printer_name, 'IppURL': ipp_url } + printers.append(printer) + return printers + except subprocess.CalledProcessError as e: + print(f"Fehler beim Abrufen der Drucker: {e}") + return [] + +def get_groups(username): + try: + result = subprocess.run(['id', '-Gnz', username], capture_output=True, text=True, check=True) + groups = result.stdout.strip().split('\0') + return groups + except subprocess.CalledProcessError as e: + print(f"Fehler beim Abrufen der Gruppen: {e}") + return [] + +def get_krb5 (): + krb5 = {} + ccachefilename = environ.get('KRB5CCNAME').replace('FILE:', '') + if ccachefilename: + try: + ccache = CCache.loadFile(ccachefilename) + cred = ccache.toKRBCRED() + cred_enc = b64encode(cred) + krb5['cred'] = cred_enc.decode('utf-8') + krb5['starttime'] = ccache.credentials[0]['time']['starttime'] + krb5['endtime'] = ccache.credentials[0]['time']['endtime'] + krb5['renew_till'] = ccache.credentials[0]['time']['renew_till'] + except: + print("Fehler beim Ticket laden") + return krb5 + +def get_mounts(): + mounts = [] + mounts.append({ 'Drive': 'H', 'RemotePath': '\\\\server.pn.steinbeis.schule' + nethome.replace('/srv/samba/schools','').replace('/','\\'), 'Name': 'Home_Server' }) + mounts.append({ 'Drive': 'T', 'RemotePath': '\\\\server.pn.steinbeis.schule\default-school\share', 'Name': 'Tausch' }) + return mounts + +def get_user_folders(): + HOME="H:" + if environ.get('HOME') != nethome: + HOME="Y:" + folders = [] + folders.append( {'Name': 'Personal', 'Path': f"{HOME}\Dokumente"} ) + folders.append( {'Name': 'My Pictures', 'Path': f"{HOME}\Bilder"} ) + folders.append( {'Name': 'My Music', 'Path': f"{HOME}\Musik"} ) + folders.append( {'Name': 'My Video', 'Path': f"{HOME}\Videos"} ) + return folders + +def get_quickaccess(): + quickaccess = [] + quickaccess.append( 'H:\\transfer' ) + return quickaccess + +def parse_args(): + parser = argparse.ArgumentParser() + #parser.add_argument('input_file', help="File in kirbi (KRB-CRED) or ccache format") + #parser.add_argument('output_file', help="Output file") + return parser.parse_args() + + +def main(): + global home, nethome + + args = parse_args() + + home = environ.get('HOME') + + vminfo['User'] = environ.get('USER') + vminfo['Groups'] = get_groups(environ.get('USER')) + + if 'teachers' in vminfo['Groups']: + nethome = f"/srv/samba/schools/default-school/teachers/{vminfo['User']}" + else: + result = subprocess.run(['find', '/srv/samba/schools/default-school/students/', '-name', vminfo['User'], '-maxdepth', '2', '-type', 'd'], capture_output=True, text=True, check=False) + nethome = result.stdout + + vminfo['Printers'] = get_printers() + vminfo['krb5'] = get_krb5() + vminfo['Mounts'] = get_mounts() + vminfo['UserShellFolders'] = get_user_folders() + vminfo['QuickAccess'] = get_quickaccess() + + vminfo_json = json.dumps(vminfo, ensure_ascii=False, indent=4) + print(vminfo_json) + +if __name__ == '__main__': + main() + + diff --git a/roles/lmn_vm/tasks/main.yml b/roles/lmn_vm/tasks/main.yml index 8ba0131..fd5bace 100644 --- a/roles/lmn_vm/tasks/main.yml +++ b/roles/lmn_vm/tasks/main.yml @@ -20,6 +20,7 @@ - virt-manager - virt-viewer - dialog # for vm-netboot menu + - python3-impacket # - name: allow all users to use VMs # lineinfile: @@ -124,6 +125,7 @@ - vm-sync - vm-link-images - vm-virtiofsd + - vm-vminfo - virtiofsd - vm-aria2 - uploadseed @@ -211,3 +213,26 @@ src: vm-netboot dest: /usr/local/bin/ mode: '0755' + +- name: Provide vminfo service + ansible.builtin.copy: + content: | + [Unit] + Description=Create .vminfo.json for VMs + [Service] + Type=simple + ExecStart=/usr/bin/bash -c 'umask 027; /usr/local/bin/vm-vminfo > "{% if localhome %}~{% else %}/lmn/media/${USER}{% endif %}/.vminfo.json"' + dest: /etc/systemd/user/vminfo.service + mode: '0644' + +- name: Provide vminfo timer + ansible.builtin.copy: + content: | + [Unit] + Description=Timer for vm-info + [Timer] + OnActiveSec=0s + OnUnitActiveSec=1h + Persistent=true + dest: /etc/systemd/user/vminfo.timer + mode: '0644'