Skip to content
Merged
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
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
2.4.2
* Correct false positive error when completing an IIS inventory job.
* Revert to specifying the version of PowerShell to use when establishing a local PowerShell Runspace.
* Fixed typo in error message.

2.4.1
* Modified the CertUtil logic to use the -addstore argument when no password is sent with the certificate information.
* Added additional error trapping and trace logs

2.4.0
* Changed the way certificates are added to cert stores. CertUtil is now used to import the PFX certificate into the associated store. The CSP is now considered when maintaining certificates, empty CSP values will result in using the machines default CSP.
* Added the Crypto Service Provider and SAN Entry Parameters to be used on Inventory queries, Adding and ReEnrollments for the WinCert, WinSQL and IISU extensions.
* Changed how Client Machine Names are handled when a 'localhost' connection is desiered. The new naming convention is: {machineName}|localmachine. This will eliminate the issue of unqiue naming conflicts.
* Changed how Client Machine Names are handled when a 'localhost' connection is desired. The new naming convention is: {machineName}|localmachine. This will eliminate the issue of unique naming conflicts.
* Updated the manifest.json to now include WinSQL ReEnrollment.
* Updated the integration-manifest.json file for new fields in cert store types.

Expand Down Expand Up @@ -49,7 +54,7 @@
2.0.0
* Add support for reenrollment jobs (On Device Key Generation) with the ability to specify a cryptographic provider. Specification of cryptographic provider allows HSM (Hardware Security Module) use.
* Local PAM Support added (requires Universal Orchestrator Framework version 10.1)
* Certificate store type changed from IISBin to IISU. See readme for migration notes.
* Certificate store type changed from IISBin to IISU. See README for migration notes.


1.1.3
Expand Down
6 changes: 5 additions & 1 deletion IISU/ClientPSCertStoreInventory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ public List<Certificate> GetCertificatesFromStore(Runspace runSpace, string stor

ps.AddScript(certStoreScript);

_logger.LogTrace($"Executing the following script:\n{certStoreScript}");

var certs = ps.Invoke();

foreach (var c in certs)
Expand All @@ -77,11 +79,13 @@ public List<Certificate> GetCertificatesFromStore(Runspace runSpace, string stor
SAN = Certificate.Utilities.FormatSAN($"{c.Properties["san"]?.Value}")
});
}

_logger.LogTrace($"found: {myCertificates.Count} certificate(s), exiting GetCertificatesFromStore()");
return myCertificates;
}
catch (Exception ex)
{
_logger.LogTrace($"An error occurred in the WinCert GetCertificatesFromStore method:\n{ex.Message}");

throw new CertificateStoreException(
$"Error listing certificate in {storePath} store on {runSpace.ConnectionInfo.ComputerName}: {ex.Message}");
}
Expand Down
41 changes: 18 additions & 23 deletions IISU/ClientPSCertStoreManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ public JobResult ImportPFXFile(string filePath, string privateKeyPassword, strin
// If no private key password is provided, import the pfx file directory to the store using addstore argument
string script = @"
param($pfxFilePath, $storePath)
$output = certutil -addstore $storePath $pfxFilePath 2>&1
$output = certutil -f -addstore $storePath $pfxFilePath 2>&1
$exit_message = ""LASTEXITCODE:$($LASTEXITCODE)""

if ($output.GetType().Name -eq ""String"")
Expand All @@ -156,20 +156,19 @@ public JobResult ImportPFXFile(string filePath, string privateKeyPassword, strin
// Use ImportPFX to import the pfx file with private key password to the appropriate cert store

string script = @"
param($pfxFilePath, $privateKeyPassword)
$output = certutil -importpfx -p $privateKeyPassword $storePath $pfxFilePath 2>&1
param($pfxFilePath, $privateKeyPassword, $storePath)
$output = certutil -f -importpfx -p $privateKeyPassword $storePath $pfxFilePath 2>&1
$exit_message = ""LASTEXITCODE:$($LASTEXITCODE)""
$stuff = certutil -dump
if ($stuff.GetType().Name -eq ""String"")

if ($output.GetType().Name -eq ""String"")
{
$stuff = @($stuff, $exit_message)
$output = @($output, $exit_message)
}
else
{
$stuff += $exit_message
$output += $exit_message
}
$output
$stuff
";

ps.AddScript(script);
Expand All @@ -184,20 +183,18 @@ public JobResult ImportPFXFile(string filePath, string privateKeyPassword, strin
{
string script = @"
param($pfxFilePath, $cspName, $storePath)
$output = certutil -csp $cspName -addstore $storePath $pfxFilePath 2>&1
$output = certutil -f -csp $cspName -addstore $storePath $pfxFilePath 2>&1
$exit_message = ""LASTEXITCODE:$($LASTEXITCODE)""

$stuff = certutil -dump
if ($stuff.GetType().Name -eq ""String"")
if ($output.GetType().Name -eq ""String"")
{
$stuff = @($stuff, $exit_message)
$output = @($output, $exit_message)
}
else
{
$stuff += $exit_message
$output += $exit_message
}
$output
$stuff
";

ps.AddScript(script);
Expand All @@ -208,21 +205,19 @@ public JobResult ImportPFXFile(string filePath, string privateKeyPassword, strin
else
{
string script = @"
param($pfxFilePath, $privateKeyPassword, $cspName)
$output = certutil -importpfx -csp $cspName -p $privateKeyPassword $storePath $pfxFilePath 2>&1
param($pfxFilePath, $privateKeyPassword, $cspName, $storePath)
$output = certutil -f -importpfx -csp $cspName -p $privateKeyPassword $storePath $pfxFilePath 2>&1
$exit_message = ""LASTEXITCODE:$($LASTEXITCODE)""

$stuff = certutil -dump
if ($stuff.GetType().Name -eq ""String"")
if ($output.GetType().Name -eq ""String"")
{
$stuff = @($stuff, $exit_message)
$output = @($output, $exit_message)
}
else
{
$stuff += $exit_message
$output += $exit_message
}
$output
$stuff
";

ps.AddScript(script);
Expand All @@ -244,9 +239,9 @@ public JobResult ImportPFXFile(string filePath, string privateKeyPassword, strin
lastExitCode = GetLastExitCode(results[^1].ToString());
_logger.LogTrace($"Last exit code: {lastExitCode}");
}
catch (Exception)
catch (Exception ex)
{
_logger.LogTrace("Unable to get the last exit code.");
_logger.LogTrace(ex.Message);
}


Expand Down
4 changes: 4 additions & 0 deletions IISU/ImplementedStoreTypes/Win/WinInventory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinCert
{
internal class WinInventory : ClientPSCertStoreInventory
{
private ILogger _logger;
public WinInventory(ILogger logger) : base(logger)
{
_logger = logger;
}

public List<CurrentInventoryItem> GetInventoryItems(Runspace runSpace, string storePath)
{
_logger.LogTrace("Entering WinCert GetInventoryItems.");
List<CurrentInventoryItem> inventoryItems = new List<CurrentInventoryItem>();

foreach (Certificate cert in base.GetCertificatesFromStore(runSpace, storePath))
Expand All @@ -50,6 +53,7 @@ public List<CurrentInventoryItem> GetInventoryItems(Runspace runSpace, string st
});
}

_logger.LogTrace($"Found {inventoryItems.Count} certificates. Exiting WinCert GetInventoryItems.");
return inventoryItems;
}
}
Expand Down
6 changes: 3 additions & 3 deletions IISU/ImplementedStoreTypes/WinIIS/Inventory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@ private JobResult PerformInventory(InventoryJobConfiguration config, SubmitInven
WinIISInventory IISInventory = new WinIISInventory(_logger);
inventoryItems = IISInventory.GetInventoryItems(myRunspace, storePath);

_logger.LogTrace($"A total of {inventoryItems.Count} were found");
_logger.LogTrace($"A total of {inventoryItems.Count} bound certificate(s) were found");
_logger.LogTrace("Closing runspace...");
myRunspace.Close();

_logger.LogTrace("Invoking Inventory..");
_logger.LogTrace("Invoking submitInventory..");
submitInventory.Invoke(inventoryItems);
_logger.LogTrace($"Inventory Invoked... {inventoryItems.Count} Items");
_logger.LogTrace($"submitInventory Invoked... {inventoryItems.Count} Items");

return new JobResult
{
Expand Down
2 changes: 1 addition & 1 deletion IISU/ImplementedStoreTypes/WinIIS/Management.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
{
_logger.LogTrace(LogHandler.FlattenException(ex));

var failureMessage = $"Managemenmt job {config.OperationType} failed for Site '{config.CertificateStoreDetails.StorePath}' on server '{config.CertificateStoreDetails.ClientMachine}' with error: '{LogHandler.FlattenException(ex)}'";
var failureMessage = $"Management job {config.OperationType} failed for Site '{config.CertificateStoreDetails.StorePath}' on server '{config.CertificateStoreDetails.ClientMachine}' with error: '{LogHandler.FlattenException(ex)}'";
_logger.LogWarning(failureMessage);

return new JobResult
Expand Down
26 changes: 22 additions & 4 deletions IISU/ImplementedStoreTypes/WinIIS/WinIISInventory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@ namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.IISU
{
internal class WinIISInventory : ClientPSCertStoreInventory
{
private ILogger _logger;
public WinIISInventory(ILogger logger) : base(logger)
{
_logger = logger;
}

public List<CurrentInventoryItem> GetInventoryItems(Runspace runSpace, string storePath)
{
_logger.LogTrace("Entering IISU GetInventoryItems");
// Get the raw certificate inventory from cert store
List<Certificate> certificates = base.GetCertificatesFromStore(runSpace, storePath);

Expand All @@ -51,22 +54,36 @@ public List<CurrentInventoryItem> GetInventoryItems(Runspace runSpace, string st
}
else
{
ps2.AddScript("Set-ExecutionPolicy RemoteSigned");
ps2.AddScript("Set-ExecutionPolicy RemoteSigned -Scope Process -Force");
ps2.AddScript("Import-Module WebAdministration");
//var result = ps.Invoke();
}

var searchScript = "Foreach($Site in get-website) { Foreach ($Bind in $Site.bindings.collection) {[pscustomobject]@{name=$Site.name;Protocol=$Bind.Protocol;Bindings=$Bind.BindingInformation;thumbprint=$Bind.certificateHash;sniFlg=$Bind.sslFlags}}}";
ps2.AddScript(searchScript);
var iisBindings = ps2.Invoke(); // Responsible for getting all bound certificates for each website

_logger.LogTrace($"Attempting to initiate the following script:\n{searchScript}");

var iisBindings = ps2.Invoke();

if (ps2.HadErrors)
{
var psError = ps2.Streams.Error.ReadAll().Aggregate(String.Empty, (current, error) => current + error.ErrorDetails.Message);
_logger.LogTrace("The previous script encountered errors. See below for more info.");
string psError = string.Empty;
try
{
psError = ps2.Streams.Error.ReadAll().Aggregate(String.Empty, (current, error) => current + (error.ErrorDetails?.Message ?? error.Exception.ToString()));
}
catch
{
}

if (psError != null) { throw new Exception(psError); }

}

if (iisBindings.Count == 0)
{
_logger.LogTrace("No binding certificates were found. Exiting IISU GetInventoryItems.");
return myBoundCerts;
}

Expand Down Expand Up @@ -123,6 +140,7 @@ public List<CurrentInventoryItem> GetInventoryItems(Runspace runSpace, string st
}
}

_logger.LogTrace($"Found {myBoundCerts.Count} bound certificates. Exiting IISU GetInventoryItems.");
return myBoundCerts;
}
}
Expand Down
2 changes: 1 addition & 1 deletion IISU/ImplementedStoreTypes/WinSQL/Management.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
{
_logger.LogTrace(LogHandler.FlattenException(ex));

var failureMessage = $"Managemenmt job {config.OperationType} failed for Site '{config.CertificateStoreDetails.StorePath}' on server '{config.CertificateStoreDetails.ClientMachine}' with error: '{LogHandler.FlattenException(ex)}'";
var failureMessage = $"Management job {config.OperationType} failed for Site '{config.CertificateStoreDetails.StorePath}' on server '{config.CertificateStoreDetails.ClientMachine}' with error: '{LogHandler.FlattenException(ex)}'";
_logger.LogWarning(failureMessage);

return new JobResult
Expand Down
10 changes: 7 additions & 3 deletions IISU/PSHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public static Runspace GetClientPsRunspace(string winRmProtocol, string clientMa
_logger.MethodEntry();

// 2.4 - Client Machine Name now follows the naming conventions of {clientMachineName}|{localMachine}
// If the clientMachineName is just 'localhost', it will maintain that as locally only (as previosuly)
// If the clientMachineName is just 'localhost', it will maintain that as locally only (as previously)
// If there is no 2nd part to the clientMachineName, a remote PowerShell session will be created

// Break the clientMachineName into parts
Expand All @@ -43,14 +43,18 @@ public static Runspace GetClientPsRunspace(string winRmProtocol, string clientMa
string machineName = parts.Length > 1 ? parts[0] : clientMachineName;
string argument = parts.Length > 1 ? parts[1] : null;

// Determine if this is truely a local connection
// Determine if this is truly a local connection
bool isLocal = (machineName.ToLower() == "localhost") || (argument != null && argument.ToLower() == "localmachine");

_logger.LogInformation($"Full clientMachineName={clientMachineName} | machineName={machineName} | argument={argument} | isLocal={isLocal}");

if (isLocal)
{
return RunspaceFactory.CreateRunspace();
//return RunspaceFactory.CreateRunspace();
PowerShellProcessInstance instance = new PowerShellProcessInstance(new Version(5, 1), null, null, false);
Runspace rs = RunspaceFactory.CreateOutOfProcessRunspace(new TypeTable(Array.Empty<string>()), instance);

return rs;
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion IISU/WindowsCertStore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<ItemGroup>
<PackageReference Include="Keyfactor.Logging" Version="1.1.1" />
<PackageReference Include="Keyfactor.Orchestrators.IOrchestratorJobExtensions" Version="0.7.0" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.2.12" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.2.19" />

</ItemGroup>

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,9 @@ Click Save to save the Certificate Store Type.
## Creating New Certificate Stores
Once the Certificate Store Types have been created, you need to create the Certificate Stores prior to using the extension.

**Note:** A new naming convention for the Client Machine allows for multiple stores on the same server with different cert store path and cert store types. This convention is \{MachineName\}\|\{[optional]localmachine\}. If the optional value is 'localmachine' (legacy 'localhost' is still supported) is supplied, a local PowerShell runspace executing in the context of the Orchestrator service account will be used to access the certificate store.
### Note Regarding Client Machine
If running as an agent (accessing stores on the server where the Universal Orchestrator Services is installed ONLY), the Client Machine can be entered, OR you can bypass a WinRM connection and access the local file system directly by adding "|LocalMachine" to the end of your value for Client Machine, for example "1.1.1.1|LocalMachine". In this instance the value to the left of the pipe (|) is ignored. It is important to make sure the values for Client Machine and Store Path together are unique for each certificate store created, as Keyfactor Command requires the Store Type you select, along with Client Machine, and Store Path together must be unique. To ensure this, it is good practice to put the full DNS or IP Address to the left of the | character when setting up a certificate store that will be accessed without a WinRM connection.

Here are the settings required for each Store Type previously configured.

<details>
Expand Down
Loading