summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaire McQuin <claire@getchef.com>2014-09-09 15:23:00 -0700
committerClaire McQuin <claire@getchef.com>2014-09-10 11:41:12 -0700
commitb050cb5c6d37a07928f5dafd678f569818dd97ce (patch)
tree9ede7393533a18b247d2507459c7abddd75d4472
parent3fb87cc744d1e1134476496dedc9125a25add859 (diff)
downloadchef-b050cb5c6d37a07928f5dafd678f569818dd97ce.tar.gz
Detect invalid X509 certificates.
-rw-r--r--lib/chef/knife/ssl_check.rb56
-rw-r--r--spec/unit/knife/ssl_check_spec.rb47
2 files changed, 96 insertions, 7 deletions
diff --git a/lib/chef/knife/ssl_check.rb b/lib/chef/knife/ssl_check.rb
index e98469d5aa..f7edd6b019 100644
--- a/lib/chef/knife/ssl_check.rb
+++ b/lib/chef/knife/ssl_check.rb
@@ -106,6 +106,54 @@ class Chef
end
end
+ def verify_X509
+ cert_debug_msg = ""
+ if configuration.trusted_certs_dir && Dir.exist?(configuration.trusted_certs_dir)
+ # Check each trusted certificate to ensure that it meets X509 standard.
+ Dir.glob(File.join(configuration.trusted_certs_dir, "*.{crt,pem}")).each do |cert_name|
+ store = OpenSSL::X509::Store.new
+ cert = OpenSSL::X509::Certificate.new(IO.read(File.expand_path(cert)))
+ begin
+ store.add(cert)
+ # test if the store can verify the cert we just added
+ unless store.verify(cert) # true if verified, false if not
+ cert_debug_msg << "#{File.expand_path(cert_name)}: #{store.error_string}\n"
+ end
+ rescue OpenSSL::X509::StoreError
+ cert_debug_msg << "#{File.expand_path(cert_name)}: #{store.error_string}\n"
+ end
+ end
+ end
+
+ unless cert_debug_msg.empty?
+ ui.msg("\n#{ui.color("Configuration Info:", :bold)}\n\n")
+ debug_ssl_settings
+ debug_chef_ssl_config
+
+ ui.warn(<<-BAD_CERTS
+There are invalid certificates in your trusted_certs_dir.
+Although Chef may be configured to trust these certificates,
+OpenSSL may not use the following certificates when verifying SSL connections:
+
+#{cert_debug_msg}
+
+#{ui.color("TO FIX THESE WARNINGS:", :bold)}
+
+We are working on documentation for generating self-signed certificates with Subject
+Alternative Names (SANs). For now, please refer to the discussion on GitHub Issue 1700
+
+ https://github.com/opscode/chef/issues/1700
+
+and direct any further questions to github.com/opscode/chef/issues or the chef
+mailing lists.
+
+BAD_CERTS
+ # @TODO: ^ needs URL
+ return false
+ end
+ return true
+ end
+
def verify_cert
ui.msg("Connecting to host #{host}:#{port}")
verify_peer_socket.connect
@@ -148,7 +196,7 @@ where your chef-server runs:
/var/opt/chef-server/nginx/ca/SERVER_HOSTNAME.crt
-Copy that file to you trusted_certs_dir (currently: #{configuration.trusted_certs_dir})
+Copy that file to your trusted_certs_dir (currently: #{configuration.trusted_certs_dir})
using SSH/SCP or some other secure method, then re-run this command to confirm
that the server's certificate is now trusted.
@@ -197,7 +245,7 @@ ADVICE
def run
validate_uri
- if verify_cert && verify_cert_host
+ if verify_X509 && verify_cert && verify_cert_host
ui.msg "Successfully verified certificates from `#{host}'"
else
exit 1
@@ -207,7 +255,3 @@ ADVICE
end
end
end
-
-
-
-
diff --git a/spec/unit/knife/ssl_check_spec.rb b/spec/unit/knife/ssl_check_spec.rb
index 32405a5977..6896a6b80f 100644
--- a/spec/unit/knife/ssl_check_spec.rb
+++ b/spec/unit/knife/ssl_check_spec.rb
@@ -95,6 +95,49 @@ E
end
end
+ describe "verifying trusted certificate X509 properties" do
+ let(:name_args) { %w{https://foo.example.com:8443} }
+
+ let(:trusted_certs_dir) { File.join(CHEF_SPEC_DATA, "trusted_certs") }
+ let(:trusted_certs) { [File.join(trusted_certs_dir, "example.crt")] }
+
+ let(:store) { OpenSSL::X509::Store.new }
+ let(:certificate) { OpenSSL::X509::Certificate.new(IO.read(trusted_certs[0])) }
+
+ before do
+ Chef::Config[:trusted_certs_dir] = trusted_certs_dir
+ Dir.stub(:glob).with(File.join(trusted_certs_dir, "*.{crt,pem}")).and_return(trusted_certs)
+ store.stub(:add).with(certificate)
+ OpenSSL::X509::Store.stub(:new).and_return(store)
+ OpenSSL::X509::Certificate.stub(:new).with(IO.read(trusted_certs[0])).and_return(certificate)
+ ssl_check.stub(:verify_cert).and_return(true)
+ ssl_check.stub(:verify_cert_host).and_return(true)
+ end
+
+ context "when the trusted certificates have valid X509 properties" do
+ before do
+ store.stub(:verify).with(certificate).and_return(true)
+ end
+
+ it "does not generate any X509 warnings" do
+ expect(ssl_check.ui).not_to receive(:warn).with(/There are invalid certificates in your trusted_certs_dir/)
+ ssl_check.run
+ end
+ end
+
+ context "when the trusted certificates have invalid X509 properties" do
+ before do
+ store.stub(:verify).with(certificate).and_return(false)
+ store.stub(:error_string).and_return("unable to get local issuer certificate")
+ end
+
+ it "generates a warning message with invalid certificate file names" do
+ expect(ssl_check.ui).to receive(:warn).with(/#{trusted_certs[0]}: unable to get local issuer certificate/)
+ ssl_check.run
+ end
+ end
+ end
+
describe "verifying the remote certificate" do
let(:name_args) { %w{https://foo.example.com:8443} }
@@ -117,6 +160,7 @@ E
context "when the remote host's certificate is valid" do
before do
+ ssl_check.should_receive(:verify_X509).and_return(true) # X509 valid certs (no warn)
ssl_socket.should_receive(:connect) # no error
ssl_socket.should_receive(:post_connection_check).with("foo.example.com") # no error
end
@@ -148,6 +192,7 @@ E
context "when the certificate's CN does not match the hostname" do
before do
+ ssl_check.should_receive(:verify_X509).and_return(true) # X509 valid certs
ssl_socket.should_receive(:connect) # no error
ssl_socket.should_receive(:post_connection_check).
with("foo.example.com").
@@ -167,6 +212,7 @@ E
context "when the cert is not signed by any trusted authority" do
before do
+ ssl_check.should_receive(:verify_X509).and_return(true) # X509 valid certs
ssl_socket.should_receive(:connect).
and_raise(OpenSSL::SSL::SSLError)
ssl_socket_for_debug.should_receive(:connect)
@@ -184,4 +230,3 @@ E
end
end
-