Server/VDI

Floating VM (VDI VMWare) 환경에서 프린터 관리하는 방법

juhpark 2024. 7. 25. 20:45
반응형

VDI에서 프린터를 관리하는 방법은 여러가지가 있다. dedicate 방식의 VM은 그냥 원하는 프린터를 직접 연결해서 사용하면 되지만, Floating  방식의 VM을 운영하는 경우 마스터이미지에 프린터를 설치할 수 밖에 없는 문제점이 발생한다. 이 경우 모든 사람에게 동일한 프린터가 보여지기 때문에 부서가 나누어져 있는 큰 조직에서는 불편할 수 밖에 없다.

이 문제를 해결하기 위해 다양한 고민을 했었고 그 결과를 공유해 보고자 한다. 

VMWare 환경에서 프린터를 사용하는 3가지 방법

  1. 단말기(또는 PC)에 프린터를 설치하고 접속된 VM에 프린터를 공유하여 단말기의 프린터로 출력할 수 있는 방법이 있다. 문제는 출력보안이 엄격한 회사에서는 사용이 불가능한 방식이다. 재택을 위해 집에서 VM에 접속하는 경우, 집에 설치된 프린터로 출력이 가능하기 때문에 이 방법은 사용을 할 수 없었다. (X)
  2. VM  마스터 이미지에 프린터를 설치하여 배포하는 방법. 이 경우 작은 조직에서는 가능한 일이겠지만, 너무 많은 프린터가 등록 되어 있거나 다른 위치의 프린터로 출력하는 등 보안상 문제점이 있을 수 밖에 없다. (X)
  3. 네트워크에 공유된 프린터를 동적으로 추가하는 방식을 선택하기로 했다. 이 방법을 사용하기 위해서는 어려가지 어려운 점들이 있었는데 아래에서 하나씩 설명하고자 한다. (O)

 

특정 단말기에만 프린터를 동적으로 추가

VM에서 단말기의 IP주소 확인하기

기본적으로 VMWare VM에서는 단말기의 IP를 확인 하는 것이 불가능하다. (보안상 기능을 제공하지 않는다나?...). 그래서 다양하게 검색하고 공부한 결과 IP를 알아내는 몇가지 방식이 있었는데

  1. 단말기에서 아이피를 획득한 다음 VM의 네트워크 공유 폴더에 IP를 저장한 파일을 업로드 하는 방식. 너무 번거롭고 ThinClient를 사용하는 경우 설정이 너무 어려워 진다. (X)
  2. 단말기와 VM사이에 UDP(50002,50003,50004) 포트로 통신이 이루어지는데, 이 패킷을 캡쳐하면 단말기의 IP를 획득할 수 있다고 ChatGPT가 알려주었다. (O)

네트워크 패킷을 캡쳐하기 위해 WinPcap 라이브러리를 설치하고,  Winpcap.au3를 AutoIT설치 디렉토리의 include에 복사해 준다. (아래 링크에서 필요한 프로그램을 다운로드 받아 설치한다)

 

WinPcap - Home

For many years, WinPcap has been recognized as the industry-standard tool for link-layer network access in Windows environments, allowing applications to capture and transmit network packets bypassing the protocol stack, and including kernel-level packet f

www.winpcap.org

 

 

WinPcap Autoit3 UDF

Code: #include #include $winpcap=_PcapSetup() ; initialize winpcap $pcap_devices=_PcapGetDeviceList() ; get devices list _ArrayDisplay($pcap_devices,"Devices list",-1,1) ; display it _PcapFree() ; close winpcap Code: ; initialise the Library $winpcap=_Pcap

opensource.grisambre.net

 

 

실제 네트워크 패킷을 캡쳐하기 위한 프로그램을 만들었다. (cap.au3)  기능은 단순하다. 네트워크 패킷 중 50002, 50003, 50004 포트를 사용하는 IP를 찾아서 ConsoleWrite 로 출력해주는 프로그램이다. 혹시 해당 포트를 사용하는 패킷이 없는 경우에는 5000개까지만 체크하고 에러를 출력한다.

(주의사항) 처음에 5000개까지 체크하도록 하는 로직을 안넣었더니, 무한루프에 빠져서, PC를 엄청 고생시키는 프로그램이 되었고 바이러스 체크 프로그램에서 멀웨어로 검출되는 바람에 고생을 좀 했었다.

#include <Winpcap.au3>


;   --------------------    HOTKEY FUNCTION & VARIABLE --------------------
Global $bHotKeyPressed = False
Func _EscPressed()
    $bHotKeyPressed=True
EndFunc
HotKeySet("{Esc}", "_EscPressed")

;   --------------------   Packet Analysis Function   --------------------
Func MyDissector ($data) 
	Local $macdst=StringMid ($data,3,2)&":"&StringMid ($data,5,2)&":"&StringMid ($data,7,2)&":"&StringMid ($data,9,2)&":"&StringMid ($data,11,2)&":"&StringMid ($data,13,2)
	Local $macsrc=StringMid ($data,15,2)&":"&StringMid ($data,17,2)&":"&StringMid ($data,19,2)&":"&StringMid ($data,21,2)&":"&StringMid ($data,23,2)&":"&StringMid ($data,25,2)
	Local $ethertype=BinaryMid ( $data, 13 ,2 )
	
	If $ethertype="0x0806" Then return "ARP "&$macsrc&" -> "&$macdst

	If $ethertype="0x0800" Then
		Local $src=Number(BinaryMid ($data, 27 ,1))&"."&Number(BinaryMid ($data, 28 ,1))&"."&Number(BinaryMid ($data, 29 ,1))&"."&Number(BinaryMid ($data, 30 ,1))
		Local $dst=Number(BinaryMid ($data, 31 ,1))&"."&Number(BinaryMid ($data, 32 ,1))&"."&Number(BinaryMid ($data, 33 ,1))&"."&Number(BinaryMid ($data, 34 ,1))
		Switch BinaryMid ($data, 24 ,1)
			Case "0x01"
			   return "ICMP "&$src&" -> "&$dst
			Case "0x02"
			   return "IGMP "&$src&" -> "&$dst
			Case "0x06"
				Local $srcport=Number(BinaryMid ($data, 35 ,1))*256+Number(BinaryMid ($data, 36 ,1))
				Local $dstport=Number(BinaryMid ($data, 37 ,1))*256+Number(BinaryMid ($data, 38 ,1))
				Local $flags=BinaryMid ($data, 48 ,1)
				Local $f=""
				If BitAND($flags,0x01) Then $f="Fin "
				If BitAND($flags,0x02) Then $f&="Syn "
				If BitAND($flags,0x04) Then $f&="Rst "
				If BitAND($flags,0x08) Then $f&="Psh "
				If BitAND($flags,0x10) Then $f&="Ack "
				If BitAND($flags,0x20) Then $f&="Urg "
				If BitAND($flags,0x40) Then $f&="Ecn "
				If BitAND($flags,0x80) Then $f&="Cwr "
				$f=StringTrimRight(StringReplace($f," ",","),1)		
				return "TCP("&$f&") "&$src&":"&$srcport&" -> "&$dst&":"&$dstport
			Case "0x11"
				Local $srcport=Number(BinaryMid ($data, 35 ,1))*256+Number(BinaryMid ($data, 36 ,1))
				Local $dstport=Number(BinaryMid ($data, 37 ,1))*256+Number(BinaryMid ($data, 38 ,1))
				;return "UDP "&$src&":"&$srcport&" -> "&$dst&":"&$dstport
				return $dst&":"&$dstport
			Case Else
				return "IP "&BinaryMid ($data, 24 ,1)&" "&$src&" -> "&$dst
		EndSwitch
		return BinaryMid ( $data, 13 ,2 )&" "&$src&" -> "&$dst
	EndIf

	If $ethertype="0x8137" OR $ethertype="0x8138" OR $ethertype="0x0022" OR $ethertype="0x0025" OR $ethertype="0x002A" OR $ethertype="0x00E0" OR $ethertype="0x00FF" Then
		return "IPX "&$macsrc&" -> "&$macdst
	EndIf

	return "["&$ethertype&"] "&$macsrc&" -> "&$macdst
EndFunc

;   --------------------    MAIN PROGRAM CODE   --------------------
$winpcap=_PcapSetup()
If ($winpcap=-1) Then
	MsgBox(16,"Pcap error !","WinPcap not found !")
	exit
EndIf

$pcap_devices=_PcapGetDeviceList()
If ($pcap_devices=-1) Then
	MsgBox(16,"Pcap error !",_PcapGetLastError())
	exit
EndIf

$int = ""
 For $i = 0 to Ubound($pcap_devices)-1
 	$int =$pcap_devices[$i][0] 
Next

$count = 0

Do
	$prom=1
	$filter=""
	$pcap=_PcapStartCapture($int,GUICtrlRead($filter),$prom)
	If ($pcap=-1) Then
		MsgBox(16,"Pcap error !",_PcapGetLastError())
		ContinueLoop
	EndIf
	$linktype=_PcapGetLinkType($pcap)	
	If ($linktype[1]<>"EN10MB") Then
		MsgBox(16,"Pcap error !","This example only works for Ethernet captures")
		ContinueLoop
	Endif
	
	If IsPtr($pcap) Then
		$time0=TimerInit()
		While (TimerDiff($time0)<500) 
			$packet=_PcapGetPacket($pcap)			
			If IsInt($packet) Then ExitLoop			
			$v = MyDissector($packet[3])
			
			if (StringInStr($v, "50002") >0) Then			
				;ConsoleWrite($count & @CRLF)
				ConsoleWrite(StringReplace($v,":50002",""))
				if IsPtr($pcap) Then _PcapStopCapture($pcap)	
				_PcapFree()	
				Exit
			ElseIf (StringInStr($v, "50003") >0) Then			
				;ConsoleWrite($count & @CRLF)
				ConsoleWrite(StringReplace($v,":50003",""))
				if IsPtr($pcap) Then _PcapStopCapture($pcap)
				_PcapFree()	
				Exit				
			ElseIf (StringInStr($v, "50004") >0) Then			
				;ConsoleWrite($count & @CRLF)
				ConsoleWrite(StringReplace($v,":50004",""))
				if IsPtr($pcap) Then _PcapStopCapture($pcap)
				_PcapFree()	
				Exit				
			EndIf			
		WEnd
		$count=$count+1		
		if $count > 5000 Then
				if IsPtr($pcap) Then _PcapStopCapture($pcap)
				_PcapFree()	
				ConsoleWrite("Can't  find host ip" & @CRLF)
				Exit			
		EndIf
	EndIf		
Until $bHotKeyPressed 

if IsPtr($pcap) Then _PcapStopCapture($pcap)
_PcapFree()	
Exit

 

위 au3 파일을 exe 파일로 만드는 aut2exe유틸을 활용해서 실행 프로그램으로 만들었다. /console 옵션이 없는 경우에는 console에 출력이 안되니 꼭 해당 옵션을 추가해야 한다.

aut2exe /in cap.au3 /out cap.exe /console

이렇게 생성된 cap.exe는 vm안에서 실행해보니 IP를 잘 출력하였다.

 

네트워크 프린터 서버 구축

이제는 네트워크 프린터를 제공해야 하는 서버(일반 PC도 가능)를 정하고, 단말기들이 배치될 사무실의 프린터를 설치한다. 그리고, 프린터 공유를 통해 외부에서 네트워크 프린터로 추가 가능하도록 만든다.

\\<컴퓨터 이름 또는 IP>\<프린터 명>

 

마스터 이미지 작업

마스터 이미지에는 위에서 만든 cap.exe 파일을 복사하고, WinPcap 라이브러리를 설치한다. 

 

IP체크 후 네트워크 프린터를 추가하는 스크립트 작성

위에서 만든 cap.exe를 호출하여 IP를 획득하고 특정 IP이면, 네트워크를 추가하는 배치 프로그램을 만들었다. 실제 프린터를 추가하는 것은 PowerShell 커맨드를 활용하여 추가 하였다. 

echo off
REM 파일명 : printer_add.cmd

"c:\windows\changeip\cap.exe" > ip.txt
set /p client_ip=<ip.txt
del ip.txt

set isSmartCenter=false

for %%A in ( "<IP1>" "<IP2>" ) do if %%A=="%client_ip%" ( set isSmartCenter=true )

if %isSmartCenter%==true (
    powershell -Command "& { Add-PrinterPort -Name '\\<서버IP>\DS_Printer' } "
    powershell -Command "& { Add-Printer -Name 'DSPrinter' -DriverName 'HP Color LaserJet M553 PCL 6' -PortName '\\<서버IP>\DS_Printer'  } "
)

위 스크립트를 작업 스케줄러에 로그인시에 걸어주면, IP1, IP2 단말기에서 접속한 경우에만 네트워크 프린터를 추가해서 사용할 수 있다. 위 printer_add.cmd 파일도 마스터 이미지에 복사한 뒤, 작업 스케줄러에 등록한다.

프린터 드라이버는 프린터를 로컬로 그냥 설치한 다음 지우면, 드라이버가 윈도우에 남아있게 된다. 어떤 드라이버가 설치되어 있는지 확인하기 위해서는 PowerShell에서 Get-PrinterDriver 명령을 통해 설치된 드라이버를 확인 할 수 있다.

# PowerShell 프린터 관련 명령어

# 프린터 드라이버 목록 가져오기
Get-PrinterDriver

# 프린터 드라이버 삭제
Remove-PrinterDriver -Name "<Printer Driver Name>"

# 프린터 포트 목록 가져오기
Get-PrinterPort

# 프린터 포트 추가/삭제하기
Add-PrinterPort -Name "<프린터 포트>"
Remove-PrinterPort -Name "<프린터 포트>"

# 프린터 추가/삭제
Add-Printer -Name "<프린터 명>" -DriverName "<드라이버명>" -PortName "<포트 명>"
Remove-Printer -Name "<프린터 명>"

 

반응형