21
21
22
22
using Newtonsoft . Json ;
23
23
using System . Collections ;
24
+ using System . Collections . Concurrent ;
25
+ using System . Drawing . Printing ;
26
+ using System . Diagnostics . CodeAnalysis ;
24
27
25
28
namespace Keyfactor . Extensions . Orchestrator . F5Orchestrator
26
29
{
@@ -34,6 +37,8 @@ internal class F5Client
34
37
private const string INVALID_KEY_SUBSTR = "key(" ;
35
38
private const string INVALID_KEY_BEG_DELIM = @"/" ;
36
39
private const string INVALID_KEY_END_DELIM = ")" ;
40
+ private const int MIN_VERSION_SUPPORTED = 14 ;
41
+ private const string VERSION_DELIMITER = "?ver=" ;
37
42
38
43
public CertificateStore CertificateStore { get ; set ; }
39
44
public string ServerUserName { get ; set ; }
@@ -43,7 +48,6 @@ internal class F5Client
43
48
public string PFXPassword { get ; set ; }
44
49
public IEnumerable < PreviousInventoryItem > Inventory { get ; set ; }
45
50
public string PrimaryNode { get ; set ; }
46
- public string F5Version { get ; set ; }
47
51
public bool IgnoreSSLWarning { get ; set ; }
48
52
public bool UseTokenAuth { get ; set ; }
49
53
private RESTHandler REST { get ; set ; }
@@ -141,26 +145,23 @@ public void RemoveEntry(string partition, string name)
141
145
ArchiveFile ( $ "/config/filestore/files_d/{ partition } _d/certificate_key_d/:{ partition } :{ name } _*", $ "{ partition } -{ name } -{ timestamp } .key") ;
142
146
LogHandlerCommon . Trace ( logger , CertificateStore , $ "Removing certificate and key at '{ partition } ' and name '{ name } '") ;
143
147
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 } ") ;
146
149
}
147
150
LogHandlerCommon . Trace ( logger , CertificateStore , $ "Archiving certificate at '{ partition } ' and name '{ name } '") ;
148
151
ArchiveFile ( $ "/config/filestore/files_d/{ partition } _d/certificate_d/:{ partition } :{ name } _*", $ "{ partition } -{ name } -{ timestamp } .crt") ;
149
152
LogHandlerCommon . Trace ( logger , CertificateStore , $ "Removing certificate at '{ partition } ' and name '{ name } '") ;
150
153
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 } ") ;
153
155
LogHandlerCommon . MethodExit ( logger , CertificateStore , "RemoveEntry" ) ;
154
156
}
155
157
156
- public bool KeyExists ( string partition , string name )
158
+ public bool KeyExists ( string partition , string keyName )
157
159
{
158
160
LogHandlerCommon . MethodEntry ( logger , CertificateStore , "KeyExists" ) ;
159
161
bool exists = false ;
160
162
161
163
try
162
164
{
163
- string keyName = GetKeyName ( name , true ) ;
164
165
string query = $ "/mgmt/tm/sys/file/ssl-key/~{ partition } ~{ keyName } ";
165
166
F5Key key = REST . Get < F5Key > ( query ) ;
166
167
exists = ( key != null ) ;
@@ -178,14 +179,13 @@ public bool KeyExists(string partition, string name)
178
179
return exists ;
179
180
}
180
181
181
- public bool CertificateExists ( string partition , string name )
182
+ public bool CertificateExists ( string partition , string crtName )
182
183
{
183
184
LogHandlerCommon . MethodEntry ( logger , CertificateStore , "CertificateExists" ) ;
184
185
bool exists = false ;
185
186
186
187
try
187
188
{
188
- string crtName = GetCrtName ( name , true ) ;
189
189
string query = $ "/mgmt/tm/sys/file/ssl-cert/~{ partition } ~{ crtName } ";
190
190
F5SSLProfile certificate = REST . Get < F5SSLProfile > ( query ) ;
191
191
exists = ( certificate != null ) ;
@@ -406,12 +406,12 @@ private void SetItemStatus(CurrentInventoryItem agentInventoryItem)
406
406
LogHandlerCommon . MethodExit ( logger , CertificateStore , "SetItemStatus" ) ;
407
407
}
408
408
409
- private CurrentInventoryItem GetInventoryItem ( string partition , string name , bool hasPrivateKey )
409
+ private CurrentInventoryItem GetInventoryItem ( string partition , string crtName , bool hasPrivateKey )
410
410
{
411
411
LogHandlerCommon . MethodEntry ( logger , CertificateStore , "GetInventoryItem" ) ;
412
412
413
413
// 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 } _*") ;
415
415
List < string > certContents = new List < string > ( ) ;
416
416
bool useChainLevel = certificateCollection . Count > 1 ;
417
417
foreach ( X509Certificate2 certificate in certificateCollection )
@@ -420,7 +420,6 @@ private CurrentInventoryItem GetInventoryItem(string partition, string name, boo
420
420
//LogHandlerCommon.Debug(logger, CertificateStore, $"ALIAS: {name}: {Convert.ToBase64String(certificate.Export(X509ContentType.Cert))}");
421
421
}
422
422
423
- string crtName = GetCrtName ( name , false ) ;
424
423
CurrentInventoryItem inventoryItem = new CurrentInventoryItem
425
424
{
426
425
ItemStatus = OrchestratorInventoryItemStatus . Unknown ,
@@ -434,61 +433,6 @@ private CurrentInventoryItem GetInventoryItem(string partition, string name, boo
434
433
return inventoryItem ;
435
434
}
436
435
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
-
492
436
// Certificate PFX Shared
493
437
#endregion
494
438
@@ -728,7 +672,7 @@ public List<CurrentInventoryItem> GetSSLProfiles(int pageSize)
728
672
// SSL Profiles
729
673
#endregion
730
674
731
- #region Auth
675
+ #region Auth & Version
732
676
733
677
private string GetToken ( string userName , string userPassword )
734
678
{
@@ -739,6 +683,32 @@ private string GetToken(string userName, string userPassword)
739
683
740
684
return loginResponse . token . token ;
741
685
}
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
+ }
742
712
#endregion
743
713
744
714
#region Bundles
@@ -822,8 +792,7 @@ public bool EntryExistsInBundle(string alias)
822
792
List < string > bundleIncludes = new List < string > ( GetCABundleIncludes ( ) ) ;
823
793
string partition = GetPartitionFromStorePath ( ) ;
824
794
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 ) ) ;
827
796
828
797
LogHandlerCommon . MethodExit ( logger , CertificateStore , "EntryExistsInBundle" ) ;
829
798
return exists ;
@@ -855,26 +824,25 @@ private string[] GetCABundleIncludes()
855
824
return includeBundle ;
856
825
}
857
826
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 )
859
828
{
860
829
LogHandlerCommon . MethodEntry ( logger , CertificateStore , "AddBundleEntry" ) ;
861
830
862
831
// Add the entry to inventory
863
- if ( ! CertificateExists ( partition , name ) )
832
+ if ( ! CertificateExists ( partition , crtName ) )
864
833
{
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 ) ;
867
836
}
868
837
else
869
838
{
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") ; }
871
840
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 ) ;
874
843
}
875
844
876
845
// Add the entry to the bundle
877
- string crtName = GetCrtName ( name , true ) ;
878
846
string crt = $ "/{ partition } /{ crtName } ";
879
847
List < string > bundleIncludes = new List < string > ( GetCABundleIncludes ( ) ) ;
880
848
if ( ! bundleIncludes . Contains ( crt ) )
@@ -886,11 +854,10 @@ public void AddBundleEntry(string bundle, string partition, string name, string
886
854
LogHandlerCommon . MethodExit ( logger , CertificateStore , "AddBundleEntry" ) ;
887
855
}
888
856
889
- public void RemoveBundleEntry ( string bundle , string partition , string name )
857
+ public void RemoveBundleEntry ( string bundle , string partition , string crtName )
890
858
{
891
859
LogHandlerCommon . MethodEntry ( logger , CertificateStore , "RemoveBundleEntry" ) ;
892
860
893
- string crtName = GetCrtName ( name , true ) ;
894
861
string crtEntry = $ "/{ partition } /{ crtName } ";
895
862
896
863
LogHandlerCommon . Trace ( logger , CertificateStore , $ "Preparing to remove bundle entry '{ crtEntry } '") ;
0 commit comments