|
| 1 | +<# |
| 2 | +.SYNOPSIS |
| 3 | + Check whether a certificate is still trusted and when it runs out or starts. |
| 4 | +.DESCRIPTION |
| 5 | + Invoke-IcingaCheckCertificate returns either 'OK', 'WARNING' or 'CRITICAL', based on the thresholds set. |
| 6 | + e.g a certificate will run out in 30 days, WARNING is set to '20d:', CRITICAL is set to '50d:'. In this case the check will return 'WARNING'. |
| 7 | + |
| 8 | + More Information on https://github.com/Icinga/icinga-powershell-plugins |
| 9 | +.FUNCTIONALITY |
| 10 | + This module is intended to be used to check if a certificate is still valid or about to become valid. |
| 11 | +.EXAMPLE |
| 12 | + You can check certificates in the local certificate store of Windows: |
| 13 | +
|
| 14 | + PS> Invoke-IcingaCheckCertificate -CertStore 'LocalMachine' -CertStorePath 'My' -CertSubject '*' -WarningEnd '30d:' -CriticalEnd '10d:' |
| 15 | + [OK] Check package "Certificates" (Match All) |
| 16 | + \_ [OK] Certificate 'test.example.com' (valid until 2033-11-19 : 4993d) valid for: 431464965.59 |
| 17 | +.EXAMPLE |
| 18 | + Also a directory with a file name pattern is possible: |
| 19 | +
|
| 20 | + PS> Invoke-IcingaCheckCertificate -CertPaths "C:\ProgramData\icinga2\var\lib\icinga2\certs" -CertName '*.crt' -WarningEnd '10000d:' |
| 21 | + [WARNING] Check package "Certificates" (Match All) - [WARNING] Certificate 'test.example.com' (valid until 2033-11-19 : 4993d) valid for, Certificate 'Icinga CA' (valid until 2032-09-18 : 4566d) valid for |
| 22 | + \_ [WARNING] Certificate 'test.example.com' (valid until 2033-11-19 : 4993d) valid for: Value "431464907.76" is lower than threshold "864000000" |
| 23 | + \_ [WARNING] Certificate 'Icinga CA' (valid until 2032-09-18 : 4566d) valid for: Value "394583054.72" is lower than threshold "864000000" |
| 24 | +.EXAMPLE |
| 25 | + The checks can be combined into a single check: |
| 26 | +
|
| 27 | + PS> Invoke-IcingaCheckCertificate -CertStore 'LocalMachine' -CertStorePath 'My' -CertThumbprint '*'-CertPaths "C:\ProgramData\icinga2\var\lib\icinga2\certs" -CertName '*.crt' -Trusted |
| 28 | + [CRITICAL] Check package "Certificates" (Match All) - [CRITICAL] Certificate 'test.example.com' trusted, Certificate 'Icinga CA' trusted |
| 29 | + \_ [CRITICAL] Check package "Certificate 'test.example.com'" (Match All) |
| 30 | + \_ [OK] Certificate 'test.example.com' (valid until 2033-11-19 : 4993d) valid for: 431464853.88 |
| 31 | + \_ [CRITICAL] Certificate 'test.example.com' trusted: Value "False" is not matching threshold "True" |
| 32 | + \_ [CRITICAL] Check package "Certificate 'Icinga CA'" (Match All) |
| 33 | + \_ [OK] Certificate 'Icinga CA' (valid until 2032-09-18 : 4566d) valid for: 394583000.86 |
| 34 | + \_ [CRITICAL] Certificate 'Icinga CA' trusted: Value "False" is not matching threshold "True" |
| 35 | +
|
| 36 | +.PARAMETER Trusted |
| 37 | + Used to switch on trusted behavior. Whether to check, If the certificate is trusted by the system root. |
| 38 | + Will return Critical in case of untrust. |
| 39 | +
|
| 40 | + Note: it is currently required that the root and intermediate CA is known and trusted by the local system. |
| 41 | +
|
| 42 | +.PARAMETER CriticalStart |
| 43 | + Used to specify a date. The start date of the certificate has to be past the date specified, otherwise the check results in critical. Use carefully. |
| 44 | + Use format like: 'yyyy-MM-dd' |
| 45 | + |
| 46 | +.PARAMETER WarningEnd |
| 47 | + Used to specify a Warning range for the end date of an certificate. In this case a string. |
| 48 | + Allowed units include: ms, s, m, h, d, w, M, y |
| 49 | +
|
| 50 | +.PARAMETER CriticalEnd |
| 51 | + Used to specify a Critical range for the end date of an certificate. In this case a string. |
| 52 | + Allowed units include: ms, s, m, h, d, w, M, y |
| 53 | + |
| 54 | +.PARAMETER CertStore |
| 55 | + Used to specify which CertStore to check. Valid choices are '*', 'LocalMachine', 'CurrentUser', '' |
| 56 | + |
| 57 | + .PARAMETER CertThumbprint |
| 58 | + Used to specify an array of Thumbprints, which are used to determine what certificate to check, within the CertStore. |
| 59 | +
|
| 60 | +.PARAMETER CertSubject |
| 61 | + Used to specify an array of Subjects, which are used to determine what certificate to check, within the CertStore. |
| 62 | + |
| 63 | +.PARAMETER CertStorePath |
| 64 | + Used to specify which path within the CertStore should be checked. |
| 65 | + |
| 66 | +.PARAMETER CertPaths |
| 67 | + Used to specify an array of paths on your system, where certificate files are. Use with CertName. |
| 68 | + |
| 69 | +.PARAMETER CertName |
| 70 | + Used to specify an array of certificate names of certificate files to check. Use with CertPaths. |
| 71 | + |
| 72 | +.INPUTS |
| 73 | + System.String |
| 74 | +.OUTPUTS |
| 75 | + System.String |
| 76 | +.LINK |
| 77 | + https://github.com/Icinga/icinga-powershell-plugins |
| 78 | +.NOTES |
| 79 | +#> |
| 80 | + |
| 81 | +function Invoke-IcingaCheckCertificate() |
| 82 | +{ |
| 83 | + param( |
| 84 | + #Checking |
| 85 | + [switch]$Trusted = $FALSE, |
| 86 | + $CriticalStart = $null, |
| 87 | + $WarningEnd = '30d:', |
| 88 | + $CriticalEnd = '10d:', |
| 89 | + #CertStore-Related Param |
| 90 | + [ValidateSet('*', 'LocalMachine', 'CurrentUser', $null)] |
| 91 | + [string]$CertStore = $null, |
| 92 | + [array]$CertThumbprint = $null, |
| 93 | + [array]$CertSubject = $null, |
| 94 | + $CertStorePath = '*', |
| 95 | + #Local Certs |
| 96 | + [array]$CertPaths = $null, |
| 97 | + [array]$CertName = $null, |
| 98 | + [switch]$Recurse = $FALSE, |
| 99 | + #Other |
| 100 | + [ValidateSet(0, 1, 2, 3)] |
| 101 | + [int]$Verbosity = 3 |
| 102 | + ); |
| 103 | + |
| 104 | + $CertData = Get-IcingaCertificateData ` |
| 105 | + -CertStore $CertStore -CertThumbprint $CertThumbprint -CertSubject $CertSubject ` |
| 106 | + -CertPaths $CertPaths -CertName $CertName -CertStorePath $CertStorePath -Recurse $Recurse; |
| 107 | + $CertPackage = New-IcingaCheckPackage -Name 'Certificates' -OperatorAnd -Verbose $Verbosity; |
| 108 | + |
| 109 | + if ($null -ne $CriticalStart) { |
| 110 | + try { |
| 111 | + [datetime]$CritDateTime = $CriticalStart |
| 112 | + } catch { |
| 113 | + Exit-IcingaThrowException -ExceptionType 'Custom' -CustomMessage 'DateTimeParseError' -InputString ( |
| 114 | + [string]::Format('The provided value "{0}" for argument "CriticalStart" could not be parsed as DateTime.', $CriticalStart) |
| 115 | + ) -Force; |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + foreach ($data in $CertData) { |
| 120 | + $Cert = $data.Cert; |
| 121 | + |
| 122 | + if ($null -eq $Cert) { |
| 123 | + # Not a valid cert file - unknown check |
| 124 | + $CertPackage.AddCheck( |
| 125 | + (New-IcingaCheck -Name ([string]::Format("Not a certificate: {0}", $data.Path))).SetUnknown() |
| 126 | + ); |
| 127 | + continue; |
| 128 | + } |
| 129 | + |
| 130 | + $SpanTilAfter = (New-TimeSpan -Start (Get-Date) -End $Cert.NotAfter); |
| 131 | + if ($Cert.Subject.Contains(',')) { |
| 132 | + [string]$CertName = $Cert.Subject.Split(",")[0]; |
| 133 | + } else { |
| 134 | + [string]$CertName = $Cert.Subject; |
| 135 | + } |
| 136 | + |
| 137 | + $CertName = $CertName -ireplace '(cn|ou)=', ''; |
| 138 | + $CheckNamePrefix = "Certificate '${CertName}'"; |
| 139 | + if ($data.ContainsKey('Path')) { |
| 140 | + $CheckNamePrefix += (" at " + $data.Path) |
| 141 | + } |
| 142 | + |
| 143 | + $checks = @(); |
| 144 | + |
| 145 | + if ($Trusted) { |
| 146 | + $CertValid = Test-Certificate $cert -ErrorAction SilentlyContinue -WarningAction SilentlyContinue; |
| 147 | + $IcingaCheck = New-IcingaCheck -Name "${CheckNamePrefix} trusted" -Value $CertValid; |
| 148 | + $IcingaCheck.CritIfNotMatch($TRUE) | Out-Null; |
| 149 | + $checks += $IcingaCheck; |
| 150 | + } |
| 151 | + |
| 152 | + if ($null -ne $CriticalStart) { |
| 153 | + $CritStart = ((New-TimeSpan -Start $Cert.NotBefore -End $CritDateTime) -gt 0) |
| 154 | + $IcingaCheck = New-IcingaCheck -Name "${CheckNamePrefix} already valid" -Value $CritStart; |
| 155 | + $IcingaCheck.CritIfNotMatch($TRUE) | Out-Null; |
| 156 | + $checks += $IcingaCheck; |
| 157 | + } |
| 158 | + |
| 159 | + if(($null -ne $WarningEnd) -Or ($null -ne $CriticalEnd)) { |
| 160 | + $ValidityInfo = ([string]::Format('valid until {0} : {1}d', $Cert.NotAfter.ToString('yyyy-MM-dd'), $SpanTilAfter.Days)); |
| 161 | + $IcingaCheck = New-IcingaCheck -Name "${CheckNamePrefix} ($ValidityInfo) valid for" -Value (New-TimeSpan -End $Cert.NotAfter.DateTime).TotalSeconds; |
| 162 | + $IcingaCheck.WarnOutOfRange((ConvertTo-SecondsFromIcingaThresholds -Threshold $WarningEnd)).CritOutOfRange((ConvertTo-SecondsFromIcingaThresholds -Threshold $CriticalEnd)) | Out-Null; |
| 163 | + $checks += $IcingaCheck; |
| 164 | + } |
| 165 | + |
| 166 | + if ($checks.Length -eq 1) { |
| 167 | + # Only add one check instead of the package |
| 168 | + $CertPackage.AddCheck($checks[0]) |
| 169 | + } else { |
| 170 | + $CertCheck = New-IcingaCheckPackage -Name $CheckNamePrefix -OperatorAnd; |
| 171 | + foreach ($check in $checks) { |
| 172 | + $CertCheck.AddCheck($check) |
| 173 | + } |
| 174 | + $CertPackage.AddCheck($CertCheck) |
| 175 | + } |
| 176 | + } |
| 177 | + |
| 178 | + return (New-IcingaCheckResult -Name 'Certificates' -Check $CertPackage -NoPerfData $TRUE -Compile); |
| 179 | +} |
0 commit comments