Di recente, ci è stato assegnato il compito di monitorare la scadenza dei certificati sui server Windows. In effetti, è successo dopo che i certificati si erano trasformati in zucche più volte, proprio quando il collega responsabile del loro rinnovo era in ferie. Dopodiché, abbiamo iniziato a nutrire dei sospetti e a riflettere su questo problema. Poiché stiamo implementando con calma il sistema di monitoraggio NetXMS, è diventato il principale e praticamente unico candidato per questo compito.
Il risultato finale è stato ottenuto in questo modo:

E il processo successivo.
Iniziamo. Non esiste un contatore integrato per la scadenza dei certificati in NetXMS, quindi è necessario crearne uno e utilizzare script per fornire i dati. Naturalmente, in Powershell, dato che siamo su Windows. Lo script deve leggere tutti i certificati nel sistema operativo, estrarre la scadenza in giorni e trasferire questo numero a NetXMS. Attraverso il suo agente. È da qui che iniziamo.
Opzione prima, la più semplice. Basta ottenere il numero di giorni fino alla scadenza del certificato con la data più vicina.
Affinché il server NetXMS sia a conoscenza del nostro parametro personalizzato, deve riceverlo dall'agente. Altrimenti, non sarà possibile aggiungere questo parametro, a causa della sua assenza. Pertanto, nel file di configurazione dell'agente nxagentd.conf aggiungiamo la riga del parametro esterno con il nome HTTPS.CertificateExpireDateSimple, in cui scriviamo l'esecuzione dello script:
ExternalParameter = HTTPS.CertificateExpireDateSimple: powershell.exe -File "servershareNetXMS_CertExpireDateSimple.ps1"Tenendo presente che lo script viene eseguito in rete, non dimenticate di considerare , e di non dimenticare anche le altre opzioni «-NoLogo -NoProfile -NonInteractive», che ometto per una migliore leggibilità del codice.
Di conseguenza, la configurazione dell'agente appare più o meno così:
#
# NetXMS agent configuration file
# Created by agent installer at Thu Jun 13 11:24:43 2019
#
MasterServers = netxms.corp.testcompany.ru
ConfigIncludeDir = C:NetXMSetcnxagentd.conf.d
LogFile = {syslog}
FileStore = C:NetXMSvar
SubAgent = ecs.nsm
SubAgent = filemgr.nsm
SubAgent = ping.nsm
SubAgent = logwatch.nsm
SubAgent = portcheck.nsm
SubAgent = winperf.nsm
SubAgent = wmi.nsm
ExternalParameter = HTTPS.CertificateExpireDateSimple: powershell.exe -File "servershareNetXMS_CertExpireDateSimple.ps1"Dopo di che è necessario salvare la configurazione e riavviare l'agente. Questo può essere fatto dalla console di NetXMS: aprire la configurazione (Edit agent’s configuration file), modificarla, eseguire Save&Apply, il che comporterà di fatto la stessa cosa. Poi ricaricare la configurazione (Poll > Configuration), se non si ha voglia di aspettare. Dopo queste azioni, dovrebbe comparire la possibilità di aggiungere il nostro parametro personalizzato.
Nella console di NetXMS, andiamo a Data Collection Configuration del server di prova, su cui ci siamo riuniti per monitorare i certificati e creiamo un nuovo parametro (in futuro, dopo la configurazione, ha senso trasferirlo nei modelli). Selezioniamo HTTPS.CertificateExpireDateSimple dall'elenco, scriviamo una Descrizione con un nome comprensibile, impostiamo il tipo su Integer e configuriamo l'intervallo di polling. Per il tempo di debugging, ha senso renderlo più breve, 30 secondi, ad esempio. Fatto, è tutto pronto per ora.
Possiamo controllare... no, è ancora presto. Attualmente, ovviamente, non otterremo nulla. Semplicemente perché non abbiamo ancora scritto lo script. Rimediamo a questa mancanza. Lo script restituirà semplicemente un numero, il numero di giorni rimanenti fino alla scadenza del certificato. Il più minimale tra tutti quelli disponibili. Ecco un esempio di script:
try {
# Otteniamo tutti i certificati dal negozio dei certificati
$lmCertificates = @( Get-ChildItem -Recurse -path 'Cert:LocalMachineMy' -ErrorAction Stop )
# Se non ci sono certificati, restituisci "10 anni"
if ($lmCertificates.Count -eq 0) { return 3650 }
# Otteniamo la data di scadenza di tutti i certificati
$expirationDates = @( $lmCertificates | ForEach-Object { return $_.NotAfter } )
# Otteniamo la data di scadenza più vicina
$minExpirationDate = ($expirationDates | Measure-Object -Minimum -ErrorAction Stop ).Minimum
# Convertiamo la data di scadenza più vicina in giorni rimanenti, arrotondando per difetto
$daysLeft = [Math]::Floor( ($minExpirationDate - [DateTime]::Now).TotalDays )
# Restituiamo il valore
return $daysLeft
}
catch {
return -1
}Ecco come funziona:

723 giorni, ci sono ancora quasi due anni fino alla scadenza del certificato. Logico, perché ho appena riemesso i certificati nella test lab di Exchange.
Questa era una soluzione semplice. Forse a qualcuno potrebbe piacere così, ma noi volevamo di più. Ci siamo posti l'obiettivo di ottenere un elenco di tutti i certificati sul server, nominativamente, e di vedere quanti giorni rimangono fino alla scadenza di ciascun certificato.
Seconda opzione, un po' più complicata.
Modifichiamo di nuovo la configurazione dell'agente e lì, invece della riga con ExternalParameter, scriviamo due altre righe:
ExternalList = HTTPS.CertificateNames: powershell.exe -File "serversharenetxms_CertExternalNames.ps1"
ExternalParameter = HTTPS.CertificateExpireDate(*): powershell.exe -File "serversharenetxms_CertExternalParameter.ps1" -CertificateId "$1"In ExternalList otteniamo semplicemente un elenco di stringhe. Nel nostro caso, un elenco di stringhe con i nomi dei certificati. Otteniamo questo elenco di stringhe tramite uno script. Il nome dell'elenco è HTTPS.CertificateNames.
Lo script NetXMS_CertNames.ps1:
#Список возможных имен сертификатов
$nameTypeList = @(
[System.Security.Cryptography.X509Certificates.X509NameType]::SimpleName,
[System.Security.Cryptography.X509Certificates.X509NameType]::DnsName,
[System.Security.Cryptography.X509Certificates.X509NameType]::DnsFromAlternativeName,
[System.Security.Cryptography.X509Certificates.X509NameType]::UrlName,
[System.Security.Cryptography.X509Certificates.X509NameType]::EmailName,
[System.Security.Cryptography.X509Certificates.X509NameType]::UpnName
)
#Ищем все сертификаты, имеющие закрытый ключ
$certList = @( Get-ChildItem -Path 'Cert:LocalMachineMy' | Where-Object { $_.HasPrivateKey -eq $true } )
#Проходим по списку сертификатов, формируем строку "Имя сертификата - Дата - Thumbprint" и возвращаем её
foreach ($cert in $certList) {
$name = '(unknown name)'
try {
$thumbprint = $cert.Thumbprint
$dateExpire = $cert.NotAfter
foreach ($nameType in $nameTypeList) {
$name_temp = $cert.GetNameInfo( $nameType, $false)
if ($name_temp -ne $null -and $name_temp -ne '') {
$name = $name_temp;
break;
}
}
Write-Output "$($name) - $($dateExpire.ToString('dd.MM.yyyy')) - [T:$($thumbprint)]"
}
catch {
Write-Error -Message "Error processing certificate list: $($_.Exception.Message)"
}
}E già in ExternalParameter Immettiamo le righe dall'elenco ExternalList all'ingresso e otteniamo lo stesso numero di giorni per ciascuna. L'identificatore è dato dal Thumbprint del certificato. Si prega di notare che HTTPS.CertificateExpireDate in questa variante contiene un asterisco (*). Questo è necessario affinché accetti variabili esterne, in particolare il nostro CertificateId.
Script NetXMS_CertExpireDate.ps1:
#Определяем входящий параметр $CertificateId
param (
[Parameter(Mandatory=$false)]
[String]$CertificateId
)
#Проверка на существование
if ($CertificateId -eq $null) {
Write-Error -Message "CertificateID parameter is required!"
return
}
#По Thumbprint из строки в $CertificateId ищем сертификат и определяем его Expiration Date
$certId = $CertificateId;
try {
if ($certId -match '^.*[T:(?<Thumbprint>[A-Z0-9]+)]$') {
$thumbprint = $Matches['Thumbprint']
$certificatePath = "Cert:LocalMachineMy$($thumbprint)"
if (Test-Path -PathType Leaf -Path $certificatePath ) {
$certificate = Get-Item -Path $certificatePath;
$certificateExpirationDate = $certificate.NotAfter
$certificateDayToLive = [Math]::Floor( ($certificateExpirationDate - [DateTime]::Now).TotalDays )
Write-Output "$($certificateDayToLive)";
}
else {
Write-Error -Message "No certificate matching this thumbprint found on this server $($certId)"
}
}
else {
Write-Error -Message "CertificateID provided in wrong format. Must be FriendlyName [T:<thumbprint>]"
}
}
catch {
Write-Error -Message "Error while executing script: $($_.Exception.Message)"
}Nella configurazione della raccolta dati del server creiamo un nuovo parametro. Nel campo Parameter selezioniamo il nostro HTTPS.CertificateExpireDate(*) dall'elenco e, (attenzione!) cambiamo l'asterisco in {instance}. Questo passaggio fondamentale consentirà di creare un contatore separato per ogni istanza (certificato). Il resto si completa come nella variante precedente:

Per consentire la creazione dei contatori, nella scheda Instance Discovery occorre selezionare Agent List dall'elenco e nel campo List Name inserire il nome del nostro ExternalList dallo script — HTTPS.CertificateNames.
Quasi fatto, basta aspettare un po' o forzare un Poll > Configuration e Poll > Instance Discovery, se proprio non si può aspettare. Come risultato otteniamo tutti i nostri certificati con le loro date di scadenza:
Va bene? Sì, solo che il verme del perfezionismo guarda quel Thumbprint inutile nel nome del contatore con occhi tristi e non riesce a farmi finire l'articolo. Per soddisfarlo, riapriamo le proprietà del contatore e nella scheda Instance Discovery nel campo "Instance discovery filter script" aggiungiamo il seguente script scritto in linguaggio interno di NetXMS:
instance = $1;
if (instance ~= "^(.*)s-s[T:[a-zA-Z0-9]+]$")
{
return %(true, instance, $1);
}
return true;che filtrerà il Thumbprint:

E per mostrarlo filtrato, nella scheda General nel campo Description cambiamo CertificateExpireDate: {instance} in CertificateExpireDate: {instance-name}:

Tutto, finalmente in finale dalla KDPV:
È bello, vero?
Resta da configurare le notifiche in modo che vengano inviate via email quando la scadenza del certificato si avvicina alla sua conclusione logica.
1. Prima di tutto, è necessario creare un modello di evento (Event Template) per attivarlo quando il valore del contatore scende al di sotto di una soglia che abbiamo impostato. Nella Event Configuration creiamo due nuovi modelli con nomi, diciamo CertificateExpireDate_Threshold_Activate con stato Warning:

e un analogo CertificateExpireDate_Threshold_Deactivate con stato Normal.
2. Poi andiamo nelle proprietà del contatore e nella scheda Tresholds impostiamo la soglia:

dove scegliamo i nostri eventi creati CertificateExpireDate_Threshold_Activate e CertificateExpireDate_Threshold_Deactivate, impostiamo il numero di campioni (Samples) a 1 (specificamente per questo contatore non ha senso impostarne di più), un valore di 30 (giorni), ad esempio, e, cosa importante, configuriamo il tempo di ripetizione dell'evento. Per i certificati in produzione imposto una volta al giorno (86400 secondi), altrimenti si può affondare negli avvisi (e, a proposito, è già successo una volta, tanto che la casella di posta si è riempita nel fine settimana). Per il tempo di debug ha senso impostare un valore più basso, 60 secondi, ad esempio.
3. In Action Configuration creiamo un modello di email di avviso, simile a questo:

Tutti questi %m, %S e così via sono macro, in cui verranno inseriti valori dal nostro parametro. Sono descritti in dettaglio nel NetXMS.
4. E, infine, unendo i punti precedenti, in Event Processing Policy creiamo una regola per la quale verrà creato un Allerta e inviato un'email:
Salviamo la politica, tutto qui, possiamo testare. Imposteremo una soglia più alta per la verifica. Il mio certificato più vicino scade tra 723 giorni, per la verifica ho impostato 724. Di conseguenza otterremo un avviso come questo:

e una notifica via email come questa:

Ora è tutto. Sarebbe possibile, ovviamente, configurare un dashboard, creare grafici, ma per i certificati si tratterebbe di linee diritte un po' prive di significato e noiose, a differenza dei grafici sul carico della CPU o della memoria, per esempio. Ma di questo ne parleremo in un altro momento.
Fonte: habr.com
