繁体   English   中英

用于远程获取多个服务器的网站证书到期的 Powershell 脚本

[英]Powershell script to get certificate expiry for a website remotely for multiple servers

我正在尝试创建一个脚本来远程获取多个服务器的网站的证书到期日期。 我有一个适用于单个服务器的脚本(需要登录服务器并执行),我需要为多个服务器远程运行它。 如何修改此脚本以远程执行多个服务器。 请指教。

 $servers = get-content D:\Certificate.txt
$DaysToExpiration = 60 #change this once it's working 
$expirationDate = (Get-Date).AddDays($DaysToExpiration)

foreach ($server in $servers)
{

$sites = Get-Website | ? { $_.State -eq "Started" } | % { $_.Name }
$certs = Get-ChildItem IIS:SSLBindings | ? {
           $sites -contains $_.Sites.Value
         } | % { $_.Thumbprint }


Get-ChildItem CERT:LocalMachine/My | ? {
  $certs -contains $_.Thumbprint -and $_.NotAfter -lt $expirationDate
}
}

https://iamoffthebus.wordpress.com/2014/02/04/powershell-to-get-remote-websites-ssl-certificate-expiration/ 的启发,我使用以下脚本:

$minimumCertAgeDays = 60
$timeoutMilliseconds = 10000
$urls = get-content .\check-urls.txt

#disabling the cert validation check. This is what makes this whole thing work with invalid certs...
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

foreach ($url in $urls)
{
    Write-Host Checking $url -f Green
    $req = [Net.HttpWebRequest]::Create($url)
    $req.Timeout = $timeoutMilliseconds
    $req.AllowAutoRedirect = $false
    try {$req.GetResponse() |Out-Null} catch {Write-Host Exception while checking URL $url`: $_ -f Red}
    $certExpiresOnString = $req.ServicePoint.Certificate.GetExpirationDateString()
    #Write-Host "Certificate expires on (string): $certExpiresOnString"
    [datetime]$expiration = [System.DateTime]::Parse($req.ServicePoint.Certificate.GetExpirationDateString())
    #Write-Host "Certificate expires on (datetime): $expiration"
    [int]$certExpiresIn = ($expiration - $(get-date)).Days
    $certName = $req.ServicePoint.Certificate.GetName()
    $certPublicKeyString = $req.ServicePoint.Certificate.GetPublicKeyString()
    $certSerialNumber = $req.ServicePoint.Certificate.GetSerialNumberString()
    $certThumbprint = $req.ServicePoint.Certificate.GetCertHashString()
    $certEffectiveDate = $req.ServicePoint.Certificate.GetEffectiveDateString()
    $certIssuer = $req.ServicePoint.Certificate.GetIssuerName()
    if ($certExpiresIn -gt $minimumCertAgeDays)
    {
        Write-Host Cert for site $url expires in $certExpiresIn days [on $expiration] -f Green
    }
    else
    {
        Write-Host WARNING: Cert for site $url expires in $certExpiresIn days [on $expiration] -f Red
        Write-Host Threshold is $minimumCertAgeDays days. Check details:`nCert name: $certName -f Red
        Write-Host Cert public key: $certPublicKeyString -f Red
        Write-Host Cert serial number: $certSerialNumber`nCert thumbprint: $certThumbprint`nCert effective date: $certEffectiveDate`nCert issuer: $certIssuer -f Red
    }
    Write-Host
    rv req
    rv expiration
    rv certExpiresIn
}

或者,您可能会发现此高级脚本很有用:

  • 您可以在报告输出为文本、Html 或 PSObject 之间切换
  • 将脚本与 urls(参数数组)或用于 urls 的输入文件或管道输入一起使用
  • 提高稳定性:正确处理 HTTP 连接上丢失的证书
  • 只需将代码放入 Check-ExpiringSslCerts.ps1 之类的文件中

这里是高级脚本代码:

[CmdletBinding(DefaultParametersetname="URLs in text file")]
Param(
  [ValidateSet('Text','Html','PSObject')]
  [string]$ReportType = 'Text',
  [int]$MinimumCertAgeDays = 60,
  [int]$TimeoutMilliseconds = 10000,
  [parameter(Mandatory=$false,ParameterSetName = "URLs in text file")]
  [string]$UrlsFile = '.\check-urls.txt',
  [parameter(Mandatory=$false,ParameterSetName = "List of URLs", 
        ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
  [string[]]$Urls
)
Begin 
{
    [string[]]$allUrls = @()
    $returnData = @()
    [bool]$ProcessedInputPipeLineByArrayItem = $false

    function CheckUrl ([string]$url, [array]$returnData)
    {
        [string]$details = $null
        if ($ReportType -eq "Html") 
        { 
            $stringHtmlEncoded = [System.Web.HttpUtility]::HtmlEncode($url)
            Write-Host "<tr><td>$stringHtmlEncoded</td>" 
        }
        if ($ReportType -eq "Text") { Write-Host Checking $url }
        $req = [Net.HttpWebRequest]::Create($url)
        $req.Timeout = $timeoutMilliseconds
        $req.AllowAutoRedirect = $false
        try 
        {
            $req.GetResponse() |Out-Null
            if ($req.ServicePoint.Certificate -eq $null) {$details = "No certificate in use for connection"}
        } 
        catch 
        {
            $details = "Exception while checking URL $url`: $_ "
        }
        if ($details -eq $null -or $details -eq "")
        {
            $certExpiresOnString = $req.ServicePoint.Certificate.GetExpirationDateString()
            #Write-Host "Certificate expires on (string): $certExpiresOnString"
            [datetime]$expiration = [System.DateTime]::Parse($req.ServicePoint.Certificate.GetExpirationDateString())
            #Write-Host "Certificate expires on (datetime): $expiration"
            [int]$certExpiresIn = ($expiration - $(get-date)).Days
            $certName = $req.ServicePoint.Certificate.GetName()
            $certPublicKeyString = $req.ServicePoint.Certificate.GetPublicKeyString()
            $certSerialNumber = $req.ServicePoint.Certificate.GetSerialNumberString()
            $certThumbprint = $req.ServicePoint.Certificate.GetCertHashString()
            $certEffectiveDate = $req.ServicePoint.Certificate.GetEffectiveDateString()
            $certIssuer = $req.ServicePoint.Certificate.GetIssuerName()
            if ($certExpiresIn -gt $minimumCertAgeDays)
            {
                if ($ReportType -eq "Html") 
                { 
                    Write-Host "<td>OKAY</td><td>$certExpiresIn</td><td>$expiration</td><td>&nbsp;</td></tr>"
                }
                if ($ReportType -eq "Text") 
                { 
                    Write-Host OKAY: Cert for site $url expires in $certExpiresIn days [on $expiration] -f Green 
                }
                if ($ReportType -eq "PSObject") 
                { 
                    $returnData += new-object psobject -property  @{Url = $url; CheckResult = "OKAY"; CertExpiresInDays = [int]$certExpiresIn; ExpirationOn = [datetime]$expiration; Details = [string]$null}
                }
            }
            else
            {
                $details = ""
                $details += "Cert for site $url expires in $certExpiresIn days [on $expiration]`n"
                $details += "Threshold is $minimumCertAgeDays days. Check details:`n"
                $details += "Cert name: $certName`n"
                $details += "Cert public key: $certPublicKeyString`n"
                $details += "Cert serial number: $certSerialNumber`n"
                $details += "Cert thumbprint: $certThumbprint`n"
                $details += "Cert effective date: $certEffectiveDate`n"
                $details += "Cert issuer: $certIssuer"
                if ($ReportType -eq "Html") 
                { 
                    Write-Host "<td>WARNING</td><td>$certExpiresIn</td><td>$expiration</td>"
                    $stringHtmlEncoded = [System.Web.HttpUtility]::HtmlEncode($details) -replace "`n", "<br />"
                    Write-Host "<tr><td>$stringHtmlEncoded</td></tr>" 
                }
                if ($ReportType -eq "Text") 
                { 
                    Write-Host WARNING: $details -f Red
                }
                if ($ReportType -eq "PSObject") 
                { 
                    $returnData += new-object psobject -property  @{Url = $url; CheckResult = "WARNING"; CertExpiresInDays = [int]$certExpiresIn; ExpirationOn = [datetime]$expiration; Details = $details}
                }
            rv expiration
            rv certExpiresIn
            }
        }
        else
        {
            if ($ReportType -eq "Html") 
            { 
                Write-Host "<td>ERROR</td><td>N/A</td><td>N/A</td>"
                $stringHtmlEncoded = [System.Web.HttpUtility]::HtmlEncode($details) -replace "`n", "<br />"
                Write-Host "<tr><td>$stringHtmlEncoded</td></tr>" 
            }
            if ($ReportType -eq "Text") 
            { 
                Write-Host ERROR: $details -f Red
            }
            if ($ReportType -eq "PSObject") 
            { 
                $returnData += new-object psobject -property  @{Url = $url; CheckResult = "ERROR"; CertExpiresInDays = $null; ExpirationOn = $null; Details = $details}
            }
        }
        if ($ReportType -eq "Text") { Write-Host }
        rv req
        return $returnData
    }

    #disabling the cert validation check. This is what makes this whole thing work with invalid certs...
    [Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

    if ($ReportType -eq "Html") 
    { 
        Write-Host "<table><tr><th>URL</th><th>Check result</th><th>Expires in days</th><th>Expires on</th><th>Details</th></tr>" 
        Add-Type -AssemblyName System.Web 
    }
}
Process
{
    if ($_ -ne $null)
    {
        CheckUrl $_ $returnData
        $ProcessedInputPipeLineByArrayItem = $true
    }
}
End
{
    if ($ProcessedInputPipeLineByArrayItem -eq $false)
    {
        if ($Urls -eq $null)
        {
            $allUrls = get-content $UrlsFile
        }
        else
        {
            $allUrls = $Urls
        }
        foreach ($url in $allUrls)
        {
            $returnData = CheckUrl $url $returnData
        }
    }
    if ($ReportType -eq "Html") { Write-Host "</table>" }
    if ($ReportType -eq "PSObject") { return $returnData }
}

输出可能类似于:

"http://www.doma.com", "https://www.domb.com" | .\Check-ExpiringSslCerts.ps1 -ReportType PSObject | ft

Url                  ExpirationOn        CertExpiresInDays CheckResult Details                             
---                  ------------        ----------------- ----------- -------                              
http://www.doma.com                                        ERROR       No certificate in use for connection 
https://www.domb.com 18.11.2017 09:33:00 87                OKAY

将您编写的整个代码放在一个脚本块中,为此,只需在开头添加以下代码:

$sb = {

并在您的代码底部添加:

}

拥有此脚本块后,您可以使用以下命令在服务器上远程运行此脚本:

$cred = Get-Credential
$servers = get-content D:\Certificate.txt
Invoke-Command -Credential $cred -ComputerName $servers -ScriptBlock $SB

希望有帮助!

您的代码片段很有帮助,但它们会在 HTTP 错误上引发错误——这是底层 .NET HttpWebRequest 对象的本质,它会对不是代码 200 的所有内容感到恐慌。

因此,如果您正在测试仅接受 POST HTTP 动词的 API 端点,您的脚本将失败,因为它会收到 405 错误消息。 或者,如果您的 URL 返回 HTTP 404,您的脚本也会失败。 幸运的是,您可以捕获第 7 层错误并捕获所需的信息,只需以这种方式修补您的代码:

try {$req.GetResponse() | Out-Null } catch {
    if ($_.Exception.InnerException.Status -eq 'ProtocolError') {

        # saving the info anyway since this is a L7 error, e.g.: 
        $certExpiresOnString = $req.ServicePoint.Certificate.GetExpirationDateString()
        [datetime]$expiration [System.DateTime]::Parse($req.ServicePoint.Certificate.GetExpirationDateString())
        $certIssuer = $req.ServicePoint.Certificate.GetIssuerName() # ...

    } else {

        # this is a real error - timeout, DNS failure etc
        Write-Host "$url, $_" -ForegroundColor Red
        Continue
    }       
}

主要来自https://gist.github.com/jstangroome/5945820 ,尽管有一些变化。 以下将获得证书。 然后需要检查$certificate.NotBefore$certificate.NotAfter

function GetCertificate([string]$domain, [Int16]$port) {
    $certificate = $null
    $TcpClient = New-Object -TypeName System.Net.Sockets.TcpClient
    $TcpClient.ReceiveTimeout = 1000
    $TcpClient.SendTimeout = 1000

    try {

        $TcpClient.Connect($domain, $port)
        $TcpStream = $TcpClient.GetStream()

        $Callback = { param($sender, $cert, $chain, $errors) return $true }

        $SslStream = New-Object -TypeName System.Net.Security.SslStream -ArgumentList @($TcpStream, $true, $Callback)
        try {

            $SslStream.AuthenticateAsClient($domain)
            $certificate = $SslStream.RemoteCertificate

        }
        finally {
            $SslStream.Dispose()
        }

    }
    finally {
        $TcpClient.Dispose()
    }

    if ($certificate) {
        if ($certificate -isnot [System.Security.Cryptography.X509Certificates.X509Certificate2]) {
            $certificate = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList $certificate
        }
    }

    return $certificate
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM