diff --git a/.github/workflows/keyfactor-starter-workflow.yml b/.github/workflows/keyfactor-starter-workflow.yml index b779b53..aa54b9c 100644 --- a/.github/workflows/keyfactor-starter-workflow.yml +++ b/.github/workflows/keyfactor-starter-workflow.yml @@ -1,33 +1,19 @@ -name: Starter Workflow -on: [workflow_dispatch, push, pull_request] +name: Keyfactor Bootstrap Workflow -jobs: - call-create-github-release-workflow: - uses: Keyfactor/actions/.github/workflows/github-release.yml@main - - call-assign-from-json-workflow: - uses: Keyfactor/actions/.github/workflows/assign-env-from-json.yml@main - - call-dotnet-build-and-release-workflow: - needs: [call-create-github-release-workflow, call-assign-from-json-workflow] - uses: Keyfactor/actions/.github/workflows/dotnet-build-and-release.yml@main - with: - release_version: ${{ needs.call-create-github-release-workflow.outputs.release_version }} - release_url: ${{ needs.call-create-github-release-workflow.outputs.release_url }} - release_dir: ${{ needs.call-assign-from-json-workflow.outputs.release_dir }} +on: + workflow_dispatch: + pull_request: + types: [opened, closed, synchronize, edited, reopened] + push: + create: + branches: + - 'release-*.*' - secrets: - token: ${{ secrets.PRIVATE_PACKAGE_ACCESS }} - - call-generate-readme-workflow: - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' - uses: Keyfactor/actions/.github/workflows/generate-readme.yml@main +jobs: + call-starter-workflow: + uses: keyfactor/actions/.github/workflows/starter.yml@v2 secrets: - token: ${{ secrets.APPROVE_README_PUSH }} - - call-update-catalog-workflow: - needs: call-assign-from-json-workflow - if: needs.call-assign-from-json-workflow.outputs.update_catalog == 'True' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') - uses: Keyfactor/actions/.github/workflows/update-catalog.yml@main - secrets: - token: ${{ secrets.SDK_SYNC_PAT }} + token: ${{ secrets.V2BUILDTOKEN}} + APPROVE_README_PUSH: ${{ secrets.APPROVE_README_PUSH}} + gpg_key: ${{ secrets.KF_GPG_PRIVATE_KEY }} + gpg_pass: ${{ secrets.KF_GPG_PASSPHRASE }} \ No newline at end of file diff --git a/Bundle/Management.cs b/Bundle/Management.cs index 7ca56d9..b88fc57 100644 --- a/Bundle/Management.cs +++ b/Bundle/Management.cs @@ -97,7 +97,7 @@ private void PerformAddJob(F5Client f5) if (!JobConfig.Overwrite) { throw new Exception($"An entry named '{name}' exists and 'overwrite' was not selected"); } LogHandlerCommon.Debug(logger, JobConfig.CertificateStoreDetails, $"Replace entry '{name}' in '{JobConfig.CertificateStoreDetails.StorePath}'"); - f5.ReplaceEntry(partition, name, JobConfig.JobCertificate.Contents); + f5.ReplaceEntry(partition, name, JobConfig.JobCertificate.Contents, null); } else { diff --git a/CHANGELOG.md b/CHANGELOG.md index 866e642..1a63531 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +v1.6.0 +- 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. + v1.5.0 - Add new optional custom paramter - UseTokenAuth - to make token auth vs basic auth (default) a selectable option diff --git a/F5Client.cs b/F5Client.cs index 818854e..51fc30a 100644 --- a/F5Client.cs +++ b/F5Client.cs @@ -83,7 +83,7 @@ public F5Client(CertificateStore certificateStore, string serverUserName, string #region Certificate/PFX Shared - public void AddEntry(string partition, string name, string b64Certificate) + public void AddEntry(string partition, string name, string b64Certificate, string certificatePassword) { LogHandlerCommon.MethodEntry(logger, CertificateStore, "AddEntry"); LogHandlerCommon.Trace(logger, CertificateStore, $"Processing certificate for partition '{partition}' and name '{name}'"); @@ -94,7 +94,7 @@ public void AddEntry(string partition, string name, string b64Certificate) if (certificate.HasPrivateKey) { LogHandlerCommon.Trace(logger, CertificateStore, $"Certificate for partition '{partition}' and name '{name}' has a private key - performing addition"); - AddPfx(entryContents, partition, name, password, null); + AddPfx(entryContents, partition, name, password, null, certificatePassword); LogHandlerCommon.Trace(logger, CertificateStore, $"PFX addition for partition '{partition}' and name '{name}' completed"); } else @@ -106,7 +106,7 @@ public void AddEntry(string partition, string name, string b64Certificate) LogHandlerCommon.MethodExit(logger, CertificateStore, "AddEntry"); } - public void ReplaceEntry(string partition, string name, string b64Certificate) + public void ReplaceEntry(string partition, string name, string b64Certificate, string certificatePassword) { LogHandlerCommon.MethodEntry(logger, CertificateStore, "ReplaceEntry"); LogHandlerCommon.Trace(logger, CertificateStore, $"Processing certificate for partition '{partition}' and name '{name}'"); @@ -118,7 +118,7 @@ public void ReplaceEntry(string partition, string name, string b64Certificate) if (certificate.HasPrivateKey) { LogHandlerCommon.Trace(logger, CertificateStore, $"Certificate for partition '{partition}' and name '{name}' has a private key - performing replacement"); - ReplacePfx(entryContents, partition, name, password); + ReplacePfx(entryContents, partition, name, password, certificatePassword); LogHandlerCommon.Trace(logger, CertificateStore, $"PFX replacement for partition '{partition}' and name '{name}' completed"); } else @@ -220,7 +220,7 @@ private void AddCertificate(byte[] entryContents, string partition, string name) LogHandlerCommon.MethodExit(logger, CertificateStore, "AddCertificate"); } - private void AddPfx(byte[] entryContents, string partition, string name, string password, string keyName) + private void AddPfx(byte[] entryContents, string partition, string name, string password, string keyName, string certificatePassword) { LogHandlerCommon.MethodEntry(logger, CertificateStore, "AddPfx"); LogHandlerCommon.Trace(logger, CertificateStore, $"Uploading PFX to {partition}-{name}.p12"); @@ -238,6 +238,8 @@ private void AddPfx(byte[] entryContents, string partition, string name, string name = $"{name}", localfile = $"/var/config/rest/downloads/{partition}-{name}.p12{keyNameParam}", passphrase = password, + keyPassphrase = String.IsNullOrEmpty(certificatePassword) ? string.Empty : certificatePassword, + keySecurityType = String.IsNullOrEmpty(certificatePassword) ? "normal" : "password", partition = partition }, "pkcs12"); } @@ -248,7 +250,7 @@ private void AddPfx(byte[] entryContents, string partition, string name, string // again with that key name appended onto the localfile parameter. An F5 hotfix is necessary to produce // this message and use the updated /pkcs12 API that accepts the separate key name. if (string.IsNullOrEmpty(keyName) && ex.message.Contains(INVALID_KEY_MSG_ID)) - AddPfx(entryContents, partition, name, password, GetKeyName(ex.message)); + AddPfx(entryContents, partition, name, password, GetKeyName(ex.message), certificatePassword); else throw (name.Contains(".crt", StringComparison.OrdinalIgnoreCase) && ex.Message.Contains("expected to exist", StringComparison.OrdinalIgnoreCase) ? @@ -287,7 +289,7 @@ private void ReplaceCertificate(byte[] entryContents, string partition, string n LogHandlerCommon.MethodExit(logger, CertificateStore, "ReplaceCertificate"); } - private void ReplacePfx(byte[] entryContents, string partition, string name, string password) + private void ReplacePfx(byte[] entryContents, string partition, string name, string password, string certificatePassword) { LogHandlerCommon.MethodEntry(logger, CertificateStore, "ReplacePfx"); string timestamp = DateTime.Now.ToString("MM-dd-yy:H:mm:ss"); @@ -297,7 +299,7 @@ private void ReplacePfx(byte[] entryContents, string partition, string name, str ArchiveFile($"/config/filestore/files_d/{partition}_d/certificate_d/:{partition}:{name}_*", $"{partition}-{name}-{timestamp}.crt"); LogHandlerCommon.Trace(logger, CertificateStore, $"Adding PFX to partition '{partition}' and name '{name}'"); - AddPfx(entryContents, partition, name, password, null); + AddPfx(entryContents, partition, name, password, null, certificatePassword); LogHandlerCommon.MethodExit(logger, CertificateStore, "ReplacePfx"); } @@ -703,6 +705,7 @@ public List GetSSLProfiles(int pageSize) { try { + LogHandlerCommon.Trace(logger, CertificateStore, $"Processing alias {profiles[i].name}"); // Exclude 'ca-bundle.crt' as that can only be managed by F5 if (profiles[i].name.Equals("ca-bundle.crt", StringComparison.OrdinalIgnoreCase) || profiles[i].name.Equals("f5-ca-bundle.crt", StringComparison.OrdinalIgnoreCase)) @@ -860,14 +863,14 @@ public void AddBundleEntry(string bundle, string partition, string name, string if (!CertificateExists(partition, name)) { LogHandlerCommon.Debug(logger, CertificateStore, $"Add entry '{name}' in '{CertificateStore.StorePath}'"); - AddEntry(partition, name, b64Certificate); + AddEntry(partition, name, b64Certificate, null); } else { if (!overwrite) { throw new Exception($"An entry named '{name}' exists and 'overwrite' was not selected"); } LogHandlerCommon.Debug(logger, CertificateStore, $"Replace entry '{name}' in '{CertificateStore.StorePath}'"); - ReplaceEntry(partition, name, b64Certificate); + ReplaceEntry(partition, name, b64Certificate, null); } // Add the entry to the bundle diff --git a/F5DataModels.cs b/F5DataModels.cs index 753da1a..7ea6aae 100644 --- a/F5DataModels.cs +++ b/F5DataModels.cs @@ -108,6 +108,8 @@ internal class F5InstallCommand public string localfile { get; set; } public string passphrase { get; set; } + public string keyPassphrase { get; set; } + public string keySecurityType { get; set; } public string partition { get; set; } } diff --git a/README.md b/README.md index bfd004c..6df838a 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ + # F5 The F5 Orchestrator allows for the remote management of F5 Stores. Discovery, Inventory, and Management functions are supported. #### Integration status: Production - Ready for use in production environments. - ## About the Keyfactor Universal Orchestrator Extension This repository contains a Universal Orchestrator Extension which is a plugin to the Keyfactor Universal Orchestrator. Within the Keyfactor Platform, Orchestrators are used to manage “certificate stores” — collections of certificates and roots of trust that are found within and used by various applications. @@ -13,23 +13,22 @@ The Universal Orchestrator is part of the Keyfactor software distribution and is The Universal Orchestrator is the successor to the Windows Orchestrator. This Orchestrator Extension plugin only works with the Universal Orchestrator and does not work with the Windows Orchestrator. - ## Support for F5 -F5 is supported by Keyfactor for Keyfactor customers. If you have a support issue, please open a support ticket with your Keyfactor representative. +F5 is supported by Keyfactor for Keyfactor customers. If you have a support issue, please open a support ticket via the Keyfactor Support Portal at https://support.keyfactor.com ###### To report a problem or suggest a new feature, use the **[Issues](../../issues)** tab. If you want to contribute actual bug fixes or proposed enhancements, use the **[Pull requests](../../pulls)** tab. - --- +--- + ## Keyfactor Version Supported The minimum version of the Keyfactor Universal Orchestrator Framework needed to run this version of the extension is 10.1 - ## Platform Specific Notes The Keyfactor Universal Orchestrator may be installed on either Windows or Linux based platforms. The certificate operations supported by a capability may vary based what platform the capability is installed on. The table below indicates what capabilities are supported based on which platform the encompassing Universal Orchestrator is running. @@ -39,7 +38,7 @@ The Keyfactor Universal Orchestrator may be installed on either Windows or Linux |Supports Management Remove|✓ |✓ | |Supports Create Store| | | |Supports Discovery|✓ |✓ | -|Supports Renrollment| | | +|Supports Reenrollment| | | |Supports Inventory|✓ |✓ | @@ -163,7 +162,7 @@ The version number of a the F5 Orchestrator can be verified by right clicking on - **Custom Capability** - Leave unchecked - **Supported Job Types** – Select Inventory and Add for all 3 types, and Discovery for CA Bundles and SSL Certificates. - **General Settings** - Select Needs Server. Leave Uses PowerShell unchecked. Select Blueprint Allowed if you plan to use blueprinting. -- **Password Settings** - Leave both options unchecked +- **Password Settings** - Leave both options unchecked for F5-WS-REST and F5-CA-REST. Select Needs Store Password for F5-SL-REST. - **All selections on Advanced tab** - Set the values on this tab ***exactly*** as they are shown in the above screen prints for each applicable store type. @@ -192,31 +191,35 @@ If you choose to manually create a F5 store In Keyfactor Command rather than run - **Container** – Optional. Select a container if utilized. -- **Client Machine & Credentials** – Required. The server name or IP Address and login credentials for the F5 device. The credentials for server login can be any of: - - - UserId/Password - - - PAM provider information to pass the UserId/Password or UserId/SSH private key credentials - - When entering the credentials, UseSSL ***must*** be selected. +- **Client Machine** – Required. The server name or IP Address for the F5 device. - **Store Path** – Required. Enter the name of the partition on the F5 device you wish to manage. This value is case sensitive, so if the partition name is "Common", it must be entered as "Common" and not "common". +- **Orchestrator** – Required. Select the orchestrator you wish to use to manage this store + - **Primary Node Online Required** – Optional. Select this if you wish to stop the orchestrator from adding, replacing or renewing certificates on nodes that are inactive. If this is not selected, adding, replacing and renewing certificates on inactive nodes will be allowed. If you choose not to add this custom field, the default value of False will be assumed. - **Primary Node** - Only required (and shown) if Primary Node Online Required is added and selected. Enter the fully qualified domain name of the F5 device that acts as the primary node in a highly available F5 implementation. If you're using a single F5 device, this will typically be the same value you entered in the Client Machine field. -- **Primary Node Check Retry Maximum** - Only required (and shown) if Primary Node Online Required is added and selected. Enter the number of times a Management-Add job will attempt to add/replace/renew a certificate if the node is inactive before failing. - - **Primary Node Check Retry Wait Seconds** - Only required (and shown) if Primary Node Online Required is added and selected. Enter the number of seconds to wait between attempts to add/replace/renew a certificate if the node is inactive. +- **Primary Node Check Retry Maximum** - Only required (and shown) if Primary Node Online Required is added and selected. Enter the number of times a Management-Add job will attempt to add/replace/renew a certificate if the node is inactive before failing. + - **Version of F5** - Required. Select v13, v14, or v15 to match the version for the F5 device being managed +- **Server Username/Server Password** - Required. The credentials for server login can be any of: + + - UserId/Password + + - PAM provider information to pass the UserId/Password or UserId/SSH private key credentials + +- **Use SSL** - Required. True if using https to access the F5 device. False if using http. + - **Ignore SSL Warning** - Optional. Select this if you wish to ignore SSL warnings from F5 that occur during API calls when the site does not have a trusted certificate with the proper SAN bound to it. If you choose not to add this custom field, the default value of False will be assumed and SSL warnings will cause errors during orchestrator extension jobs. - **Use Token Authentication** - Optional. Select this if you wish to use F5's token authentiation instead of basic authentication for all API requests. If you choose not to add this custom field, the default value of False will be assumed and basic authentication will be used for all API requests for all jobs. Setting this value to True will enable an initial basic authenticated request to acquire an authentication token, which will then be used for all subsequent API requests. -- **Orchestrator** – Required. Select the orchestrator you wish to use to manage this store +- **Store Password** - Required for F5-SL-REST only. Check "No Password" if you wish the private key of any added certificate to be set to Key Security Type "Normal". Enter a value (either a password or pointer to an installed PAM provider key for the password) to be used to encrypt the private key of any added certificate for Key Security Type of "Password". - **Inventory Schedule** – Set a schedule for running Inventory jobs or none, if you choose not to schedule Inventory at this time. @@ -246,3 +249,6 @@ First, in Keyfactor Command navigate to Certificate Locations =\> Certificate St Once the Discovery job has completed, a list of F5 certificate store locations should show in the Certificate Stores Discovery tab in Keyfactor Command. Right click on a store and select Approve to bring up a dialog that will ask for the remaining necessary certificate store parameters described in Step 2a. Complete those and click Save, and the Certificate Store should now show up in the list of stores in the Certificate Stores tab. +When creating cert store type manually, that store property names and entry parameter names are case sensitive + + diff --git a/SSLProfile/Management.cs b/SSLProfile/Management.cs index 367f6e8..d882506 100644 --- a/SSLProfile/Management.cs +++ b/SSLProfile/Management.cs @@ -57,7 +57,7 @@ public override JobResult ProcessJob(ManagementJobConfiguration config) { case CertStoreOperationType.Add: LogHandlerCommon.Debug(logger, config.CertificateStoreDetails, $"Add entry '{config.JobCertificate.Alias}' to '{config.CertificateStoreDetails.StorePath}'"); - PerformAddJob(f5); + PerformAddJob(f5, config.CertificateStoreDetails.StorePassword); break; case CertStoreOperationType.Remove: LogHandlerCommon.Trace(logger, config.CertificateStoreDetails, $"Remove entry '{config.JobCertificate.Alias}' from '{config.CertificateStoreDetails.StorePath}'"); @@ -81,7 +81,7 @@ public override JobResult ProcessJob(ManagementJobConfiguration config) } } - private void PerformAddJob(F5Client f5) + private void PerformAddJob(F5Client f5, string certificatePassword) { LogHandlerCommon.MethodEntry(logger, JobConfig.CertificateStoreDetails, "PerformAddJob"); string name = JobConfig.JobCertificate.Alias; @@ -92,12 +92,12 @@ private void PerformAddJob(F5Client f5) if (!JobConfig.Overwrite) { throw new Exception($"An entry named '{name}' exists and 'overwrite' was not selected"); } LogHandlerCommon.Debug(logger, JobConfig.CertificateStoreDetails, $"Replace entry '{name}' in '{JobConfig.CertificateStoreDetails.StorePath}'"); - f5.ReplaceEntry(partition, name, JobConfig.JobCertificate.Contents); + f5.ReplaceEntry(partition, name, JobConfig.JobCertificate.Contents, null); } else { LogHandlerCommon.Debug(logger, JobConfig.CertificateStoreDetails, $"The entry '{name}' does not exist in '{JobConfig.CertificateStoreDetails.StorePath}' and will be added"); - f5.AddEntry(partition, name, JobConfig.JobCertificate.Contents); + f5.AddEntry(partition, name, JobConfig.JobCertificate.Contents, certificatePassword); } LogHandlerCommon.MethodExit(logger, JobConfig.CertificateStoreDetails, "PerformAddJob"); } diff --git a/images/image11.png b/images/image11.png index 256ad51..00d10a4 100644 Binary files a/images/image11.png and b/images/image11.png differ diff --git a/images/image13.png b/images/image13.png index 5411965..3b8c929 100644 Binary files a/images/image13.png and b/images/image13.png differ diff --git a/integration-manifest.json b/integration-manifest.json index 5d9f0c1..506e61f 100644 --- a/integration-manifest.json +++ b/integration-manifest.json @@ -12,6 +12,7 @@ "orchestrator": { "UOFramework": "10.1", "pam_support": true, + "keyfactor_platform_version": "10.4", "win": { "supportsCreateStore": false, "supportsDiscovery": true, @@ -127,7 +128,7 @@ "EntryParameters": [], "PasswordOptions": { "EntrySupported": false, - "StoreRequired": false, + "StoreRequired": true, "Style": "Default" }, "PrivateKeyAllowed": "Optional", @@ -349,7 +350,7 @@ "BlueprintAllowed": true, "CustomAliasAllowed": "Required" } - } + } } } } diff --git a/readme_source.md b/readme_source.md index a0736a4..c18bdf0 100644 --- a/readme_source.md +++ b/readme_source.md @@ -62,7 +62,7 @@ The version number of a the F5 Orchestrator can be verified by right clicking on - **Custom Capability** - Leave unchecked - **Supported Job Types** – Select Inventory and Add for all 3 types, and Discovery for CA Bundles and SSL Certificates. - **General Settings** - Select Needs Server. Leave Uses PowerShell unchecked. Select Blueprint Allowed if you plan to use blueprinting. -- **Password Settings** - Leave both options unchecked +- **Password Settings** - Leave both options unchecked for F5-WS-REST and F5-CA-REST. Select Needs Store Password for F5-SL-REST. - **All selections on Advanced tab** - Set the values on this tab ***exactly*** as they are shown in the above screen prints for each applicable store type. @@ -91,31 +91,35 @@ If you choose to manually create a F5 store In Keyfactor Command rather than run - **Container** – Optional. Select a container if utilized. -- **Client Machine & Credentials** – Required. The server name or IP Address and login credentials for the F5 device. The credentials for server login can be any of: - - - UserId/Password - - - PAM provider information to pass the UserId/Password or UserId/SSH private key credentials - - When entering the credentials, UseSSL ***must*** be selected. +- **Client Machine** – Required. The server name or IP Address for the F5 device. - **Store Path** – Required. Enter the name of the partition on the F5 device you wish to manage. This value is case sensitive, so if the partition name is "Common", it must be entered as "Common" and not "common". +- **Orchestrator** – Required. Select the orchestrator you wish to use to manage this store + - **Primary Node Online Required** – Optional. Select this if you wish to stop the orchestrator from adding, replacing or renewing certificates on nodes that are inactive. If this is not selected, adding, replacing and renewing certificates on inactive nodes will be allowed. If you choose not to add this custom field, the default value of False will be assumed. - **Primary Node** - Only required (and shown) if Primary Node Online Required is added and selected. Enter the fully qualified domain name of the F5 device that acts as the primary node in a highly available F5 implementation. If you're using a single F5 device, this will typically be the same value you entered in the Client Machine field. -- **Primary Node Check Retry Maximum** - Only required (and shown) if Primary Node Online Required is added and selected. Enter the number of times a Management-Add job will attempt to add/replace/renew a certificate if the node is inactive before failing. - - **Primary Node Check Retry Wait Seconds** - Only required (and shown) if Primary Node Online Required is added and selected. Enter the number of seconds to wait between attempts to add/replace/renew a certificate if the node is inactive. +- **Primary Node Check Retry Maximum** - Only required (and shown) if Primary Node Online Required is added and selected. Enter the number of times a Management-Add job will attempt to add/replace/renew a certificate if the node is inactive before failing. + - **Version of F5** - Required. Select v13, v14, or v15 to match the version for the F5 device being managed +- **Server Username/Server Password** - Required. The credentials for server login can be any of: + + - UserId/Password + + - PAM provider information to pass the UserId/Password or UserId/SSH private key credentials + +- **Use SSL** - Required. True if using https to access the F5 device. False if using http. + - **Ignore SSL Warning** - Optional. Select this if you wish to ignore SSL warnings from F5 that occur during API calls when the site does not have a trusted certificate with the proper SAN bound to it. If you choose not to add this custom field, the default value of False will be assumed and SSL warnings will cause errors during orchestrator extension jobs. - **Use Token Authentication** - Optional. Select this if you wish to use F5's token authentiation instead of basic authentication for all API requests. If you choose not to add this custom field, the default value of False will be assumed and basic authentication will be used for all API requests for all jobs. Setting this value to True will enable an initial basic authenticated request to acquire an authentication token, which will then be used for all subsequent API requests. -- **Orchestrator** – Required. Select the orchestrator you wish to use to manage this store +- **Store Password** - Required for F5-SL-REST only. Check "No Password" if you wish the private key of any added certificate to be set to Key Security Type "Normal". Enter a value (either a password or pointer to an installed PAM provider key for the password) to be used to encrypt the private key of any added certificate for Key Security Type of "Password". - **Inventory Schedule** – Set a schedule for running Inventory jobs or none, if you choose not to schedule Inventory at this time.