Recentemente, abbiamo dovuto monitorare la scadenza dei certificati sui server Windows. E come è avvenuto? Dopo che i certificati si erano trasformati in zucche proprio nel momento in cui il collega con la barba, responsabile del loro rinnovo, era in ferie. Dopo questo episodio, abbiamo iniziato a sospettare e a riflettere su come affrontare la situazione. Dato che stiamo implementando senza fretta il sistema di monitoraggio NetXMS, questo è diventato il principale e, sostanzialmente, l'unico candidato per questa attività.
Il risultato finale è stato ottenuto in questo modo:

E il processo successivo.
Iniziamo. Non esiste un contatore integrato per i certificati in scadenza in NetXMS, quindi dobbiamo crearne uno nostro e utilizzare script per fornirgli i dati. Naturalmente, in PowerShell, dato che siamo su Windows. Lo script deve leggere tutti i certificati nel sistema operativo, estrarre il numero di giorni fino alla loro scadenza e trasmettere questo numero a NetXMS attraverso il suo agente. Cominciamo da lì.
Opzione uno, la più semplice. Basta ottenere il numero di giorni fino alla scadenza del certificato con la data più prossima.
Per garantire che il server NetXMS riconosca il 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, nel quale specifichiamo l'esecuzione dello script:
ExternalParameter = HTTPS.CertificateExpireDateSimple: powershell.exe -File "servershareNetXMS_CertExpireDateSimple.ps1"Considerando che lo script viene eseguito sulla rete, non dimenticate di , e di non dimenticare anche altri parametri come «-NoLogo -NoProfile -NonInteractive», che ho omesso per migliorare la leggibilità del codice.
Il che porta il file di configurazione dell'agente a sembrare 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 il file di configurazione (Edit agent’s configuration file), modificarlo, eseguire Save&Apply, il che comporterà essenzialmente la stessa cosa. Poi rileggere la configurazione (Poll > Configuration), se non si ha la forza di aspettare. Dopo queste azioni dovrebbe apparire la possibilità di aggiungere il nostro parametro personalizzato.
Nella console di NetXMS andiamo a Data Collection Configuration server di test su cui monitoreremo i certificati e creiamo un nuovo parametro (in seguito, dopo la configurazione, ha senso trasferirlo nei modelli). Selezioniamo HTTPS.CertificateExpireDateSimple dalla lista, inseriamo una descrizione con un nome comprensibile, impostiamo il tipo su Integer e configuriamo l'intervallo di polling. Per il periodo di debug è utile mantenerlo corto, ad esempio 30 secondi. Tutto pronto, per ora è sufficiente.
Possiamo controllare... no, è ancora presto. Adesso, naturalmente, non otterremo nulla. Semplicemente perché lo script non è ancora stato scritto. Correggiamo questa mancanza. Lo script restituirà semplicemente un numero, il conteggio dei giorni rimanenti prima della scadenza del certificato. Il più minimo tra tutti quelli esistenti. Ecco un esempio di script:
try {
# Otteniamo tutti i certificati dallo store dei certificati
$lmCertificates = @( Get-ChildItem -Recurse -path 'Cert:LocalMachineMy' -ErrorAction Stop )
# Se non ci sono certificati, restituiamo "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 tra tutte
$minExpirationDate = ($expirationDates | Measure-Object -Minimum -ErrorAction Stop ).Minimum
# Convertiamo la data di scadenza più vicina in numero di giorni rimanenti arrotondando per difetto
$daysLeft = [Math]::Floor( ($minExpirationDate - [DateTime]::Now).TotalDays )
# Restituiamo il valore
return $daysLeft
}
catch {
return -1
}In questo modo:

723 giorni, fino alla scadenza del certificato mancano ancora quasi due anni. Logico, perché ho recentemente riemesso i certificati per l'ambiente di test di Exchange.
Questa è stata una soluzione semplice. Forse a qualcuno andrà bene così, ma noi volevamo di più. Ci siamo dati l'obiettivo di ottenere un elenco di tutti i certificati sul server, specificando ciascuno e visualizzando quanti giorni restano fino alla scadenza del certificato.
La seconda opzione, un po' più complesso.
Di nuovo modifichiamo la configurazione dell'agente e lì, invece della riga con ExternalParameter, scriviamo altre due:
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 queste stringhe tramite uno script. Il nome dell'elenco è HTTPS.CertificateNames.
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 forniamo in input le stringhe dall'elenco ExternalList e in output otteniamo lo stesso numero di giorni per ciascuna. L'identificatore è il Thumbprint del certificato. Si noti che HTTPS.CertificateExpireDate in questa variante contiene l'asterisco (*). Questo è necessario affinché accetti variabili esterne, ovvero 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. In Parameter selezioniamo il nostro HTTPS.CertificateExpireDate(*) dall'elenco e, (attenzione!) cambiamo l'asterisco in {instance}. Questo passaggio importante permetterà di creare un contatore separato per ogni istanza (certificato). Il resto viene compilato come nella variante precedente:

Per creare contatori, nella scheda Instance Discovery è necessario selezionare Agent List dall'elenco e nel campo List Name digitare il nome del nostro ExternalList dallo script — HTTPS.CertificateNames.
Quasi pronto, basta aspettare un momento o forzare il Poll > Configuration e Poll > Instance Discovery, se non si può attendere. In questo modo otteniamo tutti i nostri certificati con le date di scadenza:
È ciò che serve? Certo, solo che un insaziabile perfezionista guarda quel Thumbprint non necessario nel nome del contatore con uno sguardo triste e non riesce a far terminare 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 (il 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 visualizzarlo filtrato, nella scheda General nel campo Description cambiamo CertificateExpireDate: {instance} in CertificateExpireDate: {instance-name}:

Tutto, finalmente in dirittura d'arrivo:
Bella, vero?
Rimane solo da impostare gli avvisi affinché vengano inviati via email quando la scadenza del certificato si avvicina.
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 prestabilita. In Configurazione Evento creiamo due nuovi modelli con nomi, diciamo CertificateExpireDate_Threshold_Activate con stato di Avviso:

e un analogo CertificateExpireDate_Threshold_Deactivate con stato Normale.
2. Poi andiamo nelle proprietà del contatore e nella scheda Soglie (Tresholds) configuriamo la soglia:

dove scegliamo i nostri eventi creati CertificateExpireDate_Threshold_Activate e CertificateExpireDate_Threshold_Deactivate, impostiamo il numero di misurazioni (Samples) a 1 (specificamente per questo contatore non ha senso impostarne di più), un valore di 30 (giorni), ad esempio, e, cosa importante, configuriamo l'intervallo di ripetizione dell'evento. Per i certificati in produzione imposto una volta al giorno (86400 secondi), altrimenti si rischia di annegare nelle notifiche (cosa che, a proposito, è già successa una volta, tanto che la casella di posta si è riempita durante il weekend). Per il tempo di debug ha senso impostare un valore più basso, ad esempio 60 secondi.
3. In Configurazione Azione creiamo un modello di email di notifica, simile a questo:

Tutti questi %m, %S e simili sono macro, che verranno sostituite con i valori del nostro parametro. Sono descritti più dettagliatamente nel NetXMS.
4. E infine, unendo i punti precedenti, in Politica di Elaborazione degli Eventi creiamo una regola che genererà un Allarme e invierà un'email:
Salviamo la politica, tutto pronto per i test. Imposteremo una soglia più alta per la verifica. Il mio certificato più vicino scade tra 723 giorni, quindi ho impostato 724 per il test. Di conseguenza otterremo un allarme come questo:

e una notifica via email come questa:

Ora è tutto completo. Certo, si potrebbe impostare un dashboard, costruire grafici, ma per i certificati sarebbero solo linee dritte, un po' noiose, a differenza di grafici di carico CPU o memoria, ad esempio. Ma di questo parleremo un'altra volta.
Fonte: habr.com
