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

15

СЦЕНАРИЙ

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

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

Что бы я хотел

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

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

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

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

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

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

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

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

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

Очиститель
источник
Вы хотите заблокировать их или просто получить уведомление, если они получат IP?
HostBits
@Cheekaleak - просто получать уведомления.
TheCleaner
А как насчет сетевых принтеров, которые используют DHCP?
Jftuga
@jftuga - мы используем статические IP-адреса для сетевых принтеров.
TheCleaner

Ответы:

6

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

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

Что делает скрипт:

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

Что вам нужно:

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

Вам также понадобятся командлеты AD Powershell от Quest, которые можно найти здесь: 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)

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

Очиститель
источник
3

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

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

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

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

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

ErikE
источник
Спасибо ErikE, я побегу с этим и дам тебе знать. Мне нужно найти способ получить информацию, запросить AD, а затем после «оповещения» игнорировать будущие проверки этой же строки ввода. Например, если он запрашивает файл каждые пять минут, я не хочу, чтобы он повторно анализировал ту же информацию и отправлял двойное предупреждение каждые 5 минут.
TheCleaner
Две вещи, которые я нахожу немного опрятными: если вы просто дадите скрипту работать, параметр wait будет постоянно ожидать появления новой строки. Вам не нужно повторно запускать сценарий. Это даст эффект толчка, а не тянуть. Кроме того, у tail (1) будет только парсинг последней 1 строки в начале. Таким образом, если диспетчер задач обнаружит, что он должен перезапустить сценарий, и вы найдете способ вставить строку-заполнитель, смещающую последнюю строку, чтобы вызвать предупреждение, вы отключите раздражение реалеров.
ErikE
1
Эрик, я нашел способ экспортировать аренду из DHCP (дерьмово, что в 2012 году есть командлет PS для него, но в 2008 году нет) в файл csv. Таким образом, я не связываюсь с реальными журналами аудита и не должен беспокоиться о том, чтобы что-то сломать с помощью ввода. Я начну возиться с выполнением остальной части кода и скоро обновлю.
TheCleaner
1

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

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

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

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

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

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

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

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

ErikE
источник
2
Эрик, спасибо. Проблема заключается в том, что DHCPsrvlog- «день» в C: \ windows \ system32 \ DHCP (с включенным аудитом DHCP в графическом интерфейсе DHCP-сервера) не записывает данные ни в какой журнал просмотра событий, включая журнал просмотра событий DHCP-сервера в разделе Applications and Services Logs(пока основано на моих исследованиях / тестировании)
TheCleaner
Я забыл об этих журналах. Но я считаю, что это возможно: проанализируйте текстовый журнал, используя get-content с директивами -wait и -tail. Это похоже на tail в * nix. Чтобы убедиться, что экземпляр всегда анализирует журнал, диспетчер задач может запланировать сценарий при запуске системы, а затем запускать каждый (кратчайший возможный интервал), но разрешать только один работающий экземпляр. Если ваше событие всплывает, включите логику.
ErikE
Оказывается, у меня есть похожая проблема с анализом журналов в Windows, я опубликую свои выводы по этой конкретной части, когда я уверен, что она работает, и, вероятно, некоторые другие строительные блоки, которые у меня лежат, которые могут быть полезны для вас. Не могли бы вы вставить пару репрезентативных, но запутанных строк из вашего журнала dhcp? Меня особенно интересует формат имени устройства.
ErikE
1

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

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

fukawi2
источник
Благодарю. Идея есть звук. Я могу пойти по этому пути, если это необходимо.
TheCleaner
Дополнительным преимуществом является то, что это также поймает новые машины, которые используют статические IP-адреса, что не было в указанной вами области, но, вероятно, должно быть.
mfinni