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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
1.1.3
* Made WinRM port a store parameter
* Made WinRM protocol a store parameter
* IISWBin 1.1.3 upgrade script.sql added to upgrade from 1.1.2

1.1.0
* Migrate to Universal Orchestrator (KF9 / .NET Core)
* Perform Renewals using RenewalThumbprint
Expand Down
38 changes: 38 additions & 0 deletions IISWBin 1.1.3 upgrade script.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
DECLARE @iiswbin INT = -1, @iiswibin2 INT = -1

SELECT @iiswbin = [StoreType] FROM [cms_agents].[CertStoreTypes] WHERE [ShortName] = 'IISWBin'
IF NOT @iiswbin = -1
BEGIN
IF NOT EXISTS(SELECT [Id] FROM [cms_agents].[CertStoreTypeProperties] WHERE [StoreTypeId] = @iiswbin AND [Name] = 'WinRm Protocol')
BEGIN
INSERT INTO [cms_agents].[CertStoreTypeProperties]
([StoreTypeId]
,[Name]
,[DisplayName]
,[Type]
,[Required]
,[DependsOn]
,[DefaultValue])
VALUES
(@iiswbin, 'WinRm Protocol', 'WinRm Protocol', 2, 1, '', 'http,https')

END
END

SELECT @iiswibin2 = [StoreType] FROM [cms_agents].[CertStoreTypes] WHERE [ShortName] = 'IISWBin'
IF NOT @iiswibin2 = -1
BEGIN
IF NOT EXISTS(SELECT [Id] FROM [cms_agents].[CertStoreTypeProperties] WHERE [StoreTypeId] = @iiswibin2 AND [Name] = 'WinRm Port')
BEGIN
INSERT INTO [cms_agents].[CertStoreTypeProperties]
([StoreTypeId]
,[Name]
,[DisplayName]
,[Type]
,[Required]
,[DependsOn]
,[DefaultValue])
VALUES
(@iiswibin2, 'WinRm Port', 'WinRm Port',0, 1, '', '5985')
END
END
94 changes: 76 additions & 18 deletions IISWithBindings/Jobs/Inventory.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Net;
using System.Security;
using Keyfactor.Logging;
using Keyfactor.Orchestrators.Common.Enums;
using Keyfactor.Orchestrators.Extensions;
Expand All @@ -23,48 +23,69 @@ private JobResult PerformInventory(InventoryJobConfiguration config, SubmitInven
{
try
{
StorePath storePath = JsonConvert.DeserializeObject<StorePath>(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
_logger.MethodEntry();
_logger.LogTrace($"Job Configuration: {JsonConvert.SerializeObject(config)}");
var storePath = JsonConvert.DeserializeObject<StorePath>(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
var inventoryItems = new List<CurrentInventoryItem>();

_logger.LogTrace($"Begin Inventory for Cert Store {$@"\\{config.CertificateStoreDetails.ClientMachine}\{config.CertificateStoreDetails.StorePath}"}");

WSManConnectionInfo connInfo = new WSManConnectionInfo(new Uri($"http://{config.CertificateStoreDetails.ClientMachine}:5985/wsman"));
var connInfo = new WSManConnectionInfo(new Uri($"{storePath?.WinRmProtocol}://{config.CertificateStoreDetails.ClientMachine}:{storePath?.WinRmPort}/wsman"));
_logger.LogTrace($"WinRm Url: {storePath?.WinRmProtocol}://{config.CertificateStoreDetails.ClientMachine}:{storePath?.WinRmPort}/wsman");

if (storePath != null)
{
SecureString pw = new NetworkCredential(config.ServerUsername, config.ServerPassword)
var pw = new NetworkCredential(config.ServerUsername, config.ServerPassword)
.SecurePassword;
_logger.LogTrace($"Credentials: UserName:{config.ServerUsername} Password:{config.ServerPassword}");
connInfo.Credential = new PSCredential(config.ServerUsername, pw);
_logger.LogTrace($"PSCredential Created {pw}");

using Runspace runSpace = RunspaceFactory.CreateRunspace(connInfo);
using var runSpace = RunspaceFactory.CreateRunspace(connInfo);
_logger.LogTrace("runSpace Created");
runSpace.Open();
PowerShellCertStore psCertStore = new PowerShellCertStore(
_logger.LogTrace("runSpace Opened");

var psCertStore = new PowerShellCertStore(
config.CertificateStoreDetails.ClientMachine, config.CertificateStoreDetails.StorePath,
runSpace);
using (PowerShell ps = PowerShell.Create())
_logger.LogTrace("psCertStore Created");

using (var ps = PowerShell.Create())
{
ps.Runspace = runSpace;
_logger.LogTrace("RunSpace Created");
ps.AddCommand("Import-Module")
.AddParameter("Name", "WebAdministration")
.AddStatement();
_logger.LogTrace("WebAdministration Imported");

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}}}";
_logger.LogTrace($"searchScript {searchScript}");
ps.AddScript(searchScript).AddStatement();
_logger.LogTrace("searchScript added...");
var iisBindings = ps.Invoke();

_logger.LogTrace("iisBindings Created...");

if (ps.HadErrors)
{
_logger.LogTrace("ps Has Errors");
var psError = ps.Streams.Error.ReadAll().Aggregate(String.Empty, (current, error) => current + error.ErrorDetails.Message);

return new JobResult
{
Result = OrchestratorJobStatusJobResult.Failure,
JobHistoryId = config.JobHistoryId,
FailureMessage =
$"Site {config.CertificateStoreDetails.StorePath} on server {config.CertificateStoreDetails.ClientMachine}: failed."
$"Site {config.CertificateStoreDetails.StorePath} on server {config.CertificateStoreDetails.ClientMachine}: failed with Error: {psError}"
};
}

if (iisBindings.Count == 0)
{
_logger.LogTrace("submitInventory About To Be Invoked No Bindings Found");
submitInventory.Invoke(inventoryItems);
_logger.LogTrace("submitInventory Invoked...");
return new JobResult
{
Result = OrchestratorJobStatusJobResult.Warning,
Expand All @@ -77,32 +98,69 @@ private JobResult PerformInventory(InventoryJobConfiguration config, SubmitInven
//in theory should only be one, but keeping for future update to chance inventory
foreach (var binding in iisBindings)
{
_logger.LogTrace("Looping Bindings...");
var thumbPrint = $"{(binding.Properties["thumbprint"]?.Value)}";
_logger.LogTrace($"thumbPrint: {thumbPrint}");
if (string.IsNullOrEmpty(thumbPrint))
continue;

var foundCert = psCertStore.Certificates.Find(m => m.Thumbprint.Equals(thumbPrint));

_logger.LogTrace($"foundCert: {foundCert?.CertificateData}");

if (foundCert == null)
continue;

var sniValue = "";
switch (Convert.ToInt16(binding.Properties["sniFlg"]?.Value))
{
case 0:
sniValue = "0 - No SNI";
break;
case 1:
sniValue = "1 - SNI Enabled";
break;
case 2:
sniValue = "2 - Non SNI Binding";
break;
case 3:
sniValue = "3 - SNI Binding";
break;

}

_logger.LogTrace($"bindingSiteName: {binding.Properties["Name"]?.Value}, bindingIpAddress: {binding.Properties["Bindings"]?.Value.ToString()?.Split(':')[0]}, bindingPort: {binding.Properties["Bindings"]?.Value.ToString()?.Split(':')[1]}, bindingHostName: {binding.Properties["Bindings"]?.Value.ToString()?.Split(':')[2]}, bindingProtocol: {binding.Properties["Protocol"]?.Value}, bindingSniFlg: {sniValue}");

var siteSettingsDict = new Dictionary<string, object>
{
{ "Site Name", binding.Properties["Name"]?.Value },
{ "Port", binding.Properties["Bindings"]?.Value.ToString()?.Split(':')[1] },
{ "IP Address", binding.Properties["Bindings"]?.Value.ToString()?.Split(':')[0] },
{ "Host Name", binding.Properties["Bindings"]?.Value.ToString()?.Split(':')[2] },
{ "Sni Flag", sniValue },
{ "Protocol", binding.Properties["Protocol"]?.Value }
};

inventoryItems.Add(
new CurrentInventoryItem
{
Certificates = new[] {foundCert.CertificateData},
Certificates = new[] { foundCert.CertificateData },
Alias = thumbPrint,
PrivateKeyEntry = foundCert.HasPrivateKey,
UseChainLevel = false,
ItemStatus = OrchestratorInventoryItemStatus.Unknown
ItemStatus = OrchestratorInventoryItemStatus.Unknown,
Parameters = siteSettingsDict
}
);
}
}

_logger.LogTrace("closing runSpace...");
runSpace.Close();
_logger.LogTrace("runSpace closed...");
}

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

return new JobResult
{
Result = OrchestratorJobStatusJobResult.Success,
Expand All @@ -118,14 +176,14 @@ private JobResult PerformInventory(InventoryJobConfiguration config, SubmitInven
Result = OrchestratorJobStatusJobResult.Failure,
JobHistoryId = config.JobHistoryId,
FailureMessage =
$"Unable to open remote certificate store: {psEx.Message}"
$"Unable to open remote certificate store: {LogHandler.FlattenException(psEx)}"
};
}
catch (Exception ex)
{
_logger.LogTrace(LogHandler.FlattenException(ex));

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

return new JobResult
Expand All @@ -143,4 +201,4 @@ public JobResult ProcessJob(InventoryJobConfiguration jobConfiguration, SubmitIn
return PerformInventory(jobConfiguration, submitInventoryUpdate);
}
}
}
}
Loading