influxdata / telegraf

Agent for collecting, processing, aggregating, and writing metrics, logs, and other arbitrary data.
https://influxdata.com/telegraf
MIT License
14.51k stars 5.55k forks source link

Extend x509_cert plugin or create x509_win_store plugin #5363

Open volhonsky opened 5 years ago

volhonsky commented 5 years ago

Feature Request

I think it is a good idea to fetch certificates data for monitoring from Windows Certificate store with telegraf.

Proposal:

Extends x509_cert plugin realization with something like what

sources = [ "LocalMachine/My","LocalMachine/Root",/etc/ssl/certs/ssl-cert-snakeoil.pem", "https://example.org:443"]

Or create new one (for example x509_win_store)

Current behavior:

Where is no plugins to monitor Windows Certificates store

Desired behavior:

Windows certificates store became monitored.

Use case: [Why is this important (helps with prioritizing requests)]

I think it will be very helpful for monitoring windows certificates expiration.

I have realization of with functionality. But i don't know how will be better: Insert this feature to existing x509_cert input plugin or create new one (for example x509_win_store).

danielnelson commented 5 years ago

This would be a nice feature but I think it would be best to add after this issue is completed: https://github.com/golang/go/issues/16736

volhonsky commented 5 years ago

I don't use SystemCertPool in my realization because this method has error. You can use directly invoke syscall.CertOpenStore for opening system certificate storage in readonly mode. And i think i can fix golang/go#16736. Create new input plugin or extend existing - that is the question. How will be better? Can you give me advise and i will perform pull request?

sjwang90 commented 3 years ago

Waiting on https://github.com/golang/go/issues/16736

sspaink commented 2 years ago

Closing as there hasn't been any activity on https://github.com/influxdata/telegraf/pull/5374, if this is still a requested feature please comment or re-open thank you!

1tft commented 3 weeks ago

Extending x509_cert plugin would still be very usefull. Currently we use this PowerShell Script for monitoring local windows certificate store. It prints out necessary information in Prometheus format. Currently we use this script with one parameter for filtering output with a certain pattern inside Subject Name (Common Name), because often we only want to monitor the machine certifcate.

<#
.SYNOPSIS
Prints information about certificates from Windows certificate store.
.PARAMETER subjectCommonNameFilter
Common Name of Subject (Wildcards allowed)
.EXAMPLE
PS> .\windows_certificate_store.ps1 HOSTNAME_filter.domain
.EXAMPLE
PS> .\windows_certificate_store.ps1 HOSTNA*
#>
param ([string]$subjectCommonNameFilter)

<#
    .SYNOPSIS
    Extracts the common name (CN) from a certificate.
    .PARAMETER entity
    A string that looks like this: CN=XXX-2023, O=YY, C=AT
#>
function ExtractCommonName {
    param ([string]$entity)

    $entityParts = $entity -split ', '

    foreach ($entityPart in $entityParts) {
        if ($entityPart.StartsWith("CN=")) {
            return $commonName=$entityPart.SubString(3)
        }
    }
    return $null
}

<#
    .SYNOPSIS
        This commandlet breaks down the certificate's subject into the Common Name, Organizational Unit, Organization, Locality, State and Country

    .DESCRIPTION
        This commandlet breaks down the certificate's subject into the Common Name, Organizational Unit, Organization, Locality, State and Country

    .PARAMETER Certificate
        A X509Certificate2 instance

    .EXAMPLE
        Get-ChildItem "Cert:\LocalMachine\My" | Get-CertificateSubjectInfo

    .INPUTS
        Any X509Certificate2 certificate

    .LINK
        https://www.powershellgallery.com/packages/CertificatePS/1.5/Content/Get-CertificateSubjectInfo.ps1
#>
function Get-CertificateSubjectInfo {
    [OutputType([string])]
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Security.Cryptography.X509Certificates.X509Certificate2]$Certificate
 )
    Process {

        $hash=[ordered]@{
            Subject=$Certificate.Subject
            FriendlyName=$Certificate.FriendlyName
            CommonName=$null
            OrganizationalUnit=$null
            Organization=$null
            Locality=$null
            State=$null
            Country=$null
        }
        $Certificate.Subject -split ', ' |ForEach-Object {
            switch ($_)
            {
                {$_ -like "CN=*"} {
                    $hash.CommonName=$_.SubString(3)
                    break
                }
                {$_ -like "OU=*"} {
                    $hash.OrganizationalUnit=$_.SubString(3)
                    break
                }
                {$_ -like "O=*"} {
                    $hash.Organization=$_.SubString(2)
                    break
                }
                {$_ -like "L=*"} {
                    $hash.Locality=$_.SubString(2)
                    break
                }
                {$_ -like "S=*"} {
                    $hash.State=$_.SubString(2)
                    break
                }
                {$_ -like "C=*"} {
                    $hash.Country=$_.SubString(2)
                    break
                }
            }
        }
        New-Object -TypeName psobject -Property $hash
    }
}

# get certificates from Windows certificate store
$certs = Get-ChildItem -Path Cert:\LocalMachine\My

foreach ($cert in $certs) {
    $certificateSubjectInfo = $cert | Get-CertificateSubjectInfo

    if ($certificateSubjectInfo.CommonName -notlike $subjectCommonNameFilter) {
        continue
    }

    $expiryDate = $cert.NotAfter
    $secondsUntilExpiry = [Math]::Round(($expiryDate - (Get-Date)).TotalSeconds)
    $issuerCommonName = ExtractCommonName($cert.Issuer)

    $unixEpoch  = Get-Date '1970-01-01'

    $startdate = [Math]::Round(($cert.NotBefore - $unixEpoch).TotalSeconds)
    $enddate = [Math]::Round(($cert.NotAfter - $unixEpoch).TotalSeconds)

    $san = $cert.Extensions | Where-Object {$_.Oid.FriendlyName -eq "Subject Alternative Name"} | ForEach-Object {$_.Format(0)}

    Write-Output "expiry{issuer_common_name=""$issuerCommonName"", common_name=""$($certificateSubjectInfo.CommonName)"", organization=""$($certificateSubjectInfo.Organization)"", organizational_unit=""$($certificateSubjectInfo.OrganizationalUnit)"", country=""$($certificateSubjectInfo.Country)"", province=""$($certificateSubjectInfo.State)"", locality=""$($certificateSubjectInfo.Locality)"", serial_number=""$($cert.SerialNumber)"", signature_algorithm=""$($cert.SignatureAlgorithm.FriendlyName)"", san=""$san""} $secondsUntilExpiry"
    Write-Output "startdate{issuer_common_name=""$issuerCommonName"", common_name=""$($certificateSubjectInfo.CommonName)"", organization=""$($certificateSubjectInfo.Organization)"", organizational_unit=""$($certificateSubjectInfo.OrganizationalUnit)"", country=""$($certificateSubjectInfo.Country)"", province=""$($certificateSubjectInfo.State)"", locality=""$($certificateSubjectInfo.Locality)"", serial_number=""$($cert.SerialNumber)"", signature_algorithm=""$($cert.SignatureAlgorithm.FriendlyName)"", san=""$san""} $startdate"
    Write-Output "enddate{issuer_common_name=""$issuerCommonName"", common_name=""$($certificateSubjectInfo.CommonName)"", organization=""$($certificateSubjectInfo.Organization)"", organizational_unit=""$($certificateSubjectInfo.OrganizationalUnit)"", country=""$($certificateSubjectInfo.Country)"", province=""$($certificateSubjectInfo.State)"", locality=""$($certificateSubjectInfo.Locality)"", serial_number=""$($cert.SerialNumber)"", signature_algorithm=""$($cert.SignatureAlgorithm.FriendlyName)"", san=""$san""} $enddate"
}