Recientemente enfrentamos la tarea de monitorear la validez de certificados en servidores con Windows. Bueno, se presentó después de que los certificados se convirtieron en calabazas varias veces, justo cuando el colega responsable de su renovación se encontraba de vacaciones. Después de eso, comenzamos a sospechar y decidimos reflexionar al respecto. Dado que estamos implementando lentamente el sistema de monitoreo NetXMS, este se convirtió en el principal y, de hecho, único candidato para esta tarea.
El resultado se obtuvo de la siguiente manera:

Y el proceso posterior.
Vamos. No hay un contador incorporado para la validez de certificados en NetXMS, así que necesitamos crear el nuestro y utilizar scripts para proporcionarle datos. Por supuesto, en Powershell, ya que es Windows. El script debe leer todos los certificados en el sistema operativo, obtener el plazo de caducidad en días y transmitir este número a NetXMS. A través de su agente. Comencemos con eso.
Opción uno, la más simple. Simplemente obtener la cantidad de días hasta la fecha de expiración del certificado más cercana.
Para que el servidor NetXMS conozca la existencia de nuestro parámetro personalizado, debe recibirlo del agente. De lo contrario, no será posible agregar este parámetro debido a su ausencia. Por lo tanto, en el archivo de configuración del agente nxagentd.conf agregamos la línea del parámetro externo con el nombre HTTPS.CertificateExpireDateSimple, en la que especificamos la ejecución del script:
ExternalParameter = HTTPS.CertificateExpireDateSimple: powershell.exe -File "servershareNetXMS_CertExpireDateSimple.ps1"Teniendo en cuenta que el script se ejecuta a través de la red, no debemos olvidar sobre , así como no olvidar otros '-NoLogo -NoProfile -NonInteractive', que omití para una mejor legibilidad del código.
Como resultado, la configuración del agente se ve aproximadamente así:
#
# 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"Después de esto, es necesario guardar la configuración y reiniciar el agente. Esto se puede hacer desde la consola de NetXMS: abrir la configuración (Editar archivo de configuración del agente), editar, ejecutar Guardar & Aplicar, lo que, de hecho, hará lo mismo. Luego, volver a leer la configuración (Poll > Configuration), si no hay fuerzas para esperar. Después de estas acciones, debería aparecer la opción de agregar nuestro parámetro personalizado.
En la consola de NetXMS vamos a Configuración de Recolección de Datos del servidor de prueba, en el que vamos a monitorear certificados y allí creamos un nuevo parámetro (más adelante, después de la configuración, tiene sentido trasladarlo a las plantillas). Elegimos HTTPS.CertificateExpireDateSimple de la lista, escribimos una descripción con un nombre claro, configuramos el tipo como Integer y ajustamos el intervalo de sondeo. Para el tiempo de depuración, es recomendable hacerlo más corto, 30 segundos, por ejemplo. Todo listo, por ahora es suficiente.
Se puede verificar... no, aún es temprano. En este momento, evidentemente, no obtendremos nada. Simplemente porque el script aún no ha sido escrito. Corregimos esta omisión. El script solo devolverá un número, la cantidad de días restantes hasta que expire el certificado. El más mínimo de todos los existentes. Ejemplo de script:
try {
# Obtenemos todos los certificados del almacén de certificados
$lmCertificates = @( Get-ChildItem -Recurse -path 'Cert:LocalMachineMy' -ErrorAction Stop )
# Si no hay certificados, devolver "10 años"
if ($lmCertificates.Count -eq 0) { return 3650 }
# Obtenemos la fecha de expiración de todos los certificados
$expirationDates = @( $lmCertificates | ForEach-Object { return $_.NotAfter } )
# Obtenemos la fecha de expiración más cercana de todas
$minExpirationDate = ($expirationDates | Measure-Object -Minimum -ErrorAction Stop ).Minimum
# Convertimos la fecha de expiración más cercana en la cantidad de días restantes, redondeando hacia abajo
$daysLeft = [Math]::Floor( ($minExpirationDate - [DateTime]::Now).TotalDays )
# Devolvemos el valor
return $daysLeft
}
catch {
return -1
}Así que queda así:

723 días, hasta la expiración del certificado hay casi dos años. Lógico, porque los certificados en el entorno de prueba de Exchange los reemití hace muy poco.
Esa fue una opción simple. Probablemente a alguien le satisfaga así, pero nosotros queríamos más. Nos propusimos la tarea de obtener una lista de todos los certificados en el servidor, uno por uno, y ver cuántos días quedan hasta que expiren.
La segunda opción, un poco más complicado.
De nuevo editamos la configuración del agente y allí, en lugar de la línea con ExternalParameter, escribimos dos diferentes:
ExternalList = HTTPS.CertificateNames: powershell.exe -File "serversharenetxms_CertExternalNames.ps1"
ExternalParameter = HTTPS.CertificateExpireDate(*): powershell.exe -File "serversharenetxms_CertExternalParameter.ps1" -CertificateId "$1"En ExternalList solo obtenemos una lista de cadenas. En nuestro caso, una lista de cadenas con los nombres de los certificados. Obtendremos esta lista de cadenas con el script. El nombre de la lista es 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)"
}
}Y ya en ExternalParameter Proporcionamos cadenas de la lista ExternalList como entrada, y obtenemos el mismo número de días para cada una como salida. El identificador es el Thumbprint del certificado. Tenga en cuenta que HTTPS.CertificateExpireDate en esta variante contiene un asterisco (*). Esto es necesario para que acepte variables externas, que es nuestro 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)"
}En la Configuración de Recolección de Datos del servidor, creamos un nuevo parámetro. En Parámetro seleccionamos nuestro HTTPS.CertificateExpireDate(*) de la lista, y, (¡atención!) cambiamos el asterisco por {instance}. Este importante momento permitirá crear un contador separado para cada instancia (certificado). El resto se completa como en la variante anterior:

Para poder crear contadores, en la pestaña Descubrimiento de Instancias debemos seleccionar la Lista de Agentes de la lista y escribir el nombre de nuestra ExternalList del script en el campo Nombre de Lista — HTTPS.CertificateNames.
Casi listo, hay que esperar un poco o forzar Poll > Configuración y Poll > Descubrimiento de Instancias, si no se puede esperar del todo. Como resultado, obtenemos todos nuestros certificados con fechas de vencimiento:
¿Es lo que necesitas? Sí, solo que una inquietud de perfeccionismo mira este innecesario Thumbprint en el nombre del contador con ojos tristes y no permite terminar el artículo. Para alimentarlo, abrimos nuevamente las propiedades del contador y en la pestaña Descubrimiento de Instancias en el campo "script de filtro de descubrimiento de instancias" añadimos el escrito en (el lenguaje interno de NetXMS) script:
instance = $1;
if (instance ~= "^(.*)s-s[T:[a-zA-Z0-9]+]$")
{
return %(true, instance, $1);
}
return true;que filtrará el Thumbprint:

Y para mostrarlo filtrado, en la pestaña General en el campo Descripción cambiamos CertificateExpireDate: {instance} por CertificateExpireDate: {instance-name}:

Eso es, finalmente, la meta está cerca:
¿No es hermoso?
Solo queda configurar las alertas para que lleguen al correo cuando el certificado esté próximo a su fecha de caducidad lógica.
1. Primero, necesitamos crear una plantilla de evento (Event Template) para activarla cuando el contador disminuya hasta un umbral que hayamos establecido. En Configuración de Eventos creamos dos nuevas plantillas con nombres, digamos, CertificateExpireDate_Threshold_Activate con el estado de Advertencia:

y una similar CertificateExpireDate_Threshold_Deactivate con el estado Normal.
2. A continuación, vamos a las propiedades del contador y en la pestaña Umbrales configuramos el umbral:

donde elegimos nuestros eventos creados CertificateExpireDate_Threshold_Activate y CertificateExpireDate_Threshold_Deactivate, establecemos la cantidad de muestras (Samples) en 1 (no tiene sentido poner más para este medidor), el valor en 30 (días), por ejemplo, y, lo que es importante, configuramos el intervalo de repetición del evento. Para certificados en producción, lo configuro una vez al día (86400 segundos), de lo contrario, podríamos inundarnos con notificaciones (lo que, por cierto, ocurrió una vez y, de hecho, se llenó el buzón de correo durante el fin de semana). Para el tiempo de depuración, vale la pena establecer un intervalo más corto, 60 segundos, por ejemplo.
3. En Configuración de la Acción creamos una plantilla de correo de notificación, como esta:

Todos estos %m, %S, etc. son macros que se reemplazarán por valores de nuestro parámetro. Se describen con más detalle en de NetXMS.
4. Y, finalmente, combinando los puntos anteriores, en Política de Procesamiento de Eventos creamos una regla que generará una Alarma y enviará un correo:
Guardamos la política y ya está, podemos probar. Pondremos un umbral más alto para la verificación. Mi certificado más cercano expira en 723 días, así que lo establecí en 724. Como resultado, obtendremos la siguiente alarma:

y esta notificación por correo:

Ahora sí, eso es todo. Podríamos, por supuesto, configurar un panel de control, construir gráficos, pero para los certificados serían líneas rectas un poco sin sentido y aburridas, a diferencia de los gráficos de carga del procesador o de memoria, por ejemplo. Pero eso lo dejaremos para otra ocasión.
Fuente: habr.com
