bosh_cli 1.3074.0 → 1.3087.0

Sign up to get free protection for your applications and to get access to all the features.
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