Моніторинг терміну дії сертифікатів у Windows на NetXMS

Нещодавно у нас постало завдання моніторингу терміну дії сертифікатів на серверах із Windows. Ну як підвелася, після того, як сертифікати кілька разів перетворювалися на гарбуз, у той самий час, коли відповідальний за їхнє продовження бородатий колега перебував у відпустці. Після цього ми з ним щось запідозрили, вирішили над цим задуматися. Так як у нас не поспішаючи впроваджується система моніторингу NetXMS, вона і стала головним і, в принципі, єдиним кандидатом на це завдання.

Результат у результаті був отриманий у такому вигляді:

Моніторинг терміну дії сертифікатів у Windows на NetXMS

А процес далі.

Поїхали. Вбудованого лічильника терміну сертифікатів, що закінчується в NetXMS немає, тому потрібно створювати свій і використовувати скрипти для забезпечення його даними. Звичайно, на Powershell, це Windows. Скрипт повинен прочитати всі сертифікати в операційній системі, взяти звідти термін їх закінчення в днях і передати це число NetXMS. Через його агента. Ось із нього і почнемо.

Варіант перший, найпростіший. Просто отримати кількість днів до закінчення терміну дії сертифіката із найближчою датою.

Щоб сервер NetXMS дізнався про існування нашого кастомного параметра, він має отримати його від агента. Інакше цей параметр неможливо буде додати через його відсутність. Тому в конфігураційний файл агента nxagentd.conf ми додаємо рядок зовнішнього параметра з ім'ям HTTPS.CertificateExpireDateSimple, в якій прописуємо запуск скрипту:

ExternalParameter = HTTPS.CertificateExpireDateSimple: powershell.exe -File "servershareNetXMS_CertExpireDateSimple.ps1"

Враховуючи, що скрипт запускається по мережі, потрібно не забути про Політика виконання, а також не забути інші "-NoLogo -NoProfile -NonInteractive", які я опустив для кращої читання коду.

В результаті чого конфіг агента виглядає приблизно так:

#
# 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"

Після цього потрібно зберегти конфіг та перезапустити агента. Можна це зробити з консолі NetXMS: відкрити конфіг (Edit agent's confuguration file), відредагувати, виконати Save&Apply, внаслідок чого, власне, станеться те саме. Потім перечитати конфігурацію (Poll > Configuration), якщо немає сил почекати. Після цих дій має з'явитись можливість додати наш кастомний параметр.

У консолі NetXMS йдемо в Data Collection Configuration піддослідного сервера, на якому ми зібралися моніторити сертифікати і створюємо там новий параметр (надалі, після налаштування є сенс перенести його в шаблони). Вибираємо HTTPS.CertificateExpireDateSimple зі списку, вписуємо Description із зрозумілим ім'ям, тип ставимо Integer та налаштовуємо інтервал опитування. На час налагодження є сенс зробити його коротшим, 30 секунд, наприклад. Все, готово, поки що достатньо.

Можна перевіряти… ні, ще зарано. Зараз, звичайно, нічого ми не отримаємо. Просто через те, що скрипт ще не написали. Виправляємо цей недогляд. Скрипт видаватиме просто цифру, кількість днів, що залишилися до закінчення терміну дії сертифіката. Найменшу з усіх наявних. Приклад скрипту:

try {
    # Получаем все сертификаты из хранилища сертификатов
    $lmCertificates = @( Get-ChildItem -Recurse -path 'Cert:LocalMachineMy' -ErrorAction Stop )
     
    # Если сертификатов нет, вернуть "10 лет"
    if ($lmCertificates.Count -eq 0) { return 3650 }
 
    # Получаем Expiration Date всех сертификатов
    $expirationDates = @( $lmCertificates | ForEach-Object { return $_.NotAfter } )
 
    # Получаем наиболее близкий Expiration Date из всех
    $minExpirationDate = ($expirationDates | Measure-Object -Minimum -ErrorAction Stop ).Minimum
 
    # Конвертируем наиболее близкий Expiration Date в количество оставшихся дней с округлением в меньшую сторону
    $daysLeft = [Math]::Floor( ($minExpirationDate - [DateTime]::Now).TotalDays )
 
    # Возвращаем значение
    return $daysLeft
}
catch {
    return -1
}

Виходить так:

Моніторинг терміну дії сертифікатів у Windows на NetXMS

723 дні, до закінчення терміну дії сертифіката ще майже два роки. Логічно тому, що сертифікати на Exchange тестового стенду я перевиписував зовсім недавно.

То був простий варіант. Напевно, когось і такий влаштує, але нам хотілося б більше. Завдання ми поставили собі отримати список всіх сертифікатів на сервері, поіменно, і в кожного бачити кількість днів, що залишилися до закінчення терміну дії сертифіката.

Другий варіант, Дещо складніше.

Знову редагуємо конфіг агента і там замість рядка з ExternalParameter пишемо дві інші:

ExternalList = HTTPS.CertificateNames: powershell.exe -File "serversharenetxms_CertExternalNames.ps1"
ExternalParameter = HTTPS.CertificateExpireDate(*): powershell.exe -File "serversharenetxms_CertExternalParameter.ps1" -CertificateId "$1"

В ExternalList ми отримуємо просто список рядків. У нашому випадку список рядків з іменами сертифікатів. Список цих рядків ми отримаємо скриптом. Ім'я списку HTTPS.CertificateNames.

Скрипт 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)"
    }
}

А вже в externalparameter ми на вхід подаємо рядки зі списку ExternalList, а на виході отримаємо ту саму кількість днів для кожної. Ідентифікатор служить Thumbprint сертифіката. Зверніть увагу, що HTTPS.CertificateExpireDate у цьому варіанті містить зірочку (*). Це необхідно для того, щоб він приймав зовнішні змінні, якраз наш CertificateId.

Скрипт 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)"
}

У Data Collection Configuration сервер створюємо новий параметр. У Parameter вибираємо наш HTTPS.CertificateExpireDate(*) зі списку, і, (увага!) міняємо зірочку на {instance}. Цей важливий момент дозволить створити окремий лічильник кожного інстансу (сертифіката). Решта заповнюється, як у попередньому варіанті:

Моніторинг терміну дії сертифікатів у Windows на NetXMS

Для того, щоб лічильники було з чого створювати, на вкладці Instance Discovery потрібно вибрати Agent List зі списку і в полі List Name вписати ім'я нашого ExternalList зі скрипту HTTPS.CertificateNames.

Майже готово, трохи почекати чи примусово зробити Poll > Configuration і Poll > Instance Discovery, якщо неможливо чекати. В результаті одержуємо всі наші сертифікати з термінами дії:

Моніторинг терміну дії сертифікатів у Windows на NetXMS

Те що треба? Так, тільки черв'ячок перфекціонізму дивиться на цей непотрібний Thumbprint на ім'я лічильника сумними очима і ніяк не дає закінчити статтю. Щоб його нагодувати, знову відкриваємо властивості лічильника і на вкладці Instance Discovery в полі Instance discovery filter script додаємо написаний на NXSL (Внутрішній мові NetXMS) скрипт:

instance = $1;
 if (instance ~= "^(.*)s-s[T:[a-zA-Z0-9]+]$")
 {
 return %(true, instance, $1);
 }
 return true;

який відфільтровуватиме Thumbprint:

Моніторинг терміну дії сертифікатів у Windows на NetXMS

А щоб його відобразити відфільтрованим, на вкладці General у полі Description змінюємо CertificateExpireDate: {instance} на CertificateExpireDate: {instance-name}:

Моніторинг терміну дії сертифікатів у Windows на NetXMS

Все, нарешті фініш з КДПВ:

Моніторинг терміну дії сертифікатів у Windows на NetXMS

Краса ж?

Залишилося налаштувати оповіщення, щоб вони приходили на пошту, коли термін сертифікату добігає свого логічного кінця.

1. Спочатку потрібно створити шаблон події (Event Template) для активації його за зменшення значення лічильника до якогось заданого нами порога. У Конфігурація події створюємо два нові шаблони з іменами, скажімо CertificateExpireDate_Threshold_Activate зі статусом Warning:

Моніторинг терміну дії сертифікатів у Windows на NetXMS

і аналогічний CertificateExpireDate_Threshold_Deactivate зі статусом Normal.

2. Далі йдемо у властивості лічильника і на вкладці Tresholds налаштовуємо поріг:

Моніторинг терміну дії сертифікатів у Windows на NetXMS

де вибираємо наші створені евенти CertificateExpireDate_Threshold_Activate і CertificateExpireDate_Threshold_Deactivate, ставимо кількість вимірів (Samples) 1 (конкретно для цього лічильника більше ставити сенсу немає), значення в 30 (днів), наприклад, і, що важливо, на. Для сертифікатів у продакшені я ставлю раз на добу (86400 секунд), інакше можна потонути в сповіщеннях (що, до речі, і трапилося один раз, причому так, що переповнилася поштова скринька за вихідні). На час налагодження є сенс поставити менше, 60 секунд, наприклад.

3. В Action Configuration створюємо шаблон листа оповіщення, типу такого:

Моніторинг терміну дії сертифікатів у Windows на NetXMS

Всі ці %m, %S тощо. — макроси, у яких будуть підставлені значення нашого параметра. Детальніше вони описані в мануалі NetXMS.

4. І, нарешті, об'єднуючи попередні пункти, Event Processing Policy створюємо правило, за яким створюватиметься Alarm і оговтуватиметься лист:

Моніторинг терміну дії сертифікатів у Windows на NetXMS

Зберігаємо політику, все можна тестувати. Поставимо поріг вище для перевірки. У мене найближчий сертифікат спливає через 723 дні, для перевірки поставив 724. У результаті отримаємо такий аларм:

Моніторинг терміну дії сертифікатів у Windows на NetXMS

і таке сповіщення поштою:

Моніторинг терміну дії сертифікатів у Windows на NetXMS

Ось тепер точно все. Можна було б, звичайно, налаштувати дашборд, побудувати графіки, але для сертифікатів це будуть дещо безглузді та нудні прямі лінії, на відміну від графіків завантаження процесора чи пам'яті, наприклад. Але, про це якось іншим разом.

Джерело: habr.com

Додати коментар або відгук