Skip to content

Commit a39caa7

Browse files
authored
Merge pull request #52 from Keyfactor/ab#64704
2 parents 8591132 + c6d62ab commit a39caa7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1133
-762
lines changed

.github/workflows/keyfactor-starter-workflow.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ on:
1111

1212
jobs:
1313
call-starter-workflow:
14-
uses: keyfactor/actions/.github/workflows/starter.yml@v2
14+
uses: keyfactor/actions/.github/workflows/starter.yml@3.1.2
1515
secrets:
1616
token: ${{ secrets.V2BUILDTOKEN}}
1717
APPROVE_README_PUSH: ${{ secrets.APPROVE_README_PUSH}}
1818
gpg_key: ${{ secrets.KF_GPG_PRIVATE_KEY }}
19-
gpg_pass: ${{ secrets.KF_GPG_PASSPHRASE }}
19+
gpg_pass: ${{ secrets.KF_GPG_PASSPHRASE }}
20+
scan_token: ${{ secrets.SAST_TOKEN }}

Bundle/Discovery.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ public override JobResult ProcessJob(DiscoveryJobConfiguration config, SubmitDis
4040
SetPAMSecrets(config.ServerUsername, config.ServerPassword, logger);
4141

4242
F5Client f5 = new F5Client(certificateStore, ServerUserName, ServerPassword, config.UseSSL, string.Empty, true, false, new List<PreviousInventoryItem>());
43+
44+
ValidateF5Release(logger, certificateStore, f5);
45+
4346
List<string> partitions = f5.GetPartitions().Select(p => p.name).ToList();
4447

4548
LogHandlerCommon.Trace(logger, certificateStore, $"Found {partitions?.Count} partitions");

Bundle/Inventory.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,19 @@ public override JobResult ProcessJob(InventoryJobConfiguration config, SubmitInv
4040
{
4141
base.ParseJobProperties();
4242
SetPAMSecrets(config.ServerUsername, config.ServerPassword, logger);
43-
F5Client f5 = new F5Client(config.CertificateStoreDetails, ServerUserName, ServerPassword, config.UseSSL, null, IgnoreSSLWarning, UseTokenAuth, config.LastInventory) { F5Version = base.F5Version };
43+
F5Client f5 = new F5Client(config.CertificateStoreDetails, ServerUserName, ServerPassword, config.UseSSL, null, IgnoreSSLWarning, UseTokenAuth, config.LastInventory);
44+
45+
ValidateF5Release(logger, JobConfig.CertificateStoreDetails, f5);
4446

4547
LogHandlerCommon.Debug(logger, JobConfig.CertificateStoreDetails, $"Getting inventory for CA Bundle '{config.CertificateStoreDetails.StorePath}'");
4648
inventory = f5.GetCABundleInventory();
4749

4850
LogHandlerCommon.Debug(logger, JobConfig.CertificateStoreDetails, $"Submitting {inventory?.Count} inventory entries for CA Bundle '{config.CertificateStoreDetails.StorePath}'");
4951
submitInventory.Invoke(inventory);
5052

53+
if (UseTokenAuth)
54+
f5.RemoveToken();
55+
5156
LogHandlerCommon.Debug(logger, JobConfig.CertificateStoreDetails, "Job complete");
5257
return new JobResult { Result = OrchestratorJobStatusJobResult.Success, JobHistoryId = config.JobHistoryId };
5358
}

Bundle/Management.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,11 @@ public override JobResult ProcessJob(ManagementJobConfiguration config)
5050

5151
F5Client f5 = new F5Client(config.CertificateStoreDetails, ServerUserName, ServerPassword, config.UseSSL, config.JobCertificate.PrivateKeyPassword, IgnoreSSLWarning, UseTokenAuth, config.LastInventory)
5252
{
53-
PrimaryNode = base.PrimaryNode,
54-
F5Version = base.F5Version
53+
PrimaryNode = base.PrimaryNode
5554
};
5655

56+
ValidateF5Release(logger, JobConfig.CertificateStoreDetails, f5);
57+
5758
switch (config.OperationType)
5859
{
5960
case CertStoreOperationType.Add:
@@ -69,6 +70,9 @@ public override JobResult ProcessJob(ManagementJobConfiguration config)
6970
throw new Exception($"Management job expecting 'Add' or 'Remove' job - received '{Enum.GetName(typeof(CertStoreOperationType), config.OperationType)}'");
7071
}
7172

73+
if (UseTokenAuth)
74+
f5.RemoveToken();
75+
7276
LogHandlerCommon.Debug(logger, JobConfig.CertificateStoreDetails, "Job complete");
7377
return new JobResult { Result = OrchestratorJobStatusJobResult.Success, JobHistoryId = config.JobHistoryId};
7478
}

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
v1.7.0
2+
- Deprecate F5 Version Custom Field for all store types.
3+
- Make Store Password a "PAM eligible" field on the orchestrator
4+
- Remove session token at end of each job
5+
- Convert documentation to use Doctool
6+
- Create separate .net6 and .net8 builds on release
7+
18
v1.6.0
29
- Add Store Password (optional) to allow for setting key type to "Password" when adding/replacing a certificate. This will encrypt the private key deployed on the F5 device with the password set as the Store Password.
310

DiscoveryBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public abstract class DiscoveryBase : F5JobBase, IDiscoveryJobExtension
1919

2020
protected DiscoveryJobConfiguration JobConfig { get; set; }
2121

22-
public string ExtensionName => string.Empty;
22+
public string ExtensionName => "Keyfactor.Extensions.Orchestrator.F5Orchestrator.Discovery";
2323

2424
public abstract JobResult ProcessJob(DiscoveryJobConfiguration config, SubmitDiscoveryUpdate submitDiscovery);
2525
}

F5Client.cs

Lines changed: 54 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
using Newtonsoft.Json;
2323
using System.Collections;
24+
using System.Collections.Concurrent;
25+
using System.Drawing.Printing;
26+
using System.Diagnostics.CodeAnalysis;
2427

2528
namespace Keyfactor.Extensions.Orchestrator.F5Orchestrator
2629
{
@@ -34,6 +37,8 @@ internal class F5Client
3437
private const string INVALID_KEY_SUBSTR = "key(";
3538
private const string INVALID_KEY_BEG_DELIM = @"/";
3639
private const string INVALID_KEY_END_DELIM = ")";
40+
private const int MIN_VERSION_SUPPORTED = 14;
41+
private const string VERSION_DELIMITER = "?ver=";
3742

3843
public CertificateStore CertificateStore { get; set; }
3944
public string ServerUserName { get; set; }
@@ -43,7 +48,6 @@ internal class F5Client
4348
public string PFXPassword { get; set; }
4449
public IEnumerable<PreviousInventoryItem> Inventory { get; set; }
4550
public string PrimaryNode { get; set; }
46-
public string F5Version { get; set; }
4751
public bool IgnoreSSLWarning { get; set; }
4852
public bool UseTokenAuth { get; set; }
4953
private RESTHandler REST { get; set; }
@@ -141,26 +145,23 @@ public void RemoveEntry(string partition, string name)
141145
ArchiveFile($"/config/filestore/files_d/{partition}_d/certificate_key_d/:{partition}:{name}_*", $"{partition}-{name}-{timestamp}.key");
142146
LogHandlerCommon.Trace(logger, CertificateStore, $"Removing certificate and key at '{partition}' and name '{name}'");
143147

144-
string keyName = GetKeyName(name, true);
145-
REST.Delete($"/mgmt/tm/sys/file/ssl-key/~{partition}~{keyName}");
148+
REST.Delete($"/mgmt/tm/sys/file/ssl-key/~{partition}~{name}");
146149
}
147150
LogHandlerCommon.Trace(logger, CertificateStore, $"Archiving certificate at '{partition}' and name '{name}'");
148151
ArchiveFile($"/config/filestore/files_d/{partition}_d/certificate_d/:{partition}:{name}_*", $"{partition}-{name}-{timestamp}.crt");
149152
LogHandlerCommon.Trace(logger, CertificateStore, $"Removing certificate at '{partition}' and name '{name}'");
150153

151-
string crtName = GetCrtName(name, true);
152-
REST.Delete($"/mgmt/tm/sys/file/ssl-cert/~{partition}~{crtName}");
154+
REST.Delete($"/mgmt/tm/sys/file/ssl-cert/~{partition}~{name}");
153155
LogHandlerCommon.MethodExit(logger, CertificateStore, "RemoveEntry");
154156
}
155157

156-
public bool KeyExists(string partition, string name)
158+
public bool KeyExists(string partition, string keyName)
157159
{
158160
LogHandlerCommon.MethodEntry(logger, CertificateStore, "KeyExists");
159161
bool exists = false;
160162

161163
try
162164
{
163-
string keyName = GetKeyName(name, true);
164165
string query = $"/mgmt/tm/sys/file/ssl-key/~{partition}~{keyName}";
165166
F5Key key = REST.Get<F5Key>(query);
166167
exists = (key != null);
@@ -178,14 +179,13 @@ public bool KeyExists(string partition, string name)
178179
return exists;
179180
}
180181

181-
public bool CertificateExists(string partition, string name)
182+
public bool CertificateExists(string partition, string crtName)
182183
{
183184
LogHandlerCommon.MethodEntry(logger, CertificateStore, "CertificateExists");
184185
bool exists = false;
185186

186187
try
187188
{
188-
string crtName = GetCrtName(name, true);
189189
string query = $"/mgmt/tm/sys/file/ssl-cert/~{partition}~{crtName}";
190190
F5SSLProfile certificate = REST.Get<F5SSLProfile>(query);
191191
exists = (certificate != null);
@@ -406,12 +406,12 @@ private void SetItemStatus(CurrentInventoryItem agentInventoryItem)
406406
LogHandlerCommon.MethodExit(logger, CertificateStore, "SetItemStatus");
407407
}
408408

409-
private CurrentInventoryItem GetInventoryItem(string partition, string name, bool hasPrivateKey)
409+
private CurrentInventoryItem GetInventoryItem(string partition, string crtName, bool hasPrivateKey)
410410
{
411411
LogHandlerCommon.MethodEntry(logger, CertificateStore, "GetInventoryItem");
412412

413413
// Get the pfx/certificate contents from the filesystem (using a wildcard as the files have slightly randomized name suffixes)
414-
X509Certificate2Collection certificateCollection = GetCertificateEntry($"/config/filestore/files_d/{partition}_d/certificate_d/:{partition}:{name}_*");
414+
X509Certificate2Collection certificateCollection = GetCertificateEntry($"/config/filestore/files_d/{partition}_d/certificate_d/:{partition}:{crtName}_*");
415415
List<string> certContents = new List<string>();
416416
bool useChainLevel = certificateCollection.Count > 1;
417417
foreach (X509Certificate2 certificate in certificateCollection)
@@ -420,7 +420,6 @@ private CurrentInventoryItem GetInventoryItem(string partition, string name, boo
420420
//LogHandlerCommon.Debug(logger, CertificateStore, $"ALIAS: {name}: {Convert.ToBase64String(certificate.Export(X509ContentType.Cert))}");
421421
}
422422

423-
string crtName = GetCrtName(name, false);
424423
CurrentInventoryItem inventoryItem = new CurrentInventoryItem
425424
{
426425
ItemStatus = OrchestratorInventoryItemStatus.Unknown,
@@ -434,61 +433,6 @@ private CurrentInventoryItem GetInventoryItem(string partition, string name, boo
434433
return inventoryItem;
435434
}
436435

437-
private string GetCrtName(string name, bool addExtension)
438-
{
439-
LogHandlerCommon.MethodEntry(logger, CertificateStore, "GetCrtName");
440-
string crtName = name;
441-
442-
switch (F5Version.ToLowerInvariant())
443-
{
444-
case "v12":
445-
throw new Exception($"F5 Version 12 is not supported by the REST-based orchestrator. The legacy SOAP-based orchestrator should be used.");
446-
case "v13":
447-
if (addExtension)
448-
{
449-
// The .crt extension must be added
450-
if (!crtName.EndsWith(".crt", StringComparison.OrdinalIgnoreCase)) { crtName = $"{crtName}.crt"; }
451-
}
452-
else
453-
{
454-
// The .crt extension must be removed
455-
if (crtName.EndsWith(".crt", StringComparison.OrdinalIgnoreCase)) { crtName = crtName.Substring(0, crtName.Length - 4); }
456-
}
457-
break;
458-
};
459-
460-
LogHandlerCommon.MethodExit(logger, CertificateStore, "GetCrtName");
461-
return crtName;
462-
}
463-
464-
private string GetKeyName(string name, bool addExtension)
465-
{
466-
LogHandlerCommon.MethodEntry(logger, CertificateStore, "GetKeyName");
467-
string keyName = name;
468-
469-
// No longer checking past version 14 for future-proofing
470-
switch (F5Version.ToLowerInvariant())
471-
{
472-
case "v12":
473-
throw new Exception($"F5 Version 12 is not supported by the REST-based orchestrator. The legacy SOAP-based orchestrator should be used.");
474-
case "v13":
475-
if (addExtension)
476-
{
477-
// The .key extension must be added
478-
if (!keyName.EndsWith(".key", StringComparison.OrdinalIgnoreCase)) { keyName = $"{keyName}.key"; }
479-
}
480-
else
481-
{
482-
// The .key extension must be removed
483-
if (keyName.EndsWith(".key", StringComparison.OrdinalIgnoreCase)) { keyName = keyName.Substring(0, keyName.Length - 4); }
484-
}
485-
break;
486-
};
487-
488-
LogHandlerCommon.MethodExit(logger, CertificateStore, "GetKeyName");
489-
return keyName;
490-
}
491-
492436
// Certificate PFX Shared
493437
#endregion
494438

@@ -728,7 +672,7 @@ public List<CurrentInventoryItem> GetSSLProfiles(int pageSize)
728672
// SSL Profiles
729673
#endregion
730674

731-
#region Auth
675+
#region Auth & Version
732676

733677
private string GetToken(string userName, string userPassword)
734678
{
@@ -739,6 +683,39 @@ private string GetToken(string userName, string userPassword)
739683

740684
return loginResponse.token.token;
741685
}
686+
687+
internal void RemoveToken()
688+
{
689+
LogHandlerCommon.MethodEntry(logger, CertificateStore, "RemoveToken");
690+
REST.Delete($"/mgmt/shared/authz/tokens/{REST.Token}");
691+
LogHandlerCommon.MethodExit(logger, CertificateStore, "RemoveToken");
692+
}
693+
694+
internal void ValidateF5Version()
695+
{
696+
LogHandlerCommon.MethodEntry(logger, CertificateStore, "IsVersionSupported");
697+
698+
string query = $"/mgmt/tm/sys/version";
699+
F5Version f5Version = REST.Get<F5Version>(query);
700+
LogHandlerCommon.Debug(logger, CertificateStore, $"Version supported self link: {f5Version.selfLink}");
701+
if (!f5Version.selfLink.Contains(VERSION_DELIMITER))
702+
return;
703+
704+
string selfLink = f5Version.selfLink;
705+
string strVersion = selfLink.Substring(selfLink.IndexOf(VERSION_DELIMITER, StringComparison.CurrentCultureIgnoreCase) + VERSION_DELIMITER.Length, 2);
706+
int version;
707+
if (!int.TryParse(strVersion, out version))
708+
return;
709+
710+
LogHandlerCommon.MethodExit(logger, CertificateStore, "IsVersionSupported");
711+
712+
if (version < MIN_VERSION_SUPPORTED)
713+
{
714+
string errMesage = $"F5 version {version.ToString()} not supported by this version of the F5 Orchestrator Extension. This orchestrator extension only supports verion {MIN_VERSION_SUPPORTED.ToString()} and later.";
715+
logger.LogError(errMesage);
716+
throw new Exception(errMesage);
717+
}
718+
}
742719
#endregion
743720

744721
#region Bundles
@@ -822,8 +799,7 @@ public bool EntryExistsInBundle(string alias)
822799
List<string> bundleIncludes = new List<string>(GetCABundleIncludes());
823800
string partition = GetPartitionFromStorePath();
824801

825-
string crtName = GetCrtName(alias, true);
826-
exists = bundleIncludes.Any<string>(i => i.Equals($"/{partition}/{crtName}", StringComparison.OrdinalIgnoreCase));
802+
exists = bundleIncludes.Any<string>(i => i.Equals($"/{partition}/{alias}", StringComparison.OrdinalIgnoreCase));
827803

828804
LogHandlerCommon.MethodExit(logger, CertificateStore, "EntryExistsInBundle");
829805
return exists;
@@ -855,26 +831,25 @@ private string[] GetCABundleIncludes()
855831
return includeBundle;
856832
}
857833

858-
public void AddBundleEntry(string bundle, string partition, string name, string b64Certificate, string alias, bool overwrite)
834+
public void AddBundleEntry(string bundle, string partition, string crtName, string b64Certificate, string alias, bool overwrite)
859835
{
860836
LogHandlerCommon.MethodEntry(logger, CertificateStore, "AddBundleEntry");
861837

862838
// Add the entry to inventory
863-
if (!CertificateExists(partition, name))
839+
if (!CertificateExists(partition, crtName))
864840
{
865-
LogHandlerCommon.Debug(logger, CertificateStore, $"Add entry '{name}' in '{CertificateStore.StorePath}'");
866-
AddEntry(partition, name, b64Certificate, null);
841+
LogHandlerCommon.Debug(logger, CertificateStore, $"Add entry '{crtName}' in '{CertificateStore.StorePath}'");
842+
AddEntry(partition, crtName, b64Certificate, null);
867843
}
868844
else
869845
{
870-
if (!overwrite) { throw new Exception($"An entry named '{name}' exists and 'overwrite' was not selected"); }
846+
if (!overwrite) { throw new Exception($"An entry named '{crtName}' exists and 'overwrite' was not selected"); }
871847

872-
LogHandlerCommon.Debug(logger, CertificateStore, $"Replace entry '{name}' in '{CertificateStore.StorePath}'");
873-
ReplaceEntry(partition, name, b64Certificate, null);
848+
LogHandlerCommon.Debug(logger, CertificateStore, $"Replace entry '{crtName}' in '{CertificateStore.StorePath}'");
849+
ReplaceEntry(partition, crtName, b64Certificate, null);
874850
}
875851

876852
// Add the entry to the bundle
877-
string crtName = GetCrtName(name, true);
878853
string crt = $"/{partition}/{crtName}";
879854
List<string> bundleIncludes = new List<string>(GetCABundleIncludes());
880855
if (!bundleIncludes.Contains(crt))
@@ -886,11 +861,10 @@ public void AddBundleEntry(string bundle, string partition, string name, string
886861
LogHandlerCommon.MethodExit(logger, CertificateStore, "AddBundleEntry");
887862
}
888863

889-
public void RemoveBundleEntry(string bundle, string partition, string name)
864+
public void RemoveBundleEntry(string bundle, string partition, string crtName)
890865
{
891866
LogHandlerCommon.MethodEntry(logger, CertificateStore, "RemoveBundleEntry");
892867

893-
string crtName = GetCrtName(name, true);
894868
string crtEntry = $"/{partition}/{crtName}";
895869

896870
LogHandlerCommon.Trace(logger, CertificateStore, $"Preparing to remove bundle entry '{crtEntry}'");

F5DataModels.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,11 @@ public class F5LoginToken
157157
public string token { get; set; }
158158
}
159159

160+
public class F5Version
161+
{
162+
public string selfLink { get; set; }
163+
}
164+
160165
// F5 data models
161166
#endregion
162167
}

0 commit comments

Comments
 (0)