Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# See GitHub's documentation for more information on this file:
# https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "nuget"
directory: "/"
schedule:
interval: "daily"
15 changes: 11 additions & 4 deletions .github/workflows/keyfactor-release-workflow.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Keyfactor Release Workflow
name: Keyfactor Bootstrap Workflow

on:
workflow_dispatch:
Expand All @@ -11,10 +11,17 @@ on:

jobs:
call-starter-workflow:
uses: keyfactor/actions/.github/workflows/starter.yml@v3.1.2
uses: keyfactor/actions/.github/workflows/starter.yml@v4
with:
command_token_url: ${{ vars.COMMAND_TOKEN_URL }}
command_hostname: ${{ vars.COMMAND_HOSTNAME }}
command_base_api_path: ${{ vars.COMMAND_API_PATH }}
secrets:
token: ${{ secrets.V2BUILDTOKEN}}
APPROVE_README_PUSH: ${{ secrets.APPROVE_README_PUSH}}
gpg_key: ${{ secrets.KF_GPG_PRIVATE_KEY }}
gpg_pass: ${{ secrets.KF_GPG_PASSPHRASE }}

scan_token: ${{ secrets.SAST_TOKEN }}
entra_username: ${{ secrets.DOCTOOL_ENTRA_USERNAME }}
entra_password: ${{ secrets.DOCTOOL_ENTRA_PASSWD }}
command_client_id: ${{ secrets.COMMAND_CLIENT_ID }}
command_client_secret: ${{ secrets.COMMAND_CLIENT_SECRET }}
61 changes: 41 additions & 20 deletions AzureKeyVault/AzureClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,17 @@ public virtual async Task<KeyVaultResource> CreateVault()

logger.LogTrace($"getting subscription info for provided subscription id {VaultProperties.SubscriptionId}");

SubscriptionResource subscription = KvManagementClient.GetSubscriptionResource(SubscriptionResource.CreateResourceIdentifier(VaultProperties.SubscriptionId));
ResourceGroupResource resourceGroup = subscription.GetResourceGroup(VaultProperties.ResourceGroupName);
var subscription = KvManagementClient.GetSubscriptionResource(SubscriptionResource.CreateResourceIdentifier(VaultProperties.SubscriptionId));

var resourceGroups = subscription.GetResourceGroups();
ResourceGroupResource resourceGroup = await resourceGroups.GetAsync(VaultProperties.ResourceGroupName);
logger.LogTrace("calling getAsync on resourcegroup...");
await resourceGroup.GetAsync();
logger.LogTrace("completed getAsync on resource group...");

var s = resourceGroup.HasData.ToString();

logger.LogTrace($"resource group has data?: {s}");

AzureLocation loc;

Expand All @@ -170,7 +179,7 @@ public virtual async Task<KeyVaultResource> CreateVault()
{
logger.LogTrace($"no Vault Region location specified for new Vault, Getting available regions for resource group {resourceGroup.Data.Name}.");
var locOptions = await resourceGroup.GetAvailableLocationsAsync();
logger.LogTrace($"got location options for subscription {subscription.Data.SubscriptionId}", locOptions);
logger.LogTrace($"got location options for subscription {VaultProperties.SubscriptionId}", locOptions);
loc = locOptions.Value.FirstOrDefault();
}
catch (Exception ex)
Expand Down Expand Up @@ -269,33 +278,46 @@ public virtual async Task<IEnumerable<CurrentInventoryItem>> GetCertificatesAsyn
{
List<CurrentInventoryItem> inventoryItems = new List<CurrentInventoryItem>();
AsyncPageable<CertificateProperties> inventory = null;
var fullInventoryList = new List<CertificateProperties>();
var failedCount = 0;
Exception innerException = null;

try
{
logger.LogTrace("calling GetPropertiesOfCertificates() on the Certificate Client");
inventory = CertClient.GetPropertiesOfCertificatesAsync();

logger.LogTrace($"created pageable request");

logger.LogTrace("iterating over result pages for complete list..");

logger.LogTrace($"got a pageable response");
await foreach (var cert in inventory)
{
logger.LogTrace($"adding cert with ID: {cert.Id} to the list.");
fullInventoryList.Add(cert); // convert to list from pages
}

logger.LogTrace($"compiled full inventory list of {fullInventoryList.Count()} certificate(s)");
}
catch (Exception ex)
catch (AuthenticationFailedException ex)
{
logger.LogError($"Error performing inventory. {ex.Message}", ex);
logger.LogError($"Authentication failed: {ex.Message}");
logger.LogError(LogHandler.FlattenException(ex));
throw;
}

logger.LogTrace("iterating over result pages for complete list..");

var fullInventoryList = new List<CertificateProperties>();
var failedCount = 0;
Exception innerException = null;

await foreach (var cert in inventory)
catch (RequestFailedException ex) // Catch other potential Azure-specific errors
{
logger.LogError($"Azure Key Vault operation failed: {ex.Status} - {ex.Message}");
logger.LogError(LogHandler.FlattenException(ex));
throw;
}
catch (Exception ex)
{
logger.LogTrace($"adding cert with ID: {cert.Id} to the list.");
fullInventoryList.Add(cert); // convert to list from pages
logger.LogError($"Error performing inventory. {ex.Message}", ex);
logger.LogError(LogHandler.FlattenException(ex));
throw;
}

logger.LogTrace($"compiled full inventory list of {fullInventoryList.Count()} certificate(s)");

foreach (var certificate in fullInventoryList)
{
logger.LogTrace($"getting details for the individual certificate with id: {certificate.Id} and name: {certificate.Name}");
Expand All @@ -317,8 +339,7 @@ public virtual async Task<IEnumerable<CurrentInventoryItem>> GetCertificatesAsyn
catch (Exception ex)
{
failedCount++;
innerException = ex;
logger.LogError($"Failed to retreive details for certificate {certificate.Name}. Exception: {ex.Message}");
logger.LogError($"Failed to retreive details for certificate {certificate.Name}. Exception: {LogHandler.FlattenException(ex)}");
// continuing with inventory instead of throwing, in case there's an issue with a single certificate
}
}
Expand Down
2 changes: 1 addition & 1 deletion AzureKeyVault/Jobs/AzureKeyVaultJob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public void InitializeStore(dynamic config)
// ClientSecret can be omitted for managed identities, required for service principal auth
VaultProperties.ClientSecret = PAMUtilities.ResolvePAMField(PamSecretResolver, logger, "Server Password", config.ServerPassword);

if (VaultProperties.ClientSecret == null)
if (string.IsNullOrEmpty(VaultProperties.ClientSecret))
{
logger.LogTrace("No client secret provided, assuming Managed Identity authentication");
}
Expand Down
18 changes: 10 additions & 8 deletions AzureKeyVault/Jobs/Management.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
using Keyfactor.Orchestrators.Extensions.Interfaces;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Security.AccessControl;

namespace Keyfactor.Extensions.Orchestrator.AzureKeyVault
{
Expand All @@ -42,15 +41,18 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
{
Result = OrchestratorJobStatusJobResult.Failure,
FailureMessage = "Invalid Management Operation"
};

string tagsJSON;
bool preserveTags;
};

logger.LogTrace("parsing entry parameters.. ");

tagsJSON = config.JobProperties[EntryParameters.TAGS] as string ?? string.Empty;
preserveTags = config.JobProperties[EntryParameters.PRESERVE_TAGS] as bool? ?? false;
string tagsJSON = string.Empty;
bool preserveTags = false;
if (config.JobProperties != null)
{
config.JobProperties.TryGetValue(EntryParameters.TAGS, out object tagsJSONObj);
config.JobProperties.TryGetValue(EntryParameters.PRESERVE_TAGS, out object preserveTagsObj);
tagsJSON = tagsJSONObj == null ? string.Empty : tagsJSONObj.ToString();
preserveTags = preserveTagsObj == null ? false : Boolean.Parse(preserveTagsObj.ToString());
}

switch (config.OperationType)
{
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
- 3.1.11
- bug fix for error when creating new Azure Keyvaults
- documentation updates

- 3.1.10
- bug fix for government cloud host name resolution

- 3.1.9
- Added optional entry parameter to indicate that existing tags should be preserved if certificate is replaced
- bug fix for government cloud host name resolution
Expand Down
63 changes: 60 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ The high level steps required to configure the Azure Keyvault Orchestrator exten

1) [Configure the Azure Keyvault for client access](#configure-the-azure-keyvault-for-client-access)

1) [Create the Store Type in Keyfactor](#create-the-akv-certificate-store-type)
1) [Create the Store Type in Keyfactor](#akv-certificate-store-type)

1) [Install the Extension on the Orchestrator](#installation)

Expand Down Expand Up @@ -544,7 +544,7 @@ To use the Azure Key Vault Universal Orchestrator extension, you **must** create


The Azure Keyvault Certificate Store Type is designed to integrate with Microsoft Azure Key Vault, enabling users to
manage and automate the lifecycle of cryptographic certificates stored in Azure Key Vault through Keyfactor Command.
manage and automate the lifecycle of cryptographic certificates stored in Azure Keyvault through Keyfactor Command.
This Certificate Store Type represents the connection and configuration necessary to interact with specific instances of
Azure Key Vault, allowing for operations such as inventory, addition, removal, and discovery of certificates and
certificate stores.
Expand All @@ -565,6 +565,11 @@ However, ensuring that the orchestrator has network access to Azure endpoints is
mindful of these caveats and limitations will help ensure successful deployment and use of the Azure Keyvault
Certificate Store Type within your organization’s security framework.

> :warning:
> The alias you provide when enrolling a certificate will be used as the certificate name in Azure Keyvault.
> Consequently; [it must _only_ contain alphanumeric characters and hyphens](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/resource-name-rules#microsoftkeyvault).
> If you encounter the error "The request URI contains an invalid name" when attempting to perform an enrollment, it is likely due to the use of disallowed characters in the alias.




Expand Down Expand Up @@ -633,7 +638,7 @@ the Keyfactor Command Portal
##### Advanced Tab
| Attribute | Value | Description |
| --------- | ----- | ----- |
| Supports Custom Alias | Optional | Determines if an individual entry within a store can have a custom Alias. |
| Supports Custom Alias | Required | Determines if an individual entry within a store can have a custom Alias. |
| Private Key Handling | Optional | This determines if Keyfactor can send the private key associated with a certificate to the store. Required because IIS certificates without private keys would be invalid. |
| PFX Password Style | Default | 'Default' - PFX password is randomly generated, 'Custom' - PFX password may be specified when the enrollment job is created (Requires the Allow Custom Password application setting to be enabled.) |

Expand All @@ -658,6 +663,44 @@ the Keyfactor Command Portal

![AKV Custom Fields Tab](docsource/images/AKV-custom-fields-store-type-dialog.png)


###### Tenant Id
The ID of the primary Azure Tenant where the KeyVaults are hosted

![AKV Custom Field - TenantId](docsource/images/AKV-custom-field-TenantId-dialog.png)



###### SKU Type
The SKU type for newly created KeyVaults (only needed if needing to create new KeyVaults in your Azure subscription via Command)

![AKV Custom Field - SkuType](docsource/images/AKV-custom-field-SkuType-dialog.png)



###### Vault Region
The Azure Region to put newly created KeyVaults (only needed if needing to create new KeyVaults in your Azure subscription via Command)

![AKV Custom Field - VaultRegion](docsource/images/AKV-custom-field-VaultRegion-dialog.png)



###### Azure Cloud
The Azure Cloud where the KeyVaults are located (only necessary if not using the standard Azure Public cloud)

![AKV Custom Field - AzureCloud](docsource/images/AKV-custom-field-AzureCloud-dialog.png)



###### Private KeyVault Endpoint
The private endpoint of your vault instance (if a private endpoint is configured in Azure)

![AKV Custom Field - PrivateEndpoint](docsource/images/AKV-custom-field-PrivateEndpoint-dialog.png)





##### Entry Parameters Tab

| Name | Display Name | Description | Type | Default Value | Entry has a private key | Adding an entry | Removing an entry | Reenrolling an entry |
Expand All @@ -669,6 +712,20 @@ the Keyfactor Command Portal

![AKV Entry Parameters Tab](docsource/images/AKV-entry-parameters-store-type-dialog.png)


##### Certificate Tags
If desired, tags can be applied to the KeyVault entries. Provide them as a JSON string of key-value pairs ie: '{'tag-name': 'tag-content', 'other-tag-name': 'other-tag-content'}'

![AKV Entry Parameter - CertificateTags](docsource/images/AKV-entry-parameters-store-type-dialog-CertificateTags.png)


##### Preserve Existing Tags
If true, this will perform a union of any tags provided with enrollment with the tags on the existing cert with the same alias and apply the result to the new certificate.

![AKV Entry Parameter - PreserveExistingTags](docsource/images/AKV-entry-parameters-store-type-dialog-PreserveExistingTags.png)



</details>

## Installation
Expand Down
7 changes: 6 additions & 1 deletion docsource/akv.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## Overview

The Azure Keyvault Certificate Store Type is designed to integrate with Microsoft Azure Key Vault, enabling users to
manage and automate the lifecycle of cryptographic certificates stored in Azure Key Vault through Keyfactor Command.
manage and automate the lifecycle of cryptographic certificates stored in Azure Keyvault through Keyfactor Command.
This Certificate Store Type represents the connection and configuration necessary to interact with specific instances of
Azure Key Vault, allowing for operations such as inventory, addition, removal, and discovery of certificates and
certificate stores.
Expand All @@ -22,3 +22,8 @@ However, ensuring that the orchestrator has network access to Azure endpoints is
mindful of these caveats and limitations will help ensure successful deployment and use of the Azure Keyvault
Certificate Store Type within your organization’s security framework.

> :warning:
> The alias you provide when enrolling a certificate will be used as the certificate name in Azure Keyvault.
> Consequently; [it must _only_ contain alphanumeric characters and hyphens](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/resource-name-rules#microsoftkeyvault).
> If you encounter the error "The request URI contains an invalid name" when attempting to perform an enrollment, it is likely due to the use of disallowed characters in the alias.

2 changes: 1 addition & 1 deletion docsource/content.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ The high level steps required to configure the Azure Keyvault Orchestrator exten

1) [Configure the Azure Keyvault for client access](#configure-the-azure-keyvault-for-client-access)

1) [Create the Store Type in Keyfactor](#create-the-akv-certificate-store-type)
1) [Create the Store Type in Keyfactor](#akv-certificate-store-type)

1) [Install the Extension on the Orchestrator](#installation)

Expand Down
Binary file modified docsource/images/AKV-advanced-store-type-dialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docsource/images/AKV-basic-store-type-dialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docsource/images/AKV-custom-fields-store-type-dialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docsource/images/AKV-entry-parameters-store-type-dialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion integration-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"BlueprintAllowed": false,
"Capability": "AKV",
"ClientMachineDescription": "The GUID of the tenant ID of the Azure Keyvault instance; for example, '12345678-1234-1234-1234-123456789abc'.",
"CustomAliasAllowed": "Optional",
"CustomAliasAllowed": "Required",
"EntryParameters": [
{
"Name": "CertificateTags",
Expand Down
Loading