Prueba de Puerto PowerShell: Una Guía de Herramientas Personalizada

Vamos a adentrarnos en cómo construir una herramienta de prueba de puerto de PowerShell que te permita probar puertos abiertos por número de puerto y etiqueta.

Para construir un script robusto que no se vaya a caer y morir en la mitad de tus servidores, es importante primero asegurarse de que se cumplan los requisitos previos necesarios para obtener el resultado final.

¿Cuáles son estos requisitos previos? Los requisitos previos son servicios como FTP, HTTP, DCOM, WMI, WSMAN, etc. La conexión que estás intentando hacer a un servidor típicamente depende de servicios como estos.

Existe una jerarquía de varios niveles de comprobaciones que puedes realizar en tus servidores antes de intentar realizar una conexión, dependiendo de qué tan minucioso quieras ser.

Para empezar, tienes toda la pila OSI para atravesar en el lado de la red. Eso no incluye los servicios en el sistema host desde el cual estás ejecutando el script y todo lo demás en el sistema host remoto.

Una de las primeras pruebas que debes realizar al consultar un servidor remoto es verificar que los puertos de red apropiados estén abiertos y accesibles. Dependiendo de los servicios que se ejecuten en el servidor remoto, dependerán los puertos que necesitas consultar.

I always used to either not even attempting to test port connections or fumbling around with finding that latest Test-Port script I had scavenged somewhere. Once I found it to figure out what ports I actually needed to a test ahead of time. Today was the final straw.

I needed to query a set of domain controllers before running some CIM queries against them. I went about my normal fumbling around and decided enough was enough and sat down and built my own, fully featured port testing script.

Con la ayuda de este script de Technet, logré crear un buen par de funciones de PowerShell que no solo te permitirán probar puertos TCP y UDP abiertos, sino también probar grupos de puertos por rol de servidor. ¡No más buscar en Google cada vez para saber qué puertos usa qué servicio!

Concedido, especialmente para Active Directory, los puertos pueden variar según el sistema operativo del servidor, varios servicios en un controlador de dominio, etc. Siéntase libre de ajustarlos según sea necesario para su entorno.

Aquí hay una captura de pantalla de un ejemplo de uso:

Testing ports with PowerShell

Como puede ver, puede especificar tantos servidores como desee y generará una lista ordenada de objetos divididos por el grupo de puertos de servicio y el puerto para cada computadora. ¡Hasta ahora ha sido muy útil! ¡Espero que le saques tanto provecho como yo!

function Test-ServerRolePortGroup {
	<#
	.SYNOPSIS
		Esta función prueba los puertos TCP/UDP abiertos según la función del servidor.
	.DESCRIPTION
		Esta función prueba todos los puertos TCP/UDP apropiados según la función del servidor, para que no tengas
		que memorizar o buscar todos los puertos que deben ser probados cada vez
		que desees verificar la conectividad remota en un servidor específico.
	.NOTES
		Referencias de puertos:
		http://technet.microsoft.com/en-us/library/dd772723(v=ws.10).aspx
		http://en.wikipedia.org/wiki/Server_Message_Block
		http://technet.microsoft.com/en-us/library/cc940063.aspx
	.PARAMETER Computername
		Uno o más nombres de computadoras remotos, separados por comas.
	.PARAMETER ServerRole
		Los servicios en la computadora para los que deseas encontrar puertos abiertos. Pueden ser
		servicios comunes como WinRm, Smb, Dns, Active Directory y NetBIOS.
	.EXAMPLE
		PS> Test-ServerRolePortGroup -Computername 'LABDC','LABDC2' -ServerRole NetBIOS,WinRm,Dns
		
		Este ejemplo prueba los puertos de red necesarios para NetBIOS, WinRm y Dns
		para operar en los servidores LABDC y LABDC2.
	#>
	
	[CmdletBinding()]
	[OutputType([System.Management.Automation.PSCustomObject])]
	param (
		[Parameter(Mandatory)]
		[ValidateScript({ Test-Connection -ComputerName $_ -Count 1 -Quiet})]
		[string[]]$Computername,
		[Parameter(Mandatory)]
		[ValidateSet('WinRm','Smb','Dns','ActiveDirectoryGeneral','ActiveDirectoryGlobalCatalog','NetBios')]
		[string[]]$ServerRole
	)
	begin {
		$PortGroups = @{
			'WinRm' = @{ 'TCP' = 5985}
			'Smb' = @{ 'TCP' = 445; 'UDP' = 445 }
			'Dns' = @{ 'TCP' = 53; 'UDP' = 53 }
			'ActiveDirectoryGeneral' = @{ 'TCP' = 25, 88, 389, 464, 636, 5722, 9389; 'UDP' = 88,123,389,464 }
			'ActiveDirectoryGlobalCatalog' = @{ 'TCP' = 3268, 3269 }
			'NetBios' = @{ 'TCP' = 135, 137, 138, 139; 'UDP' = 137,138,139 }
		}
	}
	process {
		foreach ($Computer in $Computername) {
			Write-Verbose "Beginning port tests on computer '$Computer'"
			try {
				$TestPortGroups = $PortGroups.GetEnumerator() | where { $ServerRole -contains $_.Key }
				Write-Verbose "Found '$($TestPortGroups.Count)' port group(s) to test"
				foreach ($PortGroup in $TestPortGroups) {
					$PortGroupName = $PortGroup.Key
					$PortGroupValues = $PortGroup.Value
					foreach ($Value in $PortGroupValues.GetEnumerator()) {
						$Protocol = $Value.Key
						$Ports = $Value.Value
						$TestResult = Test-Port -ComputerName $Computer -Protocol $Protocol -Port $Ports
						$TestResult | Add-Member -MemberType 'NoteProperty' -Name 'PortSet' -Value $PortGroupName
						$TestResult
					}
				}
			} catch {
				Write-Verbose "$($MyInvocation.MyCommand.Name) - Computer: $Computer - Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)"
				$false
			}
		}
	}
}

function Test-Port {
	<#
	.SYNOPSIS
		Esta función prueba los puertos TCP/UDP abiertos.
	.DESCRIPTION
		Esta función prueba cualquier puerto TCP/UDP para ver si está abierto o cerrado.
	.NOTES
		Problema conocido: Si esta función se llama de manera consecutiva de 10 a 20 veces en el mismo puerto
			y computadora, la verificación del puerto UDP mostrará $false cuando puede ser
			$true. No he descubierto por qué sucede esto.
	.PARAMETER Computername
		Uno o más nombres de computadoras remotos, separados por comas.
	.PARAMETER Port
		Uno o más números de puerto separados por comas que te gustaría probar.
	.PARAMETER Protocol
		El protocolo (UDP o TCP) que estarás probando.
	.PARAMETER TcpTimeout
		El número de milisegundos que la función esperará antes de declarar
		que el puerto TCP está cerrado.
	.PARAMETER
		El número de milisegundos que la función esperará antes de declarar
		que el puerto UDP está cerrado.
	.EXAMPLE
		PS> Test-Port -Computername 'LABDC','LABDC2' -Protocol TCP 80,443
		
		Este ejemplo prueba los puertos de red TCP 80 y 443 en ambos servidores LABDC
		y LABDC2.
	#>
	[CmdletBinding(DefaultParameterSetName='TCP')]
	[OutputType([System.Management.Automation.PSCustomObject])]
	param (
		[Parameter(Mandatory)]
		[string[]]$ComputerName,
		[Parameter(Mandatory)]
		[int[]]$Port,
		[Parameter(Mandatory)]
		[ValidateSet('TCP', 'UDP')]
		[string]$Protocol,
		[Parameter(ParameterSetName='TCP')]
		[int]$TcpTimeout = 1000,
		[Parameter(ParameterSetName = 'UDP')]
		[int]$UdpTimeout = 1000
	)
	process {
		foreach ($Computer in $ComputerName) {
			foreach ($Portx in $Port) {
				$Output = @{ 'Computername' = $Computer; 'Port' = $Portx; 'Protocol' = $Protocol; 'Result' = '' }
				Write-Verbose "$($MyInvocation.MyCommand.Name) - Beginning port test on '$Computer' on port '$Protocol<code>:$Portx'"
				if ($Protocol -eq 'TCP') {
					$TcpClient = New-Object System.Net.Sockets.TcpClient
					$Connect = $TcpClient.BeginConnect($Computer, $Portx, $null, $null)
					$Wait = $Connect.AsyncWaitHandle.WaitOne($TcpTimeout, $false)
					if (!$Wait) {
						$TcpClient.Close()
						Write-Verbose "$($MyInvocation.MyCommand.Name) - '$Computer' failed port test on port '$Protocol</code>:$Portx'"
						$Output.Result = $false
					} else {
						$TcpClient.EndConnect($Connect)
						$TcpClient.Close()
						Write-Verbose "$($MyInvocation.MyCommand.Name) - '$Computer' passed port test on port '$Protocol<code>:$Portx'"
						$Output.Result = $true
					}
					$TcpClient.Close()
					$TcpClient.Dispose()
				} elseif ($Protocol -eq 'UDP') {
					$UdpClient = New-Object System.Net.Sockets.UdpClient
					$UdpClient.Client.ReceiveTimeout = $UdpTimeout
					$UdpClient.Connect($Computer, $Portx)
					Write-Verbose "$($MyInvocation.MyCommand.Name) - Sending UDP message to computer '$Computer' on port '$Portx'"
					$a = new-object system.text.asciiencoding
					$byte = $a.GetBytes("$(Get-Date)")
					[void]$UdpClient.Send($byte, $byte.length)
					#El objeto IPEndPoint nos permitirá leer datagramas enviados desde cualquier origen.
					Write-Verbose "$($MyInvocation.MyCommand.Name) - Creating remote endpoint"
					$remoteendpoint = New-Object system.net.ipendpoint([system.net.ipaddress]::Any, 0)
					try {
						#Bloquea hasta que un mensaje regrese en este socket desde un host remoto.
						Write-Verbose "$($MyInvocation.MyCommand.Name) - Waiting for message return"
						$receivebytes = $UdpClient.Receive([ref]$remoteendpoint)
						[string]$returndata = $a.GetString($receivebytes)
						If ($returndata) {
							Write-Verbose "$($MyInvocation.MyCommand.Name) - '$Computer' passed port test on port '$Protocol</code>:$Portx'"
							$Output.Result = $true
						}
					} catch {
						Write-Verbose "$($MyInvocation.MyCommand.Name) - '$Computer' failed port test on port '$Protocol`:$Portx' with error '$($_.Exception.Message)'"
						$Output.Result = $false
					}
					$UdpClient.Close()
					$UdpClient.Dispose()
				}
				[pscustomobject]$Output
			}
		}
	}
}

Source:
https://adamtheautomator.com/powershell-test-port/