summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryan McLellan <btm@loftninjas.org>2019-05-13 16:01:51 -0400
committerGitHub <noreply@github.com>2019-05-13 16:01:51 -0400
commitdb18ba559e44e919f320e36f8ed62eb19c77676a (patch)
tree8fdf014ef29494fc382506592a41049f946aab41
parent8c986011e05aaf6043bcb8fe82f04a5ea7472a64 (diff)
parent0a1a78f404dcd2c8f1405e9e5af26dd80155ff6b (diff)
downloadchef-db18ba559e44e919f320e36f8ed62eb19c77676a.tar.gz
Merge pull request #8524 from chef/btm/retry-fingerprint
Use new Net:SSH host key verify values
-rw-r--r--RELEASE_NOTES.md3
-rw-r--r--lib/chef/knife/bootstrap.rb48
-rw-r--r--spec/unit/knife/bootstrap_spec.rb4
3 files changed, 33 insertions, 22 deletions
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 6531e522db..510fc71e08 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -204,12 +204,13 @@ In order to accommodate a combined bootstrap that supports both SSH and WinRM, s
| --connection-user | user to authenticate as, regardless of protocol |
| --connection-password| Password to authenticate as, regardless of protocol |
| --connection-port | port to connect to, regardless of protocol |
+| --ssh-verify-host-key VALUE | Verify host key. Default is 'always'. Valid values are 'accept', 'accept\_new', 'accept\_new\_or\_local\_tunnel', and 'never'. |
#### Changed Flags
| Flag | New Option | Notes |
|-----:|:-----------|:------|
-| --[no-]host-key-verify |--[no-]ssh-verify-host-key| |
+| --[no-]host-key-verify |--ssh-verify-host-key VALUE | See above for valid values. |
| --forward-agent | --ssh-forward-agent| |
| --session-timeout MINUTES | --session-timeout SECONDS|New for ssh, existing for winrm. The unit has changed from MINUTES to SECONDS for consistency with other timeouts.|
| --ssh-password | --connection-password | |
diff --git a/lib/chef/knife/bootstrap.rb b/lib/chef/knife/bootstrap.rb
index e143ebfe31..04eabc3c83 100644
--- a/lib/chef/knife/bootstrap.rb
+++ b/lib/chef/knife/bootstrap.rb
@@ -31,15 +31,6 @@ class Chef
SUPPORTED_CONNECTION_PROTOCOLS = %w{ssh winrm}.freeze
WINRM_AUTH_PROTOCOL_LIST = %w{plaintext kerberos ssl negotiate}.freeze
- # @param opt_arry [Array]
- #
- # @return [String] a friendly quoted list of items complete with "and"
- def self.friendly_opt_list(opt_array)
- opts = opt_array.map { |x| "'#{x}'" }
- return opts.join(" and ") if opts.size < 3
- opts[0..-2].join(", ") + " and " + opts[-1]
- end
-
# Common connectivity options
option :connection_user,
short: "-U USERNAME",
@@ -59,7 +50,8 @@ class Chef
option :connection_protocol,
short: "-o PROTOCOL",
long: "--connection-protocol PROTOCOL",
- description: "The protocol to use to connect to the target node. Supports: #{friendly_opt_list(SUPPORTED_CONNECTION_PROTOCOLS)}."
+ description: "The protocol to use to connect to the target node.",
+ in: SUPPORTED_CONNECTION_PROTOCOLS
option :max_wait,
short: "-W SECONDS",
@@ -88,7 +80,7 @@ class Chef
option :winrm_auth_method,
short: "-w AUTH-METHOD",
long: "--winrm-auth-method AUTH-METHOD",
- description: "The WinRM authentication method to use. Valid choices are #{friendly_opt_list(WINRM_AUTH_PROTOCOL_LIST)}.",
+ description: "The WinRM authentication method to use.",
proc: Proc.new { |protocol| Chef::Config[:knife][:winrm_auth_method] = protocol },
in: WINRM_AUTH_PROTOCOL_LIST
@@ -146,9 +138,9 @@ class Chef
description: "The SSH identity file used for authentication."
option :ssh_verify_host_key,
- long: "--[no-]ssh-verify-host-key",
- description: "Verify host key, enabled by default.",
- boolean: true
+ long: "--ssh-verify-host-key VALUE",
+ description: "Verify host key. Default is 'always'.",
+ in: %w{always accept_new accept_new_or_local_tunnel never}
#
# bootstrap options
@@ -162,7 +154,7 @@ class Chef
option :channel,
long: "--channel CHANNEL",
- description: "Install from the given channel. Valid values are 'stable, 'current', and 'unstable'. Default is 'stable'",
+ description: "Install from the given channel. Default is 'stable'.",
default: "stable",
in: %w{stable current unstable}
@@ -550,6 +542,7 @@ class Chef
$stdout.sync = true
register_client
connect!
+
unless client_builder.client_path.nil?
bootstrap_context.client_pem = client_builder.client_path
end
@@ -599,6 +592,24 @@ class Chef
ui.info("Connecting to #{ui.color(server_name, :bold)}")
opts = connection_opts.dup
do_connect(opts)
+ rescue Train::Transports::SSHFailed => e
+ if e.message =~ /fingerprint (\S+) is unknown for "(.+)"/
+ fingerprint = $1
+ hostname, ip = $2.split(",")
+ # TODO: convert the SHA256 base64 value to hex with colons
+ # 'ssh' example output:
+ # RSA key fingerprint is e5:cb:c0:e2:21:3b:12:52:f8:ce:cb:00:24:e2:0c:92.
+ # ECDSA key fingerprint is 5d:67:61:08:a9:d7:01:fd:5e:ae:7e:09:40:ef:c0:3c.
+ puts "The authenticity of host '#{hostname} (#{ip})' can't be established."
+ puts "fingerprint is #{fingerprint}."
+ ui.confirm("Are you sure you want to continue connecting") # will exit 3 on N
+ # FIXME: this should save the key to known_hosts but doesn't appear to be
+ config[:ssh_verify_host_key] = :accept_new
+ connection_opts(reset: true)
+ retry
+ end
+
+ raise e
rescue Train::Error => e
require "net/ssh"
if e.cause && e.cause.class == Net::SSH::AuthenticationFailed
@@ -781,8 +792,8 @@ class Chef
# @return a configuration hash suitable for connecting to the remote
# host via train
- def connection_opts
- return @connection_opts unless @connection_opts.nil?
+ def connection_opts(reset: false)
+ return @connection_opts unless @connection_opts.nil? || reset == true
@connection_opts = {}
@connection_opts.merge! base_opts
@connection_opts.merge! host_verify_opts
@@ -824,8 +835,7 @@ class Chef
{ self_signed: config_value(:winrm_no_verify_cert) === true }
elsif ssh?
# Fall back to the old knife config key name for back compat.
- { verify_host_key: config_value(:ssh_verify_host_key,
- :host_key_verify, true) === true }
+ { verify_host_key: config_value(:ssh_verify_host_key, :host_key_verify, "always") }
else
{}
end
diff --git a/spec/unit/knife/bootstrap_spec.rb b/spec/unit/knife/bootstrap_spec.rb
index 5280e3b64b..c45604b7d1 100644
--- a/spec/unit/knife/bootstrap_spec.rb
+++ b/spec/unit/knife/bootstrap_spec.rb
@@ -1131,7 +1131,7 @@ describe Chef::Knife::Bootstrap do
logger: Chef::Log,
keys_only: false,
sudo: false,
- verify_host_key: true,
+ verify_host_key: "always",
non_interactive: true,
}
end
@@ -1229,7 +1229,7 @@ describe Chef::Knife::Bootstrap do
expect(knife.host_verify_opts).to eq( { verify_host_key: false } )
end
it "provides a correct default when no option given" do
- expect(knife.host_verify_opts).to eq( { verify_host_key: true } )
+ expect(knife.host_verify_opts).to eq( { verify_host_key: "always" } )
end
end
end