bosh_cli 1.3074.0 → 1.3087.0

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
  SHA1:
3
- metadata.gz: 03a8b109ad4c55b514fb131b00bd54acde8f0ff2
4
- data.tar.gz: 5249fa58417ad2159886f1331496919250b04a90
3
+ metadata.gz: 9baaebb2117962d1abb67eb395ba2511f70e2fab
4
+ data.tar.gz: a3d09e561bf02bc54b9d83862496ea116d1cce60
5
5
  SHA512:
6
- metadata.gz: 22665be8b8eb91584b7240bb35ebfcc36a37bda3a08fc58e71dea069de5555d17a0c829c59ab85eaeb50656ca1f63b2997b1b13ee9f18ef7656ade0bf09d7897
7
- data.tar.gz: 3bcfcb6e37d01993ab53670fe07c9ab73646faf9e0dabe263e29f56107b9edd13d754b5991d833696f244a0c9d15af4f66ae38a4b4721b527467813b7491f6e8
6
+ metadata.gz: c6e8bcec875f1365e972686cdb11f497f284022e7c94a1111be28c62e655ed9663e007d95082e5020afcde9698229d40ee7e6cc4f641aab17311b715d0e51cd5
7
+ data.tar.gz: 5590a84655a049ceca953160d4f73d17a218c0ec08179a1eed8ed517ef5834365b508efca9777fce84f8b12905e04a8c8d4f9e1210e1ae1e6949c040ab0fc705
@@ -39,8 +39,14 @@ module Bosh::Cli
39
39
  end
40
40
 
41
41
  def director
42
- @director ||= Bosh::Cli::Client::Director.new(
43
- target, credentials, @options.select { |k, _| k == :no_track })
42
+ return @director if @director
43
+
44
+ director_client_options = [:no_track, :ca_cert]
45
+ @director = Bosh::Cli::Client::Director.new(
46
+ target,
47
+ credentials,
48
+ @options.select { |k, _| director_client_options.include?(k) }
49
+ )
44
50
  end
45
51
 
46
52
  def release
@@ -134,8 +140,9 @@ module Bosh::Cli
134
140
 
135
141
  def auth_info
136
142
  @auth_info ||= begin
137
- director_client = Client::Director.new(target)
138
- Client::Uaa::AuthInfo.new(director_client, ENV, config.ca_cert(target))
143
+ ca_cert = config.ca_cert(target)
144
+ director_client = Client::Director.new(target, nil, ca_cert: ca_cert)
145
+ Client::Uaa::AuthInfo.new(director_client, ENV, ca_cert)
139
146
  end
140
147
  end
141
148
 
@@ -35,6 +35,7 @@ module Bosh
35
35
  @track_tasks = !options.delete(:no_track)
36
36
  @num_retries = options.fetch(:num_retries, 5)
37
37
  @retry_wait_interval = options.fetch(:retry_wait_interval, 5)
38
+ @ca_cert = options[:ca_cert]
38
39
  end
39
40
 
40
41
  def uuid
@@ -292,6 +293,7 @@ module Bosh
292
293
 
293
294
  options[:payload] = JSON.generate(payload)
294
295
  options[:content_type] = 'application/json'
296
+ options[:task_success_state] = :queued
295
297
 
296
298
  request_and_track(:post, url, options)
297
299
  end
@@ -728,8 +730,34 @@ module Bosh
728
730
  http_client.receive_timeout = API_TIMEOUT
729
731
  http_client.connect_timeout = CONNECT_TIMEOUT
730
732
 
731
- http_client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
732
- http_client.ssl_config.verify_callback = Proc.new {}
733
+ if @ca_cert.nil?
734
+ http_client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
735
+ http_client.ssl_config.verify_callback = Proc.new {}
736
+ else
737
+ unless File.exists?(@ca_cert)
738
+ err('Invalid ca certificate path')
739
+ end
740
+
741
+ parsed_url = nil
742
+ begin
743
+ parsed_url = URI.parse(uri)
744
+ rescue => e
745
+ err("Failed to parse director URL: #{e.message}")
746
+ end
747
+
748
+ unless parsed_url.instance_of?(URI::HTTPS)
749
+ err('CA certificate cannot be used with HTTP protocol')
750
+ end
751
+
752
+ # pass in client certificate
753
+ begin
754
+ cert_store = OpenSSL::X509::Store.new
755
+ cert_store.add_file(@ca_cert)
756
+ rescue OpenSSL::X509::StoreError
757
+ err('Invalid SSL Cert')
758
+ end
759
+ http_client.ssl_config.cert_store = cert_store
760
+ end
733
761
 
734
762
  if @credentials
735
763
  headers['Authorization'] = @credentials.authorization_header
@@ -750,6 +778,10 @@ module Bosh
750
778
  HTTPClient::KeepAliveDisconnected,
751
779
  OpenSSL::SSL::SSLError,
752
780
  OpenSSL::X509::StoreError => e
781
+
782
+ if e.is_a?(OpenSSL::SSL::SSLError) && e.message.include?('certificate verify failed')
783
+ err('Invalid SSL Cert')
784
+ end
753
785
  raise DirectorInaccessible, "cannot access director (#{e.message})"
754
786
 
755
787
  rescue HTTPClient::BadResponseError => e
@@ -31,7 +31,7 @@ module Bosh::Cli::Command
31
31
  end
32
32
 
33
33
  if target
34
- old_director = Bosh::Cli::Client::Director.new(target, credentials)
34
+ old_director = Bosh::Cli::Client::Director.new(target, credentials, ca_cert: config.ca_cert)
35
35
  old_director_uuid = old_director.get_status["uuid"] rescue nil
36
36
  else
37
37
  old_director_uuid = nil
@@ -49,8 +49,9 @@ module Bosh::Cli::Command
49
49
  "Please find your director IP or hostname and target it first.")
50
50
  end
51
51
 
52
+ target_ca_cert = config.ca_cert(new_target_url)
52
53
  new_director = Bosh::Cli::Client::Director.new(
53
- new_target_url, credentials)
54
+ new_target_url, credentials, ca_cert: target_ca_cert)
54
55
 
55
56
  status = new_director.get_status
56
57
 
@@ -83,7 +83,7 @@ module Bosh::Cli::Command
83
83
  end
84
84
 
85
85
  director_url = normalize_url(director_url)
86
- director = Bosh::Cli::Client::Director.new(director_url)
86
+ director = Bosh::Cli::Client::Director.new(director_url, nil, ca_cert: options[:ca_cert])
87
87
 
88
88
  begin
89
89
  status = director.get_status
@@ -237,7 +237,7 @@ module Bosh::Cli::Command
237
237
  end
238
238
 
239
239
  def get_director_status
240
- Bosh::Cli::Client::Director.new(target, credentials).get_status
240
+ Bosh::Cli::Client::Director.new(target, credentials, ca_cert: config.ca_cert).get_status
241
241
  end
242
242
  end
243
243
  end
@@ -1,16 +1,13 @@
1
1
  require 'cli/job_command_args'
2
+ require 'cli/ssh_session'
2
3
 
3
4
  module Bosh::Cli
4
5
  module Command
5
6
  class Ssh < Base
6
- SSH_USER_PREFIX = 'bosh_'
7
- SSH_DSA_PUB = File.expand_path('~/.ssh/id_dsa.pub')
8
- SSH_RSA_PUB = File.expand_path('~/.ssh/id_rsa.pub')
9
7
 
10
8
  # bosh ssh
11
9
  usage 'ssh'
12
10
  desc 'Execute command or start an interactive session'
13
- option '--public_key FILE', 'Public key'
14
11
  option '--gateway_host HOST', 'Gateway host'
15
12
  option '--gateway_user USER', 'Gateway user'
16
13
  option '--gateway_identity_file FILE', 'Gateway identity file'
@@ -46,7 +43,6 @@ module Bosh::Cli
46
43
  'Note: for download /path/to/destination is a directory'
47
44
  option '--download', 'Download file'
48
45
  option '--upload', 'Upload file'
49
- option '--public_key FILE', 'Public key'
50
46
  option '--gateway_host HOST', 'Gateway host'
51
47
  option '--gateway_user USER', 'Gateway user'
52
48
  option '--gateway_identity_file FILE', 'Gateway identity file'
@@ -111,14 +107,16 @@ module Bosh::Cli
111
107
  # @param [Integer] index
112
108
  # @param [optional,String] password
113
109
  def setup_ssh(deployment_name, job, index, password)
114
- user = random_ssh_username
115
110
 
116
111
  say("Target deployment is `#{deployment_name}'")
117
112
  nl
118
113
  say('Setting up ssh artifacts')
114
+
115
+ ssh_session = SSHSession.new
116
+
119
117
  status, task_id = director.setup_ssh(
120
- deployment_name, job, index, user,
121
- public_key, encrypt_password(password))
118
+ deployment_name, job, index, ssh_session.user,
119
+ ssh_session.public_key, encrypt_password(password))
122
120
 
123
121
  unless status == :done
124
122
  err("Failed to set up SSH: see task #{task_id} log for details")
@@ -137,6 +135,8 @@ module Bosh::Cli
137
135
  end
138
136
  end
139
137
 
138
+ ssh_session.set_host_session(sessions.first)
139
+
140
140
  begin
141
141
  if options[:gateway_host]
142
142
  require 'net/ssh/gateway'
@@ -153,20 +153,17 @@ module Bosh::Cli
153
153
  gateway = nil
154
154
  end
155
155
 
156
- yield sessions, user, gateway
156
+ yield sessions, gateway, ssh_session
157
157
  ensure
158
158
  nl
159
159
  say('Cleaning up ssh artifacts')
160
+ ssh_session.cleanup
160
161
  indices = sessions.map { |session| session['index'] }
161
- director.cleanup_ssh(deployment_name, job, "^#{user}$", indices)
162
+ director.cleanup_ssh(deployment_name, job, "^#{ssh_session.user}$", indices)
162
163
  gateway.shutdown! if gateway
163
164
  end
164
165
  end
165
166
 
166
- def random_ssh_username
167
- SSH_USER_PREFIX + rand(36**9).to_s(36)
168
- end
169
-
170
167
  # @param [String] job Job name
171
168
  # @param [Integer] index Job index
172
169
  def setup_interactive_shell(deployment_name, job, index)
@@ -180,7 +177,7 @@ module Bosh::Cli
180
177
  err('Please provide ssh password') if password.blank?
181
178
  end
182
179
 
183
- setup_ssh(deployment_name, job, index, password) do |sessions, user, gateway|
180
+ setup_ssh(deployment_name, job, index, password) do |sessions, gateway, ssh_session|
184
181
  session = sessions.first
185
182
 
186
183
  unless session['status'] == 'success' && session['ip']
@@ -192,26 +189,30 @@ module Bosh::Cli
192
189
  skip_strict_host_key_checking = options[:strict_host_key_checking] =~ (/(no|false)$/i) ?
193
190
  '-o StrictHostKeyChecking=no' : '-o StrictHostKeyChecking=yes'
194
191
 
192
+ private_key_option = ssh_session.ssh_private_key_option
193
+
195
194
  if gateway
196
195
  port = gateway.open(session['ip'], 22)
197
- ssh_session = Process.spawn('ssh', "#{user}@localhost", '-p', port.to_s, skip_strict_host_key_checking)
198
- Process.waitpid(ssh_session)
196
+ known_host_option = ssh_session.ssh_known_host_option(port)
197
+ ssh_session_pid = Process.spawn('ssh', "#{ssh_session.user}@localhost", '-p', port.to_s, private_key_option, skip_strict_host_key_checking, known_host_option)
198
+ Process.waitpid(ssh_session_pid)
199
199
  gateway.close(port)
200
200
  else
201
- ssh_session = Process.spawn('ssh', "#{user}@#{session['ip']}", skip_strict_host_key_checking)
202
- Process.waitpid(ssh_session)
201
+ known_host_option = ssh_session.ssh_known_host_option(nil)
202
+ ssh_session_pid = Process.spawn('ssh', "#{ssh_session.user}@#{session['ip']}", private_key_option, skip_strict_host_key_checking, known_host_option)
203
+ Process.waitpid(ssh_session_pid)
203
204
  end
204
205
  end
205
206
  end
206
207
 
207
208
  def perform_operation(operation, deployment_name, job, index, args)
208
- setup_ssh(deployment_name, job, index, options[:default_password]) do |sessions, user, gateway|
209
+ setup_ssh(deployment_name, job, index, options[:default_password]) do |sessions, gateway, ssh_session|
209
210
  sessions.each do |session|
210
211
  unless session['status'] == 'success' && session['ip']
211
212
  err("Failed to set up SSH on #{job}/#{index}: #{session.inspect}")
212
213
  end
213
214
 
214
- with_ssh(user, session['ip'], gateway) do |ssh|
215
+ with_ssh(session['ip'], ssh_session, gateway) do |ssh|
215
216
  case operation
216
217
  when :exec
217
218
  nl
@@ -232,40 +233,24 @@ module Bosh::Cli
232
233
  end
233
234
  end
234
235
 
235
- # @return [String] Public key
236
- def public_key
237
- public_key_path = options[:public_key]
238
-
239
- if public_key_path
240
- unless File.file?(public_key_path)
241
- err("Can't find file `#{public_key_path}'")
242
- end
243
- return File.read(public_key_path)
244
- else
245
- %x[ssh-add -L 1>/dev/null 2>&1]
246
- if $?.exitstatus == 0
247
- return %x[ssh-add -L].split("\n").first
248
- else
249
- [SSH_DSA_PUB, SSH_RSA_PUB].each do |key_file|
250
- return File.read(key_file) if File.file?(key_file)
251
- end
252
- end
253
- end
254
-
255
- err('Please specify a public key file')
256
- end
257
-
258
236
  # @param [String] user
259
237
  # @param [String] ip
260
238
  # @param [optional, Net::SSH::Gateway] gateway
261
239
  # @yield [Net::SSH]
262
- def with_ssh(user, ip, gateway = nil)
240
+ def with_ssh(ip, ssh_session, gateway = nil)
263
241
  require 'net/scp'
242
+ options = { :keys => ssh_session.ssh_private_key_path }
264
243
  if gateway
265
- gateway.ssh(ip, user) { |ssh| yield ssh }
244
+ gateway.ssh(ip, ssh_session.user, options) { |ssh| yield ssh }
266
245
  else
267
246
  require 'net/ssh'
268
- Net::SSH.start(ip, user) { |ssh| yield ssh }
247
+
248
+ known_host_path = ssh_session.ssh_known_host_path(nil)
249
+ if known_host_path.length > 0
250
+ options[:user_known_hosts_file] = known_host_path
251
+ end
252
+
253
+ Net::SSH.start(ip, ssh_session.user, options) { |ssh| yield ssh }
269
254
  end
270
255
  end
271
256
  end
@@ -218,10 +218,11 @@ module Bosh::Cli
218
218
  end
219
219
 
220
220
 
221
- def save_ca_cert_path(cert_path)
221
+ def save_ca_cert_path(cert_path, for_target=nil)
222
222
  expanded_path = cert_path ? File.expand_path(cert_path) : nil
223
+ cert_target = for_target || target
223
224
  @config_file['ca_cert'] ||= {}
224
- @config_file['ca_cert'][target] = expanded_path
225
+ @config_file['ca_cert'][cert_target] = expanded_path
225
226
 
226
227
  expanded_path
227
228
  end
@@ -0,0 +1,116 @@
1
+ require 'sshkey'
2
+
3
+ module Bosh::Cli
4
+ class SSHSession
5
+
6
+ SSH_USER_PREFIX = 'bosh_'
7
+
8
+ attr_reader :public_key, :user
9
+
10
+ def initialize
11
+ @session_uuid = SecureRandom::uuid
12
+ @public_key = generate_rsa_key
13
+ @user = random_ssh_username
14
+ end
15
+
16
+ def set_host_session(session)
17
+ @host_session = session
18
+ end
19
+
20
+ def ssh_known_host_option(port)
21
+ path = user_known_host_path(port)
22
+ if path.length > 0
23
+ path = "-o UserKnownHostsFile=#{path}"
24
+ end
25
+ return path
26
+ end
27
+
28
+ def ssh_known_host_path(port)
29
+ user_known_host_path(port)
30
+ end
31
+
32
+ def ssh_private_key_option
33
+ "-i#{ssh_private_key_path}"
34
+ end
35
+
36
+ def ssh_private_key_path
37
+ File.join(ENV['HOME'], '.bosh', 'tmp', "#{@session_uuid}_key")
38
+ end
39
+
40
+ def cleanup
41
+ remove_private_key
42
+ remove_known_host_file
43
+ end
44
+
45
+ private
46
+
47
+ def generate_rsa_key
48
+ key = SSHKey.generate(
49
+ type: "RSA",
50
+ bits: 2048,
51
+ comment: "bosh-ssh",
52
+ )
53
+ add_private_key(key.private_key)
54
+ return key.ssh_public_key
55
+ end
56
+
57
+ def remove_private_key
58
+ file_name = private_key_file_name
59
+ FileUtils.rm_rf(file_name) if File.exist?(file_name)
60
+ end
61
+
62
+ def private_key_file_name
63
+ File.join(ENV['HOME'], '.bosh', 'tmp', "#{@session_uuid}_key")
64
+ end
65
+
66
+ def add_private_key(private_key)
67
+ file_name = private_key_file_name
68
+ create_dir_for_file(file_name)
69
+
70
+ key_File = File.new(file_name, "w", 0400)
71
+ key_File.puts(private_key)
72
+ key_File.close
73
+ end
74
+
75
+ def random_ssh_username
76
+ SSH_USER_PREFIX + rand(36**9).to_s(36)
77
+ end
78
+
79
+ def user_known_host_path(gatewayPort)
80
+ if @host_session.include?('host_public_key')
81
+ hostEntryIP = if gatewayPort then "[localhost]:#{gatewayPort}" else @host_session['ip'] end
82
+ hostEntry = "#{hostEntryIP} #{@host_session['host_public_key']}"
83
+ add_known_host_file(hostEntry)
84
+ return known_host_file_path
85
+ else
86
+ return String.new
87
+ end
88
+ end
89
+
90
+ def known_host_file_path
91
+ File.join(ENV['HOME'], '.bosh', 'tmp', "#{@session_uuid}_known_hosts")
92
+ end
93
+
94
+ def add_known_host_file(hostEntry)
95
+ file_name = known_host_file_path
96
+
97
+ create_dir_for_file(file_name)
98
+
99
+ known_host_file = File.new(file_name, "w")
100
+ known_host_file.puts(hostEntry)
101
+ known_host_file.close
102
+ end
103
+
104
+ def remove_known_host_file
105
+ file_name = known_host_file_path
106
+ FileUtils.rm_rf(file_name) if File.exist?(file_name)
107
+ end
108
+
109
+ def create_dir_for_file(file_name)
110
+ dirname = File.dirname(file_name)
111
+ unless File.directory?(dirname)
112
+ FileUtils.mkdir_p(dirname)
113
+ end
114
+ end
115
+ end
116
+ end
@@ -13,6 +13,7 @@ module Bosh::Cli::TaskTracking
13
13
  def initialize(director, task_id, options = {})
14
14
  @director = director
15
15
  @task_id = task_id
16
+ @task_success_state = options[:task_success_state] || :done
16
17
  @options = options
17
18
 
18
19
  @quiet = !!options[:quiet]
@@ -139,7 +140,7 @@ module Bosh::Cli::TaskTracking
139
140
  end
140
141
 
141
142
  def finished?(state)
142
- 'done error cancelled'.include?(state)
143
+ "#{@task_success_state} error cancelled".include?(state)
143
144
  end
144
145
 
145
146
  def interactive?
@@ -1,5 +1,5 @@
1
1
  module Bosh
2
2
  module Cli
3
- VERSION = '1.3074.0'
3
+ VERSION = '1.3087.0'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bosh_cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3074.0
4
+ version: 1.3087.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - VMware
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-19 00:00:00.000000000 Z
11
+ date: 2015-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bosh_common
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.3074.0
19
+ version: 1.3087.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.3074.0
26
+ version: 1.3087.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bosh-template
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.3074.0
33
+ version: 1.3087.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.3074.0
40
+ version: 1.3087.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: cf-uaa-lib
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -128,14 +128,14 @@ dependencies:
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: 1.3074.0
131
+ version: 1.3087.0
132
132
  type: :runtime
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: 1.3074.0
138
+ version: 1.3087.0
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: net-ssh
141
141
  requirement: !ruby/object:Gem::Requirement
@@ -206,6 +206,20 @@ dependencies:
206
206
  - - "~>"
207
207
  - !ruby/object:Gem::Version
208
208
  version: 0.5.4
209
+ - !ruby/object:Gem::Dependency
210
+ name: sshkey
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - "~>"
214
+ - !ruby/object:Gem::Version
215
+ version: 1.7.0
216
+ type: :runtime
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - "~>"
221
+ - !ruby/object:Gem::Version
222
+ version: 1.7.0
209
223
  - !ruby/object:Gem::Dependency
210
224
  name: rspec
211
225
  requirement: !ruby/object:Gem::Requirement
@@ -412,6 +426,7 @@ files:
412
426
  - lib/cli/resurrection.rb
413
427
  - lib/cli/runner.rb
414
428
  - lib/cli/source_control/git_ignore.rb
429
+ - lib/cli/ssh_session.rb
415
430
  - lib/cli/stemcell.rb
416
431
  - lib/cli/task_tracking.rb
417
432
  - lib/cli/task_tracking/event_log_renderer.rb