diff options
-rw-r--r-- | lib/puppet/ssl/certificate_authority.rb | 26 | ||||
-rw-r--r-- | lib/puppet/ssl/key.rb | 27 | ||||
-rwxr-xr-x | spec/unit/ssl/certificate_authority.rb | 95 | ||||
-rwxr-xr-x | spec/unit/ssl/key.rb | 30 |
4 files changed, 149 insertions, 29 deletions
diff --git a/lib/puppet/ssl/certificate_authority.rb b/lib/puppet/ssl/certificate_authority.rb index 18f881ae3..971a9965e 100644 --- a/lib/puppet/ssl/certificate_authority.rb +++ b/lib/puppet/ssl/certificate_authority.rb @@ -10,7 +10,7 @@ class Puppet::SSL::CertificateAuthority < Puppet::SSL::Host def read_key return nil unless FileTest.exist?(Puppet[:cakey]) - key = Puppet::SSL::Key.new(:ca) + key = Puppet::SSL::Key.new(name) key.password_file = Puppet[:capass] key.read(Puppet[:cakey]) @@ -20,8 +20,9 @@ class Puppet::SSL::CertificateAuthority < Puppet::SSL::Host # Generate and write the key out. def generate_key @key = Key.new(name) + @key.password_file = Puppet[:capass] @key.generate - Puppet.settings.write(:cacert) do |f| + Puppet.settings.write(:cakey) do |f| f.print @key.to_s end true @@ -56,7 +57,7 @@ class Puppet::SSL::CertificateAuthority < Puppet::SSL::Host # Always name the ca after the host we're running on. super(Puppet[:certname]) - setup_ca + setup_ca() end # Sign a given certificate request. @@ -84,15 +85,13 @@ class Puppet::SSL::CertificateAuthority < Puppet::SSL::Host cert.save(:in => :ca_file) unless self_signed end - private - # Do all of the initialization necessary to set up our # ca. def setup_ca generate_key unless key # Make sure we've got a password protecting our private key. - generate_password unless read_password + generate_password unless password? # And then make sure we've got the whole kaboodle. This will # create a self-signed CA certificate if we don't already have one, @@ -134,17 +133,8 @@ class Puppet::SSL::CertificateAuthority < Puppet::SSL::Host return serial end - # Get the CA password. - def read_password - unless defined?(@password) and @password - path = Puppet[:capass] - return nil unless FileTest.exist?(path) - - raise(Puppet::Error, "Could not read CA passfile %s" % path) unless FileTest.readable?(path) - - @password = File.read(path) - end - - @password + # Does the password file exist? + def password? + FileTest.exist? Puppet[:capass] end end diff --git a/lib/puppet/ssl/key.rb b/lib/puppet/ssl/key.rb index b8943a776..35370ac69 100644 --- a/lib/puppet/ssl/key.rb +++ b/lib/puppet/ssl/key.rb @@ -8,24 +8,35 @@ class Puppet::SSL::Key < Puppet::SSL::Base extend Puppet::Indirector indirects :key, :terminus_class => :file - attr_accessor :password_file + attr_reader :password_file # Knows how to create keys with our system defaults. def generate Puppet.info "Creating a new SSL key for %s" % name - @content = OpenSSL::PKey::RSA.new(Puppet[:keylength]) + if pass = password + @content = OpenSSL::PKey::RSA.new(Puppet[:keylength], pass) + else + @content = OpenSSL::PKey::RSA.new(Puppet[:keylength]) + end + end + + def password + return nil unless password_file + + ::File.read(password_file) + end + + # Set our password file. + def password_file=(file) + raise ArgumentError, "Password file %s does not exist" % file unless FileTest.exist?(file) + + @password_file = file end # Optionally support specifying a password file. def read(path) return super unless password_file - begin - password = ::File.read(password_file) - rescue => detail - raise Puppet::Error, "Could not read password for %s: %s" % [name, detail] - end - @content = wrapped_class.new(::File.read(path), password) end end diff --git a/spec/unit/ssl/certificate_authority.rb b/spec/unit/ssl/certificate_authority.rb index 53fb48e5f..b1b33f8b9 100755 --- a/spec/unit/ssl/certificate_authority.rb +++ b/spec/unit/ssl/certificate_authority.rb @@ -15,13 +15,102 @@ describe Puppet::SSL::CertificateAuthority do end describe "a new certificate authority" do - it "should create and store a password at :capass, a key encrypted with the password at :cakey, and a self-signed cert at :cacert" + before do + Puppet.settings.stubs(:value).with(:certname).returns "whatever" + end + + it "should create and store a password at :capass" do + Puppet.settings.expects(:value).with(:capass).returns "/path/to/pass" + + FileTest.expects(:exist?).with("/path/to/pass").returns false + + fh = mock 'filehandle' + Puppet.settings.expects(:write).with(:capass).yields fh + + fh.expects(:print).with { |s| s.length > 18 } + + [:read_key, :generate_key, :read_certificate, :generate_certificate].each do |method| + Puppet::SSL::CertificateAuthority.any_instance.stubs(method) + end + + Puppet::SSL::CertificateAuthority.new + end + + it "should create and store a key encrypted with the password at :cakey" do + Puppet.settings.stubs(:value).with(:capass).returns "/path/to/pass" + Puppet.settings.stubs(:value).with(:cakey).returns "/path/to/key" + + FileTest.expects(:exist?).with("/path/to/key").returns false + + key = mock 'key' + + Puppet::SSL::Key.expects(:new).with("whatever").returns key + key.expects(:password_file=).with("/path/to/pass") + key.expects(:generate) + + key.expects(:to_s).returns "my key" + + fh = mock 'filehandle' + Puppet.settings.expects(:write).with(:cakey).yields fh + fh.expects(:print).with("my key") + + [:generate_password, :read_certificate, :generate_certificate].each do |method| + Puppet::SSL::CertificateAuthority.any_instance.stubs(method) + end + Puppet::SSL::CertificateAuthority.any_instance.stubs(:password?).returns true + + Puppet::SSL::CertificateAuthority.new + end + + it "should create, sign, and store a self-signed cert at :cacert" do + Puppet.settings.stubs(:value).with(:cacert).returns "/path/to/cert" + + FileTest.expects(:exist?).with("/path/to/cert").returns false + + request = mock 'request' + Puppet::SSL::CertificateRequest.expects(:new).with("whatever").returns request + request.expects(:generate) + + cert = mock 'cert' + cert.expects(:to_s).returns "my cert" + Puppet::SSL::CertificateAuthority.any_instance.expects(:sign).with(request, :ca, true).returns cert + + fh = mock 'filehandle' + Puppet.settings.expects(:write).with(:cacert).yields fh + fh.expects(:print).with("my cert") + + [:password?, :generate_password, :read_key, :generate_key].each do |method| + Puppet::SSL::CertificateAuthority.any_instance.stubs(method) + end + + Puppet::SSL::CertificateAuthority.new + end end describe "an existing certificate authority" do - it "should read and decrypt the key at :cakey using the password at :capass and it should read the cert at :cacert" + it "should read and decrypt the key at :cakey using the password at :capass and it should read the cert at :cacert" do + Puppet.settings.stubs(:value).with(:certname).returns "whatever" + + paths = {} + [:capass, :cakey, :cacert].each do |value| + paths[value] = "/path/to/#{value.to_s}" + Puppet.settings.stubs(:value).with(value).returns paths[value] + FileTest.stubs(:exist?).with(paths[value]).returns true + end + + key = mock 'key' + Puppet::SSL::Key.expects(:new).with("whatever").returns key + key.expects(:password_file=).returns paths[:capass] + key.expects(:read).returns paths[:cakey] + key.stubs(:content).returns "mykey" + + cert = mock 'cert' + Puppet::SSL::Certificate.expects(:new).with("whatever").returns cert + cert.expects(:read).returns paths[:cacert] + cert.stubs(:content).returns "mycert" - it "should read the cert stored at :cacert" + Puppet::SSL::CertificateAuthority.new + end end end end diff --git a/spec/unit/ssl/key.rb b/spec/unit/ssl/key.rb index 71ef79c90..4978a591b 100755 --- a/spec/unit/ssl/key.rb +++ b/spec/unit/ssl/key.rb @@ -45,11 +45,13 @@ describe Puppet::SSL::Key do end it "should read the key with the password retrieved from the password file if one is provided" do + FileTest.stubs(:exist?).returns true @key.password_file = "/path/to/password" path = "/my/path" File.expects(:read).with(path).returns("my key") File.expects(:read).with("/path/to/password").returns("my password") + key = mock 'key' OpenSSL::PKey::RSA.expects(:new).with("my key", "my password").returns(key) @key.read(path).should equal(key) @@ -87,6 +89,34 @@ describe Puppet::SSL::Key do @instance.generate end + it "should fail if a provided password file does not exist" do + FileTest.expects(:exist?).with("/path/to/pass").returns false + + lambda { @instance.password_file = "/path/to/pass" }.should raise_error(ArgumentError) + end + + it "should return the contents of the password file as its password" do + FileTest.expects(:exist?).with("/path/to/pass").returns true + File.expects(:read).with("/path/to/pass").returns "my password" + + @instance.password_file = "/path/to/pass" + + @instance.password.should == "my password" + end + + it "should create the private key with any provided password" do + Puppet.settings.stubs(:value).with(:keylength).returns(50) + + FileTest.expects(:exist?).with("/path/to/pass").returns true + File.expects(:read).with("/path/to/pass").returns "my password" + + @instance.password_file = "/path/to/pass" + + OpenSSL::PKey::RSA.expects(:new).with(50, "my password").returns(@key) + + @instance.generate + end + it "should set the content to the generated key" do OpenSSL::PKey::RSA.stubs(:new).returns(@key) @instance.generate |