Showing posts with label domain controller. Show all posts
Showing posts with label domain controller. Show all posts

26 April, 2014

Validate Domain Controller certificates - AD

This is a specific post about Domain Controller Authentication certificates but the problem and the solution can be applied to any type of certificate you have on your servers.

By default, a domain controller uses LDAP to provide your clients data from Active Directory (TCP port 389).  For example when a client wants to check if a user is member of a group, everything goes through the network in clear text.
If you want to provide LDAP over SSL in your domain to make the LDAP traffic secured, you need to have a so called Domain Controller Authentication certificate (which is in fact a template that describes a certificate for Client and Server authentication plus smart card logon) added to the DCs personal certificate container and taaadaaam, LDAPS will be available (TCP port 636), you should see on your DC something like this:


To make sure the certificate is always valid and does not expire, you can setup auto enrolment via GPO if you have a nice AD integrated PKI infrastructure. However, auto enrolment can sometimes fail if for example someone messes up the permissions on the CA server or folder permissions on domain controllers and if that's done at the wrong time, your DC certificate can expire and bang, there's your outage on a Sunday afternoon when some applications stop working because they can't access AD via LDAPS.

The best solution is to put some monitoring in place, e.g. via SCOM or anything similar which checks certificates periodically and if they are about to expire, sends an alert.

However, if you just want to query your DCs to see how those certificates are at a point in time or you want periodic report on them, it's easier to simply write a couple of lines in PowerShell.

Enumerate certificates on remote hosts

It's easy to get a list of certificates from a remote host:
$srv = "c3podc1"
$certStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("\\$srv\My", "LocalMachine")
$certStore.Open("ReadOnly")
$certStore.certificates


But this does not give you all the fields you want to read, e.g. you may have many certs installed on your DC but you only want to know about the Domain Controller Authentication one, so you need to somehow enumerate the Cert Template names as well (see screen shot above), here is the trick to list all certificates' template names:
$certStore.certificates | %{($_.extensions | ?{$_.oid.friendlyname -match "template"}).format(0) -replace "(.+)?=(.+)\((.+)?", '$2'}

Basically, you need to go through each certificate's 'extensions' and see if the 'oid.friedlyname' contain template, if it is, then use the format method of the X509Extension object to get the name of the template. You will get it with a lot of junk, like this:

Template=Domain Controller Authentication(1.3.6.1.4.1.311.21.8.13987996.9101750.1067918.14758690.631985.210.1.28), Major Version Number=110, Minor Version Number=0

You can use the -replace operator to pick out the string which comes after the first '=':

List Domain Controller Authentication certificates

Now we can list all certificates, we can even pick up the one with Domain Controller Authentication template, we just need to read the date when it expires and then mark it with some RAG (red /amber / green) status based on how close it is to be expired -for me I mark it RED if it is to expire within 30 days because based on my cert template auto enrolment should renew the cert in the last 6 weeks:

Here is the simplified script (you can add function to send mails, log actions...etc., based on some of the previous posts in this blog):
 $hostlist = @($Input)  
   
 foreach($srv in $hostlist){  
    $certStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("\\$srv\My", "LocalMachine")  
    $certStore.Open("ReadOnly")  
    $certStore.certificates | %{  
       $obj = "" | Select Subject,Template,ValidUntil,RAG  
       $obj.Subject = ($_.extensions | ?{$_.oid.friendlyname -match "Subject Alternative Name"}).format(0) -replace "^.+=", ""  
       $obj.Template = ($_.extensions | ?{$_.oid.friendlyname -match "template"}).format(0) -replace "(.+)?=(.+)\((.+)?", '$2'  
       $obj.ValidUntil = $_.NotAfter  
   
       if($obj.Template -ieq "Domain Controller Authentication"){  
          if((get-date($obj.ValidUntil)) -gt (Get-Date).adddays(30)){  
             $obj.RAG = "GREEN"  
          }  
          else{  
             $obj.RAG = "RED"  
          }  
          $obj     
       }     
    }  
 }  
   


t




11 November, 2012

Get list of GCs - Active Directory


Ok, here is the next bit. I have many domain controllers (DC) in an Active Directory forest and need to know which domain controller is a Global Catalog (GC). Read it carefully: it's not enough to list which DCs have the GC flag set, I need to know which DC is properly advertised as a GC. Why? Because having enough number of healthy GCs in a forest is essential for Exchange Address Book lookups and for Universal Group membership caching. And again, you can activate the GC flag in dssite.msc many times, if you have an AD database with size of 10+ GB, it will take time to get all the global catalog data built up and replicated across.

Obviously, if you had 2 DCs, you could look into the eventlog and see if there's any eventid 1126 in the log, but doing it every day with every domain controller, after reboot...nah, you don't want to go down that way.

First, let's get the list of DCs in a particular domain. There are many ways to do this, i.e. if you don't have Windows 2008 in your environment , you can do this:
([System.DirectoryServices.ActiveDirectory.DomainController]::findall((new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain","tatooine.com"))))

If you have 2008 DCs:
import-module ActiveDirectory
$DCs = Get-ADDomainController -filter * -DomainName tatooine.com

To list which DC is advertised as a GC, you can use the isGlobalCatalogReady RootDSE attribute, on Windos 2003 DCs:
gc dcs.txt | %{$p="" | select ComputerName,Is_GC; $p.ComputerName=$_; $p.Is_gc=(([adsi]("LDAP://" + $_ + "/RootDSE")).isGlobalCatalogReady); $p}


On Windows 2008 DCs:
$GCs = Get-ADDomainController -filter { IsGlobalCatalog -eq $True}

Let's combine this with checking which DC should really be a GC, so where the GC flag is set and have a full list of DCs with the two parameters:
- GC flag's status on the server
- Is GC ready flag on the server

$objColl = @(); $DCs | %{
   # create object with 3 properties   $psObj = "" | select ComputerName,Is_GC_Ready,Is_GC_set
   $psObj.ComputerName = $_     # get if the particular DC is advertised as a GC
   $RootDSE = ([adsi]("LDAP://" + $_ + "/RootDSE"))
   $psObj.Is_gc_ready = $RootDSE.isGlobalCatalogReady
     # enumerate the GC flag of the server
   $ntdsObj = $RootDSE.Get('dsServiceName')
   $psObj.Is_gc_set = ([adsi]"LDAP://$_/$ntdsObj").Get('options')
     $objColl += $psObj
   $psObj}

The object collection can be filtered afterwards, i.e. I want to know the list of DCs which have GC flag set but they are not advertised as GCs:
$objColl | ?{($_.Is_GC_set -eq 1) -and ($_.Is_GC_Ready -eq $false)}

Or list the DCs which do not have the GC flag set:
$objColl | ?{($_.Is_GC_set -eq 0)

Feel free to edit it and experiment with the ActiveDirectory cmdlets on Windows 2008 (Get-ADDomainController)

Clipboar friendly code:
$objColl = @(); 
$DCs | %{ 
  
      # create object with 3 properties
      $psObj = "" | select ComputerName,Is_GC_Ready,Is_GC_set
      $psObj.ComputerName = $_
  
      # get if the particular DC is advertised as a GC
      $RootDSE = ([adsi]("LDAP://" + $_ + "/RootDSE"))
      $psObj.Is_gc_ready = $RootDSE.isGlobalCatalogReady
  
      # enumerate the GC flag of the server
      $ntdsObj = $RootDSE.Get('dsServiceName')
      $psObj.Is_gc_set = ([adsi]"LDAP://$_/$ntdsObj").Get('options')
  
      $objColl += $psObj
      $psObj
}  

May the force...
t