Skip to content

Commit 13affc2

Browse files
Gsma Support
* Refactored code adding Windows cert store logic, including renaming IISU to WinIIS. * Added PowerShell class to perform get-childitem from cert store * Refactored code allowing multiple types of Cert Stores, including Win Cert and IIS (WebHosting) Cert Stores. * Fixed a problem adding certs to a cert store that had a space in the name (ie. Remote Desktop) * Removed logging of PAM credentials which was logging the info in plain text. (#55) * Created custom Configuration Property Parser (#57) * Created custom Configuration Property Parser so not to display or log passwords. * Masked Private Ket Password * Modified logging to write out as JSON object * adding store-type definitions for `WinCert` and `IISU` * Fixed missing parameter error when writing out settings to log file. * Added GMSA functionality * Hook Up Testing Program * When localhost, no remote connection will be established. Only local powershell workspace will be created. * Fix dictionary error (#67) * Fixed missing parameter error when writing out settings to log file. * Make test cases in readme collapsible * Fix casing on Username * Added description for site name error when issuing a WinCert job. * Fix dictionary error (#67) * Fixed missing parameter error when writing out settings to log file. * Tested 'localhost' connections so not to use WinRM. * Testing issues creating local runspace * Use out of process PowerShell when running locally * release_dir updated to reflect new build paths for net6.0 * Updated change log to reflect when using 'locahost' no WinRm session will be used. * Updated the comment for local PowerShell support and removed a redundant comment from the change log. * Clarify version needed for GMSA
1 parent 89759d7 commit 13affc2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1301
-499
lines changed

.github/workflows/keyfactor-starter-workflow.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
with:
2525
release_version: ${{ needs.call-create-github-release-workflow.outputs.release_version }}
2626
release_url: ${{ needs.call-create-github-release-workflow.outputs.release_url }}
27-
release_dir: IISU/bin/Release/netcoreapp3.1
27+
release_dir: IISU/bin/Release/net6.0
2828
secrets:
2929
token: ${{ secrets.PRIVATE_PACKAGE_ACCESS }}
3030

CHANGELOG.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
1+
2.2.0
2+
* 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.
3+
* 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
4+
* Moved to .NET 6
5+
6+
2.1.1
7+
* Fixed the missing site name error when issuing a WinCert job when writing trace log settings to the log file.
8+
* 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.
9+
* Display name for IISU changed to "IIS Bound Certificate".
10+
* Display name for WinCert changed to "Windows Certificate".
11+
* Display names for several Store and Entry parameters changed to be more descriptive and UI friendly.
12+
* Significant readme cleanup
13+
114
2.1.0
2-
* Fixed issue that was occuring during renewal when there were bindings outside of http and https like net.tcp
15+
* Fixed issue that was occurring during renewal when there were bindings outside of http and https like net.tcp
316
* Added PAM registration/initialization documentation in README.md
417
* Resolved Null HostName error
518
* Added WinCert Cert Store Type
@@ -8,7 +21,7 @@
821

922
2.0.0
1023
* 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.
11-
* Local PAM Support added (requires Univesal Orchestrator Framework version 10.1)
24+
* Local PAM Support added (requires Universal Orchestrator Framework version 10.1)
1225
* Certificate store type changed from IISBin to IISU. See readme for migration notes.
1326

1427

IISU/ClientPSCertStoreReEnrollment.cs

Lines changed: 48 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,16 @@
2121
using System.Collections.ObjectModel;
2222
using System.Management.Automation.Runspaces;
2323
using System.Management.Automation;
24+
using System.Management.Automation.Remoting;
2425
using System.Net;
2526
using System.Security.Cryptography.X509Certificates;
2627
using System.Text;
2728
using Microsoft.Extensions.Logging;
2829
using Keyfactor.Orchestrators.Extensions.Interfaces;
2930
using System.Linq;
3031
using System.IO;
31-
32+
using Microsoft.PowerShell;
33+
3234
namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore
3335
{
3436
internal class ClientPSCertStoreReEnrollment
@@ -52,37 +54,28 @@ public JobResult PerformReEnrollment(ReenrollmentJobConfiguration config, Submit
5254
var serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", config.ServerUsername);
5355
var serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", config.ServerPassword);
5456

55-
56-
5757
// Extract values necessary to create remote PS connection
58-
JobProperties properties = JsonConvert.DeserializeObject<JobProperties>(config.CertificateStoreDetails.Properties,
59-
new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
60-
61-
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri($"{properties?.WinRmProtocol}://{config.CertificateStoreDetails.ClientMachine}:{properties?.WinRmPort}/wsman"))
62-
{
63-
IncludePortInSPN = properties.SpnPortFlag
64-
};
65-
var pw = new NetworkCredential(serverUserName, serverPassword).SecurePassword;
66-
_logger.LogTrace($"Credentials: UserName:{serverUserName}");
67-
68-
connectionInfo.Credential = new PSCredential(serverUserName, pw);
69-
_logger.LogTrace($"PSCredential Created {pw}");
70-
71-
// Establish new remote ps session
72-
_logger.LogTrace("Creating remote PS Workspace");
73-
using var runSpace = RunspaceFactory.CreateRunspace(connectionInfo);
74-
_logger.LogTrace("Workspace created");
58+
JobProperties jobProperties = JsonConvert.DeserializeObject<JobProperties>(config.CertificateStoreDetails.Properties,
59+
new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
60+
61+
string protocol = jobProperties.WinRmProtocol;
62+
string port = jobProperties.WinRmPort;
63+
bool IncludePortInSPN = jobProperties.SpnPortFlag;
64+
string clientMachineName = config.CertificateStoreDetails.ClientMachine;
65+
string storePath = config.CertificateStoreDetails.StorePath;
66+
67+
_logger.LogTrace($"Establishing runspace on client machine: {clientMachineName}");
68+
using var runSpace = PsHelper.GetClientPsRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);
69+
70+
_logger.LogTrace("Runspace created");
7571
runSpace.Open();
76-
_logger.LogTrace("Workspace opened");
72+
_logger.LogTrace("Runspace opened");
7773

78-
// NEW
79-
var ps = PowerShell.Create();
74+
PowerShell ps = PowerShell.Create();
8075
ps.Runspace = runSpace;
8176

8277
string CSR = string.Empty;
8378

84-
string storePath = config.CertificateStoreDetails.StorePath;
85-
8679
var subjectText = config.JobProperties["subjectText"];
8780
var providerName = config.JobProperties["ProviderName"];
8881
var keyType = config.JobProperties["keyType"];
@@ -100,23 +93,23 @@ public JobResult PerformReEnrollment(ReenrollmentJobConfiguration config, Submit
10093

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

103-
ps.AddScript($"Set-Content $infFilename [NewRequest]");
104-
ps.AddScript($"Add-Content $infFilename 'Subject = \"{subjectText}\"'");
105-
ps.AddScript($"Add-Content $infFilename 'ProviderName = \"{providerName}\"'");
106-
ps.AddScript($"Add-Content $infFilename 'MachineKeySet = True'");
107-
ps.AddScript($"Add-Content $infFilename 'HashAlgorithm = SHA256'");
108-
ps.AddScript($"Add-Content $infFilename 'KeyAlgorithm = {keyType}'");
109-
ps.AddScript($"Add-Content $infFilename 'KeyLength={keySize}'");
110-
ps.AddScript($"Add-Content $infFilename 'KeySpec = 0'");
96+
ps.AddScript($"Set-Content $infFilename -Value [NewRequest]");
97+
ps.AddScript($"Add-Content $infFilename -Value 'Subject = \"{subjectText}\"'");
98+
ps.AddScript($"Add-Content $infFilename -Value 'ProviderName = \"{providerName}\"'");
99+
ps.AddScript($"Add-Content $infFilename -Value 'MachineKeySet = True'");
100+
ps.AddScript($"Add-Content $infFilename -Value 'HashAlgorithm = SHA256'");
101+
ps.AddScript($"Add-Content $infFilename -Value 'KeyAlgorithm = {keyType}'");
102+
ps.AddScript($"Add-Content $infFilename -Value 'KeyLength={keySize}'");
103+
ps.AddScript($"Add-Content $infFilename -Value 'KeySpec = 0'");
111104

112105
if (SAN != null)
113106
{
114-
ps.AddScript($"Add-Content $infFilename '[Extensions]'");
115-
ps.AddScript(@"Add-Content $infFilename '2.5.29.17 = ""{text}""'");
107+
ps.AddScript($"Add-Content $infFilename -Value '[Extensions]'");
108+
ps.AddScript(@"Add-Content $infFilename -Value '2.5.29.17 = ""{text}""'");
116109

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

@@ -153,7 +146,7 @@ public JobResult PerformReEnrollment(ReenrollmentJobConfiguration config, Submit
153146

154147
try
155148
{
156-
ps.AddScript($"$CSR = Get-Content $csrFilename");
149+
ps.AddScript($"$CSR = Get-Content $csrFilename -Raw");
157150
_logger.LogTrace("Attempting to get the contents of the CSR file.");
158151
results = ps.Invoke();
159152
_logger.LogTrace("Finished getting the CSR Contents.");
@@ -177,14 +170,14 @@ public JobResult PerformReEnrollment(ReenrollmentJobConfiguration config, Submit
177170
results = ps.Invoke();
178171

179172
if (hasError) runSpace.Close();
180-
}
181-
182-
// Get the byte array
183-
var CSRContent = ps.Runspace.SessionStateProxy.GetVariable("CSR").ToString();
173+
}
174+
175+
// Get the byte array
176+
var RawContent = runSpace.SessionStateProxy.GetVariable("CSR");
184177

185178
// Sign CSR in Keyfactor
186179
_logger.LogTrace("Get the signed CSR from KF.");
187-
X509Certificate2 myCert = submitReenrollment.Invoke(CSRContent);
180+
X509Certificate2 myCert = submitReenrollment.Invoke(RawContent.ToString());
188181

189182
if (myCert != null)
190183
{
@@ -257,6 +250,19 @@ public JobResult PerformReEnrollment(ReenrollmentJobConfiguration config, Submit
257250
};
258251
}
259252

253+
}
254+
catch (PSRemotingTransportException psEx)
255+
{
256+
var failureMessage = $"ReEnrollment job failed for Site '{config.CertificateStoreDetails.StorePath}' on server '{config.CertificateStoreDetails.ClientMachine}' with a PowerShell Transport Exception: {psEx.Message}";
257+
_logger.LogError(failureMessage + LogHandler.FlattenException(psEx));
258+
259+
return new JobResult
260+
{
261+
Result = OrchestratorJobStatusJobResult.Failure,
262+
JobHistoryId = config.JobHistoryId,
263+
FailureMessage = failureMessage
264+
};
265+
260266
}
261267
catch (Exception ex)
262268
{

IISU/ClientPSIIManager.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public ClientPSIIManager(ReenrollmentJobConfiguration config, string serverUsern
101101
bool includePortInSPN = jobProperties.SpnPortFlag;
102102

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

146146
_logger.LogTrace($"Establishing runspace on client machine: {ClientMachineName}");
147-
_runSpace = PSHelper.GetClientPSRunspace(winRmProtocol, ClientMachineName, winRmPort, includePortInSPN, serverUsername, serverPassword);
147+
_runSpace = PsHelper.GetClientPsRunspace(winRmProtocol, ClientMachineName, winRmPort, includePortInSPN, serverUsername, serverPassword);
148148
}
149149
catch (Exception e)
150150
{

IISU/ImplementedStoreTypes/Win/Inventory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ private JobResult PerformInventory(InventoryJobConfiguration config, SubmitInven
7171
if (storePath != null)
7272
{
7373
_logger.LogTrace($"Establishing runspace on client machine: {clientMachineName}");
74-
using var myRunspace = PSHelper.GetClientPSRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);
74+
using var myRunspace = PsHelper.GetClientPsRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword);
7575
myRunspace.Open();
7676

7777
_logger.LogTrace("Runspace is now open");

IISU/ImplementedStoreTypes/Win/Management.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
6666
long JobHistoryID = config.JobHistoryId;
6767

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

7171
var complete = new JobResult
7272
{

IISU/ImplementedStoreTypes/WinIIS/Inventory.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,14 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
using System;
16-
using System.Collections.Generic;
17-
using System.Linq;
18-
using System.Management.Automation;
19-
using System.Management.Automation.Runspaces;
20-
using System.Net;
2115
using Keyfactor.Logging;
2216
using Keyfactor.Orchestrators.Common.Enums;
2317
using Keyfactor.Orchestrators.Extensions;
2418
using Keyfactor.Orchestrators.Extensions.Interfaces;
2519
using Microsoft.Extensions.Logging;
2620
using Newtonsoft.Json;
21+
using System;
22+
using System.Collections.Generic;
2723

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

42+
4643
return PerformInventory(jobConfiguration, submitInventoryUpdate);
4744
}
4845

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

7673
_logger.LogTrace("Runspace is now open");
7774
_logger.LogTrace($"Attempting to read bound IIS certificates from cert store: {storePath}");
7875
WinIISInventory IISInventory = new WinIISInventory(_logger);
7976
inventoryItems = IISInventory.GetInventoryItems(myRunspace, storePath);
80-
77+
8178
_logger.LogTrace($"A total of {inventoryItems.Count} were found");
8279
_logger.LogTrace("Closing runspace...");
8380
myRunspace.Close();

IISU/ImplementedStoreTypes/WinIIS/Management.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
6161
long JobHistoryID = config.JobHistoryId;
6262

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

6666
var complete = new JobResult
6767
{

IISU/ImplementedStoreTypes/WinIIS/WinIISInventory.cs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using Keyfactor.Orchestrators.Common.Enums;
1616
using Keyfactor.Orchestrators.Extensions;
1717
using Microsoft.Extensions.Logging;
18+
using Microsoft.PowerShell;
1819
using System;
1920
using System.Collections.Generic;
2021
using System.Linq;
@@ -38,21 +39,30 @@ public List<CurrentInventoryItem> GetInventoryItems(Runspace runSpace, string st
3839
// Contains the inventory items to be sent back to KF
3940
List<CurrentInventoryItem> myBoundCerts = new List<CurrentInventoryItem>();
4041

41-
using (var ps = PowerShell.Create())
42+
using (PowerShell ps2 = PowerShell.Create())
4243
{
43-
ps.Runspace = runSpace;
44+
ps2.Runspace = runSpace;
4445

45-
ps.AddCommand("Import-Module")
46-
.AddParameter("Name", "WebAdministration")
47-
.AddStatement();
46+
if (runSpace.RunspaceIsRemote)
47+
{
48+
ps2.AddCommand("Import-Module")
49+
.AddParameter("Name", "WebAdministration")
50+
.AddStatement();
51+
}
52+
else
53+
{
54+
ps2.AddScript("Set-ExecutionPolicy RemoteSigned");
55+
ps2.AddScript("Import-Module WebAdministration");
56+
//var result = ps.Invoke();
57+
}
4858

4959
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}}}";
50-
ps.AddScript(searchScript).AddStatement();
51-
var iisBindings = ps.Invoke(); // Responsible for getting all bound certificates for each website
60+
ps2.AddScript(searchScript);
61+
var iisBindings = ps2.Invoke(); // Responsible for getting all bound certificates for each website
5262

53-
if (ps.HadErrors)
63+
if (ps2.HadErrors)
5464
{
55-
var psError = ps.Streams.Error.ReadAll().Aggregate(String.Empty, (current, error) => current + error.ErrorDetails.Message);
65+
var psError = ps2.Streams.Error.ReadAll().Aggregate(String.Empty, (current, error) => current + error.ErrorDetails.Message);
5666
}
5767

5868
if (iisBindings.Count == 0)

0 commit comments

Comments
 (0)