train-winrm 0.2.6 → 0.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f72fe0398deeb2ddebaa5bce7177f0bfb5e85394c371a5443d8d08f2dec8c009
4
- data.tar.gz: f2201788d2a72840c262312953b57f4d9c391b22ed435c66fa81774539469b3b
3
+ metadata.gz: f285f418da910b5aa18969c6eb97f9702024bbdbaca9ec085d293708af3172e5
4
+ data.tar.gz: 81e3e1f9f10a11d62b02911514acbb076d3bcc91424d11d6a39fed1db24afa6f
5
5
  SHA512:
6
- metadata.gz: 411d8547046c5a3c85bcf327a806227604df7dfa5cdb3ee1972bb6ae5456b96e949e28497f0b0f5af68d402b1802b39282b3271a2c9646908d46e868bf21bff0
7
- data.tar.gz: 73045b37df8ed789d94e1aa476b2370128ac812f945c2a9819cb6e15acf977d8483ba241b7dec498b5756482ba080df79f6223ac3c70c005fcfb6b458419186f
6
+ metadata.gz: 4555a472e56ecf3f72f1f84e2002053d400305370daa41a3f39ce83d993c0859e6e0d089bcab236d173d2fe76bf4731feca62a8591bf63ac7784cf5ca1ccb061
7
+ data.tar.gz: ef30862cd5a211b093a44779069ff3b0273d50478fec2fe9eb276b630462254a990e8d119226f9f8f8f56bb41b992ae0e7f9374a5ff794a9eec596fadf0e886b
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  #
4
2
  # Author:: Salim Afiune (<salim@afiunemaya.com.mx>)
5
3
  # Author:: Matt Wrock (<matt@mattwrock.com>)
@@ -35,6 +33,8 @@
35
33
 
36
34
  require "train"
37
35
  require "train/plugins"
36
+ # This module may need to directly require WinRM to reference its exception classes
37
+ require "winrm" unless defined?(WinRM)
38
38
 
39
39
  module TrainPlugins
40
40
  module WinRM
@@ -49,6 +49,7 @@ module TrainPlugins
49
49
  @connection_retry_sleep = @options.delete(:connection_retry_sleep)
50
50
  @max_wait_until_ready = @options.delete(:max_wait_until_ready)
51
51
  @operation_timeout = @options.delete(:operation_timeout)
52
+ @shell_type = @options.delete(:winrm_shell_type)
52
53
  end
53
54
 
54
55
  # (see Base::Connection#close)
@@ -109,15 +110,47 @@ module TrainPlugins
109
110
  Train::File::Remote::Windows.new(self, path)
110
111
  end
111
112
 
112
- def run_command_via_connection(command, &data_handler)
113
+ def run_command_via_connection(command, opts = {}, &data_handler)
113
114
  return if command.nil?
114
115
 
115
116
  logger.debug("[WinRM] #{self} (#{command})")
116
117
  out = ""
118
+ response = nil
119
+ timeout = opts[:timeout]&.to_i
120
+
121
+ # Run the command in a thread, to support timing out the command
122
+ thr = Thread.new do
123
+ # Surface any exceptions in this thread back to this method
124
+ Thread.current.report_on_exception = false
125
+ Thread.current.abort_on_exception = true
126
+ begin
127
+ response = session.run(command) do |stdout, _|
128
+ yield(stdout) if data_handler && stdout
129
+ out << stdout if stdout
130
+ end
131
+ rescue ::WinRM::WinRMHTTPTransportError => e
132
+ # If this command hits timeout, there is also a potential race in the HTTP transport
133
+ # where decryption is attempted on an empty message.
134
+ raise e unless timeout && e.to_s == "Could not decrypt NTLM message. ()."
135
+ rescue RuntimeError => e
136
+ # Ref: https://github.com/WinRb/WinRM/issues/315
137
+ # If this command hits timeout, calling close with the command currently running causes
138
+ # a RuntimeError error in WinRM's cleanup code. This specific error can be ignored.
139
+ # The command will be terminated and further commands can be sent on the connection.
140
+ raise e unless timeout && e.to_s == "opts[:shell_id] is required"
141
+ end
142
+ end
117
143
 
118
- response = session.run(command) do |stdout, _|
119
- yield(stdout) if data_handler && stdout
120
- out << stdout if stdout
144
+ if timeout
145
+ res = thr.join(timeout)
146
+ unless res
147
+ msg = "PowerShell command '(#{command})' reached timeout (#{timeout}s)"
148
+ logger.info("[WinRM] #{msg}")
149
+ close
150
+ raise Train::CommandTimeoutReached.new msg
151
+ end
152
+ else
153
+ thr.join
121
154
  end
122
155
 
123
156
  CommandResult.new(out, response.stderr, response.exitcode)
@@ -197,7 +230,7 @@ module TrainPlugins
197
230
  opts[:operation_timeout] = @operation_timeout unless @operation_timeout.nil?
198
231
  @service = ::WinRM::Connection.new(options.merge(opts))
199
232
  @service.logger = logger
200
- @service.shell(:powershell)
233
+ @service.shell(@shell_type)
201
234
  end
202
235
  end
203
236
 
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  #
4
2
  # Author:: Salim Afiune (<salim@afiunemaya.com.mx>)
5
3
  # Author:: Matt Wrock (<matt@mattwrock.com>)
@@ -21,8 +19,6 @@
21
19
  # See the License for the specific language governing permissions and
22
20
  # limitations under the License.
23
21
 
24
- require "rbconfig"
25
- require "uri"
26
22
  require "train"
27
23
  require "train/errors"
28
24
  require "train/plugins"
@@ -50,6 +46,7 @@ module TrainPlugins
50
46
 
51
47
  # ref: https://github.com/winrb/winrm#transports
52
48
  SUPPORTED_WINRM_TRANSPORTS = %i{negotiate ssl plaintext kerberos}.freeze
49
+ SUPPORTED_WINRM_SHELL_TYPES = %i{powershell elevated cmd}.freeze
53
50
 
54
51
  # common target configuration
55
52
  option :host, required: true
@@ -76,6 +73,10 @@ module TrainPlugins
76
73
  # from the winrm endpoint. Does not mean that the command has
77
74
  # completed in this time, only that the server has ack'd the request.
78
75
  option :operation_timeout, default: nil
76
+ option :winrm_shell_type , default: "powershell"
77
+ option :client_cert , default: nil
78
+ option :client_key , default: nil
79
+ option :client_key_pass , default: nil
79
80
 
80
81
  def initialize(opts)
81
82
  super(opts)
@@ -109,6 +110,11 @@ module TrainPlugins
109
110
  raise Train::ClientError, "Unsupported transport type: #{winrm_transport.inspect}"
110
111
  end
111
112
 
113
+ winrm_shell_type = opts[:winrm_shell_type].to_sym
114
+ unless SUPPORTED_WINRM_SHELL_TYPES.include?(winrm_shell_type)
115
+ raise Train::ClientError, "Unsupported winrm shell type: #{winrm_shell_type.inspect}"
116
+ end
117
+
112
118
  # remove leading '/'
113
119
  path = (opts[:path] || "").sub(%r{^/+}, "")
114
120
 
@@ -116,6 +122,7 @@ module TrainPlugins
116
122
  end
117
123
 
118
124
  WINRM_FS_SPEC_VERSION = "~> 1.0".freeze
125
+ WINRM_ELEVATED_SPEC_VERSION = "~> 1.2.2".freeze
119
126
 
120
127
  # Builds the hash of options needed by the Connection object on
121
128
  # construction.
@@ -142,13 +149,17 @@ module TrainPlugins
142
149
  service: opts[:kerberos_service],
143
150
  ca_trust_file: opts[:ca_trust_file],
144
151
  ssl_peer_fingerprint: opts[:ssl_peer_fingerprint],
152
+ winrm_shell_type: opts[:winrm_shell_type],
153
+ client_cert: opts[:client_cert],
154
+ client_key: opts[:client_key],
155
+ key_pass: opts[:client_key_pass],
145
156
  }
146
157
  end
147
158
 
148
159
  # Creates a new WinRM Connection instance and save it for potential
149
160
  # future reuse.
150
161
  #
151
- # @param options [Hash] conneciton options
162
+ # @param options [Hash] connection options
152
163
  # @return [WinRM::Connection] a WinRM Connection instance
153
164
  # @api private
154
165
  def create_new_connection(options, &block)
@@ -166,32 +177,41 @@ module TrainPlugins
166
177
  spec_version = WINRM_FS_SPEC_VERSION.dup
167
178
  logger.debug("winrm-fs requested," \
168
179
  " loading WinRM::FS gem (#{spec_version})")
169
- gem "winrm-fs", spec_version
170
- first_load = require "winrm-fs"
171
- load_winrm_transport!
180
+ load_dependency("winrm-fs", spec_version)
181
+
182
+ spec_version = WINRM_ELEVATED_SPEC_VERSION.dup
183
+ logger.debug("winrm-elevated requested," \
184
+ " loading WinRM-elevated gem (#{spec_version})")
185
+ load_dependency("winrm-elevated", spec_version)
186
+ end
187
+
188
+ def load_dependency(gem_name, spec_version)
189
+ gem gem_name, spec_version
190
+ first_load = require gem_name
191
+ load_winrm_transport!(gem_name)
172
192
 
173
193
  if first_load
174
- logger.debug("WinRM::FS library loaded")
194
+ logger.debug("#{gem_name} library loaded")
175
195
  else
176
- logger.debug("WinRM::FS previously loaded")
196
+ logger.debug("#{gem_name} previously loaded")
177
197
  end
178
198
  rescue LoadError => e
179
199
  logger.fatal(
180
- "The `winrm-fs' gem is missing and must" \
200
+ "The `#{gem_name}' gem is missing and must" \
181
201
  " be installed or cannot be properly activated. Run" \
182
- " `gem install winrm-fs --version '#{spec_version}'`" \
202
+ " `gem install #{gem_name} --version '#{spec_version}'`" \
183
203
  " or add the following to your Gemfile if you are using Bundler:" \
184
- " `gem 'winrm-fs', '#{spec_version}'`."
204
+ " `gem '#{gem_name}', '#{spec_version}'`."
185
205
  )
186
206
  raise Train::UserError,
187
- "Could not load or activate WinRM::FS (#{e.message})"
207
+ "Could not load or activate #{gem_name} (#{e.message})"
188
208
  end
189
209
 
190
210
  # Load WinRM::Transport code.
191
211
  #
192
212
  # @api private
193
- def load_winrm_transport!
194
- silence_warnings { require "winrm-fs" }
213
+ def load_winrm_transport!(gem_name)
214
+ silence_warnings { require gem_name }
195
215
  end
196
216
 
197
217
  # Return the last saved WinRM connection instance.
@@ -5,6 +5,6 @@
5
5
 
6
6
  module TrainPlugins
7
7
  module WinRM
8
- VERSION = "0.2.6".freeze
8
+ VERSION = "0.2.13".freeze
9
9
  end
10
10
  end
data/lib/train-winrm.rb CHANGED
@@ -11,7 +11,7 @@
11
11
  libdir = File.dirname(__FILE__)
12
12
  $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
13
13
 
14
- # It's traditonal to keep your gem version in a separate file, so CI can find it easier.
14
+ # It's traditional to keep your gem version in a separate file, so CI can find it easier.
15
15
  require_relative "train-winrm/version"
16
16
 
17
17
  # A train plugin has three components: Transport, Connection, and (optionally) Platform.
metadata CHANGED
@@ -1,29 +1,49 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: train-winrm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chef InSpec Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-31 00:00:00.000000000 Z
11
+ date: 2022-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: winrm
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.3.6
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '3.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 2.3.6
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: winrm-elevated
15
35
  requirement: !ruby/object:Gem::Requirement
16
36
  requirements:
17
37
  - - "~>"
18
38
  - !ruby/object:Gem::Version
19
- version: '2.0'
39
+ version: 1.2.2
20
40
  type: :runtime
21
41
  prerelease: false
22
42
  version_requirements: !ruby/object:Gem::Requirement
23
43
  requirements:
24
44
  - - "~>"
25
45
  - !ruby/object:Gem::Version
26
- version: '2.0'
46
+ version: 1.2.2
27
47
  - !ruby/object:Gem::Dependency
28
48
  name: winrm-fs
29
49
  requirement: !ruby/object:Gem::Requirement
@@ -70,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
90
  - !ruby/object:Gem::Version
71
91
  version: '0'
72
92
  requirements: []
73
- rubygems_version: 3.0.3
93
+ rubygems_version: 3.1.4
74
94
  signing_key:
75
95
  specification_version: 4
76
96
  summary: Windows WinRM API Transport for Train