From b7b37f7b2e6efcf0ab5960d19b44e101b370535b Mon Sep 17 00:00:00 2001 From: Adrian Cunnelly Date: Wed, 30 Jan 2019 10:02:07 +0000 Subject: [PATCH] Allow SecurityProtocol to be specified for a repo A parameter (securityprotocols) has been added to the psrepository type which will accept a list of security protocols that the repository will accept, the list can contain any values can be specified for the ServicePointManager.SecurityProtocol Property (see https://docs.microsoft.com/en-us/dotnet/api/system.net.servicepointmanager.securityprotocol) e.g. psrepository { 'psrepo': ensure => present, source_location => 'https://local.repo.domain/', installation_policy => 'trusted', securityprotocols => [ TLS11, TLS12 ] } This is to resolve an SSL connection issue that can occur if the PowerShell repository enforces the use of a specific TLS version, but the Windows client has a default protocol version that is lower. --- README.md | 16 ++++++++ lib/puppet/provider/package/powershellcore.rb | 41 +++++++++++++++++-- .../provider/psrepository/powershellcore.rb | 21 ++++++++++ lib/puppet/type/psrepository.rb | 12 ++++++ 4 files changed, 87 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 33886a2..cc8abd7 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,18 @@ psrepository { 'my-internal-repo': } ``` +If the PowerShell repository enforces a specific security protocol, e.g. TLS 1.2, then this can be specified using the securityprotocols parameter, see [SecurityProtocolType Enum](https://docs.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype) for a list of allowed values. + +```puppet +psrepository { 'my-internal-repo': + ensure => present, + source_location => 'http://myrepo.corp.com/api/nuget/powershell', + installation_policy => 'trusted', + provider => 'windowspowershell', + securityprotocols => [ 'Tls12' ] +} +``` + Manifests can then refer to that repository using the `package` resource. ```puppet @@ -235,6 +247,10 @@ The url to the repository that you would like to register. Must be a valid HTTP Manages the installation policy used for the PSRepository. Valid values are `trusted` or `untrusted` +#### `securityprotocols` + +An optional array of security protocols to use when accessing the repository. Values specified will be used to set the [ServicePointManager.SecurityProtocol](https://docs.microsoft.com/en-us/dotnet/api/system.net.servicepointmanager.securityprotocol) property, if this parameter is not specified then the system default protocol will be used. + ### Providers #### `windowspowershell` diff --git a/lib/puppet/provider/package/powershellcore.rb b/lib/puppet/provider/package/powershellcore.rb index e9bf471..2c93a27 100644 --- a/lib/puppet/provider/package/powershellcore.rb +++ b/lib/puppet/provider/package/powershellcore.rb @@ -44,6 +44,35 @@ def update self.class.invoke_ps_command update_command end + # Takes an array of security protocol types, e.g. [Tls,Tls11], + # see https://docs.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype + # and produces a PowerShell command that can be used to set + # the ServicePointManager.SecurityProtocol Property, + # see https://docs.microsoft.com/en-us/dotnet/api/system.net.servicepointmanager.securityprotocol + # If securityprotocols are specified for a repository, then the + # ServicePointManager.SecurityProtocol Property needs to be set + # before any request to the repository. + # @param protocols [Array] + # @return PowerShell command + def join_protocols(protocols) + return unless protocols + + command = "[Net.ServicePointManager]::SecurityProtocol = 0" + protocols.each do |val| + command << " -bor [Net.SecurityProtocolType]::#{val}" + end + command + end + + # Gets the value of the securityprotocols argument from the + # specified psrepository resource and converts them into a + # PowerShell command to set the ServicePointManager.SecurityProtocol Property. + def securityprotocols(repository) + psrepo = resource.catalog.resource(:psrepository,repository) + proto = psrepo.parameters[:securityprotocols] unless psrepo.nil? + join_protocols(proto.value) unless proto.nil? + end + # Turns a array of install_options into flags to be passed to a command. # The options can be passed as a string or hash. Note that passing a hash # should only be used in case "-foo bar" must be passed, @@ -85,7 +114,8 @@ def self.instances_command end def install_command - command = "Install-Module #{@resource[:name]} -Scope AllUsers -Force" + command = "#{securityprotocols(@resource[:source])};" + command << "Install-Module #{@resource[:name]} -Scope AllUsers -Force" command << " -RequiredVersion #{@resource[:ensure]}" unless [:present, :latest].include? @resource[:ensure] command << " -Repository #{@resource[:source]}" if @resource[:source] command << " #{install_options(@resource[:install_options])}" if @resource[:install_options] @@ -97,11 +127,16 @@ def uninstall_command end def latest_command - "$mod = Find-Module #{@resource[:name]}; $mod.Version.ToString()" + command = "#{securityprotocols(@resource[:source])};" + command << "$mod = Find-Module #{@resource[:name]}" + command << " -Repository #{@resource[:source]}" if @resource[:source] + command << "; $mod.Version.ToString()" + command end def update_command - command = "Install-Module #{@resource[:name]} -Scope AllUsers -Force" + command = "#{securityprotocols(@resource[:source])};" + command << "Install-Module #{@resource[:name]} -Scope AllUsers -Force" command << " -Repository #{@resource[:source]}" if @resource[:source] command << " #{install_options(@resource[:install_options])}" if @resource[:install_options] command diff --git a/lib/puppet/provider/psrepository/powershellcore.rb b/lib/puppet/provider/psrepository/powershellcore.rb index 37296e2..6f79125 100644 --- a/lib/puppet/provider/psrepository/powershellcore.rb +++ b/lib/puppet/provider/psrepository/powershellcore.rb @@ -77,6 +77,26 @@ def self.instances_command COMMAND end + # Takes an array of security protocol types, e.g. [Tls,Tls11], + # see https://docs.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype + # and produces a PowerShell command that can be used to set + # the ServicePointManager.SecurityProtocol Property, + # see https://docs.microsoft.com/en-us/dotnet/api/system.net.servicepointmanager.securityprotocol + # If securityprotocols are specified for a repository, then the + # ServicePointManager.SecurityProtocol Property needs to be set + # before any request to the repository. + # @param protocols [Array] + # @return PowerShell command + def join_protocols(protocols) + return unless protocols + + command = "[Net.ServicePointManager]::SecurityProtocol = 0" + protocols.each do |val| + command << " -bor [Net.SecurityProtocolType]::#{val}" + end + command + end + def create_command <<-COMMAND $params = @{ @@ -84,6 +104,7 @@ def create_command SourceLocation = '#{@resource[:source_location]}' InstallationPolicy = '#{@resource[:installation_policy]}' } + #{join_protocols(@resource[:securityprotocols])} Register-PSRepository @params COMMAND end diff --git a/lib/puppet/type/psrepository.rb b/lib/puppet/type/psrepository.rb index 496d3ba..ccabbef 100644 --- a/lib/puppet/type/psrepository.rb +++ b/lib/puppet/type/psrepository.rb @@ -34,4 +34,16 @@ defaultto :untrusted newvalues(:trusted, :untrusted) end + + newparam(:securityprotocols, :array_matching => :all) do + desc "An array of security protocols which should be used when accessing the PS repository. + See: https://docs.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype?view=netframework-4.7.2. + e.g. securityprotocols => [Tls,Tls11,TLS12] + If this is not specified the system default TLS settings will be used." + + # Make sure we convert to an array. + munge do |value| + [value].flatten + end + end end