Serveur DHCP Windows - recevez une notification lorsqu'un périphérique non joint à AD obtient une adresse IP

15

SCÉNARIO

Pour simplifier cela, c'est l'exemple le plus simple:

J'ai un contrôleur de domaine standard Windows 2008 R2 avec le rôle de serveur DHCP. Il distribue des IP via différentes étendues IPv4, aucun problème là-bas.

CE QUE J'AIMERAIS

Je voudrais un moyen de créer une notification / entrée de journal des événements / similaire chaque fois qu'un appareil obtient un bail d'adresse DHCP et que cet appareil N'EST PAS un ordinateur joint à un domaine dans Active Directory. Peu importe que ce soit un Powershell personnalisé, etc.

Bottom line = Je voudrais un moyen de savoir quand des périphériques hors domaine sont sur le réseau sans utiliser 802.1X pour le moment. Je sais que cela ne tiendra pas compte des appareils IP statiques. J'ai un logiciel de surveillance qui analysera le réseau et trouvera des périphériques, mais ce n'est pas aussi détaillé.

RECHERCHE FAITE / OPTIONS CONSIDÉRÉES

Je ne vois pas de telles possibilités avec la journalisation intégrée.

Oui, je connais le 802.1X et j'ai la possibilité de l'implémenter à long terme à cet endroit, mais nous sommes loin d'un projet comme celui-ci, et même si cela résoudrait les problèmes d'authentification réseau, cela m'a toujours été utile à l'extérieur des objectifs 802.1X.

J'ai cherché quelques bits de script, etc. qui pourraient s'avérer utiles, mais les choses que je trouve me font croire que mon google-fu me fait défaut en ce moment.

Je crois que la logique ci-dessous est bonne (en supposant qu'il n'y a pas de solution existante ):

  1. L'appareil reçoit une adresse DHCP
  2. L'entrée du journal des événements est enregistrée (l'ID d'événement 10 dans le journal d'audit DHCP devrait fonctionner (car un nouveau bail est ce qui m'intéresserait le plus, pas les renouvellements): http://technet.microsoft.com/en-us/library /dd759178.aspx )
  3. À ce stade, un script quelconque devrait probablement prendre le relais des "STEPS" restants ci-dessous.
  4. En quelque sorte, interrogez ce journal DHCP pour ces ID d'événement 10 (j'adorerais pousser, mais je suppose que tirer est le seul recours ici)
  5. Analyser la requête pour le nom de l'appareil auquel le nouveau bail est attribué
  6. Recherchez AD pour le nom de l'appareil
  7. SI pas trouvé dans la MA, envoyer un e - mail de notification

Si quelqu'un a des idées sur la façon de procéder correctement, je l'apprécierais vraiment. Je ne cherche pas un "donnez-moi le code" mais j'aimerais savoir s'il existe des alternatives à la liste ci-dessus ou si je ne pense pas clairement et qu'une autre méthode existe pour collecter ces informations. Si vous avez des extraits de code / commandes PS que vous souhaitez partager pour y parvenir, c'est encore mieux.

Le nettoyeur
la source
Cherchez-vous à les bloquer, ou tout simplement être averti s'ils obtiennent une adresse IP?
HostBits
@Cheekaleak - soyez simplement averti.
TheCleaner
Qu'en est-il des imprimantes réseau qui utilisent DHCP?
jftuga
@jftuga - nous utilisons des adresses IP statiques pour les imprimantes réseau.
TheCleaner

Réponses:

6

Avec beaucoup de remerciements à ErikE et aux autres ici, j'ai emprunté un chemin ... Je ne dirai pas que c'est le bon chemin, mais le script Powershell que j'ai trouvé fait l'affaire.

Le code est ci-dessous si quelqu'un le veut. Il suffit de l'exécuter manuellement en pointant sur chaque serveur DHCP ou de le planifier (en pointant de nouveau chaque serveur DHCP dans le script).

Ce que fait le script:

  1. Obtient les informations de bail du serveur DHCP (baux ipv4)
  2. Sort les baux dans un fichier csv
  3. Relit ce fichier CSV pour interroger AD
  4. Requêtes AD pour l'ordinateur
  5. S'il n'est pas trouvé, les sorties vers un nouveau fichier txt
  6. Crée un fichier txt final de liste unique à partir de celui créé au n ° 5 ci-dessus (car il peut y avoir des dupes si le client s'enregistre plus d'une fois ou avec plus d'un adaptateur)
  7. envoie par e-mail le contenu du fichier de sortie final à un administrateur

Ce dont vous aurez besoin:

Le script utilise le module AD ( import-module activedirectory), il est donc préférable de l'exécuter sur un AD DC exécutant DHCP. Si ce n'est pas le cas pour vous, vous pouvez installer le module PowerShell AD: http://blogs.msdn.com/b/rkramesh/archive/2012/01/17/how-to-add-active-directory- module-dans-powershell-dans-windows-7.aspx

Vous aurez également besoin des applets de commande AD Powershell de Quest qui se trouvent ici: http://www.quest.com/powershell/activeroles-server.aspx . Installez-les AVANT d' exécuter le script ou il échouera.

Le script lui-même (aseptisé, vous devrez configurer certaines des variables pour répondre à vos besoins, comme les noms des fichiers d'entrée, le domaine auquel se connecter, le serveur DHCP pour se connecter, les paramètres de messagerie vers la fin, 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)

J'espère que cela aide quelqu'un d'autre!

Le nettoyeur
la source
3

OK, je ne suis pas sûr de suivre l'étiquette ici, mais je poste une deuxième réponse au lieu de modifier la précédente, car elle contenait des informations qui pourraient être utiles à quelqu'un même si cela ne se révélait pas pertinent dans ce cas. Si cela fait de moi un idiot dans ce forum, n'hésitez pas à m'informer de mes erreurs.

Le problème est divisé en plusieurs parties, voici des suggestions pour celles que je trouve les plus intéressantes. Sans exemples du journal, c'est le mieux que je puisse faire, il ne s'agit donc que de suggestions et non de solutions.

Pour analyser le journal, utilisez get-contentle -waitparamètre. Pour mon cas d'utilisation, il suffit de trouver une erreur dans un journal d'erreurs.

C'est ce qui a fonctionné pour mon propre cas d'utilisation, pardonnez le formatage:

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: $_" `
        }

Au lieu de, $_ -match "ERROR"vous devrez séparer le champ ID du journal et le nom de l'ordinateur d'une manière ou d'une autre. Je ne suis pas sûr de savoir comment procéder de la meilleure façon actuellement, mais comme il where-object -matchoffre un support regex, je suppose que cela pourrait être une option. Vous pouvez également commencer par stocker la variable $ _ dans une autre nouvelle variable, pour pouvoir la récupérer à votre convenance plus tard dans le pipeline, à l'intérieur de boucles foreach imbriquées, etc.

En supposant que vous pouvez obtenir le nom de l'ordinateur, je suppose que l' get-adcomputerapplet de commande serait votre moyen le plus simple d'interroger votre AD ( import-module activedirectory), et je suppose qu'en cas d'erreur, envoyer un courrier?

Utiliser le import-csvserait bien sûr beaucoup plus élégant dans votre cas, mais je ne connais aucun moyen de le suivre (si quelqu'un arrive à lire ceci et connaît une astuce dans cette ruelle alors s'il vous plaît, partagez s'il vous plaît).

ErikE
la source
Merci ErikE, je vais courir avec ça et je vous le ferai savoir. Je vais devoir trouver un moyen de récupérer les informations, interroger AD, puis après l '"alerte" ignorer les vérifications futures de cette même ligne d'entrée. Par exemple, s'il interroge le fichier toutes les cinq minutes, je ne veux pas qu'il analyse les mêmes informations et envoie une alerte de dupe toutes les 5 minutes.
TheCleaner
Deux choses que je trouve un peu soignées: si vous laissez simplement le script s'exécuter, le paramètre d'attente le fera constamment attendre qu'une nouvelle ligne apparaisse. Vous n'avez pas besoin de réexécuter le script. Cela donnera un effet push plutôt que pull. De plus, la queue (1) aura juste analysé la dernière 1 ligne au début. Ainsi, si le gestionnaire de tâches constate qu'il doit redémarrer le script et que vous trouvez un moyen d'injecter une ligne d'espace réservé déplaçant la dernière ligne pour déclencher une alerte, vous aurez désarmé l'ennui des realerts.
ErikE
1
Erik, j'ai trouvé un moyen d'exporter les baux de DHCP (merde que 2012 a une applet de commande PS mais pas 2008) vers un fichier csv. De cette façon, je ne plaisante pas avec les journaux d'audit réels et je n'ai pas à me soucier de casser quoi que ce soit avec une entrée. Je vais commencer à jouer avec le reste du code et à le mettre à jour bientôt.
TheCleaner
1

En supposant que vous êtes certain de l'ID d'événement et qu'aucun autre événement ne se connecte à cet ID dans le journal DHCP, sauf ceux qui vous intéressent, push est en effet une option.

1) Ouvrez le Gestionnaire de serveur, accédez au journal DHCP dans l'Observateur d'événements.

2) Trouvez une entrée représentative à laquelle vous souhaitez joindre votre action. Sélectionnez-le et faites un clic droit.

3) Choisissez "Attacher une tâche à cet événement".

4) L'assistant de création de tâche s'ouvre, retirez-le de là ...

Il existe en fait une option de messagerie explicite, mais si vous avez besoin de plus de logique que cela, vous êtes bien sûr libre d'utiliser l'option start-a-program pour lancer powershell.exe et y attacher un script. Il y a beaucoup d'excellents howtos googleable sur la façon de laisser le Gestionnaire des tâches exécuter des scripts PowerShell si vous avez besoin de conseils.

L'alternative immédiate que je vois consiste à utiliser pull en analysant le journal des événements à l'aide de PowerShell à intervalles planifiés. "The Microsoft Scripting Guy", alias Ed Wilson, a écrit des articles de blog impressionnants sur la façon d'analyser le journal des événements en utilisant les applets de commande disponibles dans les différentes versions de PowerShell, donc prendre son blog comme point de départ serait ma suggestion.

En ce qui concerne les applets de commande réelles, je n'ai pas le temps pour le moment de retirer ma réserve d'extraits de code à portée de main, mais j'y reviendrai dans un jour ou deux et je pourrai contribuer si personne d'autre ne s'est joint à certains bien choisis, ou si vous n'avez pas ´t pas tout résolu par vous-même :-)

ErikE
la source
2
Erik, merci. Le problème ici est que le DHCPsrvlog- "jour" dans C: \ windows \ system32 \ DHCP (avec l'audit DHCP activé dans l'interface graphique du serveur DHCP), n'écrit dans aucun journal de l'Observateur d'événements, y compris le journal de l'Observateur d'événements DHCP-Server sous Applications and Services Logs(jusqu'à présent basé sur mes recherches / tests)
TheCleaner
J'avais oublié ces journaux. Mais je crois que c'est un lieu possible: analyser le journal de texte en utilisant get-content avec les directives -wait et -tail. Ceci est similaire à la queue dans * nix. Pour vous assurer qu'une instance analyse toujours le journal, le Gestionnaire des tâches peut planifier le script au démarrage du système, puis démarrer chaque (intervalle le plus court possible) mais n'autoriser qu'une seule instance en cours d'exécution. Si votre événement apparaît, lancez la logique.
ErikE
Il s'avère que j'ai un problème similaire d'analyse de journaux à résoudre sur Windows, je publierai mes résultats sur cette partie particulière lorsque je suis certain que cela fonctionne, et probablement d'autres blocs de construction que j'ai traîner qui devraient vous être utiles. Pourriez-vous coller quelques lignes représentatives mais obscurcies de votre journal DHCP? Je suis particulièrement intéressé par le format de nom d'appareil.
ErikE
1

Bien que cela ne réponde pas à votre solution souhaitée, une option qui peut atteindre votre objectif est d'utiliser arpwatch( lien ) pour vous avertir lorsqu'un nouvel hôte (précédemment invisible) est vu sur le réseau.

Une alternative à Windows arpwatchsemble être décaféinatide, mais je ne l'ai jamais utilisée, je ne peux donc pas en parler en bien ou en mal.

fukawi2
la source
Merci. L'idée est bonne. Je peux emprunter cette voie si nécessaire.
TheCleaner
L'avantage supplémentaire est que cela intercepterait également les nouvelles machines qui utilisent des adresses IP statiques, ce qui n'était pas dans la portée indiquée, mais devrait probablement l'être.
mfinni