Recentemente, tivemos que monitorar a validade dos certificados em servidores com Windows. E isso ocorreu, depois que os certificados se tornaram inoperantes várias vezes, justo quando o colega responsável pela renovação estava de férias. Depois disso, suspeitamos de algo e decidimos refletir sobre isso. Como estamos implementando lentamente o sistema de monitoramento NetXMS, ele se tornou o principal e, na verdade, único candidato para essa tarefa.
O resultado, então, foi obtido na seguinte forma:

E o próprio processo a seguir.
Vamos lá. Não há um contador embutido para a validade dos certificados no NetXMS, portanto, precisamos criar o nosso e usar scripts para fornecer os dados. Claro, em Powershell, já que é Windows. O script deve ler todos os certificados do sistema operacional, obter a data de validade em dias e passar esse número para o NetXMS. Através de seu agente. É por isso que começaremos com isso.
Primeira opção, a mais simples. Bastar obter o número de dias até o vencimento do certificado com a data mais próxima.
Para que o servidor NetXMS conheça a existência do nosso parâmetro personalizado, ele deve recebê-lo do agente. Caso contrário, esse parâmetro não poderá ser adicionado por não existir. Portanto, no arquivo de configuração do agente nxagentd.conf , adicionamos a linha do parâmetro externo com o nome HTTPS.CertificateExpireDateSimple, na qual especificamos a execução do script:
ExternalParameter = HTTPS.CertificateExpireDateSimple: powershell.exe -File "servershareNetXMS_CertExpireDateSimple.ps1"Considerando que o script é executado pela rede, não se deve esquecer da , e também não esquecer outros parâmetros como «-NoLogo -NoProfile -NonInteractive», que omiti para melhor clareza do código.
Como resultado, a configuração do agente fica aproximadamente assim:
#
# 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"Após isso, é necessário salvar a configuração e reiniciar o agente. Isso pode ser feito a partir do console NetXMS: abrir a configuração (Edit agent’s configuration file), editar, realizar Save&Apply, e, portanto, o mesmo acontecerá. Em seguida, releia a configuração (Poll > Configuration), se não houver forças para esperar. Após essas etapas, deve haver a possibilidade de adicionar nosso parâmetro personalizado.
No console NetXMS, vá para Configuração de Coleta de Dados do servidor de teste onde vamos monitorar os certificados e criamos um novo parâmetro (mais tarde, após a configuração, faz sentido transferi-lo para os templates). Selecionamos HTTPS.CertificateExpireDateSimple da lista, inserimos uma descrição com um nome claro, definimos o tipo como Integer e configuramos o intervalo de consulta. Para o período de depuração, é razoável torná-lo mais curto, por exemplo, 30 segundos. Pronto, isso é suficiente por enquanto.
Podemos verificar... não, ainda é cedo. Neste momento, obviamente, não obteremos nada. Apenas porque o script ainda não foi escrito. Vamos corrigir essa omissão. O script irá simplesmente retornar um número, a quantidade de dias restantes até o vencimento do certificado. O mais baixo entre todos os disponíveis. Exemplo de script:
try {
# Obtém todos os certificados do repositório de certificados
$lmCertificates = @( Get-ChildItem -Recurse -path 'Cert:LocalMachineMy' -ErrorAction Stop )
# Se não houver certificados, retorna "10 anos"
if ($lmCertificates.Count -eq 0) { return 3650 }
# Obtém a data de expiração de todos os certificados
$expirationDates = @( $lmCertificates | ForEach-Object { return $_.NotAfter } )
# Obtém a data de expiração mais próxima entre todos
$minExpirationDate = ($expirationDates | Measure-Object -Minimum -ErrorAction Stop ).Minimum
# Converte a data de expiração mais próxima em quantidade de dias restantes, arredondando para baixo
$daysLeft = [Math]::Floor( ($minExpirationDate - [DateTime]::Now).TotalDays )
# Retorna o valor
return $daysLeft
}
catch {
return -1
}Acontece assim:

723 dias, até a expiração do certificado restam quase dois anos. Lógico, porque os certificados no Exchange do ambiente de teste foram reemitidos recentemente.
Essa foi a opção simples. Provavelmente, alguém ficará satisfeito com isso, mas nós queríamos mais. O objetivo que nos propusemos foi obter uma lista de todos os certificados no servidor, de nome por nome, e ver quantos dias restam até o vencimento de cada certificado.
A segunda opção, um pouco mais complicado.
Novamente, editamos a configuração do agente e, em vez da linha com ExternalParameter, escrevemos duas outras:
ExternalList = HTTPS.CertificateNames: powershell.exe -File "serversharenetxms_CertExternalNames.ps1"
ExternalParameter = HTTPS.CertificateExpireDate(*): powershell.exe -File "serversharenetxms_CertExternalParameter.ps1" -CertificateId "$1"Em ExternalList obtemos apenas uma lista de strings. No nosso caso, uma lista de strings com os nomes dos certificados. Essa lista de strings será obtida pelo script. O nome da lista — 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 já em ExternalParameter Nós fornecemos strings da lista ExternalList, e recebemos a mesma quantidade de dias para cada uma. O identificador é o Thumbprint do certificado. Observe que HTTPS.CertificateExpireDate neste caso contém um asterisco (*). Isso é necessário para que ele aceite variáveis externas, como nosso 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)"
}Na configuração de coleta de dados do servidor, criamos um novo parâmetro. No parâmetro, escolhemos nosso HTTPS.CertificateExpireDate(*) da lista, e, (atenção!) trocamos o asterisco por {instance}. Este ponto importante permitirá criar um contador separado para cada instância (certificado). O restante é preenchido como na versão anterior:

Para que os contadores possam ser criados, na aba Instance Discovery é necessário escolher Agent List da lista e no campo List Name inserir o nome da nossa ExternalList do script — HTTPS.CertificateNames.
Quase pronto, basta esperar um pouco ou forçar a execução de Poll > Configuration e Poll > Instance Discovery, se for impossível aguardar. Como resultado, obtemos todos nossos certificados com suas datas de validade:
É isso que precisamos? Sim, mas o verme do perfeccionismo olha para esse Thumbprint desnecessário no nome do contador com olhos tristes e não permite que eu termine o artigo. Para alimentá-lo, abrimos novamente as propriedades do contador e na aba Instance Discovery no campo ‘Instance discovery filter script’ adicionamos o script escrito em (a linguagem interna do NetXMS):
instance = $1;
if (instance ~= "^(.*)s-s[T:[a-zA-Z0-9]+]$")
{
return %(true, instance, $1);
}
return true;que filtrará o Thumbprint:

E para exibi-lo filtrado, na aba General no campo Description trocamos CertificateExpireDate: {instance} por CertificateExpireDate: {instance-name}:

Pronto, finalmente a linha de chegada:
Bonito, não é?
Restam apenas configurar os alertas para que sejam enviados por e-mail quando o prazo do certificado se aproximar do seu término lógico.
1. Primeiro, precisamos criar um template de evento (Event Template) para ativá-lo quando o valor do contador cair abaixo de um limite definido por nós. Em Event Configuration criamos dois novos templates com nomes, vamos dizer CertificateExpireDate_Threshold_Activate com o status Warning:

e um semelhante CertificateExpireDate_Threshold_Deactivate com o status Normal.
2. Em seguida, vamos às propriedades do contador e na aba Tresholds configuramos o limite:

onde escolhemos os nossos eventos criados CertificateExpireDate_Threshold_Activate e CertificateExpireDate_Threshold_Deactivate, definimos o número de medições (Samples) como 1 (especificamente para este contador não faz sentido colocar mais), um valor de 30 (dias), por exemplo, e, o que é importante, configuramos o tempo de repetição do evento. Para os certificados em produção, eu configuro uma vez por dia (86400 segundos), caso contrário, podemos ser inundados de notificações (o que, aliás, já aconteceu uma vez, de forma que a caixa de entrada ficou cheia durante o fim de semana). Para o tempo de depuração, vale a pena colocar um intervalo menor, como 60 segundos, por exemplo.
3. No Configuração da Ação criamos um modelo de e-mail de notificação, parecido com este:

Todos esses %m, %S e assim por diante — são macros, onde serão inseridos os valores do nosso parâmetro. Eles estão descritos em mais detalhes no NetXMS.
4. E, finalmente, unindo os pontos anteriores, na Política de Processamento de Eventos criamos uma regra pela qual será gerado um Alarme e enviado um e-mail:
Salvamos a política, está tudo pronto para testar. Vamos definir um limite mais alto, para verificação. Meu certificado mais próximo expira em 723 dias, então, para teste, coloquei 724. Como resultado, receberemos este alarme:

e esta notificação por e-mail:

Agora sim, tudo certo. Claro que poderíamos configurar um painel, criar gráficos, mas para os certificados isso resultaria em linhas retas bastante sem sentido e chatas, ao contrário dos gráficos de uso da CPU ou memória, por exemplo. Mas, isso é um tema para outra ocasião.
Fonte: habr.com
