Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ab#62441 #27

Merged
merged 14 commits into from
Dec 6, 2024
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
2.3.0
* Added support for Template Only Commits
* Added support for Template Stack Commits
* Added support for ingoring Trusted Default Certs on inventory to speed up the inventory job

2.2.1
* Fixed URL Encoding on Palo Username and Pwd that caused invalid credentials error

Expand Down
4 changes: 2 additions & 2 deletions PaloAlto.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30717.126
# Visual Studio Version 17
VisualStudioVersion = 17.11.35222.181
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PaloAlto", "PaloAlto\PaloAlto.csproj", "{33FBC5A1-3466-4F10-B9A6-7186F804A65A}"
EndProject
Expand Down
41 changes: 37 additions & 4 deletions PaloAlto/Client/PaloAltoClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using System.Net.Http.Headers;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
Expand Down Expand Up @@ -99,6 +100,22 @@ public async Task<NamedListResponse> GetDeviceGroupList()
_logger.LogError($"Error Occured in PaloAltoClient.GetDeviceGroupList: {e.Message}");
throw;
}
}

public async Task<NamedListResponse> GetTemplateStackList()
{
try
{
var uri =
$"/api/?type=config&action=get&xpath=/config/devices/entry[@name='localhost.localdomain']/template-stack/entry/@name&key={ApiKey}";
var response = await GetXmlResponseAsync<NamedListResponse>(await HttpClient.GetAsync(uri));
return response;
}
catch (Exception e)
{
_logger.LogError($"Error Occured in PaloAltoClient.GetDeviceGroupList: {e.Message}");
throw;
}
}

public async Task<CommitResponse> GetCommitResponse()
Expand All @@ -118,15 +135,31 @@ public async Task<CommitResponse> GetCommitResponse()
}
}

public async Task<CommitResponse> GetCommitAllResponse(string deviceGroup)
public async Task<CommitResponse> GetCommitAllResponse(string deviceGroup,string storePath,string templateStack)
{
try
{
//Palo alto claims this commented out line works for push to devices by userid but can't get this to work
//var uri = $"/api/?&type=commit&action=all&cmd=<commit-all><shared-policy><admin><member>{ServerUserName}</member></admin><device-group><entry name=\"{deviceGroup}\"/></device-group></shared-policy></commit-all>&key={ApiKey}";
var uri =
$"/api/?&type=commit&action=all&cmd=<commit-all><shared-policy><device-group><entry name=\"{deviceGroup}\"/></device-group></shared-policy></commit-all>&key={ApiKey}";
var response = await GetXmlResponseAsync<CommitResponse>(await HttpClient.GetAsync(uri));
var uri = string.Empty;
if (!String.IsNullOrEmpty(deviceGroup))
{
uri =
$"/api/?&type=commit&action=all&cmd=<commit-all><shared-policy><device-group><entry name=\"{deviceGroup}\"/></device-group></shared-policy></commit-all>&key={ApiKey}";
}
else
{
uri =$"/api/?&type=commit&action=all&cmd=<commit-all><template><name>{GetTemplateName(storePath)}</name></template></commit-all>&key={ApiKey}";
}

var response = await GetXmlResponseAsync<CommitResponse>(await HttpClient.GetAsync(uri));

if (!String.IsNullOrEmpty(templateStack))
{
uri = $"/api/?&type=commit&action=all&cmd=<commit-all><template-stack><name>{templateStack}</name></template-stack></commit-all>&key={ApiKey}";
Thread.Sleep(60000); //Some delay built in so pushes to devices work
response = await GetXmlResponseAsync<CommitResponse>(await HttpClient.GetAsync(uri));
}
return response;
}
catch (Exception e)
Expand Down
6 changes: 6 additions & 0 deletions PaloAlto/JobProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ public class JobProperties
[DefaultValue("")]
public string DeviceGroup { get; set; }

[JsonProperty("TemplateStack")]
[DefaultValue("")]
public string TemplateStack { get; set; }

[JsonProperty("InventoryTrustedCerts")]
[DefaultValue(false)]
public bool InventoryTrustedCerts { get; set; }
}
}
45 changes: 23 additions & 22 deletions PaloAlto/Jobs/Inventory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,28 +127,29 @@ private JobResult PerformInventory(InventoryJobConfiguration config, SubmitInven
}
}).Where(acsii => acsii?.Certificates != null).ToList());


foreach (var trustedRootCert in trustedRootPayload.TrustedRootResult.TrustedRootCa.Entry)
try
{
_logger.LogTrace($"Building Trusted Root Inventory Item Alias: {trustedRootCert.Name}");
var certificatePem = client.GetCertificateByName(trustedRootCert.Name);
_logger.LogTrace($"Certificate String Back From Palo Pem: {certificatePem.Result}");
var bytes = Encoding.ASCII.GetBytes(certificatePem.Result);
var cert = new X509Certificate2(bytes);
_logger.LogTrace(
$"Building Trusted Root Inventory Item Pem: {certificatePem.Result} Has Private Key: {cert.HasPrivateKey}");
inventoryItems.Add(BuildInventoryItem(trustedRootCert.Name, certificatePem.Result, cert.HasPrivateKey, true));
}
catch(Exception e)
{
_logger.LogWarning(
$"Could not fetch the certificate: {trustedRootCert.Name} associated with issuer {trustedRootCert.Issuer} error {LogHandler.FlattenException(e)}.");
sb.Append(
$"Could not fetch the certificate: {trustedRootCert.Name} associated with issuer {trustedRootCert.Issuer}.{Environment.NewLine}");
warningFlag = true;
}

if (StoreProperties.InventoryTrustedCerts)
{
foreach (var trustedRootCert in trustedRootPayload.TrustedRootResult.TrustedRootCa.Entry)
try
{
_logger.LogTrace($"Building Trusted Root Inventory Item Alias: {trustedRootCert.Name}");
var certificatePem = client.GetCertificateByName(trustedRootCert.Name);
_logger.LogTrace($"Certificate String Back From Palo Pem: {certificatePem.Result}");
var bytes = Encoding.ASCII.GetBytes(certificatePem.Result);
var cert = new X509Certificate2(bytes);
_logger.LogTrace(
$"Building Trusted Root Inventory Item Pem: {certificatePem.Result} Has Private Key: {cert.HasPrivateKey}");
inventoryItems.Add(BuildInventoryItem(trustedRootCert.Name, certificatePem.Result, cert.HasPrivateKey, true));
}
catch (Exception e)
{
_logger.LogWarning(
$"Could not fetch the certificate: {trustedRootCert.Name} associated with issuer {trustedRootCert.Issuer} error {LogHandler.FlattenException(e)}.");
sb.Append(
$"Could not fetch the certificate: {trustedRootCert.Name} associated with issuer {trustedRootCert.Issuer}.{Environment.NewLine}");
warningFlag = true;
}
}
_logger.LogTrace("Submitting Inventory To Keyfactor via submitInventory.Invoke");
submitInventory.Invoke(inventoryItems);
_logger.LogTrace("Submitted Inventory To Keyfactor via submitInventory.Invoke");
Expand Down
7 changes: 5 additions & 2 deletions PaloAlto/Jobs/Management.cs
Original file line number Diff line number Diff line change
Expand Up @@ -615,13 +615,16 @@ private string CommitChanges(ManagementJobConfiguration config, PaloAltoClient c
var deviceGroup = StoreProperties?.DeviceGroup;
_logger.LogTrace($"Device Group {deviceGroup}");

var templateStack = StoreProperties?.TemplateStack;
_logger.LogTrace($"Template Stack {templateStack}");

//If there is a template and device group then push to all firewall devices because it is Panorama
if (IsPanoramaDevice(config) && deviceGroup?.Length > 0)
if (Validators.IsValidPanoramaVsysFormat(config.CertificateStoreDetails.StorePath) || Validators.IsValidPanoramaFormat(config.CertificateStoreDetails.StorePath))
{
_logger.LogTrace("It is a panorama device, build some delay in there so it works, pan issue.");
Thread.Sleep(120000); //Some delay built in so pushes to devices work
_logger.LogTrace("Done sleeping");
var commitAllResponse = client.GetCommitAllResponse(deviceGroup).Result;
var commitAllResponse = client.GetCommitAllResponse(deviceGroup,config.CertificateStoreDetails.StorePath,templateStack).Result;
_logger.LogTrace("Logging commit response from panorama.");
LogResponse(commitAllResponse);
if (commitAllResponse.Status != "success")
Expand Down
32 changes: 24 additions & 8 deletions PaloAlto/Validators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@ private static string GetTemplateName(string storePath)
return templateName;
}

static bool IsValidPanoramaFormat(string input)
public static bool IsValidPanoramaFormat(string input)
{
string pattern = @"^/config/devices/entry\[@name='[^\]]+'\]/template/entry\[@name='[^']+'\]/config/shared$";
Regex regex = new Regex(pattern);
return regex.IsMatch(input);
}

static bool IsValidFirewallVsysFormat(string input)
public static bool IsValidFirewallVsysFormat(string input)
{
string pattern = @"^/config/devices/entry\[@name='localhost\.localdomain'\]/vsys/entry\[@name='[^']+'\]$";
return Regex.IsMatch(input, pattern);
Expand All @@ -77,12 +77,20 @@ public static (bool valid, JobResult result) ValidateStoreProperties(JobProperti
}

// If it is a firewall (store path of /) then you don't need the Group Name
if (!storePath.Contains("template",System.StringComparison.CurrentCultureIgnoreCase))
if (!storePath.Contains("template", System.StringComparison.CurrentCultureIgnoreCase))
{
if (!string.IsNullOrEmpty(storeProperties?.DeviceGroup))
{
errors +=
"You do not need a device group with a Palo Alto Firewall. It is only required for Panorama.";
}
}
if (!string.IsNullOrEmpty(storeProperties?.TemplateStack))
{
errors +=
"You do not need a Template Stack with a Palo Alto Firewall. It is only required for Panorama.";
}
}


// Considered Panorama device if store path is not "/" and there is a valid value for store path
if (storePath.Contains("template", System.StringComparison.CurrentCultureIgnoreCase))
Expand All @@ -91,10 +99,6 @@ public static (bool valid, JobResult result) ValidateStoreProperties(JobProperti
new PaloAltoClient(clientMachine,
serverUserName, serverPassword); //Api base URL Plus Key

if (string.IsNullOrEmpty(storeProperties?.DeviceGroup))
{
errors += "You need to specify a device group when working with Panorama.";
}

if (!string.IsNullOrEmpty(storeProperties?.DeviceGroup))
{
Expand All @@ -105,8 +109,20 @@ public static (bool valid, JobResult result) ValidateStoreProperties(JobProperti
errors +=
$"Could not find your Device Group In Panorama. Valid Device Groups are {string.Join(",", deviceList.Result.Result.Entry.Select(d => d.Name))}";
}
}

if (!string.IsNullOrEmpty(storeProperties?.TemplateStack))
{
var templateStackList = client.GetTemplateStackList();
var templateStacks = templateStackList.Result.Result.Entry.Where(d => d.Name == storeProperties?.TemplateStack);
if (!templateStacks.Any())
{
errors +=
$"Could not find your Template Stacks In Panorama. Valid Device Groups are {string.Join(",", templateStackList.Result.Result.Entry.Select(d => d.Name))}";
}
}


//Validate Template Exists in Panorama, required for Panorama
var templateList = client.GetTemplateList();
var templates = templateList.Result.Result.Entry.Where(d => d.Name == GetTemplateName(storePath));
Expand Down
2 changes: 1 addition & 1 deletion PaloAltoTestConsole/FirewallInventory.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"ClientMachine": "ClientMachineGoesHere",
"StorePath": "/",
"StorePassword": "",
"Properties": "{\"ServerUsername\":\"UserNameGoesHere\",\"ServerPassword\":\"PasswordGoesHere\",\"ServerUseSsl\":\"true\",\"DeviceGroup\":\"\"}",
"Properties": "{\"ServerUsername\":\"UserNameGoesHere\",\"ServerPassword\":\"PasswordGoesHere\",\"ServerUseSsl\":\"true\",\"DeviceGroup\":\"\",\"InventoryTrustedCerts\": false,\"TemplateStack\":\"TemplateStackGoesHere\"}",
"Type": 105
},
"JobCancelled": false,
Expand Down
2 changes: 1 addition & 1 deletion PaloAltoTestConsole/KeyfactorClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public async Task<KeyfactorEnrollmentResult> EnrollCertificate(string commonName
var request = new RestRequest("/KeyfactorAPI/Enrollment/PFX", Method.Post);
request.AddHeader("X-Keyfactor-Requested-With", "APIClient");
request.AddHeader("x-certificateformat", "PFX");
request.AddHeader("Authorization", "Basic Q29tbWFuZFxLRkFkbWluOldoNUcyVGM2VkJZalNNcEM=");
request.AddHeader("Authorization", "Basic Authtoken");
request.AddHeader("Content-Type", "application/json");
var enrollRequest = new KeyfactorEnrollmentRequest
{
Expand Down
2 changes: 1 addition & 1 deletion PaloAltoTestConsole/ManagementRemove.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"ClientMachine": "ClientMachineGoesHere",
"StorePath": "TemplateNameGoesHere",
"StorePassword": null,
"Properties": "{\"ServerUsername\":\"UserNameGoesHere\",\"ServerPassword\":\"PasswordGoesHere\",\"ServerUseSsl\":\"true\",\"DeviceGroup\":\"DeviceGroupGoesHere\"}",
"Properties": "{\"ServerUsername\":\"UserNameGoesHere\",\"ServerPassword\":\"PasswordGoesHere\",\"ServerUseSsl\":\"true\",\"DeviceGroup\":\"DeviceGroupGoesHere\",\"InventoryTrustedCerts\": false,\"TemplateStack\":\"TemplateStackGoesHere\"}",
"Type": 105
},
"OperationType": 3,
Expand Down
2 changes: 1 addition & 1 deletion PaloAltoTestConsole/PanoramaInventory.json
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@
"ClientMachine": "ClientMachineGoesHere",
"StorePath": "TemplateNameGoesHere",
"StorePassword": "",
"Properties": "{\"ServerUsername\":\"UserNameGoesHere\",\"ServerPassword\":\"PasswordGoesHere\",\"ServerUseSsl\":\"true\",\"DeviceGroup\":\"DeviceGroupGoesHere\"}",
"Properties": "{\"ServerUsername\":\"UserNameGoesHere\",\"ServerPassword\":\"PasswordGoesHere\",\"ServerUseSsl\":\"true\",\"DeviceGroup\":\"DeviceGroupGoesHere\",\"InventoryTrustedCerts\": false,\"TemplateStack\":\"TemplateStackGoesHere\"}",
"Type": 105
},
"JobCancelled": false,
Expand Down
2 changes: 1 addition & 1 deletion PaloAltoTestConsole/PanoramaMgmt.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"ClientMachine": "ClientMachineGoesHere",
"StorePath": "TemplateNameGoesHere",
"StorePassword": null,
"Properties": "{\"ServerUsername\":\"UserNameGoesHere\",\"ServerPassword\":\"PasswordGoesHere\",\"ServerUseSsl\":\"true\",\"DeviceGroup\":\"DeviceGroupGoesHere\"}",
"Properties": "{\"ServerUsername\":\"UserNameGoesHere\",\"ServerPassword\":\"PasswordGoesHere\",\"ServerUseSsl\":\"true\",\"DeviceGroup\":\"DeviceGroupGoesHere\",\"InventoryTrustedCerts\": false,\"TemplateStack\":\"TemplateStackGoesHere\"}",
"Type": 105
},
"OperationType": 2,
Expand Down
Loading
Loading