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,39 @@ private string GetToken(string userName, string userPassword)
739
683
740
684
return loginResponse . token . token ;
741
685
}
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
+ }
742
719
#endregion
743
720
744
721
#region Bundles
@@ -822,8 +799,7 @@ public bool EntryExistsInBundle(string alias)
822
799
List < string > bundleIncludes = new List < string > ( GetCABundleIncludes ( ) ) ;
823
800
string partition = GetPartitionFromStorePath ( ) ;
824
801
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 ) ) ;
827
803
828
804
LogHandlerCommon . MethodExit ( logger , CertificateStore , "EntryExistsInBundle" ) ;
829
805
return exists ;
@@ -855,26 +831,25 @@ private string[] GetCABundleIncludes()
855
831
return includeBundle ;
856
832
}
857
833
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 )
859
835
{
860
836
LogHandlerCommon . MethodEntry ( logger , CertificateStore , "AddBundleEntry" ) ;
861
837
862
838
// Add the entry to inventory
863
- if ( ! CertificateExists ( partition , name ) )
839
+ if ( ! CertificateExists ( partition , crtName ) )
864
840
{
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 ) ;
867
843
}
868
844
else
869
845
{
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") ; }
871
847
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 ) ;
874
850
}
875
851
876
852
// Add the entry to the bundle
877
- string crtName = GetCrtName ( name , true ) ;
878
853
string crt = $ "/{ partition } /{ crtName } ";
879
854
List < string > bundleIncludes = new List < string > ( GetCABundleIncludes ( ) ) ;
880
855
if ( ! bundleIncludes . Contains ( crt ) )
@@ -886,11 +861,10 @@ public void AddBundleEntry(string bundle, string partition, string name, string
886
861
LogHandlerCommon . MethodExit ( logger , CertificateStore , "AddBundleEntry" ) ;
887
862
}
888
863
889
- public void RemoveBundleEntry ( string bundle , string partition , string name )
864
+ public void RemoveBundleEntry ( string bundle , string partition , string crtName )
890
865
{
891
866
LogHandlerCommon . MethodEntry ( logger , CertificateStore , "RemoveBundleEntry" ) ;
892
867
893
- string crtName = GetCrtName ( name , true ) ;
894
868
string crtEntry = $ "/{ partition } /{ crtName } ";
895
869
896
870
LogHandlerCommon . Trace ( logger , CertificateStore , $ "Preparing to remove bundle entry '{ crtEntry } '") ;
0 commit comments