Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
v1.3.0
- Add ability to manage custom VDOMs. PLEASE NOTE this release contains a breaking change. Store Path MUST contain the value for the VDOM the certificate will be managing. `root` must be entered to manage the default 'root' VDOM.

v1.2.0
- Allow for the management (renew/replace) of bound certificates

Expand Down
2 changes: 2 additions & 0 deletions Fortigate/Api/cmdb_certificate_resource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,7 @@ public class cmdb_certificate_resource
public string file_content { get; set; }

public string scope { get; set; }

public string vdom { get; set; }
}
}
4 changes: 2 additions & 2 deletions Fortigate/Fortigate.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
Expand All @@ -8,7 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.0.0" />
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" />
<PackageReference Include="Keyfactor.Logging" Version="1.1.1" />
<PackageReference Include="Keyfactor.Orchestrators.IOrchestratorJobExtensions" Version="0.7.0" />

Expand Down
89 changes: 65 additions & 24 deletions Fortigate/FortigateStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
{
private ILogger logger { get; set; }
private string FortigateHost { get; set; }
private string VDOM { get; set; }


private static readonly string available_certificates = "/api/v2/monitor/system/available-certificates";
Expand All @@ -46,8 +47,7 @@
private static readonly string import_certificate_api = "/api/v2/monitor/vpn-certificate/local/import";

private static readonly string get_certificate_api = "/api/v2/cmdb/certificate/local/";

private static readonly string update_certificate_api = "/api/v2/cmdb/certificate/local/";
private static readonly string get_vdom_api = "/api/v2/cmdb/system/vdom/";

Check warning on line 50 in Fortigate/FortigateStore.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

The field 'FortigateStore.get_certificate_api' is assigned but its value is never used

Check warning on line 50 in Fortigate/FortigateStore.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

The field 'FortigateStore.get_certificate_api' is assigned but its value is never used

//api/v2/cmdb/vpn.certificate/local/test?vdom=root
private static readonly string delete_certificate_api = "/api/v2/cmdb/vpn.certificate/local/";
Expand All @@ -61,7 +61,7 @@
};
private readonly HttpClient client;

public FortigateStore(string fortigateHost, string accessToken)
public FortigateStore(string fortigateHost, string accessToken, string vdom)
{
logger = LogHandler.GetClassLogger(this.GetType());

Expand All @@ -70,6 +70,9 @@
client = new HttpClient(handler);
FortigateHost = fortigateHost;
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {accessToken}");
VDOM = string.IsNullOrEmpty(vdom) ? "root" : vdom;

ValidateVDOM();

logger.MethodExit(LogLevel.Debug);
}
Expand All @@ -78,9 +81,11 @@
{
logger.MethodEntry(LogLevel.Debug);

Dictionary<string, string> parameters = new Dictionary<string, string>();
parameters.Add("vdom", VDOM);
try
{
DeleteResource(delete_certificate_api + alias);
DeleteResource(delete_certificate_api + alias, parameters);
}
catch (Exception ex)
{
Expand All @@ -104,8 +109,7 @@

var endpoint = "/api/v2/cmdb/" + path + "/" + name;

var parameters = new Dictionary<String, String>();
parameters.Add("vdom", "root");
var parameters = new Dictionary<String, String> { { "vdom", VDOM } };

try
{
Expand All @@ -127,8 +131,7 @@
logger.MethodEntry(LogLevel.Debug);

var parameters = new Dictionary<String, String>();
parameters.Add("vdom", "root");
parameters.Add("scope", "global");
parameters.Add("vdom", VDOM);
parameters.Add("mkey", alias);
parameters.Add("qtypes", $"[{qtype.ToString()}]");

Expand Down Expand Up @@ -238,16 +241,14 @@
certname = alias,
key_file_content = privateKey,
file_content = cert,
scope = "global",
//password = password,
scope = "vdom",
vdom = VDOM,
type = "regular"
};

var parameters = new Dictionary<String, String>();
parameters.Add("vdom", "root");
try
{
PostAsJson(import_certificate_api, cert_resource, parameters);
PostAsJson(import_certificate_api, cert_resource);
}
catch (Exception ex)
{
Expand All @@ -267,8 +268,11 @@

try
{
string endpoint = string.IsNullOrEmpty(mkey) ? available_certificates : available_certificates;
Dictionary<String, String> parameters = mkey == null ? null : new Dictionary<string, string> { { "mkey", mkey } };
string endpoint = available_certificates;
Dictionary<String, String> parameters = new Dictionary<string, string>();
if (!string.IsNullOrEmpty(mkey))
parameters.Add("mkey", mkey);
parameters.Add("vdom", VDOM);
var result = GetResource(endpoint, parameters);
certificates = JsonConvert.DeserializeObject<FortigateResponse<Certificate[]>>(result).results;
}
Expand All @@ -288,47 +292,84 @@
return certificates;
}

public string DownloadFileAsString(string mkey, string type)
public string DownloadFileAsString(string mkey, string type, out bool isError)
{
logger.MethodEntry(LogLevel.Debug);

isError = false;
var parameters = new Dictionary<String, String>();
parameters.Add("mkey", mkey);
parameters.Add("type", type);
parameters.Add("vdom", VDOM);

string content = string.Empty;

try
{
var response = client.GetAsync(GetUrl(download_certificate, parameters)).GetAwaiter().GetResult();
var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
if (!response.IsSuccessStatusCode)
throw new Exception($"Error retrieving certificate {mkey}: {content}");

return content;
}
catch (Exception ex)
{
logger.LogError(FortigateException.FlattenExceptionMessages(ex, $"Error retrieving downloading file {mkey}: "));
throw;
isError = true;
}
finally
{
logger.MethodExit(LogLevel.Debug);
}

return content;
}

public void ValidateVDOMScope(string alias)
{
logger.MethodEntry(LogLevel.Debug);

try
{
Certificate[] certs = List(alias);
if (certs.Length > 0 && certs[0].range.ToLower() == "global")
throw new Exception($"Certificate {alias} is scoped as global. Global certificates cannot be replaced or deleted by this integration.");
}
finally
{
logger.MethodExit(LogLevel.Debug);
}
}

private void ValidateVDOM()
{
logger.MethodEntry(LogLevel.Debug);

try
{
var response = client.GetAsync(GetUrl(get_vdom_api + VDOM, null)).GetAwaiter().GetResult();
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
throw new FortigateException($"VDOM {VDOM} not found.");
if (!response.IsSuccessStatusCode)
throw new FortigateException($"Error retrieving VDOM {VDOM}. Status={response.StatusCode.ToString()}, Error={response.Content} {response.ReasonPhrase}");
}
finally
{
logger.MethodExit(LogLevel.Debug);
}
}

private String PostAsJson(string endpoint, cmdb_certificate_resource obj, Dictionary<String, String> additionalParams = null)
private String PostAsJson(string endpoint, cmdb_certificate_resource obj)
{
logger.MethodEntry(LogLevel.Debug);

string content = "";
var url = GetUrl(endpoint, additionalParams);
var url = GetUrl(endpoint);
var stringContent = new StringContent(JsonConvert.SerializeObject(obj), Encoding.UTF8, "application/json");
stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

try
{
HttpResponseMessage responseMessage = client.PostAsync(url, stringContent).GetAwaiter().GetResult();
content = responseMessage.Content.ReadAsStringAsync().GetAwaiter().GetResult();
var content = responseMessage.Content.ReadAsStringAsync().GetAwaiter().GetResult();
if (!responseMessage.IsSuccessStatusCode)
throw new Exception($"Error adding certificate {obj.certname}: {content}");

Expand Down
23 changes: 17 additions & 6 deletions Fortigate/Inventory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using Keyfactor.Logging;
using Microsoft.Extensions.Logging;
using Keyfactor.Orchestrators.Extensions.Interfaces;
using Org.BouncyCastle.Tls;

namespace Keyfactor.Extensions.Orchestrator.Fortigate
{
Expand All @@ -37,19 +38,25 @@ public JobResult ProcessJob(InventoryJobConfiguration config, SubmitInventoryUpd
ILogger logger = LogHandler.GetClassLogger(this.GetType());
logger.LogDebug($"Begin {config.Capability} for job id {config.JobId}...");
logger.LogDebug($"Client Machine: {config.CertificateStoreDetails.ClientMachine}");

FortigateStore store = new FortigateStore(config.CertificateStoreDetails.ClientMachine, PAMUtilities.ResolvePAMField(_resolver, logger, "Fortigate Access Key", config.CertificateStoreDetails.StorePassword));
logger.LogDebug($"Store Path: {config.CertificateStoreDetails.StorePath}");

List<CurrentInventoryItem> inventoryItems = new List<CurrentInventoryItem>();
bool atLeastOneError = false;

try
{
FortigateStore store = new FortigateStore(config.CertificateStoreDetails.ClientMachine, PAMUtilities.ResolvePAMField(_resolver, logger, "Fortigate Access Key", config.CertificateStoreDetails.StorePassword), config.CertificateStoreDetails.StorePath);

Api.Certificate[] certificates = store.List(null);

bool isError;

foreach (var cert in certificates)
{
if (cert.type == "local-cer")
{
var certFile = store.DownloadFileAsString(cert.name, cert.type);
var certFile = store.DownloadFileAsString(cert.name, cert.type, out isError);
if (isError) atLeastOneError = true;

var item = new CurrentInventoryItem()
{
Expand All @@ -66,16 +73,20 @@ public JobResult ProcessJob(InventoryJobConfiguration config, SubmitInventoryUpd
}
catch (Exception ex)
{
logger.LogError($"Exception for {config.Capability}: {FortigateException.FlattenExceptionMessages(ex, string.Empty)} for job id {config.JobId}");
return new JobResult() { Result = OrchestratorJobStatusJobResult.Failure, JobHistoryId = config.JobHistoryId, FailureMessage = FortigateException.FlattenExceptionMessages(ex, $"Site {config.CertificateStoreDetails.ClientMachine}") };
logger.LogError($"Exception for {config.Capability}: {FortigateException.FlattenExceptionMessages(ex, string.Empty)} for job id {config.JobId} ");
return new JobResult() { Result = OrchestratorJobStatusJobResult.Failure, JobHistoryId = config.JobHistoryId, FailureMessage = FortigateException.FlattenExceptionMessages(ex, $"Site {config.CertificateStoreDetails.ClientMachine} ") };
}

try
{
logger.LogDebug("Sending certificates back to Command:" + inventoryItems.Count);
submitInventory.Invoke(inventoryItems);
logger.LogDebug($"...End {config.Capability} job for job id {config.JobId}");
return new JobResult() { Result = OrchestratorJobStatusJobResult.Success, JobHistoryId = config.JobHistoryId };

if (atLeastOneError)
return new JobResult() { Result = OrchestratorJobStatusJobResult.Warning, JobHistoryId = config.JobHistoryId, FailureMessage = "At least one certificate was unable to be retrieved, Please check the log for more information." };
else
return new JobResult() { Result = OrchestratorJobStatusJobResult.Success, JobHistoryId = config.JobHistoryId };
}
catch (Exception ex)
{
Expand Down
9 changes: 6 additions & 3 deletions Fortigate/Management.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,14 @@ public JobResult ProcessJob(ManagementJobConfiguration config)

logger.LogDebug($"Begin {config.Capability} for job id {config.JobId}...");
logger.LogDebug($"Client Machine: {config.CertificateStoreDetails.ClientMachine}");

FortigateStore store = new FortigateStore(config.CertificateStoreDetails.ClientMachine, PAMUtilities.ResolvePAMField(_resolver, logger, "Fortigate Access Key", config.CertificateStoreDetails.StorePassword));
logger.LogDebug($"Store Path: {config.CertificateStoreDetails.StorePath}");

try
{
FortigateStore store = new FortigateStore(config.CertificateStoreDetails.ClientMachine, PAMUtilities.ResolvePAMField(_resolver, logger, "Fortigate Access Key", config.CertificateStoreDetails.StorePassword), config.CertificateStoreDetails.StorePath);

store.ValidateVDOMScope(config.JobCertificate.Alias);

//Management jobs, unlike Discovery, Inventory, and Reenrollment jobs can have 3 different purposes:
switch (config.OperationType)
{
Expand All @@ -74,7 +77,7 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
}
catch (Exception ex)
{
logger.LogError($"Exception for {config.Capability}: {FortigateException.FlattenExceptionMessages(ex, string.Empty)} for job id {config.JobId}");
logger.LogError($"Exception for {config.Capability}: {FortigateException.FlattenExceptionMessages(ex, string.Empty)} for job id {config.JobId} ");
return new JobResult() { Result = OrchestratorJobStatusJobResult.Failure, JobHistoryId = config.JobHistoryId, FailureMessage = FortigateException.FlattenExceptionMessages(ex, $"Site {config.CertificateStoreDetails.ClientMachine}:") };
}

Expand Down
Loading