diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b20a99..c5bf3d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +2.3.0 +* Added Sql Server Binding Support + 2.2.2 * Removed empty constructor to resolve PAM provider error when using WinCert store types diff --git a/IISU/ClientPSCertStoreManager.cs b/IISU/ClientPSCertStoreManager.cs index ccb9696..ba0972a 100644 --- a/IISU/ClientPSCertStoreManager.cs +++ b/IISU/ClientPSCertStoreManager.cs @@ -104,7 +104,7 @@ function InstallPfxToMachineStore([byte[]]$bytes, [string]$password, [string]$st { _logger.LogTrace("ps Has Errors"); var psError = ps.Streams.Error.ReadAll() - .Aggregate(string.Empty, (current, error) => current + error.ErrorDetails.Message); + .Aggregate(string.Empty, (current, error) => current + error?.ErrorDetails.Message); { return new JobResult { diff --git a/IISU/ClientPsSqlManager.cs b/IISU/ClientPsSqlManager.cs new file mode 100644 index 0000000..9132ef5 --- /dev/null +++ b/IISU/ClientPsSqlManager.cs @@ -0,0 +1,373 @@ +// Copyright 2022 Keyfactor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Keyfactor.Logging; +using Keyfactor.Orchestrators.Common.Enums; +using Keyfactor.Orchestrators.Extensions; +using Microsoft.Extensions.Logging; +using Microsoft.Management.Infrastructure.Serialization; +using Newtonsoft.Json; +using System; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Security.Cryptography.X509Certificates; + +namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore +{ + internal class ClientPsSqlManager + { + private string SqlServiceUser { get; set; } + private string SqlInstanceName { get; set; } + private bool RestartService { get; set; } + private string RegistryPath { get; set; } + private string RenewalThumbprint { get; set; } = ""; + private string ClientMachineName { get; set; } + private long JobHistoryID { get; set; } + private readonly ILogger _logger; + private readonly Runspace _runSpace; + + private PowerShell ps; + + public ClientPsSqlManager(ManagementJobConfiguration config, string serverUsername, string serverPassword) + { + _logger = LogHandler.GetClassLogger(); + + try + { + ClientMachineName = config.CertificateStoreDetails.ClientMachine; + JobHistoryID = config.JobHistoryId; + + if (config.JobProperties.ContainsKey("InstanceName")) + { + var instanceRef = config.JobProperties["InstanceName"]?.ToString(); + SqlInstanceName = string.IsNullOrEmpty(instanceRef) ? "MSSQLSERVER":instanceRef; + } + + // Establish PowerShell Runspace + var jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate }); + string winRmProtocol = jobProperties.WinRmProtocol; + string winRmPort = jobProperties.WinRmPort; + bool includePortInSPN = jobProperties.SpnPortFlag; + RestartService = jobProperties.RestartService; + + _logger.LogTrace($"Establishing runspace on client machine: {ClientMachineName}"); + _runSpace = PsHelper.GetClientPsRunspace(winRmProtocol, ClientMachineName, winRmPort, includePortInSPN, serverUsername, serverPassword); + } + catch (Exception e) + { + throw new Exception($"Error when initiating a SQL Management Job: {e.Message}", e.InnerException); + } + } + + public ClientPsSqlManager(InventoryJobConfiguration config,Runspace runSpace) + { + _logger = LogHandler.GetClassLogger(); + + try + { + ClientMachineName = config.CertificateStoreDetails.ClientMachine; + JobHistoryID = config.JobHistoryId; + + // Establish PowerShell Runspace + var jobProperties = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate }); + string winRmProtocol = jobProperties.WinRmProtocol; + string winRmPort = jobProperties.WinRmPort; + bool includePortInSPN = jobProperties.SpnPortFlag; + + _logger.LogTrace($"Establishing runspace on client machine: {ClientMachineName}"); + _runSpace = runSpace; + } + catch (Exception e) + { + throw new Exception($"Error when initiating a SQL Management Job: {e.Message}", e.InnerException); + } + } + + public JobResult UnBindCertificate() + { + try + { + _logger.MethodEntry(); + + _runSpace.Open(); + ps = PowerShell.Create(); + ps.Runspace = _runSpace; + + RegistryPath = GetSqlCertRegistryLocation(SqlInstanceName, ps); + + var funcScript = string.Format($"Clear-ItemProperty -Path \"{RegistryPath}\" -Name Certificate"); + foreach (var cmd in ps.Commands.Commands) + { + _logger.LogTrace("Logging PowerShell Command"); + _logger.LogTrace(cmd.CommandText); + } + + _logger.LogTrace($"funcScript {funcScript}"); + ps.AddScript(funcScript); + _logger.LogTrace("funcScript added..."); + ps.Invoke(); + _logger.LogTrace("funcScript Invoked..."); + + if (ps.HadErrors) + { + var psError = ps.Streams.Error.ReadAll() + .Aggregate(string.Empty, (current, error) => current + error.ErrorDetails.Message); + { + return new JobResult + { + Result = OrchestratorJobStatusJobResult.Failure, + JobHistoryId = JobHistoryID, + FailureMessage = $"Unable to unbind certificate to Sql Server" + }; + } + } + + return new JobResult + { + Result = OrchestratorJobStatusJobResult.Success, + JobHistoryId = JobHistoryID, + FailureMessage = "" + }; + } + catch (Exception e) + { + return new JobResult + { + Result = OrchestratorJobStatusJobResult.Failure, + JobHistoryId = JobHistoryID, + FailureMessage = $"Error Occurred in unbind {LogHandler.FlattenException(e)}" + }; + } + finally + { + _runSpace.Close(); + ps.Runspace.Close(); + ps.Dispose(); + } + } + + public string GetSqlInstanceValue(string instanceName,PowerShell ps) + { + try + { + var funcScript = string.Format(@$"Get-ItemPropertyValue ""HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL"" -Name {instanceName}"); + foreach (var cmd in ps.Commands.Commands) + { + _logger.LogTrace("Logging PowerShell Command"); + _logger.LogTrace(cmd.CommandText); + } + + _logger.LogTrace($"funcScript {funcScript}"); + ps.AddScript(funcScript); + _logger.LogTrace("funcScript added..."); + var SqlInstanceValue = ps.Invoke()[0].ToString(); + _logger.LogTrace("funcScript Invoked..."); + ps.Commands.Clear(); + + if (!ps.HadErrors) + { + return SqlInstanceValue; + } + return null; + } + catch (Exception e) + { + throw new Exception($"Error when initiating getting instance name from registry: {e.Message}", e.InnerException); + } + } + + public string GetSqlCertRegistryLocation(string instanceName,PowerShell ps) + { + return $"HKLM:\\SOFTWARE\\Microsoft\\Microsoft SQL Server\\{GetSqlInstanceValue(instanceName,ps)}\\MSSQLServer\\SuperSocketNetLib\\"; + } + + public string GetSqlServerServiceName(string instanceValue) + { + if(string.IsNullOrEmpty(instanceValue)) + return string.Empty; + + //Default SQL Instance has this format + if (instanceValue.Split('.')[1] == "MSSQLSERVER") + return "MSSQLSERVER"; + + //Named Instance service has this format + return $"MSSQL`${instanceValue.Split('.')[1]}"; + } + + public JobResult BindCertificates(string renewalThumbprint, X509Certificate2 x509Cert) + { + try + { + var bindingError = string.Empty; + RenewalThumbprint = renewalThumbprint; + + _runSpace.Open(); + ps = PowerShell.Create(); + ps.Runspace = _runSpace; + if (!string.IsNullOrEmpty(renewalThumbprint)) + { + var funcScript = string.Format(@$"(Get-ItemProperty ""HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server"").InstalledInstances"); + ps.AddScript(funcScript); + _logger.LogTrace("funcScript added..."); + var instances = ps.Invoke(); + ps.Commands.Clear(); + foreach (var instance in instances) + { + var regLocation = GetSqlCertRegistryLocation(instance.ToString(), ps); + + funcScript = string.Format(@$"Get-ItemPropertyValue ""{regLocation}"" -Name Certificate"); + ps.AddScript(funcScript); + _logger.LogTrace("funcScript added..."); + var thumbprint = ps.Invoke()[0].ToString(); + ps.Commands.Clear(); + + if (RenewalThumbprint.Contains(thumbprint, StringComparison.CurrentCultureIgnoreCase)) + { + bindingError=BindCertificate(x509Cert, ps); + } + } + } + else + { + bindingError=BindCertificate(x509Cert, ps); + } + + if (bindingError.Length == 0) + { + return new JobResult + { + Result = OrchestratorJobStatusJobResult.Success, + JobHistoryId = JobHistoryID, + FailureMessage = "" + }; + } + else + { + return new JobResult + { + Result = OrchestratorJobStatusJobResult.Failure, + JobHistoryId = JobHistoryID, + FailureMessage = bindingError + }; + } + + } + catch (Exception e) + { + return new JobResult + { + Result = OrchestratorJobStatusJobResult.Failure, + JobHistoryId = JobHistoryID, + FailureMessage = $"Error Occurred in BindCertificates {LogHandler.FlattenException(e)}" + }; + } + finally + { + _runSpace.Close(); + ps.Runspace.Close(); + ps.Dispose(); + } + + } + public string BindCertificate(X509Certificate2 x509Cert,PowerShell ps) + { + try + { + _logger.MethodEntry(); + + + //If they comma separated the instance entry param, they are trying to install to more than 1 instance + var instances = SqlInstanceName.Split(','); + + foreach (var instanceName in instances) + { + RegistryPath = GetSqlCertRegistryLocation(instanceName, ps); + + var thumbPrint = string.Empty; + if (x509Cert != null) + thumbPrint = x509Cert.Thumbprint.ToLower(); //sql server config mgr expects lower + + var funcScript = string.Format($"Set-ItemProperty -Path \"{RegistryPath}\" -Name Certificate {thumbPrint}"); + foreach (var cmd in ps.Commands.Commands) + { + _logger.LogTrace("Logging PowerShell Command"); + _logger.LogTrace(cmd.CommandText); + } + + _logger.LogTrace($"funcScript {funcScript}"); + ps.AddScript(funcScript); + _logger.LogTrace("funcScript added..."); + ps.Invoke(); + _logger.LogTrace("funcScript Invoked..."); + + _logger.LogTrace("Setting up Acl Access for Manage Private Keys"); + ps.Commands.Clear(); + + //Get the SqlServer Service User Name + var serviceName = GetSqlServerServiceName(GetSqlInstanceValue(instanceName, ps)); + funcScript = @$"(Get-WmiObject Win32_Service -Filter ""Name='{serviceName}'"").StartName"; + ps.AddScript(funcScript); + _logger.LogTrace("funcScript added..."); + SqlServiceUser = ps.Invoke()[0].ToString(); + _logger.LogTrace("funcScript Invoked..."); + _logger.LogTrace("Got service login user for ACL Permissions"); + ps.Commands.Clear(); + + funcScript = $@"$thumbprint = '{thumbPrint}' + $Cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object {{ $_.Thumbprint -eq $thumbprint }} + $privKey = $Cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName + $keyPath = ""$($env:ProgramData)\Microsoft\Crypto\RSA\MachineKeys\"" + $privKeyPath = (Get-Item ""$keyPath\$privKey"") + $Acl = Get-Acl $privKeyPath + $Ar = New-Object System.Security.AccessControl.FileSystemAccessRule(""{SqlServiceUser.Replace("$", "`$")}"", ""Read"", ""Allow"") + $Acl.SetAccessRule($Ar) + Set-Acl $privKeyPath.FullName $Acl"; + + ps.AddScript(funcScript); + ps.Invoke(); + _logger.LogTrace("ACL FuncScript Invoked..."); + + //If user filled in a service name in the store then restart the SQL Server Services + if (RestartService) + { + _logger.LogTrace("Starting to Restart SQL Server Service..."); + ps.Commands.Clear(); + funcScript = $@"Restart-Service -Name ""{serviceName}"" -Force"; + + ps.AddScript(funcScript); + ps.Invoke(); + _logger.LogTrace("Invoked Restart SQL Server Service...."); + } + + if (ps.HadErrors) + { + var psError = ps.Streams.Error.ReadAll() + .Aggregate(string.Empty, (current, error) => current + error?.Exception.Message); + { + return psError; + } + } + } + return ""; + } + catch (Exception e) + { + return LogHandler.FlattenException(e); + } + + } + } +} + diff --git a/IISU/ImplementedStoreTypes/WinSQL/Inventory.cs b/IISU/ImplementedStoreTypes/WinSQL/Inventory.cs new file mode 100644 index 0000000..1950a84 --- /dev/null +++ b/IISU/ImplementedStoreTypes/WinSQL/Inventory.cs @@ -0,0 +1,142 @@ +// Copyright 2022 Keyfactor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +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.WinSql +{ + public class Inventory : WinCertJobTypeBase, IInventoryJobExtension + { + private ILogger _logger; + + public string ExtensionName => string.Empty; + + public Inventory(IPAMSecretResolver resolver) + { + _resolver = resolver; + } + + public JobResult ProcessJob(InventoryJobConfiguration jobConfiguration, SubmitInventoryUpdate submitInventoryUpdate) + { + _logger = LogHandler.GetClassLogger(); + _logger.MethodEntry(); + + + return PerformInventory(jobConfiguration, submitInventoryUpdate); + } + + private JobResult PerformInventory(InventoryJobConfiguration config, SubmitInventoryUpdate submitInventory) + { + try + { + var inventoryItems = new List(); + + string myConfig = config.ToString(); + + _logger.LogTrace(JobConfigurationParser.ParseInventoryJobConfiguration(config)); + + string serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", config.ServerUsername); + string serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", config.ServerPassword); + + // Deserialize specific job properties + var jobProperties = JsonConvert.DeserializeObject(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; + + if (storePath != null) + { + _logger.LogTrace($"Establishing runspace on client machine: {clientMachineName}"); + 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 SQL Server certificates from cert store: {storePath}"); + + SQLServerInventory sqlInventory = new SQLServerInventory(_logger); + inventoryItems = sqlInventory.GetInventoryItems(myRunspace, config); + if (inventoryItems != null) + { + _logger.LogTrace($"A total of {inventoryItems.Count} were found"); + _logger.LogTrace("Closing runspace..."); + myRunspace.Close(); + + _logger.LogTrace("Invoking Inventory.."); + submitInventory.Invoke(inventoryItems); + _logger.LogTrace($"Inventory Invoked... {inventoryItems.Count} Items"); + + return new JobResult + { + Result = OrchestratorJobStatusJobResult.Success, + JobHistoryId = config.JobHistoryId, + FailureMessage = "" + }; + } + else + { + return new JobResult + { + Result = OrchestratorJobStatusJobResult.Failure, + JobHistoryId = config.JobHistoryId, + FailureMessage = "Inventory Items was null, ensure sql server is installed on the machine." + }; + } + } + + return new JobResult + { + Result = OrchestratorJobStatusJobResult.Warning, + JobHistoryId = config.JobHistoryId, + FailureMessage = + $"No certificates were found in the Certificate Store Path: {storePath} on server: {clientMachineName}" + }; + } + catch (CertificateStoreException psEx) + { + _logger.LogTrace(psEx.Message); + return new JobResult + { + Result = OrchestratorJobStatusJobResult.Failure, + JobHistoryId = config.JobHistoryId, + FailureMessage = + $"Unable to open remote certificate store: {LogHandler.FlattenException(psEx)}" + }; + } + catch (Exception ex) + { + _logger.LogTrace(LogHandler.FlattenException(ex)); + + 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 + { + Result = OrchestratorJobStatusJobResult.Failure, + JobHistoryId = config.JobHistoryId, + FailureMessage = failureMessage + }; + } + } + } +} \ No newline at end of file diff --git a/IISU/ImplementedStoreTypes/WinSQL/Management.cs b/IISU/ImplementedStoreTypes/WinSQL/Management.cs new file mode 100644 index 0000000..aaa5d8b --- /dev/null +++ b/IISU/ImplementedStoreTypes/WinSQL/Management.cs @@ -0,0 +1,167 @@ +// Copyright 2022 Keyfactor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Management.Automation.Runspaces; +using Keyfactor.Logging; +using Keyfactor.Orchestrators.Common.Enums; +using Keyfactor.Orchestrators.Extensions; +using Keyfactor.Orchestrators.Extensions.Interfaces; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; + +namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinSql +{ + public class Management : WinCertJobTypeBase, IManagementJobExtension + { + private ILogger _logger; + + public string ExtensionName => string.Empty; + + private Runspace myRunspace; + + private string RenewalThumbprint; + + public Management(IPAMSecretResolver resolver) + { + _resolver = resolver; + } + + public JobResult ProcessJob(ManagementJobConfiguration config) + { + try + { + _logger = LogHandler.GetClassLogger(); + _logger.MethodEntry(); + + _logger.LogTrace(JobConfigurationParser.ParseManagementJobConfiguration(config)); + + string serverUserName = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server UserName", config.ServerUsername); + string serverPassword = PAMUtilities.ResolvePAMField(_resolver, _logger, "Server Password", config.ServerPassword); + + var jobProperties = JsonConvert.DeserializeObject(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; + long JobHistoryID = config.JobHistoryId; + + _logger.LogTrace($"Establishing runspace on client machine: {clientMachineName}"); + myRunspace = PsHelper.GetClientPsRunspace(protocol, clientMachineName, port, IncludePortInSPN, serverUserName, serverPassword); + + var complete = new JobResult + { + Result = OrchestratorJobStatusJobResult.Failure, + FailureMessage = + "Invalid Management Operation" + }; + + switch (config.OperationType) + { + case CertStoreOperationType.Add: + _logger.LogTrace("Entering Add..."); + myRunspace.Open(); + complete = PerformAddCertificate(config, serverUserName, serverPassword); + myRunspace.Close(); + _logger.LogTrace("After Perform Addition..."); + break; + case CertStoreOperationType.Remove: + _logger.LogTrace("Entering Remove..."); + _logger.LogTrace("After PerformRemoval..."); + myRunspace.Open(); + complete = PerformRemoveCertificate(config, serverUserName, serverPassword); + myRunspace.Close(); + _logger.LogTrace("After Perform Removal..."); + break; + } + + _logger.MethodExit(); + return complete; + } + catch (Exception ex) + { + _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)}'"; + _logger.LogWarning(failureMessage); + + return new JobResult + { + Result = OrchestratorJobStatusJobResult.Failure, + JobHistoryId = config.JobHistoryId, + FailureMessage = failureMessage + }; + } + } + + private JobResult PerformAddCertificate(ManagementJobConfiguration config, string serverUsername, string serverPassword) + { + _logger.LogTrace("Before PerformAddition..."); + + string certificateContents = config.JobCertificate.Contents; + string privateKeyPassword = config.JobCertificate.PrivateKeyPassword; + string storePath = config.CertificateStoreDetails.StorePath; + long jobNumber = config.JobHistoryId; + + ClientPSCertStoreManager manager = new ClientPSCertStoreManager(_logger, myRunspace, jobNumber); + JobResult result = manager.AddCertificate(certificateContents, privateKeyPassword, storePath); + + if (result.Result == OrchestratorJobStatusJobResult.Success) + { + + if (config.JobProperties.ContainsKey("RenewalThumbprint")) + { + RenewalThumbprint = config.JobProperties["RenewalThumbprint"].ToString(); + _logger.LogTrace($"Found Thumbprint Will Renew all Certs with this thumbprint: {RenewalThumbprint}"); + } + + // Bind to SQL Server + ClientPsSqlManager sqlManager = new ClientPsSqlManager(config, serverUsername, serverPassword); + result = sqlManager.BindCertificates(RenewalThumbprint,manager.X509Cert); + return result; + + + + } else return result; + } + + private JobResult PerformRemoveCertificate(ManagementJobConfiguration config, string serverUsername, string serverPassword) + { + _logger.LogTrace("Before Remove Certificate..."); + + string storePath = config.CertificateStoreDetails.StorePath; + long jobNumber = config.JobHistoryId; + + // Clear registry entry for SQL Server + ClientPsSqlManager sqlManager = new ClientPsSqlManager(config, serverUsername, serverPassword); + JobResult result = sqlManager.UnBindCertificate(); + + if (result.Result == OrchestratorJobStatusJobResult.Success) + { + ClientPSCertStoreManager manager = new ClientPSCertStoreManager(_logger, myRunspace, jobNumber); + manager.RemoveCertificate(config.JobCertificate.Alias, storePath); + + return new JobResult + { + Result = OrchestratorJobStatusJobResult.Success, + JobHistoryId = config.JobHistoryId, + FailureMessage = "" + }; + } + else return result; + } + } +} \ No newline at end of file diff --git a/IISU/ImplementedStoreTypes/WinSQL/SQLServerInventory.cs b/IISU/ImplementedStoreTypes/WinSQL/SQLServerInventory.cs new file mode 100644 index 0000000..49b2cb3 --- /dev/null +++ b/IISU/ImplementedStoreTypes/WinSQL/SQLServerInventory.cs @@ -0,0 +1,114 @@ +// Copyright 2022 Keyfactor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Keyfactor.Orchestrators.Common.Enums; +using Keyfactor.Orchestrators.Extensions; +using Microsoft.Extensions.Logging; +using Microsoft.Management.Infrastructure; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Management.Automation; +using System.Management.Automation.Runspaces; + +namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinSql +{ + internal class SQLServerInventory : ClientPSCertStoreInventory + { + private string SqlInstanceName { get; set; } + + public SQLServerInventory(ILogger logger) : base(logger) + { + } + + public List GetInventoryItems(Runspace runSpace, InventoryJobConfiguration jobConfig) + { + var jobProperties = JsonConvert.DeserializeObject(jobConfig.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate }); + //SqlInstanceName = jobProperties.SqlInstanceName; + + // Get the raw certificate inventory from cert store + List certificates = base.GetCertificatesFromStore(runSpace, jobConfig.CertificateStoreDetails.StorePath); + + // Contains the inventory items to be sent back to KF + List myBoundCerts = new List(); + using (PowerShell ps2 = PowerShell.Create()) + { + //runSpace.Open(); + ps2.Runspace = runSpace; + + //Get all the installed instances of Sql Server + var funcScript = string.Format(@$"(Get-ItemProperty ""HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server"").InstalledInstances"); + ps2.AddScript(funcScript); + //.LogTrace("funcScript added..."); + var instances = ps2.Invoke(); + ps2.Commands.Clear(); + var psSqlManager = new ClientPsSqlManager(jobConfig, runSpace); + var commonInstances=new Dictionary(); + + if (instances != null && instances[0] != null) + { + foreach (var instance in instances) + { + var regLocation = psSqlManager.GetSqlCertRegistryLocation(instance.ToString(), ps2); + + funcScript = string.Format(@$"Get-ItemPropertyValue ""{regLocation}"" -Name Certificate"); + ps2.AddScript(funcScript); + //_logger.LogTrace("funcScript added..."); + var thumbprint = ps2.Invoke()[0].ToString(); + ps2.Commands.Clear(); + if (string.IsNullOrEmpty(thumbprint)) continue; + thumbprint = thumbprint.ToUpper(); + + if (!commonInstances.ContainsKey(thumbprint)) + { + commonInstances.Add(thumbprint, instance.ToString()); + } + else + { + commonInstances[thumbprint] = commonInstances[thumbprint] + "," + instance.ToString(); + } + } + + foreach (var kp in commonInstances) + { + Certificate foundCert = certificates.Find(m => m.Thumbprint.ToUpper().Equals(kp.Key)); + + if (foundCert == null) continue; + + var sqlSettingsDict = new Dictionary + { + { "InstanceName", kp.Value.ToString() } + }; + + myBoundCerts.Add( + new CurrentInventoryItem + { + Certificates = new[] { foundCert.CertificateData }, + Alias = kp.Key, + PrivateKeyEntry = foundCert.HasPrivateKey, + UseChainLevel = false, + ItemStatus = OrchestratorInventoryItemStatus.Unknown, + Parameters = sqlSettingsDict + }); + } + return myBoundCerts; + } + else + { + return null; + } + } + + } + } +} diff --git a/IISU/Models/JobProperties.cs b/IISU/Models/JobProperties.cs index 8714c21..5fb03bf 100644 --- a/IISU/Models/JobProperties.cs +++ b/IISU/Models/JobProperties.cs @@ -1,4 +1,4 @@ -// Copyright 2023 Keyfactor +// Copyright 2022 Keyfactor // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -48,6 +48,9 @@ public JobProperties() [DefaultValue(SniFlag.None)] public SniFlag SniFlag { get; set; } + [JsonProperty("RestartService")] + [DefaultValue(true)] + public bool RestartService { get; set; } } internal enum SniFlag diff --git a/IISU/WindowsCertStore.csproj b/IISU/WindowsCertStore.csproj index 0a417fd..fb4cca1 100644 --- a/IISU/WindowsCertStore.csproj +++ b/IISU/WindowsCertStore.csproj @@ -14,11 +14,8 @@ - - - diff --git a/IISU/manifest.json b/IISU/manifest.json index 6fa8618..d7c7c64 100644 --- a/IISU/manifest.json +++ b/IISU/manifest.json @@ -24,6 +24,14 @@ "CertStores.WinCert.ReEnrollment": { "assemblypath": "WindowsCertStore.dll", "TypeFullName": "Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinCert.ReEnrollment" + }, + "CertStores.WinSql.Inventory": { + "assemblypath": "WindowsCertStore.dll", + "TypeFullName": "Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinSql.Inventory" + }, + "CertStores.WinSql.Management": { + "assemblypath": "WindowsCertStore.dll", + "TypeFullName": "Keyfactor.Extensions.Orchestrator.WindowsCertStore.WinSql.Management" } } } diff --git a/README.md b/README.md index 9ae545a..3b05b52 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ The Universal Orchestrator is the successor to the Windows Orchestrator. This Or ## Support for WinCertStore Orchestrator -WinCertStore Orchestrator +WinCertStore Orchestrator is supported by Keyfactor for Keyfactor customers. If you have a support issue, please open a support ticket with your Keyfactor representative. ###### To report a problem or suggest a new feature, use the **[Issues](../../issues)** tab. If you want to contribute actual bug fixes or proposed enhancements, use the **[Pull requests](../../pulls)** tab. @@ -202,7 +202,74 @@ None of the above entry parameters have the "Depends On" field set. Click Save to save the Certificate Store Type. +
+ SQL Server Extension + +**In Keyfactor Command create a new Certificate Store Type as specified below:** + +**Basic Settings:** + +CONFIG ELEMENT | VALUE | DESCRIPTION +--|--|-- +Name | Windows SQL Server Certificate| Display name for the store type (may be customized) +Short Name| WinSql | Short display name for the store type +Custom Capability | Leave Unchecked | Store type name orchestrator will register with. Check the box to allow entry of value +Supported Job Types | Inventory, Add, Remove | Job types the extension supports +Needs Server | Checked | Determines if a target server name is required when creating store +Blueprint Allowed | Unchecked | Determines if store type may be included in an Orchestrator blueprint +Uses PowerShell | Unchecked | Determines if underlying implementation is PowerShell +Requires Store Password | Unchecked | Determines if a store password is required when configuring an individual store. +Supports Entry Password | Unchecked | Determines if an individual entry within a store can have a password. + +![](images/SQLServerCertStoreBasic.png) + +**Advanced Settings:** + +CONFIG ELEMENT | VALUE | DESCRIPTION +--|--|-- +Store Path Type | Fixed | Fixed to a defined path. SQL Server Supports the Personal or "My" store on the Local Machine. +Store Path Value | My | Fixed Value My on the Local Machine Store. +Supports Custom Alias | Forbidden | Determines if an individual entry within a store can have a custom Alias. +Private Keys | Required | This determines if Keyfactor can send the private key associated with a certificate to the store. Required because SQL Server certificates without private keys would be useless. +PFX Password Style | Default or Custom | "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.) + +![](images/SQLServerCertStoreAdvanced.png) + +**Custom Fields:** + +Custom fields operate at the certificate store level and are used to control how the orchestrator connects to the remote +target server containing the certificate store to be managed + +Name|Display Name|Type|Default Value / Options|Required|Description +---|---|---|---|---|--- +WinRm Protocol|WinRm Protocol|Multiple Choice| https,http |Yes|Protocol that target server WinRM listener is using +WinRm Port|WinRm Port|String|5986|Yes| Port that target server WinRM listener is using. Typically 5985 for HTTP and 5986 for HTTPS +spnwithport|SPN With Port|Bool|false|No|Internally set the -IncludePortInSPN option when creating the remote PowerShell connection. Needed for some Kerberos configurations. +ServerUsername|Server Username|Secret||No|The username to log into the target server (This field is automatically created). Check the No Value Checkbox when using GMSA Accounts. +ServerPassword|Server Password|Secret||No|The password that matches the username to log into the target server (This field is automatically created). Check the No Value Checkbox when using GMSA Accounts. +ServerUseSsl|Use SSL|Bool|true|Yes|Determine whether the server uses SSL or not (This field is automatically created) +RestartService|Restart SQL Service After Cert Installed|Bool|False|Yes|If true, Orchestrator will restart the SQL Server Service after installing the certificate. + + +*Note that some of the Names in the first column above have spaces and some do not, it is important to configure the Name field exactly as above.* + +![](images/SQLServerCustomFields.png) + +**Entry Parameters:** + +Entry parameters are inventoried and maintained for each entry within a certificate store. +They are typically used to support binding of a certificate to a resource. + +Name|Display Name| Type|Default Value|Required When|Description +---|---|---|---|---|--- +InstanceName | Instance Name|String||Not required | When enrolling leave blank or use MSSQLServer for the Default Instance, Instance Name for an Instance or MSSQLServer,Instance Name if enrolling to multiple instances plus the default instance. + +![](images/SQLServerEntryParams.png) + +Click Save to save the Certificate Store Type. + +
WinCert Extension @@ -303,8 +370,33 @@ Click Save to save the settings for this Certificate Store
-WinCert Certificate Store +SQL Server Certificate Store + +In Keyfactor Command, navigate to Certificate Stores from the Locations Menu. Click the Add button to create a new Certificate Store using the settings defined below. + +#### STORE CONFIGURATION +CONFIG ELEMENT |DESCRIPTION +----------------|--------------- +Category | Select SQL Server Bound Certificate or the customized certificate store display name from above. +Container | Optional container to associate certificate store with. +Client Machine | Hostname of the Windows Server containing the certificate store to be managed. If this value is a hostname, a WinRM session will be established using the credentials specified in the Server Username and Server Password fields. +Store Path | Windows certificate store to manage. Fixed to "My". +Orchestrator | Select an approved orchestrator capable of managing SQL Server Bound Certificates. +WinRm Protocol | Protocol to use when establishing the WinRM session. (Listener on Client Machine must be configured for selected protocol.) +WinRm Port | Port WinRM listener is configured for (HTTPS default is 5986) +SPN with Port | Typically False. Needed in some Kerberos configurations. +Server Username | Account to use when establishing the WinRM session to the Client Machine. Account needs to be an administrator or have been granted rights to manage IIS configuration and manipulate the local machine certificate store. If no account is specified, the security context of the Orchestrator service account will be used. +Server Password | Password to use when establishing the WinRM session to the Client Machine +Restart SQL Service After Cert Installed | For each instance the certificate is tied to, the service for that instance will be restarted after the certificate is successfully installed. +Use SSL | Ignored for this certificate store type. Transport encryption is determined by the WinRM Protocol Setting +Inventory Schedule | The interval that the system will use to report on what certificates are currently in the store. +![](images/SQLServerAddCertStore.png) + +Click Save to save the settings for this Certificate Store +
+
+WinCert Certificate Store In Keyfactor Command, navigate to Certificate Stores from the Locations Menu. Click the Add button to create a new Certificate Store using the settings defined below. @@ -329,6 +421,7 @@ Inventory Schedule | The interval that the system will use to report on what cer
+ ## Test Cases
IISU @@ -352,4 +445,20 @@ Case Number|Case Name|Enrollment Params|Expected Results|Passed|Screenshot 15 |New Cert Enrollment Default Site No HostName|**Site Name:** Default Web Site
**Port:** 443
**IP Address:**`*`
**Host Name:**
**Sni Flag:** 0 - No SNI
**Protocol:** https|New Binding Installed with no HostName|True|![](images/TestCase15Results.gif)
+
+WinSql + +Case Number|Case Name|Enrollment Params|Expected Results|Passed|Screenshot +----|------------------------|------------------------------------|--------------|----------------|------------------------- +1 |New Cert Enrollment To Default Instance Leave Blank|**Intance Name:** |Cert will be Installed to default Instance, Service will be restarted for default instance|True|![](images/SQLTestCase1.gif) +2 |New Cert Enrollment To Default Instance MSSQLServer|**Intance Name:** MSSQLServer|Cert will be Installed to default Instance, Service will be restarted for default instance|True|![](images/SQLTestCase2.gif) +3 |New Cert Enrollment To Instance1|**Intance Name:** Instance1|Cert will be Installed to Instance1, Service will be restarted for Instance1|True|![](images/SQLTestCase3.gif) +4 |New Cert Enrollment To Instance1 and Default Instance|**Intance Name:** MSSQLServer,Instance1|Cert will be Installed to Default Instance and Instance1, Service will be restarted for Default Instance and Instance1|True|![](images/SQLTestCase4.gif) +5 |One Click Renew Cert Enrollment To Instance1 and Default Instance|N/A|Cert will be Renewed/Installed to Default Instance and Instance1, Service will be restarted for Default Instance and Instance1|True|![](images/SQLTestCase5.gif) +6 |Remove Cert From Instance1 and Default Instance|**Intance Name:** |Cert from TC5 will be Removed From Default Instance and Instance1|True|![](images/SQLTestCase6.gif) +7 |Inventory Different Certs Different Instance|N/A|2 Certs will be inventoried and each tied to its Instance|True|![](images/SQLTestCase7.gif) +8 |Inventory Same Cert Different Instance|N/A|2 Certs will be inventoried the cert will have a comma separated list of Instances|True|![](images/SQLTestCase8.gif) +9 |Inventory Against Machine Without SQL Server|N/A|Will fail with error saying it can't find SQL Server|True|![](images/SQLTestCase9.gif) + +
diff --git a/images/SQLServerAddCertStore.png b/images/SQLServerAddCertStore.png new file mode 100644 index 0000000..483ce7c Binary files /dev/null and b/images/SQLServerAddCertStore.png differ diff --git a/images/SQLServerCertStoreAdvanced.png b/images/SQLServerCertStoreAdvanced.png new file mode 100644 index 0000000..fb28b51 Binary files /dev/null and b/images/SQLServerCertStoreAdvanced.png differ diff --git a/images/SQLServerCertStoreBasic.png b/images/SQLServerCertStoreBasic.png new file mode 100644 index 0000000..8b4514d Binary files /dev/null and b/images/SQLServerCertStoreBasic.png differ diff --git a/images/SQLServerCertStoreCustomFields.png b/images/SQLServerCertStoreCustomFields.png new file mode 100644 index 0000000..eeaaf5f Binary files /dev/null and b/images/SQLServerCertStoreCustomFields.png differ diff --git a/images/SQLServerCertStoreEntryParams.png b/images/SQLServerCertStoreEntryParams.png new file mode 100644 index 0000000..d360132 Binary files /dev/null and b/images/SQLServerCertStoreEntryParams.png differ diff --git a/images/SQLTestCase1.gif b/images/SQLTestCase1.gif new file mode 100644 index 0000000..f48011b Binary files /dev/null and b/images/SQLTestCase1.gif differ diff --git a/images/SQLTestCase2.gif b/images/SQLTestCase2.gif new file mode 100644 index 0000000..904e58b Binary files /dev/null and b/images/SQLTestCase2.gif differ diff --git a/images/SQLTestCase3.gif b/images/SQLTestCase3.gif new file mode 100644 index 0000000..e109be3 Binary files /dev/null and b/images/SQLTestCase3.gif differ diff --git a/images/SQLTestCase4.gif b/images/SQLTestCase4.gif new file mode 100644 index 0000000..ea0a3f9 Binary files /dev/null and b/images/SQLTestCase4.gif differ diff --git a/images/SQLTestCase5.gif b/images/SQLTestCase5.gif new file mode 100644 index 0000000..45a28d2 Binary files /dev/null and b/images/SQLTestCase5.gif differ diff --git a/images/SQLTestCase6.gif b/images/SQLTestCase6.gif new file mode 100644 index 0000000..d61ab75 Binary files /dev/null and b/images/SQLTestCase6.gif differ diff --git a/images/SQLTestCase7.gif b/images/SQLTestCase7.gif new file mode 100644 index 0000000..7134f30 Binary files /dev/null and b/images/SQLTestCase7.gif differ diff --git a/images/SQLTestCase8.gif b/images/SQLTestCase8.gif new file mode 100644 index 0000000..7c2b2d1 Binary files /dev/null and b/images/SQLTestCase8.gif differ diff --git a/images/SQLTestCase9.gif b/images/SQLTestCase9.gif new file mode 100644 index 0000000..8f6dafa Binary files /dev/null and b/images/SQLTestCase9.gif differ diff --git a/integration-manifest.json b/integration-manifest.json index fc87aa3..f558b94 100644 --- a/integration-manifest.json +++ b/integration-manifest.json @@ -4,6 +4,8 @@ "name": "WinCertStore Orchestrator", "status": "production", "link_github": true, + "update_catalog": true, + "support_level": "kf-supported", "description": "The Windows Certificate Store Orchestrator Extension implements two certificate store types. 1) “WinCert” which manages certificates in a Windows local machine store, and 2) “IISU” which manages certificates and their bindings in a Windows local machine store that are bound to Internet Information Server (IIS) websites. These extensions replace the now deprecated “IIS” cert store type that ships with Keyfactor Command. The “IISU” extension also replaces the “IISBin” certificate store type from prior versions of this repository. This orchestrator extension is in the process of being renamed from “IIS Orchestrator” as it now supports certificates that are not in use by IIS.", "about": { "orchestrator": { @@ -319,8 +321,98 @@ "PowerShell": false, "BlueprintAllowed": false, "CustomAliasAllowed": "Forbidden" + }, + { + "Name": "WinSql", + "ShortName": "WinSql", + "Capability": "WinSql", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": false, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "WinRm Protocol", + "DisplayName": "WinRm Protocol", + "Type": "MultipleChoice", + "DependsOn": null, + "DefaultValue": "https,http", + "Required": true + }, + { + "Name": "WinRm Port", + "DisplayName": "WinRm Port", + "Type": "String", + "DependsOn": null, + "DefaultValue": "5986", + "Required": true + }, + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": null, + "DefaultValue": "true", + "Required": true + }, + { + "Name": "RestartService", + "DisplayName": "Restart SQL Service After Cert Installed", + "Type": "Bool", + "DependsOn": null, + "DefaultValue": "false", + "Required": true + } + ], + "EntryParameters": [ + { + "Name": "InstanceName", + "DisplayName": "Instance Name", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + } + } + ], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathValue": "My", + "PrivateKeyAllowed": "Optional", + "JobProperties": [ + "InstanceName" + ], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": true, + "CustomAliasAllowed": "Forbidden" } ] } } -} +} \ No newline at end of file diff --git a/readme_source.md b/readme_source.md index 7d6b600..02a6d47 100644 --- a/readme_source.md +++ b/readme_source.md @@ -101,7 +101,74 @@ None of the above entry parameters have the "Depends On" field set. Click Save to save the Certificate Store Type. +
+ SQL Server Extension + +**In Keyfactor Command create a new Certificate Store Type as specified below:** + +**Basic Settings:** + +CONFIG ELEMENT | VALUE | DESCRIPTION +--|--|-- +Name | Windows SQL Server Certificate| Display name for the store type (may be customized) +Short Name| WinSql | Short display name for the store type +Custom Capability | Leave Unchecked | Store type name orchestrator will register with. Check the box to allow entry of value +Supported Job Types | Inventory, Add, Remove | Job types the extension supports +Needs Server | Checked | Determines if a target server name is required when creating store +Blueprint Allowed | Unchecked | Determines if store type may be included in an Orchestrator blueprint +Uses PowerShell | Unchecked | Determines if underlying implementation is PowerShell +Requires Store Password | Unchecked | Determines if a store password is required when configuring an individual store. +Supports Entry Password | Unchecked | Determines if an individual entry within a store can have a password. + +![](images/SQLServerCertStoreBasic.png) + +**Advanced Settings:** + +CONFIG ELEMENT | VALUE | DESCRIPTION +--|--|-- +Store Path Type | Fixed | Fixed to a defined path. SQL Server Supports the Personal or "My" store on the Local Machine. +Store Path Value | My | Fixed Value My on the Local Machine Store. +Supports Custom Alias | Forbidden | Determines if an individual entry within a store can have a custom Alias. +Private Keys | Required | This determines if Keyfactor can send the private key associated with a certificate to the store. Required because SQL Server certificates without private keys would be useless. +PFX Password Style | Default or Custom | "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.) + +![](images/SQLServerCertStoreAdvanced.png) + +**Custom Fields:** + +Custom fields operate at the certificate store level and are used to control how the orchestrator connects to the remote +target server containing the certificate store to be managed + +Name|Display Name|Type|Default Value / Options|Required|Description +---|---|---|---|---|--- +WinRm Protocol|WinRm Protocol|Multiple Choice| https,http |Yes|Protocol that target server WinRM listener is using +WinRm Port|WinRm Port|String|5986|Yes| Port that target server WinRM listener is using. Typically 5985 for HTTP and 5986 for HTTPS +spnwithport|SPN With Port|Bool|false|No|Internally set the -IncludePortInSPN option when creating the remote PowerShell connection. Needed for some Kerberos configurations. +ServerUsername|Server Username|Secret||No|The username to log into the target server (This field is automatically created). Check the No Value Checkbox when using GMSA Accounts. +ServerPassword|Server Password|Secret||No|The password that matches the username to log into the target server (This field is automatically created). Check the No Value Checkbox when using GMSA Accounts. +ServerUseSsl|Use SSL|Bool|true|Yes|Determine whether the server uses SSL or not (This field is automatically created) +RestartService|Restart SQL Service After Cert Installed|Bool|False|Yes|If true, Orchestrator will restart the SQL Server Service after installing the certificate. + + +*Note that some of the Names in the first column above have spaces and some do not, it is important to configure the Name field exactly as above.* + +![](images/SQLServerCustomFields.png) + +**Entry Parameters:** + +Entry parameters are inventoried and maintained for each entry within a certificate store. +They are typically used to support binding of a certificate to a resource. + +Name|Display Name| Type|Default Value|Required When|Description +---|---|---|---|---|--- +InstanceName | Instance Name|String||Not required | When enrolling leave blank or use MSSQLServer for the Default Instance, Instance Name for an Instance or MSSQLServer,Instance Name if enrolling to multiple instances plus the default instance. + +![](images/SQLServerEntryParams.png) + +Click Save to save the Certificate Store Type. + +
WinCert Extension @@ -202,8 +269,33 @@ Click Save to save the settings for this Certificate Store
-WinCert Certificate Store +SQL Server Certificate Store + +In Keyfactor Command, navigate to Certificate Stores from the Locations Menu. Click the Add button to create a new Certificate Store using the settings defined below. + +#### STORE CONFIGURATION +CONFIG ELEMENT |DESCRIPTION +----------------|--------------- +Category | Select SQL Server Bound Certificate or the customized certificate store display name from above. +Container | Optional container to associate certificate store with. +Client Machine | Hostname of the Windows Server containing the certificate store to be managed. If this value is a hostname, a WinRM session will be established using the credentials specified in the Server Username and Server Password fields. +Store Path | Windows certificate store to manage. Fixed to "My". +Orchestrator | Select an approved orchestrator capable of managing SQL Server Bound Certificates. +WinRm Protocol | Protocol to use when establishing the WinRM session. (Listener on Client Machine must be configured for selected protocol.) +WinRm Port | Port WinRM listener is configured for (HTTPS default is 5986) +SPN with Port | Typically False. Needed in some Kerberos configurations. +Server Username | Account to use when establishing the WinRM session to the Client Machine. Account needs to be an administrator or have been granted rights to manage IIS configuration and manipulate the local machine certificate store. If no account is specified, the security context of the Orchestrator service account will be used. +Server Password | Password to use when establishing the WinRM session to the Client Machine +Restart SQL Service After Cert Installed | For each instance the certificate is tied to, the service for that instance will be restarted after the certificate is successfully installed. +Use SSL | Ignored for this certificate store type. Transport encryption is determined by the WinRM Protocol Setting +Inventory Schedule | The interval that the system will use to report on what certificates are currently in the store. +![](images/SQLServerAddCertStore.png) + +Click Save to save the settings for this Certificate Store +
+
+WinCert Certificate Store In Keyfactor Command, navigate to Certificate Stores from the Locations Menu. Click the Add button to create a new Certificate Store using the settings defined below. @@ -228,6 +320,7 @@ Inventory Schedule | The interval that the system will use to report on what cer
+ ## Test Cases
IISU @@ -251,3 +344,19 @@ Case Number|Case Name|Enrollment Params|Expected Results|Passed|Screenshot 15 |New Cert Enrollment Default Site No HostName|**Site Name:** Default Web Site
**Port:** 443
**IP Address:**`*`
**Host Name:**
**Sni Flag:** 0 - No SNI
**Protocol:** https|New Binding Installed with no HostName|True|![](images/TestCase15Results.gif)
+
+WinSql + +Case Number|Case Name|Enrollment Params|Expected Results|Passed|Screenshot +----|------------------------|------------------------------------|--------------|----------------|------------------------- +1 |New Cert Enrollment To Default Instance Leave Blank|**Intance Name:** |Cert will be Installed to default Instance, Service will be restarted for default instance|True|![](images/SQLTestCase1.gif) +2 |New Cert Enrollment To Default Instance MSSQLServer|**Intance Name:** MSSQLServer|Cert will be Installed to default Instance, Service will be restarted for default instance|True|![](images/SQLTestCase2.gif) +3 |New Cert Enrollment To Instance1|**Intance Name:** Instance1|Cert will be Installed to Instance1, Service will be restarted for Instance1|True|![](images/SQLTestCase3.gif) +4 |New Cert Enrollment To Instance1 and Default Instance|**Intance Name:** MSSQLServer,Instance1|Cert will be Installed to Default Instance and Instance1, Service will be restarted for Default Instance and Instance1|True|![](images/SQLTestCase4.gif) +5 |One Click Renew Cert Enrollment To Instance1 and Default Instance|N/A|Cert will be Renewed/Installed to Default Instance and Instance1, Service will be restarted for Default Instance and Instance1|True|![](images/SQLTestCase5.gif) +6 |Remove Cert From Instance1 and Default Instance|**Intance Name:** |Cert from TC5 will be Removed From Default Instance and Instance1|True|![](images/SQLTestCase6.gif) +7 |Inventory Different Certs Different Instance|N/A|2 Certs will be inventoried and each tied to its Instance|True|![](images/SQLTestCase7.gif) +8 |Inventory Same Cert Different Instance|N/A|2 Certs will be inventoried the cert will have a comma separated list of Instances|True|![](images/SQLTestCase8.gif) +9 |Inventory Against Machine Without SQL Server|N/A|Will fail with error saying it can't find SQL Server|True|![](images/SQLTestCase9.gif) + +