Windows DHCP Server - отримуйте сповіщення, коли пристрій, який не приєднався до AD, отримує IP-адресу


15

СЦЕНАРІО

Спростити це до найпростішого прикладу:

У мене стандартний постійний струм для 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 подій (я б хотів натиснути, але я здогадуюсь, що "єдиний регрес"
  5. Проаналізуйте запит на ім’я пристрою, якому призначена нова оренда
  6. Запит запиту AD на ім’я пристрою
  7. Якщо оголошення не знайдено в AD, надішліть повідомлення електронною поштою

Якщо у когось є ідеї, як правильно це зробити, я дуже вдячний. Я не шукаю "дай кодек", але хотів би дізнатися, чи є альтернативи вищевказаному списку або якщо я не думаю, що є чітким і чи існує інший метод для збору цієї інформації. Якщо у вас є фрагменти коду / команди PS, якими ви хочете поділитися, щоб допомогти досягти цього, ще краще.


Ви хочете заблокувати їх або просто отримуєте сповіщення, якщо отримують IP-адресу?
HostBits

@Cheekaleak - просто отримуйте сповіщення.
TheCleaner

Що з мережевими принтерами, які використовують DHCP?
jftuga

@jftuga - ми використовуємо статичні IP-адреси для мережевих принтерів.
TheCleaner

Відповіді:


6

Дякую Еріку та іншим тут, я пішов шляхом ... Я не скажу, що це правильний шлях, але сценарій Powershell, який я придумав, робить свою справу.

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

Що робить сценарій:

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

Що вам знадобиться:

У скрипті використовується модуль AD ( import-module activedirectory), тому його найкраще запустити на AD DC, що працює DHCP. Якщо у вас це не так, ви можете встановити модуль AD 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)

Сподіваюсь, що допоможе хтось інший!


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

Якщо припустити, що ви можете отримати ім'я комп’ютера, я думаю, get-adcomputerкомандлет буде вашим найпростішим способом запиту вашої AD ( import-module activedirectory), і я думаю, що при помилці надіслати пошту?

Використовуючи import-csvб, звичайно , набагато більш витонченим у вашому випадку, але я не знаю ні одного способу хвостових його (якщо хто - небудь трапляється , щоб прочитати це і знає своє діло до цього провулка, будь ласка, будь ласка складеному).


Дякую Еріку, я побіжу з цим і дам вам знати. Мені потрібно буде знайти спосіб захоплення інформації, запит AD, а потім після "попередження" ігнорувати майбутні перевірки того самого рядка введення. Наприклад, якщо він запитує файл кожні п’ять хвилин, я не хочу, щоб він переосмислював ту саму інформацію та надсилав сповіщення кожні кожні 5 хвилин.
TheCleaner

Дві речі, які я вважаю легкими акуратними: Якщо ви просто дозволите запустити сценарій, параметр очікування буде постійно чекати появи нового рядка. Не потрібно повторювати сценарій. Його будуть давати ефект поштовху, а не тягнути. Крім того, у хвоста (1) він буде просто розбирати останній 1 рядок на початку. Таким чином, якщо менеджер завдань виявить, що йому доведеться перезапустити скрипт, і ви знайдете спосіб ввести рядок-заповнювач, зміщуючи останній рядок, щоб викликати сповіщення, ви обеззброїли роздратування реагентів.
ЕрікЕ

1
Еріку, я знайшов спосіб експорту оренди з DHCP (дивно, що 2012 має командлет PS для цього, але 2008 - ні) у файл csv. Таким чином я не псуюся з фактичними журналами аудиту і не потрібно перейматися тим, що щось порушують із введенням даних. Я почну возитися з тим, щоб виконати решту коду і скоро оновити.
TheCleaner

1

За припущенням, що вам відомо про ідентифікатор події і що до цього ідентифікатора в журналі DHCP немає жодного іншого журналу подій, окрім тих, що вас цікавлять, натиснення справді є варіантом.

1) Відкрийте диспетчер серверів, перейдіть до журналу DHCP у переглядачі подій.

2) Знайдіть репрезентативний запис, до якого ви хочете долучити свою дію. Виберіть його та клацніть правою кнопкою миші.

3) Оберіть "Приєднати завдання до цієї події".

4) Відкриється майстер створення завдань, зніміть його звідти ...

Насправді є явна опція електронної пошти, але якщо вам потрібна більше логіки, ніж ви, звичайно, можете скористатися параметром запуску програми, щоб розпалити powershell.exe і приєднати до нього сценарій. Існує маса чудових інструментів Google щодо того, як дозволити Диспетчеру завдань запускати сценарії оболонок повноважень, якщо вам потрібні вказівки.

Безпосередньою альтернативою, яку я бачу, є використання потягу, розбираючи Журнал подій, використовуючи паттерн силу через заплановані інтервали. "Хлопець сценарію Майкрософт", відомий також як Ед Вілсон, написав кілька дивовижних публікацій в блозі про те, як проаналізувати Журнал подій, використовуючи командлети, доступні в різних версіях повноцінної оболонки, тож вважати його блог відправним моментом було б моєю пропозицією.

Що стосується фактичних командлетів, у мене зараз немає часу, щоб витягнути мою скриньку зручних фрагментів, але я загляну заново через день-два і, можливо, зробимо внесок, якщо ніхто не зіткнувся з якимись обраними, або у вас немає Я все вирішив сам :-)


2
Еріку, дякую. Проблема тут полягає в тому, що DHCPsrvlog- "день" у C: \ windows \ system32 \ DHCP (при включеному аудиті DHCP в GUI сервера DHCP) не записує до жодного журналу перегляду подій, включаючи журнал перегляду подій DHCP-Server під Applications and Services Logs(поки що грунтується на моїх дослідженнях / тестуваннях)
TheCleaner

Я забув про ці колоди. Але я вважаю, що це можливе місце: проаналізуйте текстовий журнал, використовуючи get-content з директивами -wait та -tail. Це схоже на хвіст у * nix. Щоб переконатися, що примірник завжди розбирає журнал, диспетчер завдань може запланувати сценарій при запуску системи, потім запустити кожен (можливий найкоротший інтервал), але дозволити лише один запущений екземпляр. Якщо ваша подія спливе, введіть логіку.
ErikE

Виявляється, у мене є аналогічна проблема розбору журналу, яку потрібно вирішити в Windows, я опублікую свої висновки з тієї конкретної частини, коли я впевнений, що вона працює, і, ймовірно, деякі інші будівельні блоки, які я лежу навколо, які повинні бути корисними для вас. Чи можете ви вставити пару репрезентативних, але затьмарених рядків із журналу dhcp? Мене особливо цікавить формат імені пристрою.
ErikE

1

Хоча це не стосується бажаного рішення, можливим варіантом, який може досягти вашої мети, є використання arpwatch( посилання ) для сповіщення про появу в мережі нового (раніше небаченого) хоста.

Альтернатива для Windows, arpwatchздається, кофеїнатид, але я ніколи її не використовував, тому не можу говорити про це добре чи погано.


Спасибі. Ідея є звук. Я можу піти цією дорогою, якщо потрібно.
TheCleaner

Додатковою перевагою є те, що це також охопить нові машини, які використовують статичні IP-адреси, що не було у вашому заявленому обсязі, але, мабуть, повинно бути.
mfinni
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.