Check Logging Status of Palo Alto Firewalls
Posted on November 12, 2020 by
UPDATE 2020-12-28: After my initial post, I found out that the 7x and 52xx models have a different format for the ‘show logging-status’ command. I have updated the code to take those into account. In addition, I have modified the input to take a CSV value pair of firewall,apiKey.
Logs are at the heart of any firewall. So making sure that logs are available to query at all times is important.
One of the features of the Palo Alto firewalls is the ability to forward logs to a Panorama or the Cortex Data Lake service. But it’s important to confirm that the firewall is actually forwarding logs and that some error hasn’t stopped log forwarding.
The easiest way to manually check this on individual firewalls is the “show logging-status” command. It will display the last time logs were forwarded.
But this can be very tedious in an environment with many firewalls. To help automate this process, I created a Powershell script to check the results of “show logging-status” on a given list of firewalls. If any of the firewalls have not forwarded logs in 2+ hours, the list is displayed and (optionally) an email can be sent with the results.
Get-LoggingStatus Powershell script
The script below uses the Palo Alto API to query a list of firewalls. So an API key needs to be configured on the firewalls for this script to work.
See this article to configure an API key on your firewalls.
param
(
[Parameter(Mandatory=$True,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True,
HelpMessage='CSV file with list of Palo Alto firewalls and their API key. Format: fw,apiKey')]
[string]$list,
[Parameter(Mandatory=$False,
ValueFromPipeline=$False,
HelpMessage='If used, email will be sent. If excluded, no email will be sent.')]
[switch]$sendEmail = $false
)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
if (-Not ("TrustAllCertsPolicy" -as [type]))
{
add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy
{
public bool CheckValidationResult
(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem
)
{
return true;
}
}
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
}
$emailFrom = "<YourEmailAddress"
$emailTo = '<YourEmailAddress>'
$emailSubject = "Palo Alto FW Logging Issues"
$smtpServer = "<YourEmailServer>"
# How many hours the firewall's last log forward can be behind the current time.
$hourOffset = "2"
$fwLIst = Get-Content $list
$apiStem = "api/?type=op&cmd=%3Cshow%3E%3Clogging-status%3E%3C%2Flogging-status%3E%3C%2Fshow%3E"
$fwNotSendingLogs = $null
$htmlMessage = New-Object System.Text.StringBuilder
$textMessage = New-Object System.Text.StringBuilder
foreach ($listItem in $fwLIst)
{
$fw,$apiKey = $listItem -split ","
$url = "https://$($fw.trim())/$apiStem&key=$($apiKey.trim())"
$WebRequestContent = $null
$ErrResp = $null
[xml]$WebResponse = $null
try
{
$WebRequestContent = Invoke-WebRequest -Uri "$url" -Method Get
}
catch
{
$streamReader = [System.IO.StreamReader]::new($_.Exception.Response.GetResponseStream())
$ErrResp = $streamReader.ReadToEnd() | ConvertFrom-Json
$streamReader.Close()
}
if ($WebRequestContent -ne $null)
{
[xml]$WebResponse = $WebRequestContent.Content
}
if (($ErrResp) -or ($WebResponse -eq $null))
{
Write-Host "There was an error!" -ForegroundColor Red
Write-Host $ErrResp -ForegroundColor Yellow
}
elseif($WebResponse.response.status -ne "success")
{
Write-Host "There was an error!" -ForegroundColor Red
Write-Host $WebResponse.response.result -ForegroundColor Yellow
Write-Host $WebResponse.response.status -ForegroundColor Yellow
}
else
{
$ConnStatus = $WebResponse.response.result | Select-String -Pattern 'Connection Status.*' -AllMatches | ForEach-Object{$_.Matches}
$LogCollectors = $WebResponse.response.result | Select-String -Pattern 'Log Collector.*' -AllMatches | ForEach-Object{$_.Matches}
$trafficString = $WebResponse.response.result | Select-String -Pattern 'traffic.*' -AllMatches | ForEach-Object{$_.Matches}
$i=0
if ($ConnStatus)
{
foreach ($ConnStringMatch in $ConnStatus)
{
if ($ConnStringMatch -match "lr - Active")
{
$logType,$logLastCreated,$logLastFwd,$seqNumFwd,$seqNumAck,$totalLogsFwd = $trafficString[$i] -split "\s{2,}"
$lcLabel,$lcValue = $LogCollectors[$i] -split ":"
$lcValue = $lcValue.Trim()
# Add offset hours to the Last Log Fwred datetime. If this is less than the current datetime, logs are not being fowarded. Add the firewall to the list.
if ($logLastFwd -as [DateTime])
{
if ((Get-Date $logLastFwd).AddHours($hourOffset) -lt (Get-Date))
{
$fwNotSendingLogs += $fw
$null = $htmlMessage.AppendLine("<tr><td>$fw</td><td>$lcValue</td><td>$logLastFwd</td></tr>")
$null = $textMessage.AppendLine("$fw using Log Collector $lcValue last forwarded logs on $logLastFwd")
}
}
else
{
$fwNotSendingLogs += $fw
$null = $htmlMessage.AppendLine("<tr><td>$fw</td><td>$lcValue</td><td>$logLastFwd</td></tr>")
$null = $textMessage.AppendLine("$fw using Log Collector $lcValue last forwarded logs on $logLastFwd")
}
}
$i++
}
}
elseif ($trafficString)
{
$trafficString = $WebResponse.response.result | Where {$_ -match "traffic.*"} | Foreach {$Matches[0]}
$logType,$logLastCreated,$logLastFwd,$seqNumFwd,$seqNumAck,$totalLogsFwd = $trafficString -split "\s{2,}"
# Add offset hours to the Last Log Fwred datetime. If this is less than the current datetime, logs are not being fowarded. Add the firewall to the list.
if ((Get-Date $logLastFwd).AddHours($hourOffset) -lt (Get-Date))
{
$fwNotSendingLogs += $fw
$null = $htmlMessage.AppendLine("<tr><td>$fw</td><td>$logLastFwd</td></tr>")
$null = $textMessage.AppendLine("$fw last forwarded logs on $logLastFwd")
}
}
}
}
if ($fwNotSendingLogs)
{
$htmlhead = "<html>
<style>
BODY{font-family: Consolas; font-size: 10pt;}
H1{font-size: 22px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}
H2{font-size: 18px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}
H3{font-size: 16px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}
TABLE{border: 1px solid black; border-collapse: collapse; font-size: 9pt;}
TH{border: 1px solid #969595; background: #dddddd; padding: 5px; color: #000000;}
TD{border: 1px solid #969595; padding: 5px; }
td.pass{background: #B7EB83;}
td.warn{background: #FFF275;}
td.fail{background: #FF2626; color: #ffffff;}
td.info{background: #85D4FF;}
tr:hover {background-color: #85D4FF}
</style>
<body>
<p>The following Palo Alto firewalls have stopped forwarding logs:</p>
<table>
<tr><th>Firewall</th><th>Log Collector Name</th><th>Last Log Fowarded</th></tr>"
$htmltail = "</table></body></html>"
$body = $htmlhead + $htmlMessage + $htmltail
If ($sendEmail)
{
Send-MailMessage -To $emailTo -body $body -BodyAsHtml -From $emailFrom -Subject $emailSubject -Priority High -smtpServer $smtpServer
}
Write-Host $textMessage -ForegroundColor Yellow
}
else
{
Write-Host "All firewalls are forwarding logs."
}
Make sure to edit the highlighted lines to add your email settings.
To use the script, save it as “Get-LoggingStatus.ps1” and run it with these command line parameters.
Get-LoggingStatus.ps1 -list "C:\PathTo\firewall.txt" [-sendEmail]
The “-list” parameter takes a CSV formatted file with the list of firewalls and their associated API key.
The “-sendEmail” parameter is optional. If used and any firewalls are not sending logs, it will send an email.
The variable “$hourOffset” sets a threshold on how many hours the firewall can be behind for log forwarding.
If you want to test the functionality of the script, set the “$hourOffset” variable to a negative number, like -2. The script will think that all firewalls are failing to forward logs and cause the script to display a list of results similar to the following.
firewall01 last forwarded logs on YYYY/MM/DD HH:MM:SS
firewall02 last forwarded logs on YYYY/MM/DD HH:MM:SS
...
Just make sure to change the variable back to 2 or whatever threshold you want to set before using the script.
If one or more firewalls are NOT forwarding logs, the best solution is probably to open a case with support. But at least the problem can be detected quickly before logs are lost.