Script – ESXi Hardening mit PowerCLI

Kürzlich  habe ich einen Beitrag zum härten der vSphere VM Konfigurationen veröffentlicht (Projekt nun auch auf GitHub), etwas Komplexer gestaltet sich das Thema ESXi Hardening mit PowerCLI.

Hier sind wesentlich mehr Aspekte zu beachten. Auch der VMware Security Hardening Guide für vSphere 5.5 Update 1 (Download) gibt nicht so viel her wie für VMs.

Grundlegend ist aber meines Erachtens nach für das ESXi Hardening  folgendes zu tun:

  • Nur benötigte Dienste starten
  • Wenn eingehende Ports offen sind, diese per Firewall einschränken
  • Wenn SSH benötigt wird, Timeouts festlegen
  • Logging und Zeitsynchronisation aktivieren

Ich habe mir hierfür wieder ein PowerCLI Script erstellt. Allerdings, wegen den umfangreichen Parametern, mit einer XML als Eingabe.

ESXi Hardening mit PowerCLI - PS Konsole

ESXi-Hardening – In Aktion (Beispiel)

Dieses Script ist noch in der Entwicklung und bisher nicht mit allen gewünschten Aktionen versehen.
Wer sich an der Weiterentwickelung beteiligen möchte, ist gerne eingeladen (GitHub Projekt).

ESXi Hardening mit PowerCLI – Komponenten

ESXi Hardening – XML Eingabe

<?xml version="1.0" encoding="utf-8"?>
<Config version="2.0">
  <Variable Name="SNMP_NET" Value="192.168.0.0/16" />
  <Variable Name="MGMT_NET" Value="192.168.0.0/16" />
    <NTP>
      <First>192.168.130.1</First>
      <Second>192.168.130.2</Second>
    </NTP>
    <SSH>
      <Enabled>True</Enabled>
      <Timeout>900</Timeout>
    </SSH>
    <Syslog>
      <Enabled>True</Enabled>
      <Server>udp://192.168.130.10:514</Server>
    </Syslog>
</Config>

ESXi Hardening -PowerCLI Script

#############################################################################  
# ESXi Hardening Script 
# Written by Markus Kraus
# Version 1.1, 02.2016  
#  
# http://mycloudrevolution.com/ 
#  
# Changelog:  
# 2016.01 ver 1.0 Base Release  
# 2016.02 ver 1.1   
#  
#  
##############################################################################  

## Preparation
# Load Snapin (if not already loaded)
if (!(Get-PSSnapin -name VMware.VimAutomation.Core -ErrorAction:SilentlyContinue)) {
	if (!(Add-PSSnapin -PassThru VMware.VimAutomation.Core)) {
		# Error out if loading fails
		write-host "`nFATAL ERROR: Cannot load the VIMAutomation Core Snapin. Is the PowerCLI installed?`n"
		exit
	}
}

## Global
$mgmtServices = @("sshClient","webAccess")
$MenueForegroundcolor = "Black"
## Inputs
# Menue
Write-Host `n"ESXi Hardening Module" -ForeGroundColor $MenueForegroundcolor
Write-Host `n"Type 'q' or hit enter to drop to shell"`n
Write-Host -NoNewLine "<" -foregroundcolor $MenueForegroundcolor
Write-Host -NoNewLine "ESXi or vCenter Connection?"
Write-Host -NoNewLine ">" -foregroundcolor $MenueForegroundcolor
Write-Host -NoNewLine "["
Write-Host -NoNewLine "A" -foregroundcolor $MenueForegroundcolor
Write-Host -NoNewLine "]"

Write-Host -NoNewLine `t`n "A1 - " -foregroundcolor $MenueForegroundcolor
Write-host -NoNewLine "vCenter"
Write-Host -NoNewLine `t`n "A2 - " -foregroundcolor $MenueForegroundcolor
Write-host -NoNewLine "ESXi"

$sel = Read-Host "Which option?"

# Connections
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
Switch ($sel) {
    "A1" {
	$vCenter = [Microsoft.VisualBasic.Interaction]::InputBox("vCenter Host FQDN or IP", "Host", "vCenter.test.lab")
	$HostExclude = [Microsoft.VisualBasic.Interaction]::InputBox("ESXi Hosts to exclude", "WildCard", "esx01|esx02") 
	# Start vCenter Connection
	Write-Host "`nStarting to Process vCenter Connection to " $vCenter " ..."-ForegroundColor Magenta
	$OpenConnection = $global:DefaultVIServers | where { $_.Name -eq $vCenter }
	if($OpenConnection.IsConnected) {
		Write-Host "`nvCenter is Already Connected..." -ForegroundColor Blue
		$VIConnection = $OpenConnection
	} else {
		Write-Host "`nConnecting vCenter..."
		$VIConnection = Connect-VIServer -Server $vCenter
	}

	if (-not $VIConnection.IsConnected) {
		Write-Error "`nError: vCenter Connection Failed"
    	Exit
	}
	# End vCenter Connection

	$ESXiHostList = Get-VMHost | Where-Object {$_.Name -notmatch $HostExclude}
	}
    "A2" {
	$ESXiHost = [Microsoft.VisualBasic.Interaction]::InputBox("ESXi Host FQDN or IP", "Host", "esx01.test.lab") 
	# Start ESXi Connection
	Write-Host "`nStarting to Process ESXi Connection to " $ESXiHost " ..."-ForegroundColor Magenta
	$OpenConnection = $global:DefaultVIServers | where { $_.Name -eq $ESXiHost }
	if($OpenConnection.IsConnected) {
		Write-Host "`nESXi is Already Connected..." -ForegroundColor Blue
		$VIConnection = $OpenConnection
	} else {
		Write-Host "`nConnecting ESXi..."
		$VIConnection = Connect-VIServer -Server $ESXiHost
	}

	if (-not $VIConnection.IsConnected) {
		Write-Error "`nError: ESXi Connection Failed"
    	Exit
	}
	# End ESXi Connection
	$ESXiHostList = Get-VMHost
	}
	{($_ -like "*q*") -or ($_ -eq "")} {
        Write-Host "`nNo input or 'q' seen... dropping to shell" -foregroundColor $foregroundColor       
        Exit
    }         
}
If (($ESXiHostList).Count -lt 1) {
    Write-Error "`nError: No Hosts to Process... Exiting"
    Exit
    }

# Read XML
$Validate = $true
If (Test-Path ".\Config.xml") {
        try {$Variable = [XML] (Get-Content ".\Config.xml")} catch {$Validate = $false;Write-Host "Invalid Config.xml" -ForegroundColor Red}
    } Else {
        $Validate = $false
        Write-Host "Missing Config.xml" -ForegroundColor Red
		Exit
    }
If ($Validate) {
        Write-Host "Reading XML Inputs:" -ForegroundColor Green
        $NTP1 = $Variable.Config.NTP.First
        $NTP2 = $Variable.Config.NTP.Second
        Write-Host "NTP List: $NTP1, $NTP2" -ForegroundColor DarkGray

        $SSHenaled = $Variable.Config.SSH.Enabled
        Write-Host "Enabling SSH: $SSHenaled" -ForegroundColor DarkGray
        $SSHTimeout = $Variable.Config.SSH.Timeout
        Write-Host "SSH Tmeout: $SSHTimeout" -ForegroundColor DarkGray

        $SyslogEnaled = $Variable.Config.Syslog.Enabled
        Write-Host "Enabling Syslog: $SyslogEnaled" -ForegroundColor DarkGray
        $SyslogServer = $Variable.Config.Syslog.Server
        Write-Host "Syslog Server: $SyslogServer" -ForegroundColor DarkGray


        $SNMP_NET = ($Variable.Config.Variable | Where-Object {$_.Name -eq "SNMP_NET"}).Value
        Write-Host "Firewall SNMP Network: $SNMP_NET" -ForegroundColor DarkGray
        $MGMT_NET = ($Variable.Config.Variable | Where-Object {$_.Name -eq "MGMT_NET"}).Value
        Write-Host "Firewall MGMT Network (SSH, vSphere Client): $MGMT_NET" -ForegroundColor DarkGray
    }


## Execute
foreach ($ESXiHost in $ESXiHostList){
    # Configure NTP
    Write-Host "NTP Configuration on $ESXiHost started..." -ForegroundColor Green
    $ESXiHost | Add-vmhostntpserver -ntpserver $NTP1  -confirm:$False -ErrorAction SilentlyContinue
    $ESXiHost | Add-vmhostntpserver -ntpserver $NTP2  -confirm:$False -ErrorAction SilentlyContinue
    $ntpservice = $ESXiHost | get-vmhostservice | Where-Object {$_.key -eq "ntpd"} 
    Set-vmhostservice -HostService $ntpservice -Policy "on" -confirm:$False | Out-Null
    $hosttimesystem = get-view $ESXiHost.ExtensionData.ConfigManager.DateTimeSystem 
    $hosttimesystem.UpdateDateTime([DateTime]::UtcNow) 
    start-vmhostservice -HostService $ntpservice -confirm:$False | Out-Null
    Write-Host "NTP Configuration on $ESXiHost finished..." -ForegroundColor Green

    # Report Services with Enabled Incomming Port Exceptions
    Write-Host "Services on $ESXiHost with Enabled Incomming Port Exceptions:" -ForegroundColor Green
    $ESXiHost| Get-VMHostFirewallException | where {$_.Enabled -eq "True" -and $_.IncomingPorts -ne ""} | Out-Default

    $esxcli = Get-EsxCli -VMHost $ESXiHost
    # SNMP
    Try {
        Write-Host "Configuring Firewall SNMP Strict Exception on $ESXiHost started..." -ForegroundColor Green
        $esxcli.network.firewall.ruleset.set($false,$true,'snmp')
        }

        Catch {
        
            Switch -Wildcard ($_.Exception)
                {
                "*Already use allowed ip list*"
                {Write-Host "...Already use allowed ip list" -ForegroundColor Blue}

                Default
                { Write-Host $_.Exception -ForegroundColor Red}
                }
            }
    Try {
        Write-Host "Configuring Firewall SNMP IP Exception on $ESXiHost started..." -ForegroundColor Green
        $esxcli.network.firewall.ruleset.allowedip.add($SNMP_NET,'snmp') 
        }

        Catch {
        
            Switch -Wildcard ($_.Exception)
                {
                "*Ip address already exist*"
                {Write-Host "...Ip address already exist" -ForegroundColor Blue}

                Default
                { Write-Host $_.Exception -ForegroundColor Red}
                }
            }
    # mgmtServices
    foreach ($mgmtService in $mgmtServices){
        Try {
            Write-Host "Configuring Firewall $mgmtService Strict Exception on $ESXiHost started..." -ForegroundColor Green
            $esxcli.network.firewall.ruleset.set($false,$true,$mgmtService)
            }

            Catch {
        
                Switch -Wildcard ($_.Exception)
                    {
                    "*Already use allowed ip list*"
                    {Write-Host "...Already use allowed ip list" -ForegroundColor Blue}

                    Default
                    { Write-Host $_.Exception -ForegroundColor Red}
                    }
                }
        Try {
            Write-Host "Configuring Firewall $mgmtService IP Exception on $ESXiHost started..." -ForegroundColor Green
            $esxcli.network.firewall.ruleset.allowedip.add($MGMT_NET,$mgmtService) 
            }

            Catch {
        
                Switch -Wildcard ($_.Exception)
                   {
                    "*Ip address already exist*"
                    {Write-Host "...Ip address already exist" -ForegroundColor Blue}

                    Default
                    { Write-Host $_.Exception -ForegroundColor Red}
                    }
                }
    }
# SSH Service
    if ($SSHenaled -eq "True"){
        #Enable SSH and disable SSH Warning
        Write-Host "Configuring SSH Service on $ESXiHost started..." -ForegroundColor Green
        $SSHService = $ESXiHost | Get-VMHostService | where {$_.Key -eq 'TSM-SSH'} 
        Start-VMHostService -HostService $SSHService -Confirm:$false | Out-Null
        Set-VMHostService -HostService $SSHService -Policy Automatic | Out-Null
        Get-AdvancedSetting -Entity $ESXiHost.name -Name UserVars.SuppressShellWarning | Set-AdvancedSetting -Value 1 -Confirm:$false | Out-Null
        Get-AdvancedSetting -Entity $ESXiHost.name -Name UserVars.ESXiShellInteractiveTimeOut | Set-AdvancedSetting -Value $SSHTimeout -Confirm:$false | Out-Null
        Get-AdvancedSetting -Entity $ESXiHost.name -Name UserVars.ESXiShellTimeOut | Set-AdvancedSetting -Value $SSHTimeout -Confirm:$false | Out-Null
        }
        else{
        #Disabling SSH and Enabling SSH Warning
        Write-Host "Configuring SSH Service on $ESXiHost started..." -ForegroundColor Green
        $SSHService = $ESXiHost | Get-VMHostService | where {$_.Key -eq 'TSM-SSH'} 
        Stop-VMHostService -HostService $SSHService -Confirm:$false | Out-Null
        Set-VMHostService -HostService $SSHService -Policy Off | Out-Null
        Get-AdvancedSetting -Entity $ESXiHost.name -Name UserVars.SuppressShellWarning | Set-AdvancedSetting -Value 0 -Confirm:$false | Out-Null
        }
# Syslog Servcice
    if ($Syslogenaled -eq "True"){
        #Enabling Syslog and Configuring
        Write-Host "Configuring Syslog Service on $ESXiHost started..." -ForegroundColor Green
        $ESXiHost | Get-VMHostFirewallException |?{$_.Name -eq 'syslog'} | Set-VMHostFirewallException -Enabled:$true | Out-Null
        Get-AdvancedSetting -Entity $ESXiHost.name -Name Syslog.global.logHost | Set-AdvancedSetting -Value $SyslogServer -Confirm:$false | Out-Null
        }
        else{
        #Disabling Syslog
        Write-Host "Disabling Syslog Service on $ESXiHost started..." -ForegroundColor Green
        $ESXiHost | Get-VMHostFirewallException |?{$_.Name -eq 'syslog'} | Set-VMHostFirewallException -Enabled:$false | Out-Null
        Get-AdvancedSetting -Entity $ESXiHost.name -Name Syslog.global.logHost | Set-AdvancedSetting -Value "" -Confirm:$false | Out-Null
        }
    
}
Disconnect-VIServer -Force -Confirm:$false

Leave a Reply