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
2 changes: 1 addition & 1 deletion .github/workflows/keyfactor-starter-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
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: IISU/bin/Release/netcoreapp3.1
release_dir: IISU/bin/Release/net6.0
secrets:
token: ${{ secrets.PRIVATE_PACKAGE_ACCESS }}

Expand Down
17 changes: 15 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
2.2.0
* Added Support for GMSA Account by using no value for ServerUsernanme and ServerPassword. KF Command version 10.2 or later is required to specify empty credentials.
* Added local PowerShell support, triggered when specifying 'localhost' as the client machine while using the IISU or WinCert Orchestrator. This change was tested using KF Command 10.3
* Moved to .NET 6

2.1.1
* Fixed the missing site name error when issuing a WinCert job when writing trace log settings to the log file.
* Several display names changed in the documented certificate store type definitions. There are no changes to the internal type or parameter names, so no migration is necessary for currently configured stores.
* Display name for IISU changed to "IIS Bound Certificate".
* Display name for WinCert changed to "Windows Certificate".
* Display names for several Store and Entry parameters changed to be more descriptive and UI friendly.
* Significant readme cleanup

2.1.0
* Fixed issue that was occuring during renewal when there were bindings outside of http and https like net.tcp
* Fixed issue that was occurring during renewal when there were bindings outside of http and https like net.tcp
* Added PAM registration/initialization documentation in README.md
* Resolved Null HostName error
* Added WinCert Cert Store Type
Expand All @@ -8,7 +21,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 Univesal Orchestrator Framework version 10.1)
* Local PAM Support added (requires Universal Orchestrator Framework version 10.1)
* Certificate store type changed from IISBin to IISU. See readme for migration notes.


Expand Down
90 changes: 48 additions & 42 deletions IISU/ClientPSCertStoreReEnrollment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@
using System.Collections.ObjectModel;
using System.Management.Automation.Runspaces;
using System.Management.Automation;
using System.Management.Automation.Remoting;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Microsoft.Extensions.Logging;
using Keyfactor.Orchestrators.Extensions.Interfaces;
using System.Linq;
using System.IO;

using Microsoft.PowerShell;

namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore
{
internal class ClientPSCertStoreReEnrollment
Expand All @@ -52,37 +54,28 @@ public JobResult PerformReEnrollment(ReenrollmentJobConfiguration config, Submit
var serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", config.ServerUsername);
var serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", config.ServerPassword);



// Extract values necessary to create remote PS connection
JobProperties properties = JsonConvert.DeserializeObject<JobProperties>(config.CertificateStoreDetails.Properties,
new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });

WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri($"{properties?.WinRmProtocol}://{config.CertificateStoreDetails.ClientMachine}:{properties?.WinRmPort}/wsman"))
{
IncludePortInSPN = properties.SpnPortFlag
};
var pw = new NetworkCredential(serverUserName, serverPassword).SecurePassword;
_logger.LogTrace($"Credentials: UserName:{serverUserName}");

connectionInfo.Credential = new PSCredential(serverUserName, pw);
_logger.LogTrace($"PSCredential Created {pw}");

// Establish new remote ps session
_logger.LogTrace("Creating remote PS Workspace");
using var runSpace = RunspaceFactory.CreateRunspace(connectionInfo);
_logger.LogTrace("Workspace created");
JobProperties jobProperties = JsonConvert.DeserializeObject<JobProperties>(config.CertificateStoreDetails.Properties,
new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });

string protocol = jobProperties.WinRmProtocol;
string port = jobProperties.WinRmPort;
bool IncludePortInSPN = jobProperties.SpnPortFlag;
string clientMachineName = config.CertificateStoreDetails.ClientMachine;
string storePath = config.CertificateStoreDetails.StorePath;

_logger.LogTrace($"Establishing runspace on client machine: {clientMachineName}");
using var runSpace = PsHelper.GetClientPsRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);

_logger.LogTrace("Runspace created");
runSpace.Open();
_logger.LogTrace("Workspace opened");
_logger.LogTrace("Runspace opened");

// NEW
var ps = PowerShell.Create();
PowerShell ps = PowerShell.Create();
ps.Runspace = runSpace;

string CSR = string.Empty;

string storePath = config.CertificateStoreDetails.StorePath;

var subjectText = config.JobProperties["subjectText"];
var providerName = config.JobProperties["ProviderName"];
var keyType = config.JobProperties["keyType"];
Expand All @@ -100,23 +93,23 @@ public JobResult PerformReEnrollment(ReenrollmentJobConfiguration config, Submit

ps.AddScript("if (Test-Path $csrFilename) { Remove-Item $csrFilename }");

ps.AddScript($"Set-Content $infFilename [NewRequest]");
ps.AddScript($"Add-Content $infFilename 'Subject = \"{subjectText}\"'");
ps.AddScript($"Add-Content $infFilename 'ProviderName = \"{providerName}\"'");
ps.AddScript($"Add-Content $infFilename 'MachineKeySet = True'");
ps.AddScript($"Add-Content $infFilename 'HashAlgorithm = SHA256'");
ps.AddScript($"Add-Content $infFilename 'KeyAlgorithm = {keyType}'");
ps.AddScript($"Add-Content $infFilename 'KeyLength={keySize}'");
ps.AddScript($"Add-Content $infFilename 'KeySpec = 0'");
ps.AddScript($"Set-Content $infFilename -Value [NewRequest]");
ps.AddScript($"Add-Content $infFilename -Value 'Subject = \"{subjectText}\"'");
ps.AddScript($"Add-Content $infFilename -Value 'ProviderName = \"{providerName}\"'");
ps.AddScript($"Add-Content $infFilename -Value 'MachineKeySet = True'");
ps.AddScript($"Add-Content $infFilename -Value 'HashAlgorithm = SHA256'");
ps.AddScript($"Add-Content $infFilename -Value 'KeyAlgorithm = {keyType}'");
ps.AddScript($"Add-Content $infFilename -Value 'KeyLength={keySize}'");
ps.AddScript($"Add-Content $infFilename -Value 'KeySpec = 0'");

if (SAN != null)
{
ps.AddScript($"Add-Content $infFilename '[Extensions]'");
ps.AddScript(@"Add-Content $infFilename '2.5.29.17 = ""{text}""'");
ps.AddScript($"Add-Content $infFilename -Value '[Extensions]'");
ps.AddScript(@"Add-Content $infFilename -Value '2.5.29.17 = ""{text}""'");

foreach (string s in SAN.ToString().Split("&"))
{
ps.AddScript($"Add-Content $infFilename '_continue_ = \"{s + "&"}\"'");
ps.AddScript($"Add-Content $infFilename -Value '_continue_ = \"{s + "&"}\"'");
}
}

Expand Down Expand Up @@ -153,7 +146,7 @@ public JobResult PerformReEnrollment(ReenrollmentJobConfiguration config, Submit

try
{
ps.AddScript($"$CSR = Get-Content $csrFilename");
ps.AddScript($"$CSR = Get-Content $csrFilename -Raw");
_logger.LogTrace("Attempting to get the contents of the CSR file.");
results = ps.Invoke();
_logger.LogTrace("Finished getting the CSR Contents.");
Expand All @@ -177,14 +170,14 @@ public JobResult PerformReEnrollment(ReenrollmentJobConfiguration config, Submit
results = ps.Invoke();

if (hasError) runSpace.Close();
}

// Get the byte array
var CSRContent = ps.Runspace.SessionStateProxy.GetVariable("CSR").ToString();
}
// Get the byte array
var RawContent = runSpace.SessionStateProxy.GetVariable("CSR");

// Sign CSR in Keyfactor
_logger.LogTrace("Get the signed CSR from KF.");
X509Certificate2 myCert = submitReenrollment.Invoke(CSRContent);
X509Certificate2 myCert = submitReenrollment.Invoke(RawContent.ToString());

if (myCert != null)
{
Expand Down Expand Up @@ -257,6 +250,19 @@ public JobResult PerformReEnrollment(ReenrollmentJobConfiguration config, Submit
};
}

}
catch (PSRemotingTransportException psEx)
{
var failureMessage = $"ReEnrollment job failed for Site '{config.CertificateStoreDetails.StorePath}' on server '{config.CertificateStoreDetails.ClientMachine}' with a PowerShell Transport Exception: {psEx.Message}";
_logger.LogError(failureMessage + LogHandler.FlattenException(psEx));

return new JobResult
{
Result = OrchestratorJobStatusJobResult.Failure,
JobHistoryId = config.JobHistoryId,
FailureMessage = failureMessage
};

}
catch (Exception ex)
{
Expand Down
4 changes: 2 additions & 2 deletions IISU/ClientPSIIManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public ClientPSIIManager(ReenrollmentJobConfiguration config, string serverUsern
bool includePortInSPN = jobProperties.SpnPortFlag;

_logger.LogTrace($"Establishing runspace on client machine: {ClientMachineName}");
_runSpace = PSHelper.GetClientPSRunspace(winRmProtocol, ClientMachineName, winRmPort, includePortInSPN, serverUsername, serverPassword);
_runSpace = PsHelper.GetClientPsRunspace(winRmProtocol, ClientMachineName, winRmPort, includePortInSPN, serverUsername, serverPassword);
}
catch (Exception e)
{
Expand Down Expand Up @@ -144,7 +144,7 @@ public ClientPSIIManager(ManagementJobConfiguration config, string serverUsernam
bool includePortInSPN = jobProperties.SpnPortFlag;

_logger.LogTrace($"Establishing runspace on client machine: {ClientMachineName}");
_runSpace = PSHelper.GetClientPSRunspace(winRmProtocol, ClientMachineName, winRmPort, includePortInSPN, serverUsername, serverPassword);
_runSpace = PsHelper.GetClientPsRunspace(winRmProtocol, ClientMachineName, winRmPort, includePortInSPN, serverUsername, serverPassword);
}
catch (Exception e)
{
Expand Down
2 changes: 1 addition & 1 deletion IISU/ImplementedStoreTypes/Win/Inventory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ private JobResult PerformInventory(InventoryJobConfiguration config, SubmitInven
if (storePath != null)
{
_logger.LogTrace($"Establishing runspace on client machine: {clientMachineName}");
using var myRunspace = PSHelper.GetClientPSRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);
using var myRunspace = PsHelper.GetClientPsRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);
myRunspace.Open();

_logger.LogTrace("Runspace is now open");
Expand Down
2 changes: 1 addition & 1 deletion IISU/ImplementedStoreTypes/Win/Management.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
long JobHistoryID = config.JobHistoryId;

_logger.LogTrace($"Establishing runspace on client machine: {clientMachineName}");
myRunspace = PSHelper.GetClientPSRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);
myRunspace = PsHelper.GetClientPsRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);

var complete = new JobResult
{
Expand Down
13 changes: 5 additions & 8 deletions IISU/ImplementedStoreTypes/WinIIS/Inventory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Net;
using Keyfactor.Logging;
using Keyfactor.Orchestrators.Common.Enums;
using Keyfactor.Orchestrators.Extensions;
using Keyfactor.Orchestrators.Extensions.Interfaces;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;

namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.IISU
{
Expand All @@ -43,6 +39,7 @@ public JobResult ProcessJob(InventoryJobConfiguration jobConfiguration, SubmitIn
_logger = LogHandler.GetClassLogger<Inventory>();
_logger.MethodEntry();


return PerformInventory(jobConfiguration, submitInventoryUpdate);
}

Expand Down Expand Up @@ -70,14 +67,14 @@ private JobResult PerformInventory(InventoryJobConfiguration config, SubmitInven
if (storePath != null)
{
_logger.LogTrace($"Establishing runspace on client machine: {clientMachineName}");
using var myRunspace = PSHelper.GetClientPSRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);
using var myRunspace = PsHelper.GetClientPsRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);
myRunspace.Open();

_logger.LogTrace("Runspace is now open");
_logger.LogTrace($"Attempting to read bound IIS certificates from cert store: {storePath}");
WinIISInventory IISInventory = new WinIISInventory(_logger);
inventoryItems = IISInventory.GetInventoryItems(myRunspace, storePath);

_logger.LogTrace($"A total of {inventoryItems.Count} were found");
_logger.LogTrace("Closing runspace...");
myRunspace.Close();
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 @@ -61,7 +61,7 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
long JobHistoryID = config.JobHistoryId;

_logger.LogTrace($"Establishing runspace on client machine: {clientMachineName}");
myRunspace = PSHelper.GetClientPSRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);
myRunspace = PsHelper.GetClientPsRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);

var complete = new JobResult
{
Expand Down
28 changes: 19 additions & 9 deletions IISU/ImplementedStoreTypes/WinIIS/WinIISInventory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Keyfactor.Orchestrators.Common.Enums;
using Keyfactor.Orchestrators.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.PowerShell;
using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -38,21 +39,30 @@ public List<CurrentInventoryItem> GetInventoryItems(Runspace runSpace, string st
// Contains the inventory items to be sent back to KF
List<CurrentInventoryItem> myBoundCerts = new List<CurrentInventoryItem>();

using (var ps = PowerShell.Create())
using (PowerShell ps2 = PowerShell.Create())
{
ps.Runspace = runSpace;
ps2.Runspace = runSpace;

ps.AddCommand("Import-Module")
.AddParameter("Name", "WebAdministration")
.AddStatement();
if (runSpace.RunspaceIsRemote)
{
ps2.AddCommand("Import-Module")
.AddParameter("Name", "WebAdministration")
.AddStatement();
}
else
{
ps2.AddScript("Set-ExecutionPolicy RemoteSigned");
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}}}";
ps.AddScript(searchScript).AddStatement();
var iisBindings = ps.Invoke(); // Responsible for getting all bound certificates for each website
ps2.AddScript(searchScript);
var iisBindings = ps2.Invoke(); // Responsible for getting all bound certificates for each website

if (ps.HadErrors)
if (ps2.HadErrors)
{
var psError = ps.Streams.Error.ReadAll().Aggregate(String.Empty, (current, error) => current + error.ErrorDetails.Message);
var psError = ps2.Streams.Error.ReadAll().Aggregate(String.Empty, (current, error) => current + error.ErrorDetails.Message);
}

if (iisBindings.Count == 0)
Expand Down
Loading