Skip to content

Commit 838151d

Browse files
author
Lee Fine
committed
ab#64704
1 parent 8591132 commit 838151d

16 files changed

+89
-125
lines changed

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: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ 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();

Bundle/Management.cs

Lines changed: 3 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:

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: 47 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,32 @@ private string GetToken(string userName, string userPassword)
739683

740684
return loginResponse.token.token;
741685
}
686+
687+
internal void ValidateF5Version()
688+
{
689+
LogHandlerCommon.MethodEntry(logger, CertificateStore, "IsVersionSupported");
690+
691+
string query = $"/mgmt/tm/sys/version";
692+
F5Version f5Version = REST.Get<F5Version>(query);
693+
LogHandlerCommon.Debug(logger, CertificateStore, $"Version supported self link: {f5Version.selfLink}");
694+
if (!f5Version.selfLink.Contains(VERSION_DELIMITER))
695+
return;
696+
697+
string selfLink = f5Version.selfLink;
698+
string strVersion = selfLink.Substring(selfLink.IndexOf(VERSION_DELIMITER, StringComparison.CurrentCultureIgnoreCase) + VERSION_DELIMITER.Length, 2);
699+
int version;
700+
if (!int.TryParse(strVersion, out version))
701+
return;
702+
703+
LogHandlerCommon.MethodExit(logger, CertificateStore, "IsVersionSupported");
704+
705+
if (version < MIN_VERSION_SUPPORTED)
706+
{
707+
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.";
708+
logger.LogError(errMesage);
709+
throw new Exception(errMesage);
710+
}
711+
}
742712
#endregion
743713

744714
#region Bundles
@@ -822,8 +792,7 @@ public bool EntryExistsInBundle(string alias)
822792
List<string> bundleIncludes = new List<string>(GetCABundleIncludes());
823793
string partition = GetPartitionFromStorePath();
824794

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

828797
LogHandlerCommon.MethodExit(logger, CertificateStore, "EntryExistsInBundle");
829798
return exists;
@@ -855,26 +824,25 @@ private string[] GetCABundleIncludes()
855824
return includeBundle;
856825
}
857826

858-
public void AddBundleEntry(string bundle, string partition, string name, string b64Certificate, string alias, bool overwrite)
827+
public void AddBundleEntry(string bundle, string partition, string crtName, string b64Certificate, string alias, bool overwrite)
859828
{
860829
LogHandlerCommon.MethodEntry(logger, CertificateStore, "AddBundleEntry");
861830

862831
// Add the entry to inventory
863-
if (!CertificateExists(partition, name))
832+
if (!CertificateExists(partition, crtName))
864833
{
865-
LogHandlerCommon.Debug(logger, CertificateStore, $"Add entry '{name}' in '{CertificateStore.StorePath}'");
866-
AddEntry(partition, name, b64Certificate, null);
834+
LogHandlerCommon.Debug(logger, CertificateStore, $"Add entry '{crtName}' in '{CertificateStore.StorePath}'");
835+
AddEntry(partition, crtName, b64Certificate, null);
867836
}
868837
else
869838
{
870-
if (!overwrite) { throw new Exception($"An entry named '{name}' exists and 'overwrite' was not selected"); }
839+
if (!overwrite) { throw new Exception($"An entry named '{crtName}' exists and 'overwrite' was not selected"); }
871840

872-
LogHandlerCommon.Debug(logger, CertificateStore, $"Replace entry '{name}' in '{CertificateStore.StorePath}'");
873-
ReplaceEntry(partition, name, b64Certificate, null);
841+
LogHandlerCommon.Debug(logger, CertificateStore, $"Replace entry '{crtName}' in '{CertificateStore.StorePath}'");
842+
ReplaceEntry(partition, crtName, b64Certificate, null);
874843
}
875844

876845
// Add the entry to the bundle
877-
string crtName = GetCrtName(name, true);
878846
string crt = $"/{partition}/{crtName}";
879847
List<string> bundleIncludes = new List<string>(GetCABundleIncludes());
880848
if (!bundleIncludes.Contains(crt))
@@ -886,11 +854,10 @@ public void AddBundleEntry(string bundle, string partition, string name, string
886854
LogHandlerCommon.MethodExit(logger, CertificateStore, "AddBundleEntry");
887855
}
888856

889-
public void RemoveBundleEntry(string bundle, string partition, string name)
857+
public void RemoveBundleEntry(string bundle, string partition, string crtName)
890858
{
891859
LogHandlerCommon.MethodEntry(logger, CertificateStore, "RemoveBundleEntry");
892860

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

896863
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
}

F5JobBase.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
// OR CONDITIONS OF ANY KIND, either express or implied. See the License for
88
// thespecific language governing permissions and limitations under the
99
// License.
10-
using Keyfactor.Orchestrators.Extensions.Interfaces;
10+
using Keyfactor.Orchestrators.Extensions;
11+
using Keyfactor.Orchestrators.Extensions.Interfaces;
1112
using Microsoft.Extensions.Logging;
1213
using System;
1314
using System.Collections.Generic;
@@ -29,5 +30,14 @@ internal void SetPAMSecrets(string serverUserName, string serverPassword, ILogge
2930
ServerUserName = PAMUtilities.ResolvePAMField(_resolver, logger, "Server User Name", serverUserName);
3031
ServerPassword = PAMUtilities.ResolvePAMField(_resolver, logger, "Server Password", serverPassword);
3132
}
33+
34+
internal void ValidateF5Release(ILogger logger, CertificateStore certificateStore, F5Client f5Client)
35+
{
36+
LogHandlerCommon.MethodEntry(logger, certificateStore, "ValidateF5Release");
37+
38+
f5Client.ValidateF5Version();
39+
40+
LogHandlerCommon.MethodExit(logger, certificateStore, "ValidateF5Release");
41+
}
3242
}
3343
}

F5Orchestrator.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
5-
<TargetFramework>netcoreapp3.1</TargetFramework>
5+
<TargetFramework>net6.0</TargetFramework>
66
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
77
<Version>13.3</Version>
88
<AssemblyVersion>13.3.0.0</AssemblyVersion>

InventoryBase.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,10 @@ public abstract class InventoryBase : F5JobBase, IInventoryJobExtension
2121

2222
protected InventoryJobConfiguration JobConfig { get; set; }
2323

24-
protected string F5Version { get; set; }
2524
protected bool IgnoreSSLWarning { get; set; }
2625
protected bool UseTokenAuth { get; set; }
2726

28-
public string ExtensionName => string.Empty;
27+
public string ExtensionName => "Keyfactor.Extensions.Orchestrator.F5Orchestrator.Inventory";
2928

3029
public abstract JobResult ProcessJob(InventoryJobConfiguration config, SubmitInventoryUpdate submitInventory);
3130

@@ -34,10 +33,6 @@ protected void ParseJobProperties()
3433
LogHandlerCommon.MethodEntry(logger, JobConfig.CertificateStoreDetails, "ParseJobProperties");
3534
dynamic properties = JsonConvert.DeserializeObject(JobConfig.CertificateStoreDetails.Properties.ToString());
3635

37-
if (string.IsNullOrEmpty(properties.F5Version?.ToString())) { throw new Exception("Missing job property string: F5Version"); }
38-
F5Version = properties.F5Version.ToString();
39-
LogHandlerCommon.Trace(logger, JobConfig.CertificateStoreDetails, $"F5 version '{F5Version}'");
40-
4136
IgnoreSSLWarning = properties.IgnoreSSLWarning == null || string.IsNullOrEmpty(properties.IgnoreSSLWarning.Value) ? false : bool.Parse(properties.IgnoreSSLWarning.Value);
4237
UseTokenAuth = properties.UseTokenAuth == null || string.IsNullOrEmpty(properties.UseTokenAuth.Value) ? false : bool.Parse(properties.UseTokenAuth.Value);
4338
LogHandlerCommon.Trace(logger, JobConfig.CertificateStoreDetails, $"Ignore SSL Warnings '{IgnoreSSLWarning.ToString()}'");

0 commit comments

Comments
 (0)