Windows DHCP Server - receba uma notificação quando um dispositivo não associado ao AD obtiver um endereço IP

15

CENÁRIO

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

Eu tenho um controlador de domínio padrão do Windows 2008 R2 com a função de servidor DHCP. Ele distribui IPs através de vários escopos IPv4, não há problema.

O QUE EU GOSTARIA

Eu gostaria de uma maneira de criar uma entrada de notificação / log de eventos / semelhante sempre que um dispositivo recebe uma concessão de endereço DHCP e esse dispositivo NÃO é um computador ingressado no domínio no Active Directory. Não importa se é Powershell personalizado, etc.

Conclusão = gostaria de saber quando dispositivos que não são de domínio estão na rede sem usar o 802.1X no momento. Eu sei que isso não será responsável por dispositivos IP estáticos. Eu tenho um software de monitoramento que irá verificar a rede e encontrar dispositivos, mas não é tão granular em detalhes.

PESQUISA REALIZADA / OPÇÕES CONSIDERADAS

Não vejo tais possibilidades com o registro incorporado.

Sim, eu conheço o 802.1X e tenho a capacidade de implementá-lo a longo prazo neste local, mas ainda estamos longe de um projeto como esse e, embora isso resolvesse problemas de autenticação de rede, isso ainda é útil para mim fora de objetivos 802.1X.

Procurei alguns bits de script etc. que podem ser úteis, mas as coisas que estou encontrando me levam a acreditar que meu google-fu está falhando comigo no momento.

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

  1. O dispositivo recebe um endereço DHCP
  2. A entrada do log de eventos é registrada (a identificação do evento 10 no log de auditoria do DHCP deve funcionar (uma vez que uma nova concessão é a que mais me interessaria, não as renovações): http://technet.microsoft.com/en-us/library /dd759178.aspx )
  3. Nesse ponto, um script de algum tipo provavelmente teria que assumir os demais "PASSOS" abaixo.
  4. De alguma forma, consulte esse log do DHCP para esses IDs de evento 10 (eu adoraria enviar, mas acho que o pull é o único recurso aqui)
  5. Analise a consulta para o nome do dispositivo ao qual está sendo atribuída a nova concessão
  6. Consulta AD para o nome do dispositivo
  7. Se não for encontrado no AD, envie um email de notificação

Se alguém tiver alguma idéia de como fazer isso corretamente, eu realmente aprecio isso. Não estou procurando um "me dê o codez", mas gostaria de saber se existem alternativas para a lista acima ou se não estou pensando claramente e existe outro método para coletar essas informações. Se você tiver comandos de snippets de código / PS que gostaria de compartilhar para ajudar a fazer isso, tanto melhor.

O limpador
fonte
Você deseja bloqueá-los ou apenas ser notificado se eles obtiverem um IP?
HostBits 4/11/13
@ Cheekaleak - basta ser notificado.
TheCleaner 4/13
E as impressoras de rede que usam DHCP?
jftuga
@ jftuga - usamos IPs estáticos para impressoras de rede.
TheCleaner

Respostas:

6

Com muito obrigado a ErikE e aos outros aqui, segui um caminho ... Não direi que é o caminho certo, mas o script do Powershell que inventei faz o truque.

O código está abaixo se alguém quiser. Basta executá-lo apontando manualmente para cada servidor DHCP ou agendá-lo (novamente apontando 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. Envia as concessões para um arquivo csv
  3. Lê novamente esse arquivo CSV para consultar o AD
  4. Consulta o AD ao computador
  5. Se não encontrado, gera um novo arquivo txt
  6. Cria um arquivo txt final de lista exclusivo do criado em # 5 acima (pois pode haver enganos se o cliente registrar mais de uma vez ou com mais de um adaptador)
  7. envia por e-mail o conteúdo do arquivo final para um administrador

O que você precisará:

O script usa o módulo AD ( import-module activedirectory), portanto, é melhor executá-lo em um AD DC executando DHCP. Se esse não for o seu caso, você pode instalar o módulo AD powershell: http://blogs.msdn.com/b/rkramesh/archive/2012/01/17/how-to-add-active-directory- module-in-powershell-in-windows-7.aspx

Você também precisará dos cmdlets do AD Powershell da Quest encontrados aqui: http://www.quest.com/powershell/activeroles-server.aspx . Instale ESTES ANTES de executar o script ou ele falhará.

O script em si (higienizado, você precisará configurar algumas das variáveis ​​para atender às suas necessidades, como os nomes dos arquivos de entrada, o domínio ao qual se conectar, o servidor dhcp ao qual se conectar, as configurações de e-mail no 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!

O limpador
fonte
3

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

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

Para analisar o log, use get-contentcom o -waitparâmetro Para o meu caso de uso, basta encontrar um erro em um log de erros.

Isto é 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. Eu não tenho certeza de como fazer isso da melhor maneira no momento, mas desde que where-object -matchdá suporte a regex, acho que poderia ser uma opção. Você também pode começar armazenando a variável $ _ em outra nova variável, para buscá-la conforme sua conveniência mais tarde no pipeline, dentro de loops foreach aninhados etc.

Supondo que você possa obter o nome do computador, acho que o get-adcomputercmdlet seria a maneira mais simples de consultar seu AD ( import-module activedirectory) e, por erro, envie um email?

Usar o import-csvobviamente seria muito mais elegante no seu caso, mas não conheço nenhuma maneira de segui-lo (se alguém ler isso e souber um truque naquele beco, por favor, compartilhe).

ErikE
fonte
Obrigado ErikE, eu vou continuar com isso e informá-lo. Vou precisar descobrir uma maneira de obter as informações, consultar o AD e, depois do "alerta", ignorar as verificações futuras dessa mesma linha de entrada. Por exemplo, se ele consultar o arquivo a cada cinco minutos, não quero que ele repita as mesmas informações e envie um alerta falso a cada 5 minutos.
TheCleaner 5/11
Duas coisas que eu acho bem legais: se você deixar o script rodar, o parâmetro wait fará com que ele esteja constantemente esperando que uma nova linha apareça. Você não precisa executar novamente o script. Ele dará um efeito de empurrão ao invés de puxar. Além disso, a cauda (1) fará com que apenas analise a última 1 linha no início. Portanto, se o gerenciador de tarefas achar que precisa reiniciar o script e você encontrar uma maneira de injetar uma linha de espaço reservado deslocando a última linha para acionar um alerta, você terá desarmado o aborrecimento dos realerts.
ErikE
1
Erik, eu encontrei uma maneira de exportar as concessões do DHCP (porcaria que 2012 tenha um cmdlet PS, mas 2008 não) para um arquivo csv. Dessa forma, eu não mexo com os logs de auditoria reais e não preciso me preocupar em quebrar nada com a entrada. Vou começar a mexer com o restante do código e atualizar em breve.
TheCleaner 5/11
1

Supondo que você tenha certeza da identificação do evento e que nenhum outro evento faça logon nessa identificação no log do DHCP, exceto nos de seu interesse, o push é realmente uma opção.

1) Abra o Gerenciador do Servidor, 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 para criação de tarefas é aberto, retire-o de lá ...

Na verdade, existe uma opção de email explícita, mas se você precisar de mais lógica do que isso, é claro que está livre para usar a opção de iniciar um programa para iniciar o powershell.exe e anexar um script a ele. Há muitos excelentes tutoriais googleable sobre como deixar o Gerenciador de Tarefas executar scripts do PowerShell, se você precisar de orientação.

A alternativa imediata que vejo é usar pull analisando o log de eventos usando o PowerShell em intervalos agendados. "The 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, usar o blog como ponto de partida seria minha sugestão.

Quanto aos cmdlets reais, não tenho tempo agora para retirar meu estoque de trechos úteis, mas analisarei novamente em um dia ou dois e poderá contribuir se ninguém mais aparecer com alguns bem escolhidos ou se você não tiver Resolveu tudo sozinho :-)

ErikE
fonte
2
Erik, obrigado. O problema aqui é que o DHCPsrvlog- "dia" em C: \ windows \ system32 \ DHCP (com a auditoria DHCP ativada na GUI do servidor DHCP), não grava em nenhum log do Visualizador de Eventos, incluindo o log do visualizador de eventos do Servidor DHCP em Applications and Services Logs(até agora com base em minha pesquisa / teste)
TheCleaner
Eu tinha esquecido esses logs. Mas acredito que este é um local possível: analise o log de texto usando get-content com as diretivas -wait e -tail. Isso é semelhante ao tail em * nix. Para garantir que uma instância esteja sempre analisando o log, o Gerenciador de Tarefas pode agendar o script na inicialização do sistema e iniciar cada (menor intervalo possível), mas permitir apenas uma instância em execução. Se o seu evento aparecer, ative a lógica.
ErikE
Acontece que eu tenho um problema de análise de log semelhante para resolver no Windows, publicarei minhas descobertas nessa parte em particular quando tiver certeza de que funciona e, provavelmente, outros blocos de construção que tenho por aí que devem ser úteis para você. Você poderia colar algumas linhas representativas, mas ofuscadas, do seu log dhcp? Estou particularmente interessado no formato do nome do dispositivo.
ErikE
1

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

Uma alternativa para o Windows arpwatchparece ser descafeinatídeo, mas eu nunca o usei, portanto não posso falar por isso bom ou ruim.

fukawi2
fonte
Obrigado. A ideia existe som. Eu posso seguir esse caminho, se necessário.
TheCleaner
O benefício adicional é que isso também traria novas máquinas que usam IPs estáticos, o que não estava no seu escopo declarado, mas provavelmente deveria estar.
mfinni