summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNoah Kantrowitz <noah@coderanger.net>2018-05-30 15:59:57 -0700
committerNoah Kantrowitz <noah@coderanger.net>2018-05-30 15:59:57 -0700
commit3f7ffb322fb8f414ebf28eaa4b6fe4c94d7857a9 (patch)
tree848b088488ae5f92f0d785e8c4ba0165d302c2b5
parenta074d491722bf665da843e76672ffbadf92e3661 (diff)
downloadchef-3f7ffb322fb8f414ebf28eaa4b6fe4c94d7857a9.tar.gz
Add support for signing requests using ssh-agent.
Signed-off-by: Noah Kantrowitz <noah@coderanger.net>
-rw-r--r--chef-config/lib/chef-config/config.rb6
-rw-r--r--lib/chef/http/auth_credentials.rb8
-rw-r--r--lib/chef/http/authenticator.rb9
-rw-r--r--lib/chef/knife/raw.rb7
-rw-r--r--lib/chef/server_api.rb2
5 files changed, 24 insertions, 8 deletions
diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb
index ef792b2db7..5e641138d3 100644
--- a/chef-config/lib/chef-config/config.rb
+++ b/chef-config/lib/chef-config/config.rb
@@ -592,7 +592,7 @@ module ChefConfig
# the 'mixlib-authorization' project for more detail). Currently, versions
# 1.0, 1.1, and 1.3 are available.
default :authentication_protocol_version do
- if fips
+ if fips || ssh_agent_signing
"1.3"
else
"1.1"
@@ -621,6 +621,10 @@ module ChefConfig
# never be set to true or its possibly an easily exploitable security hole.
default :follow_client_key_symlink, false
+ # Enable ssh-agent signing mode. This requires {client_key} be set to a
+ # public key rather than the usual private key.
+ default :ssh_agent_signing, false
+
# This secret is used to decrypt encrypted data bag items.
default(:encrypted_data_bag_secret) do
if File.exist?(platform_specific_path("/etc/chef/encrypted_data_bag_secret"))
diff --git a/lib/chef/http/auth_credentials.rb b/lib/chef/http/auth_credentials.rb
index e2494c9405..eeb9136607 100644
--- a/lib/chef/http/auth_credentials.rb
+++ b/lib/chef/http/auth_credentials.rb
@@ -28,8 +28,10 @@ class Chef
class AuthCredentials
attr_reader :client_name, :key
- def initialize(client_name = nil, key = nil)
- @client_name, @key = client_name, key
+ def initialize(client_name = nil, key = nil, use_ssh_agent: false)
+ @client_name = client_name
+ @key = key
+ @use_ssh_agent = use_ssh_agent
end
def sign_requests?
@@ -48,7 +50,7 @@ class Chef
host = request_params.delete(:host) || "localhost"
sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object(request_params)
- signed = sign_obj.sign(key).merge({ :host => host })
+ signed = sign_obj.sign(key, use_ssh_agent: @use_ssh_agent).merge({ :host => host })
signed.inject({}) { |memo, kv| memo["#{kv[0].to_s.upcase}"] = kv[1]; memo }
end
diff --git a/lib/chef/http/authenticator.rb b/lib/chef/http/authenticator.rb
index 65367af107..a20653a055 100644
--- a/lib/chef/http/authenticator.rb
+++ b/lib/chef/http/authenticator.rb
@@ -40,7 +40,7 @@ class Chef
@sign_request = true
@signing_key_filename = opts[:signing_key_filename]
@key = load_signing_key(opts[:signing_key_filename], opts[:raw_key])
- @auth_credentials = AuthCredentials.new(opts[:client_name], @key)
+ @auth_credentials = AuthCredentials.new(opts[:client_name], @key, use_ssh_agent: opts[:ssh_agent_signing])
@version_class = opts[:version_class]
@api_version = opts[:api_version]
end
@@ -89,12 +89,15 @@ class Chef
else
return nil
end
- @key = OpenSSL::PKey::RSA.new(@raw_key)
+ # Pass in '' as the passphrase to avoid OpenSSL prompting on the TTY if
+ # given an encrypted key. This also helps if using a single file for
+ # both the public and private key with ssh-agent mode.
+ @key = OpenSSL::PKey::RSA.new(@raw_key, '')
rescue SystemCallError, IOError => e
Chef::Log.warn "Failed to read the private key #{key_file}: #{e.inspect}"
raise Chef::Exceptions::PrivateKeyMissing, "I cannot read #{key_file}, which you told me to use to sign requests!"
rescue OpenSSL::PKey::RSAError
- msg = "The file #{key_file} or :raw_key option does not contain a correctly formatted private key.\n"
+ msg = "The file #{key_file} or :raw_key option does not contain a correctly formatted private key or the key is encrypted.\n"
msg << "The key file should begin with '-----BEGIN RSA PRIVATE KEY-----' and end with '-----END RSA PRIVATE KEY-----'"
raise Chef::Exceptions::InvalidPrivateKey, msg
end
diff --git a/lib/chef/knife/raw.rb b/lib/chef/knife/raw.rb
index 76b83d2212..2916736e66 100644
--- a/lib/chef/knife/raw.rb
+++ b/lib/chef/knife/raw.rb
@@ -39,10 +39,15 @@ class Chef
:default => false,
:description => "Use webui proxy authentication. Client key must be the webui key."
+ # We need a custom HTTP client class here because we don't want to even
+ # try to decode the body, in case we get back corrupted JSON or whatnot.
class RawInputServerAPI < Chef::HTTP
def initialize(options = {})
+ # If making a change here, also update Chef::ServerAPI.
options[:client_name] ||= Chef::Config[:node_name]
- options[:signing_key_filename] ||= Chef::Config[:client_key]
+ options[:raw_key] ||= Chef::Config[:client_key_contents]
+ options[:signing_key_filename] ||= Chef::Config[:client_key] unless options[:raw_key]
+ options[:ssh_agent_signing] ||= Chef::Config[:ssh_agent_signing]
super(Chef::Config[:chef_server_url], options)
end
use Chef::HTTP::JSONOutput
diff --git a/lib/chef/server_api.rb b/lib/chef/server_api.rb
index c501544954..62fe319b86 100644
--- a/lib/chef/server_api.rb
+++ b/lib/chef/server_api.rb
@@ -30,9 +30,11 @@ class Chef
class ServerAPI < Chef::HTTP
def initialize(url = Chef::Config[:chef_server_url], options = {})
+ # # If making a change here, also update Chef::Knife::Raw::RawInputServerAPI.
options[:client_name] ||= Chef::Config[:node_name]
options[:raw_key] ||= Chef::Config[:client_key_contents]
options[:signing_key_filename] ||= Chef::Config[:client_key] unless options[:raw_key]
+ options[:ssh_agent_signing] ||= Chef::Config[:ssh_agent_signing]
options[:signing_key_filename] = nil if chef_zero_uri?(url)
options[:inflate_json_class] = false
super(url, options)