Назад | Перейти на главную страницу

Windows DHCP Server - получать уведомление, когда устройство, не подключенное к AD, получает IP-адрес

СЦЕНАРИЙ

Чтобы упростить это до самого простого примера:

У меня стандартный контроллер домена Windows 2008 R2 с ролью DHCP-сервера. Он раздает IP-адреса через различные области IPv4, здесь нет проблем.

ЧТО МНЕ НРАВИТСЯ

Мне нужен способ создать запись уведомления / журнала событий / аналогичную всякий раз, когда устройство получает аренду DHCP-адреса и это устройство НЕ ЯВЛЯЕТСЯ компьютер, присоединенный к домену в Active Directory. Для меня не имеет значения, кастомный ли это Powershell и т. Д.

Итог = Мне нужен способ узнать, когда в сети находятся устройства, не относящиеся к домену, без использования 802.1X в данный момент. Я знаю, что это не будет учитывать статические IP-устройства. У меня есть программное обеспечение для мониторинга, которое сканирует сеть и находит устройства, но оно не так детально.

ИССЛЕДОВАНИЕ ПРОВЕДЕНО / РАССМОТРЕННЫЕ ВАРИАНТЫ

Я не вижу таких возможностей со встроенным логированием.

Да, я знаю о 802.1X и имею возможность реализовать его в долгосрочной перспективе в этом месте, но мы находимся некоторое время вдали от подобного проекта, и хотя это решит проблемы сетевой аутентификации, это все еще полезно для меня за пределами целей 802.1X.

Я поискал некоторые фрагменты сценария и т. Д., Которые могут оказаться полезными, но то, что я нахожу, заставляет меня поверить, что мой google-fu подводит меня в данный момент.

Я считаю, что приведенная ниже логика верна (при условии, что нет какого-либо существующего решения):

  1. Устройство получает адрес DHCP
  2. Запись в журнале событий записывается (событие с идентификатором 10 в журнале аудита DHCP должно работать (поскольку меня больше всего интересует новая аренда, а не продление): http://technet.microsoft.com/en-us/library/dd759178.aspx)
  3. На этом этапе какой-то сценарий, вероятно, должен будет заменить оставшиеся ниже «ШАГИ».
  4. Как-нибудь запросите этот журнал DHCP для этих событий с идентификатором 10 (я бы хотел нажать, но я предполагаю, что pull - единственный выход здесь)
  5. Разобрать запрос на имя устройства, которому назначена новая аренда
  6. Запросить в AD имя устройства
  7. ЕСЛИ не найдено в AD, отправьте уведомление по электронной почте

Если у кого-то есть идеи, как это правильно сделать, я буду очень признателен. Я не ищу «дай мне код», но хотел бы знать, есть ли альтернативы приведенному выше списку, или я не думаю ясно, и существует другой метод для сбора этой информации. Если у вас есть фрагменты кода / команды PS, которыми вы хотели бы поделиться, чтобы помочь в этом, тем лучше.

Большое спасибо ErikE и остальным здесь, я пошел по пути ... Я не скажу, что это правильный путь, но скрипт Powershell, который я придумал, помогает.

Код ниже, если кто-то этого хочет. Просто запустите его вручную, указывая на каждый DHCP-сервер, или запланируйте его (снова указывая на каждый DHCP-сервер в сценарии).

Что делает сценарий:

  1. Получает информацию об аренде от DHCP-сервера (аренда ipv4)
  2. Выводит аренду в файл csv
  3. Читает обратно в этот CSV-файл для запроса AD
  4. Запрашивает AD для компьютера
  5. Если не найдено, выводит в новый txt файл
  6. Создает окончательный текстовый файл уникального списка из файла, созданного в # 5 выше (поскольку могут быть дублирования, если клиент регистрируется более одного раза или с более чем одним адаптером)
  7. отправляет по электронной почте содержимое окончательного выходного файла администратору

Что вам понадобится:

Скрипт использует модуль AD (import-module activedirectory), поэтому его лучше всего запускать на AD DC с DHCP. Если это не ваш случай, вы можете установить модуль PowerShell AD: http://blogs.msdn.com/b/rkramesh/archive/2012/01/17/how-to-add-active-directory-module-in-powershell-in-windows-7.aspx

Вам также понадобятся командлеты Quest AD Powershell, которые можно найти здесь: http://www.quest.com/powershell/activeroles-server.aspx . Установите ЭТИ ПЕРЕД запуск скрипта, иначе он не удастся.

Сам скрипт (очищенный, вам нужно будет настроить некоторые переменные в соответствии с вашими потребностями, такие как имена входных файлов, домен для подключения, сервер DHCP для подключения, настройки электронной почты в конце и т. Д.):

# Get-nonADclientsOnDHCP.ps1

# Author : TheCleaner http://serverfault.com/users/7861/thecleaner with a big thanks for a lot of the lease grab code to Assaf Miron on code.google.com

# Description : This Script grabs the current leases on a Windows DHCP server, outputs it to a csv
# then takes that csv file as input and determines if the lease is from a non-AD joined computer.  It then emails
# an administrator notification.  Set it up on a schedule of your choosing in Task Scheduler.
# This helps non-802.1X shops keep track of rogue DHCP clients that aren't part of the domain.

#

# Input : leaselog.csv

# Output: Lease log = leaselog.csv
# Output: Rogue Clients with dupes = RogueClients.txt
# Output: Rogue Clients - unique = RogueClientsFinal.txt

$DHCP_SERVER = "PUT YOUR SERVER NAME OR IP HERE" # The DHCP Server Name

$LOG_FOLDER = "C:\DHCP" # A Folder to save all the Logs

# Create Log File Paths

$LeaseLog = $LOG_FOLDER+"\LeaseLog.csv"

#region Create Scope Object

# Create a New Object

$Scope = New-Object psobject

# Add new members to the Object

$Scope | Add-Member noteproperty "Address" ""

$Scope | Add-Member noteproperty "Mask" ""

$Scope | Add-Member noteproperty "State" ""

$Scope | Add-Member noteproperty "Name" ""

$Scope | Add-Member noteproperty "LeaseDuration" ""

# Create Each Member in the Object as an Array

$Scope.Address = @()

$Scope.Mask = @()

$Scope.State = @()

$Scope.Name = @()

$Scope.LeaseDuration = @()

#endregion


#region Create Lease Object

# Create a New Object

$LeaseClients = New-Object psObject

# Add new members to the Object

$LeaseClients | Add-Member noteproperty "IP" ""

$LeaseClients | Add-Member noteproperty "Name" ""

$LeaseClients | Add-Member noteproperty "Mask" ""

$LeaseClients | Add-Member noteproperty "MAC" ""

$LeaseClients | Add-Member noteproperty "Expires" ""

$LeaseClients | Add-Member noteproperty "Type" ""

# Create Each Member in the Object as an Array

$LeaseClients.IP = @()

$LeaseClients.Name = @()

$LeaseClients.MAC = @()

$LeaseClients.Mask = @()

$LeaseClients.Expires = @()

$LeaseClients.Type = @()

#endregion


#region Create Reserved Object

# Create a New Object

$LeaseReserved = New-Object psObject

# Add new members to the Object

$LeaseReserved | Add-Member noteproperty "IP" ""

$LeaseReserved | Add-Member noteproperty "MAC" ""

# Create Each Member in the Object as an Array

$LeaseReserved.IP = @()

$LeaseReserved.MAC = @()

#endregion


#region Define Commands

#Commad to Connect to DHCP Server

$NetCommand = "netsh dhcp server \\$DHCP_SERVER"

#Command to get all Scope details on the Server

$ShowScopes = "$NetCommand show scope"

#endregion


function Get-LeaseType( $LeaseType )

{

# Input : The Lease type in one Char

# Output : The Lease type description

# Description : This function translates a Lease type Char to it's relevant Description


Switch($LeaseType){

"N" { return "None" }

"D" { return "DHCP" }

"B" { return "BOOTP" }

"U" { return "UNSPECIFIED" }

"R" { return "RESERVATION IP" }

}

}


function Check-Empty( $Object ){

# Input : An Object with values.

# Output : A Trimmed String of the Object or '-' if it's Null.

# Description : Check the object if its null or not and return it's value.

If($Object -eq $null)

{

return "-"

}

else

{

return $Object.ToString().Trim()

}

}


function out-CSV ( $LogFile, $Append = $false) {

# Input : An Object with values, Boolean value if to append the file or not, a File path to a Log File

# Output : Export of the object values to a CSV File

# Description : This Function Exports all the Values and Headers of an object to a CSV File.

#  The Object is recieved with the Input Const (Used with Pipelineing) or the $inputObject

Foreach ($item in $input){

# Get all the Object Properties

$Properties = $item.PsObject.get_properties()

# Create Empty Strings - Start Fresh

$Headers = ""

$Values = ""

# Go over each Property and get it's Name and value

$Properties | %{ 

$Headers += $_.Name + ","

$Values += $_.Value

}

# Output the Object Values and Headers to the Log file

If($Append -and (Test-Path $LogFile)) {

$Values | Out-File -Append -FilePath $LogFile -Encoding Unicode

}

else {

# Used to mark it as an Powershell Custum object - you can Import it later and use it

# "#TYPE System.Management.Automation.PSCustomObject" | Out-File -FilePath $LogFile

$Headers | Out-File -FilePath $LogFile -Encoding Unicode

$Values | Out-File -Append -FilePath $LogFile -Encoding Unicode

}

}

}


#region Get all Scopes in the Server 

# Run the Command in the Show Scopes var

$AllScopes = Invoke-Expression $ShowScopes

# Go over all the Results, start from index 5 and finish in last index -3

for($i=5;$i -lt $AllScopes.Length-3;$i++)

{

# Split the line and get the strings

$line = $AllScopes[$i].Split("-")

$Scope.Address += Check-Empty $line[0]

$Scope.Mask += Check-Empty $line[1]

$Scope.State += Check-Empty $line[2]

# Line 3 and 4 represent the Name and Comment of the Scope

# If the name is empty, try taking the comment

If (Check-Empty $line[3] -eq "-") {

$Scope.Name += Check-Empty $line[4]

}

else { $Scope.Name += Check-Empty $line[3] }

}

# Get all the Active Scopes IP Address

$ScopesIP = $Scope | Where { $_.State -eq "Active" } | Select Address

# Go over all the Adresses to collect Scope Client Lease Details

Foreach($ScopeAddress in $ScopesIP.Address){

# Define some Commands to run later - these commands need to be here because we use the ScopeAddress var that changes every loop

#Command to get all Lease Details from a specific Scope - when 1 is amitted the output includes the computer name

$ShowLeases = "$NetCommand scope "+$ScopeAddress+" show clients 1"

#Command to get all Reserved IP Details from a specific Scope

$ShowReserved = "$NetCommand scope "+$ScopeAddress+" show reservedip"

#Command to get all the Scopes Options (Including the Scope Lease Duration)

$ShowScopeDuration = "$NetCommand scope "+$ScopeAddress+" show option"

# Run the Commands and save the output in the accourding var

$AllLeases = Invoke-Expression $ShowLeases 

$AllReserved = Invoke-Expression $ShowReserved 

$AllOptions = Invoke-Expression $ShowScopeDuration

# Get the Lease Duration from Each Scope

for($i=0; $i -lt $AllOptions.count;$i++) 

{ 

# Find a Scope Option ID number 51 - this Option ID Represents  the Scope Lease Duration

if($AllOptions[$i] -match "OptionId : 51")

{ 

# Get the Lease Duration from the Specified line

$tmpLease = $AllOptions[$i+4].Split("=")[1].Trim()

# The Lease Duration is recieved in Ticks / 10000000

$tmpLease = [int]$tmpLease * 10000000; # Need to Convert to Int and Multiply by 10000000 to get Ticks

# Create a TimeSpan Object

$TimeSpan = New-Object -TypeName TimeSpan -ArgumentList $tmpLease

# Calculate the $tmpLease Ticks to Days and put it in the Scope Lease Duration

$Scope.LeaseDuration += $TimeSpan.TotalDays

# After you found one Exit the For

break;

} 

}

# Get all Client Leases from Each Scope

for($i=8;$i -lt $AllLeases.Length-4;$i++)

{

# Split the line and get the strings

$line = [regex]::split($AllLeases[$i],"\s{2,}")

# Check if you recieve all the lines that you need

$LeaseClients.IP += Check-Empty $line[0]

$LeaseClients.Mask += Check-Empty $line[1].ToString().replace("-","").Trim()

$LeaseClients.MAC += $line[2].ToString().substring($line[2].ToString().indexOf("-")+1,$line[2].toString().Length-1).Trim()

$LeaseClients.Expires += $(Check-Empty $line[3]).replace("-","").Trim()

$LeaseClients.Type += Get-LeaseType $(Check-Empty $line[4]).replace("-","").Trim()

$LeaseClients.Name += Check-Empty $line[5]

}

# Get all Client Lease Reservations from Each Scope

for($i=7;$i -lt $AllReserved.Length-5;$i++)

{

# Split the line and get the strings

$line = [regex]::split($AllReserved[$i],"\s{2,}")

$LeaseReserved.IP += Check-Empty $line[0]

$LeaseReserved.MAC += Check-Empty $line[2]

}

}

#endregion 


#region Create a Temp Scope Object

# Create a New Object

$tmpScope = New-Object psobject

# Add new members to the Object

$tmpScope | Add-Member noteproperty "Address" ""

$tmpScope | Add-Member noteproperty "Mask" ""

$tmpScope | Add-Member noteproperty "State" ""

$tmpScope | Add-Member noteproperty "Name" ""

$tmpScope | Add-Member noteproperty "LeaseDuration" ""

#endregion

#region Create a Temp Lease Object

# Create a New Object

$tmpLeaseClients = New-Object psObject

# Add new members to the Object

$tmpLeaseClients | Add-Member noteproperty "IP" ""

$tmpLeaseClients | Add-Member noteproperty "Name" ""

$tmpLeaseClients | Add-Member noteproperty "Mask" ""

$tmpLeaseClients | Add-Member noteproperty "MAC" ""

$tmpLeaseClients | Add-Member noteproperty "Expires" ""

$tmpLeaseClients | Add-Member noteproperty "Type" ""

#endregion

#region Create a Temp Reserved Object

# Create a New Object

$tmpLeaseReserved = New-Object psObject

# Add new members to the Object

$tmpLeaseReserved | Add-Member noteproperty "IP" ""

$tmpLeaseReserved | Add-Member noteproperty "MAC" ""

#endregion

# Go over all the Client Lease addresses and export each detail to a temporary var and out to the log file

For($l=0; $l -lt $LeaseClients.IP.Length;$l++)

{

# Get all Scope details to a temp var

$tmpLeaseClients.IP = $LeaseClients.IP[$l] + ","

$tmpLeaseClients.Name = $LeaseClients.Name[$l] + ","

$tmpLeaseClients.Mask =  $LeaseClients.Mask[$l] + ","

$tmpLeaseClients.MAC = $LeaseClients.MAC[$l] + ","

$tmpLeaseClients.Expires = $LeaseClients.Expires[$l] + ","

$tmpLeaseClients.Type = $LeaseClients.Type[$l]

# Export with the Out-CSV Function to the Log File

$tmpLeaseClients | out-csv $LeaseLog -append $true

}



#Continue on figuring out if the DHCP lease clients are in AD or not

#Import the Active Directory module
import-module activedirectory

#import Quest AD module
Add-PSSnapin Quest.ActiveRoles.ADManagement

#connect to AD
Connect-QADService PUTTHEFQDNOFYOURDOMAINHERE_LIKE_DOMAIN.LOCAL | Out-Null

# get input CSV
$leaselogpath = "c:\DHCP\LeaseLog.csv"
Import-csv -path $leaselogpath | 
#query AD for computer name based on csv log
foreach-object `
{ 
   $NameResult = Get-QADComputer -DnsName $_.Name
   If ($NameResult -eq $null) {$RogueSystem = $_.Name}
   $RogueSystem | Out-File C:\DHCP\RogueClients.txt -Append
   $RogueSystem = $null

}
Get-Content C:\DHCP\RogueClients.txt | Select-Object -Unique | Out-File C:\DHCP\RogueClientsFinal.txt
Remove-Item C:\DHCP\RogueClients.txt

#send email to netadmin
$smtpserver = "SMTP SERVER IP"
$from="DHCPSERVER@domain.com"
$to="TheCleaner@domain.com"
$subject="Non-AD joined DHCP clients"
$body= (Get-Content C:\DHCP\RogueClientsFinal.txt) -join '<BR>&nbsp;<BR>'
$mailer = new-object Net.Mail.SMTPclient($smtpserver)
$msg = new-object Net.Mail.MailMessage($from,$to,$subject,$body)
$msg.IsBodyHTML = $true
$mailer.send($msg)

Надеюсь, это поможет кому-то другому!

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

Задача разделена на несколько частей, вот предложения по наиболее интересным. Без примеров из журнала это лучшее, что я могу сделать, так что это просто предложения, а не решения.

Для анализа журнала используйте get-content с -wait параметр. Для моего случая использования достаточно найти ошибку в журнале ошибок.

Вот что сработало для моего собственного варианта использования, простите за форматирование:

get-content E:\temp13\log.txt -tail(1) -wait | where {$_ -match "ERROR"} |
    foreach {
        send-mailmessage `
        -port 25 `
        -smtpserver my.mail.server `
        -from logmon@a.b `
        -to erike@a.b `
        -subject "test logmonitor" `
        -body "ERROR found: $_" `
        }

Вместо $_ -match "ERROR" вам нужно как-то разделить поле идентификатора журнала и имя компьютера. Я не знаю, как это сделать наилучшим образом прямо сейчас, но поскольку where-object -match дает поддержку регулярных выражений, я думаю, это может быть вариант. Вы также можете начать с сохранения переменной $ _ в другой новой переменной, чтобы иметь возможность забрать ее в удобное для вас время в конвейере, внутри вложенных циклов foreach и т. Д.

Предполагая, что вы можете получить имя компьютера, я думаю, что get-adcomputer командлет будет вашим самым простым способом запроса вашего AD (import-module activedirectory), а я догадываюсь об ошибке отправить почту?

Используя import-csv было бы, конечно, гораздо более элегантно в вашем случае, но я не знаю, как с этим справиться (если кто-нибудь прочитает это и знает трюк в этом переулке, пожалуйста, поделитесь).

Хотя это не касается вашего желаемого решения, вариант, который может достичь вашей цели, - это использовать arpwatch(ссылка на сайт), чтобы уведомить вас, когда в сети появляется новый (ранее невидимый) хост.

Альтернатива Windows для arpwatch по-видимому декафеинатид но я никогда не использовал его, поэтому не могу сказать, хорошо это или плохо.

Если вы уверены в идентификаторе события и что в журнале DHCP с этим идентификатором не регистрируются никакие другие события, кроме тех, которые вам интересны, push действительно возможен.

1) Откройте диспетчер серверов, перейдите в журнал DHCP в средстве просмотра событий.

2) Найдите репрезентативную запись, к которой вы хотите прикрепить свое действие. Выберите его и щелкните правой кнопкой мыши.

3) Выберите «Присоединить задачу к этому событию».

4) Откроется Мастер создания задачи, забери оттуда ...

На самом деле существует явный вариант электронной почты, но если вам нужно больше логики, вы, конечно, можете использовать параметр start-a-program, чтобы запустить powershell.exe и прикрепить к нему сценарий. Существует множество отличных руководств, которые можно использовать в Google, о том, как разрешить диспетчеру задач запускать сценарии PowerShell, если вам нужно руководство.

Прямая альтернатива, которую я вижу, - использовать pull, анализируя журнал событий с помощью PowerShell через запланированные интервалы. «Microsoft Scripting Guy», он же Эд Уилсон, написал несколько замечательных сообщений в блогах о том, как анализировать журнал событий с помощью командлетов, доступных в различных версиях PowerShell, поэтому я бы посоветовал взять его блог в качестве отправной точки.

Что касается настоящих командлетов, у меня сейчас нет времени, чтобы вытащить свой запас удобных фрагментов, но я загляну снова через день или два и может внести свой вклад, если никто другой не представился с некоторыми хорошо подобранными или у вас нет Сам не решил :-)