Servidor DHCP do Windows - recebe uma notificação quando um dispositivo não associado ao AD recebe um endereço IP

15

CENÁRIO

Para simplificar isso, é o exemplo mais fácil:

Eu tenho um DC padrão do Windows 2008 R2 com a função de servidor DHCP. Distribui IPs através de vários escopos IPv4, sem problemas.

O QUE EU GOSTARIA

Eu gostaria de uma maneira de criar uma notificação / entrada de log de eventos / similar sempre que um dispositivo obtivesse uma concessão de endereço DHCP e esse dispositivo NÃO fosse um computador associado ao domínio no Active Directory. Não importa para mim se é costume Powershell, etc.

Linha de fundo = Gostaria de saber quando dispositivos sem domínio estão na rede sem usar o 802.1X no momento. Eu sei que isso não conta para dispositivos IP estáticos. Eu tenho um software de monitoramento que verifica a rede e encontra dispositivos, mas não é tão granular em detalhes.

PESQUISA FEITA / OPÇÕES CONSIDERADAS

Não vejo essas possibilidades com o registro em log incorporado.

Sim, estou ciente do 802.1X e tenho a capacidade de implementá-lo a longo prazo neste local, mas estamos a algum tempo distante de um projeto como esse e, embora isso resolva problemas de autenticação de rede, isso ainda é útil para mim fora dos objetivos 802.1X.

Eu procurei por alguns scripts, etc., que podem ser úteis, mas as coisas que estou encontrando me levam a acreditar que o meu google-fu está falhando comigo no momento.

Acredito que a lógica abaixo seja sólida ( supondo que não exista alguma solução existente ):

  1. O dispositivo recebe o endereço DHCP
  2. A entrada do log de eventos é registrada (o ID de evento 10 no log de auditoria do DHCP deve funcionar (já que uma nova concessão é o que eu estaria mais interessado, não renovações): link )
  3. Nesse ponto, um script de algum tipo provavelmente terá que ser substituído pelos "STEPS" restantes abaixo.
  4. De alguma forma, consultar esse log de DHCP para esses ID's de evento 10 (eu adoraria empurre, mas eu estou supondo que puxar é o único recurso aqui)
  5. Analise a consulta para o nome do dispositivo que está sendo atribuído à nova concessão
  6. Consulta AD para o nome do dispositivo
  7. Se não for encontrado no AD, envie um e-mail de notificação

Se alguém tiver alguma ideia de como fazer isso corretamente, eu realmente aprecio isso. Eu não estou procurando por um "gimme the codez", mas gostaria de saber se existem alternativas para a lista acima ou se eu não estou pensando claro e existe outro método para coletar essas informações. Se você tiver snippets de código / comandos PS que gostaria de compartilhar para ajudar a realizar isso, melhor ainda.

    
por TheCleaner 04.11.2013 / 20:50

4 respostas

6

Com muito obrigado a ErikE e aos outros aqui, eu segui um caminho ... Eu não direi que é o caminho certo, mas o roteiro de Powershell que eu criei faz o truque.

O código está abaixo se alguém quiser. Basta executá-lo manualmente apontando para cada servidor DHCP ou agendá-lo (apontando novamente para cada servidor DHCP no script).

O que o script faz:

  1. Obtém informações de concessão do servidor DHCP (concessões ipv4)
  2. Gera as concessões para um arquivo csv
  3. Lê de volta nesse arquivo CSV para consultar o AD
  4. Consulta o AD para o computador
  5. Se não forem encontradas saídas para um novo arquivo txt
  6. Cria um arquivo txt final de lista única daquele criado no item 5 acima (já que pode haver enganos se o cliente se registrar mais de uma vez ou com mais de um adaptador)
  7. envia por e-mail o conteúdo do arquivo de saída final para um administrador

O que você precisa:

O script usa o módulo do AD ( import-module activedirectory ), portanto, é melhor executá-lo em um DC do AD DC em execução. Se este não for o seu caso, você pode instalar o módulo AD powershell:

Você também precisará dos cmdlets AD Powershell da Quest encontrados aqui: link . Instale ESTAS ANTES executando o script ou ele falhará.

O script em si (desinfetado, você precisará configurar algumas das variáveis para atender às suas necessidades, como nomes de arquivos de entrada, domínio para conexão, servidor dhcp para conexão, configurações de e-mail próximas ao final etc.):

# 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="[email protected]"
$to="[email protected]"
$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)

Espero que ajude alguém!

    
por 06.11.2013 / 23:52
3

OK, não tenho certeza se estou seguindo a etiqueta aqui, mas estou postando uma segunda resposta em vez de editar minha anterior, pois ela contém algumas informações que podem ser úteis para alguém, mesmo se forem irrelevantes para este caso. Se isso me fizer um idiota neste fórum, fique à vontade para me informar sobre meus erros.

O problema está dividido em várias partes, aqui estão sugestões para os que eu acho mais interessantes. Sem exemplos do log, isso é o melhor que posso fazer, por isso são apenas sugestões e não soluções.

Para analisar o log use get-content com o parâmetro -wait . Para meu caso de uso, é suficiente encontrar um erro em um log de erros.

Isso é o que funcionou para meu próprio caso de uso, perdoe a formatação:

get-content E:\temp13\log.txt -tail(1) -wait | where {$_ -match "ERROR"} |
    foreach {
        send-mailmessage '
        -port 25 '
        -smtpserver my.mail.server '
        -from [email protected] '
        -to [email protected] '
        -subject "test logmonitor" '
        -body "ERROR found: $_" '
        }

Em vez do $_ -match "ERROR" , você precisaria separar o campo de ID do log e o nome do computador de alguma forma. Não tenho certeza de como fazer isso da melhor maneira agora, mas como where-object -match oferece suporte a regex, acho que isso poderia ser uma opção. Você também pode começar armazenando a variável $ _ em outra nova variável, para poder pegá-la quando quiser, mais adiante no pipeline, dentro de loops foreach aninhados, etc.

Supondo que você possa obter o nome do computador, eu acho que o cmdlet get-adcomputer seria a maneira mais simples de consultar o seu AD ( import-module activedirectory ), e eu acho que no erro enviar email?

Usar o import-csv seria, claro, muito mais elegante no seu caso, mas eu não estou ciente de nenhuma maneira de usá-lo (se alguém lesse isso e soubesse um truque naquele beco, por favor, por favor, compartilhe ).

    
por 05.11.2013 / 02:03
1

Supondo que você tenha certeza da ID do evento e que nenhum outro evento registra esse ID no log do DHCP, mas os que você está interessado, o push é de fato uma opção.

1) Abra o Gerenciador de Servidores, vá para o log do DHCP no Visualizador de Eventos.

2) Encontre uma entrada representativa à qual você deseja anexar sua ação. Selecione e clique com o botão direito.

3) Escolha "Anexar tarefa a este evento".

4) O Assistente de criação de tarefas é aberto, tire-o de lá ...

Existe, na verdade, uma opção de email explícita, mas se você precisar de mais lógica do que é claro, você está livre para usar a opção start-a-program para ativar o powershell.exe e anexar um script a ele. Há uma abundância de excelentes conhecimentos googláveis sobre como permitir que o Gerenciador de Tarefas execute scripts de powershell se você precisar de orientação.

A alternativa imediata que vejo é usar pull ao analisar o Log de Eventos usando o powershell em intervalos programados. "O Microsoft Scripting Guy", também conhecido como Ed Wilson, escreveu algumas postagens impressionantes sobre como analisar o log de eventos usando os cmdlets disponíveis nas diferentes versões do PowerShell, portanto, tomar seu blog como ponto de partida seria minha sugestão.

Quanto aos cmdlets reais, não tenho tempo agora para extrair meu estoque de trechos úteis, mas voltarei a vê-los em um ou dois dias e poderei contribuir se mais ninguém tiver participado com alguns bem escolhidos, ou você não resolveu tudo sozinho: -)

    
por 04.11.2013 / 21:24
1

Embora isso não resolva a solução desejada, uma opção que pode atingir seu objetivo é utilizar arpwatch ( link ) para notificá-lo quando um novo host (inédito) é visto na rede.

Uma alternativa do Windows para arpwatch parece ser decaffeinatid mas nunca usei, por isso não posso falar bem ou mal.

    
por 04.11.2013 / 23:06