Опрос сети на предмет используемых версий контроллеров HP iLO

Несколько дней назад компания Hewlett Packard Enterprise опубликовала бюллетень безопасности HPESBHF03797 с информацией о наличии множественных удалённо эксплуатируемых уязвимостей в контроллерах управления HP Integrated Lights-Out 2 (iLO2) с прошивкой версии 2.29. Контроллеры iLO2 используются на серверных платформах HP пятого (G5) и шестого (G6) поколений в линейке 300-тых моделей, например ProLiant DL360/380, а также в некоторых других моделях, например ProLiant DL320s Gen1. Для закрытия уязвимостей необходимо выполнить установку прошивки версии 2.31 (7 Dec 2017). И если в локальной сети развёрнут репозиторий VCRM, то исполняемый файл прошивки можно добавить в этот репозиторий ранее описанным способом (для возможности установки прошивки на Windows Server 2012/2012 R2). Также прошивку можно обновить подключившись напрямую к веб-интерфейсу, встроенному в iLO2. Но в этой заметке речь пойдёт не о самой процедуре обновления, а о том, как провести аудит используемых версий iLO на всех серверах локальной сети. Такой аудит может быть полезен, с одной стороны - для того, чтобы предварительно оценить необходимый объем контроллеров, требующих обновления, и с другой стороны – в контрольных целях, в случае, если в организации между администраторами есть разделение на зоны обслуживания серверного оборудования.

 

Вариант удалённого определения версий iLO

Одним из методов проведения аудита версионности контроллеров iLO, может быть метод прямого опроса контроллера по протоколу HTTP. Такой опрос возможен без необходимости аутентификации на встроенном в iLO веб-сервере в том случае, если в настройках контроллера в разделе Administration > Management (для iLO2) включен параметр Level of Data Returned.

Здесь же можно найти и ссылку на возвращаемый в результате HTTP запроса XML документ. Ссылка эта выглядит следующим образом:

http://iLO-IP-Address/xmldata?item=all

Вывод будет иметь примерно следующий вид:

Как видим, здесь есть интересующая нас информация о версии контроллера iLO и даже больше.

Как вы понимаете, если вдруг по каким-то невероятным причинам обновление контроллера до актуальной версии всё-таки не планируется, то можно, как минимум, отключить вывод этих полезных XML-данных в целях сокрытия информации о том, что на данном устройстве используется устаревшая уязвимая версия прошивки. И в таком случае возвращаемые данные будут иметь следующий вид:

С источником получения данных определились, теперь - о реализации самого опроса сети.

 

Сканируем сеть с Linux - Bash-скрипт

Из готовых к использованию, и действительно работающих, решений по опросу сети именно на предмет получения данных об iLO, мне удалось найти разве что только Bash-скрипт девятилетней давности из статьи NachoTech Blog - How to find all the iLO’s on your network. Любители Linux вполне могут использовать этот скрипт на современном дистрибутиве Linux c nmap и curl на борту. Привожу немного откорректированный вариант скрипта, протестированный на Debian GNU/Linux 8.10:

#!/bin/bash
#
# Search a local network segment for iLOs
# The iLO is the Integrated Lights-Out management processor
# used on HP ProLiant and BladeSystem servers
#
scriptversion="1.1"
#
# Author: iggy@nachotech.com
#
# Website: http://blog.nachotech.com/?p=63
#
# Requires: tr sed expr curl nmap
#
# Tested with: Nmap 4.20, curl 7.17.1, RHEL4
#
# Note:
# Discovery of an iLO is dependent upon the Virtual Media port
# being set to the default of 17988.  If this has been changed
# by the iLO administrator, then this script will NOT find it.
#
# Also, if the iLO XML Reply Data Return has been Disabled by
# the iLO administrator, this script will not be able to
# gather any information about the server.  It will still be
# discovered, but all you will see is its IP address.
#
# History:
# ver. 1.1 (16 Jan 2018)
# * Add ILO3/ILO4 Support
# * Tested on Debian GNU/Linux 8.10 / Nmap 6.47 / curl 7.38.0

# GLOBAL VARIABLES
#
scriptname="findilos"
iloips="/tmp/tmpilos.$$"
iloxml="/tmp/tmpiloxml.$$"
ilohwvers="/tmp/tmpilohwvers.$$"
declare -i ilosfound=0

# FUNCTIONS
#
function parseiloxml {
  fgrep "$1" $iloxml > /dev/null 2>&1
  if [ $? -ne 0 ]
  then
    # tag not found in xml output, return empty string
    parsedstring="N/A"
  else
    # tag was found - now we parse it from the output
    tempstring=$( cat $iloxml | tr -d -c [:print:] | sed "s/^.*<$1>//" | sed "s/<.$1.*//")
    # trim off leading and trailing whitespace
    parsedstring=`expr match "$tempstring" '[ \t]*\(.*[^ \t]\)[ \t]*$'`
  fi
}
#
function is_installed {
  which $1 > /dev/null 2>&1
  if [ $? -ne 0 ]
  then
    printf "\nERROR: %s not installed.\n\n" $1
    exit 255
  fi
}

# MAIN
#
# check for tools that we depend upon
#
is_installed tr
is_installed sed
is_installed expr
is_installed curl
is_installed nmap
#
# check syntax - should have 1 and only 1 parameter on cmdline
#
if [ $# -ne 1 ]; then
  printf "%s %s ( http://blog.nachotech.com/ )\n" $scriptname $scriptversion
  printf "Usage: %s {target network specification}\n" $scriptname
  printf "TARGET NETWORK SPECIFICATION:\n"
  printf "  Can pass hostnames, IP addresses, networks, etc.\n"
  printf "  Ex: server1.company.com, company.com/24, 192.168.0.1/16, 10.0.0-255.1-254\n"
  printf "EXAMPLE:\n"
  printf "  %s 16.32.64.0/22\n" $scriptname
  exit 255
fi

iprange=$1
#
# prepare lookup file for iLO hardware versions
# Data returned via HWRI is very variable
cat > $ilohwvers << EOF
iLO-1 shows hw version ASIC:  2
iLO-2 shows hw version ASIC:  7
iLO-3 shows hw version ASIC:  8
iLO-3 shows hw version ASIC:  9
iLO-4 shows hw version ASIC: 12
iLO-4 shows hw version ASIC: 16
i-iLO shows hw version T0
EOF
#
# scan a range of IP addresses looking for an
# open tcp port 17988 (the iLO virtual media port)
#
printf "Scanning..."
nmap -n -P0 -sS -p 17988 -oG - $iprange | fgrep /open/ | awk '{print $2}' > $iloips
printf "\n\n"
#
# open and read the list of IP addresses one at a time
#
exec 3< $iloips
echo "--------------- ------ -------- ------------ -------------------------"
echo "iLO IP Address  iLO HW iLO FW   Server S/N   Server Model"
echo "--------------- ------ -------- ------------ -------------------------"
while read iloip <&3 ; do
  ilosfound=$ilosfound+1
  #
  # attempt to read the xmldata from iLO, no password required
  #
  curl --proxy "" --fail --silent --max-time 3 http://$iloip/xmldata?item=All > $iloxml
  #
  # parse out the Server model (server product name) from the XML output
  #
  parseiloxml SPN;  servermodel=$parsedstring
  parseiloxml SBSN; sernum=$parsedstring
  parseiloxml PN;   ilotype=$parsedstring
  parseiloxml FWRI; ilofirmware=$parsedstring
  parseiloxml HWRI; ilohardware=$parsedstring
  ilohwver=$(grep "$ilohardware" $ilohwvers|awk '{print $1}')
  if [ "$ilohwver" == "" ]; then
    ilohwver="N/A"
  fi
  if [ "$sernum" == "" ]; then
    sernum="N/A"
  fi
  printf "%-15s %-6s %-8s %-12s %s\n" $iloip "$ilohwver" "$ilofirmware" "$sernum" "$servermodel"
done
printf "\n%d iLOs found on network target %s.\n\n" $ilosfound $iprange
rm -f $iloips $iloxml $ilohwvers
exit 0

Закидываем скрипт на Linux-систему, делаем его исполняемым и, установив недостающие зависимости, выполняем первый проверочный вызов, опрашивая какой-нибудь конкретный контроллер iLO:

# chmod +x scripts/network-scan-for-hp-ilo.sh
# apt-get install curl
# scripts/network-scan-for-hp-ilo.sh ilo001.holding.com

Scanning...

--------------- ------ -------- ------------ -------------------------
iLO IP Address  iLO HW iLO FW   Server S/N   Server Model
--------------- ------ -------- ------------ -------------------------
10.6.4.100      iLO-2  2.31     CZC70050JY   ProLiant DL380 G5

1 iLOs found on network target ilo001.holding.com.

В случае если на контроллере iLO вывод XML отключен, то результат мы получим соответствующий. Контроллер скрипту ответит, но, как и следует ожидать, ничего о себе не расскажет.

Scanning...

--------------- ------ -------- ------------ -------------------------
iLO IP Address  iLO HW iLO FW   Server S/N   Server Model
--------------- ------ -------- ------------ -------------------------
10.6.4.100      N/A    N/A      N/A          N/A

1 iLOs found on network target ilo001.holding.com.

В качестве параметра при вызове скрипта можно использовать имя iLO, либо IP адрес, либо диапазон IP адресов, либо адрес сети. Например, попробуем просканировать сеть 10.3.1.0/24

# scripts/network-scan-for-hp-ilo.sh 10.3.1.0/24

Scanning...

--------------- ------ -------- ------------ -------------------------
iLO IP Address  iLO HW iLO FW   Server S/N   Server Model
--------------- ------ -------- ------------ -------------------------
10.3.1.20       iLO-2  2.25     CZC8417L81   ProLiant DL380 G6
10.3.1.30       iLO-2  2.22     CZ38486S49   ProLiant DL320 G6
10.3.1.41       iLO-2  2.29     CZC84755S8   ProLiant DL380 G5
10.3.1.60       iLO-4  2.50     CZ28401H03   ProLiant DL380p Gen8
10.3.1.61       iLO-4  2.50     CZ28401H0X   ProLiant DL380p Gen8

5 iLOs found on network target 10.3.1.0/24.

Теперь в одном месте мы видим информацию о всех наших контроллерах и можем оценить необходимый объем обновлений. Теперь поговорим о реализации подобного скрипта для обитателей экосистемы Windows. Кстати, запыхавшихся любителей подискутировать на тему о том, что в Windows тоже есть Bash, прошу дышать ровнее. Кесарю-кесарево.

 

 

Сканируем сеть с Windows - PowerShell-скрипт

Не смотря на то, что выше обозначенный скрипт уже дал представление о том, как можно получить желаемый результат, и его осталось лишь реализовать путём написания скрипта в PowerShell, я, как и любой в меру ленивый человек, решил поискать готовые реализации задачи. На глаза попался скрипт из заметки Black Mountain - PowerShell - Find iLO on a subnet. Но данный скрипт, во первых, использует сторонний модуль PowerShell, а во вторых, оперирует вызовом System.Net.WebClient, который, как стало очевидно после некоторых экспериментов, имеет некоторые недостатки (например невозможность задать таймаут ожидания ответа веб-сервера), которые при определённых обстоятельствах могут до безобразия "испортить всю малину". Чтобы не быть голословным, скажу что сканирование сети класса "С" в 254 хоста этим скриптом выполняется в моём случае 16-17 минут, в том время как представленный выше Bash-скрипт делает это примерно за пару минут. А что если просканировать нужно площадку с пачкой таких сетей… а регион с пачкой таких площадок? Тут стало очевидно, что нужно как-то оптимизировать это дело. По примеру из навороченного скрипта SubNet Scan, удалось отвязаться от использования стороннего PS-модуля для извлечения списка IP адресов из входного диапазона. Получить возможность оптимизации HTTP-запросов, выполняемых к каждому отдельно взятому iLO, удалось благодаря замене System.Net.WebClient на объект System.Net.WebRequest. Ну и конечно, грешно было бы на такой задаче не использовать распараллеливание обработки с помощью PowerShell Workflow. В итоге получился переработанный вариант, который "прожевал" ту же сеть примерно за минуту:

[System.Net.IPAddress]$StartScanIP = "10.3.1.1"
[System.Net.IPAddress]$EndScanIP = "10.3.1.254"
#
$Watch = [System.Diagnostics.Stopwatch]::StartNew()
$Watch.Start()
#
$ScanIPRange = @() 
if($EndScanIP -ne $null) 
{
  $StartIP = $StartScanIP -split '\.' 
  [Array]::Reverse($StartIP)   
  $StartIP = ([System.Net.IPAddress]($StartIP -join '.')).Address  
  #               
  $EndIP = $EndScanIP -split '\.' 
  [Array]::Reverse($EndIP)   
  $EndIP = ([System.Net.IPAddress]($EndIP -join '.')).Address  
  #               
  For ($x=$StartIP; $x -le $EndIP; $x++) {     
      $IP = [System.Net.IPAddress]$x -split '\.' 
      [Array]::Reverse($IP)    
      $ScanIPRange += $IP -join '.'  
  }
} 
  else 
{ 
 $ScanIPRange = $StartScanIP 
} 

Workflow Network-Scan{
  param($ippool)    
  $WFResult = @()
  foreach -parallel -throttlelimit 50 ($ip in $ippool)
  {
    $iLOReturn = inlinescript
    {
       #Test-Connection -ipaddres $USING:ip -Count 2 -ErrorAction SilentlyContinue
       [System.Xml.XmlDocument]$xd = New-Object System.Xml.XmlDocument
       $url = "http://$USING:ip/xmldata?item=ALL"
       try{                            
        $WebRequest = [System.Net.WebRequest]::Create($url)
        $WebRequest.Timeout = 2000 #Default 600000
        $WebRequest.AuthenticationLevel = "None"
        $WebResponse = $WebRequest.GetResponse()
        $sr = New-Object System.IO.StreamReader($WebResponse.GetResponseStream())
        $rawxml = $sr.ReadToEnd()             
        $xd = $rawxml
        $ilo = New-Object System.Object
        $ilo | Add-Member -MemberType NoteProperty -Name "iLOIP" -Value $USING:ip
        $ilo | Add-Member -MemberType NoteProperty -Name "iLOType" $xd.RIMP.MP.PN
        $ilo | Add-Member -MemberType NoteProperty -Name "iLOFW" $xd.RIMP.MP.FWRI
        $ilo | Add-Member -MemberType NoteProperty -Name "SrvSN" $xd.RIMP.HSI.SBSN.trim()
        $ilo | Add-Member -MemberType NoteProperty -Name "SrvModel" $xd.RIMP.HSI.SPN
        }#try
        catch {}
        return $ilo
     } #inlinescript
     $WORKFLOW:WFResult += $iLOReturn
   }
  return $WORKFLOW:WFResult
}
$Result = Network-Scan $ScanIPRange
$Result | Sort-Object iLOType,iLOFW,iLOIP | Format-Table -GroupBy iLOType -Autosize `
  @{Name="iLO IP Address";Expression = { $_.iLOIP }; Alignment="Center"},
  @{Name="iLO Type";Expression = { $_.iLOType };Alignment="Center"},
  @{Name="iLO Firmware";Expression = { $_.iLOFW };Alignment="Center"},
  @{Name="Server SN";Expression = { $_.SrvSN };Alignment="Center"},
  @{Name="Server Model";Expression = { $_.SrvModel };Alignment="Left"}

Write-Host $Result.Count "iLOs found on network range " $StartScanIP "-" $EndScanIP
$Watch.Stop()
Write-Host "Script time:" $Watch.Elapsed

Разумеется, это черновой вариант скрипта, так как его можно дополнить обработкой входящих параметров и добавить функции разбора разных типов указания IP диапазонов, как например в скрипте List IP addresses in a range using Powershell. Для использования скрипта достаточно задать адрес начала и окончания IP-диапазона сканирования в первых двух строках. Скрипт покажет нам информацию в разбивке разных поколений ILO, отсортировав внутри каждого информацию по версии, примерно в таком виде:

   iLOType: Integrated Lights-Out 2 (iLO 2)

iLO IP Address            iLO Type             iLO Firmware Server SN  Server Model     
--------------            --------             ------------ ---------  ------------     
 10.3.1.30   Integrated Lights-Out 2 (iLO 2)     2.22     CZ38486S49 ProLiant DL320 G6
 10.3.1.20   Integrated Lights-Out 2 (iLO 2)     2.25     CZC8417L81 ProLiant DL380 G6
 10.3.1.41   Integrated Lights-Out 2 (iLO 2)     2.29     CZC84755S8 ProLiant DL380 G5


   iLOType: Integrated Lights-Out 4 (iLO 4)

iLO IP Address            iLO Type             iLO Firmware Server SN  Server Model        
--------------            --------             ------------ ---------  ------------        
 10.3.1.60   Integrated Lights-Out 4 (iLO 4)     2.50     CZ28401H03 ProLiant DL380p Gen8
 10.3.1.61   Integrated Lights-Out 4 (iLO 4)     2.50     CZ28401H0X ProLiant DL380p Gen8


10 iLOs found on network range  10.3.1.1 - 10.3.1.254
Script time: 00:01:12.4675614

Выкрутив значение $WebRequest.Timeout (время ожидания ответа веб-сервера в миллисекундах, которое в скрипте установлено в 2 секунды) в допустимый для вашей сетевой инфраструктуры показатель, можно получить желаемую информацию при относительно небольших затратах времени.

Разумеется, стоит упомянуть и про существование такой штуки, как специальный набор командлетов HPE Scripting Tools for Windows PowerShell. Однако, как я понял, у него есть ограничения по версиям ILO, поэтому, в рамках поставленной задачи простейшего опроса сети, вполне будет достаточно двух представленных выше вариантов скриптов.

Только один комментарий Комментировать

  1. Konstantin Malakhov /

    Отличный скрип, не хватает Host Common Name только, т.к. не всегда есть понимание где какое ilo...

Добавить комментарий