Class: Puppet::SSL::Host

Inherits:
Object show all
Extended by:
Util::Cacher
Defined in:
lib/puppet/ssl/host.rb

Overview

The class that manages all aspects of our SSL certificates – private keys, public keys, requests, etc.

Constant Summary collapse

Key =

Yay, ruby’s strange constant lookups.

Puppet::SSL::Key
CA_NAME =
Puppet::SSL::CA_NAME
Certificate =
Puppet::SSL::Certificate
CertificateRequest =
Puppet::SSL::CertificateRequest
CertificateRevocationList =
Puppet::SSL::CertificateRevocationList
CA_MODES =
{
  # Our ca is local, so we use it as the ultimate source of information
  # And we cache files locally.
  :local => [:ca, :file],
  # We're a remote CA client.
  :remote => [:rest, :file],
  # We are the CA, so we don't have read/write access to the normal certificates.
  :only => [:ca],
  # We have no CA, so we just look in the local file store.
  :none => [:file]
}

Class Attribute Summary collapse

Instance Attribute Summary collapse

Attributes included from Util::Cacher::Expirer

#timestamp

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Util::Cacher

extended, included

Methods included from Util::Cacher::Expirer

#dependent_data_expired?, #expire

Constructor Details

#initialize(name = nil) ⇒ Host

Returns a new instance of Host.



192
193
194
195
196
# File 'lib/puppet/ssl/host.rb', line 192

def initialize(name = nil)
  @name = (name || Puppet[:certname]).downcase
  @key = @certificate = @certificate_request = nil
  @ca = (name == self.class.ca_name)
end

Class Attribute Details

.ca_locationObject

Returns the value of attribute ca_location.



41
42
43
# File 'lib/puppet/ssl/host.rb', line 41

def ca_location
  @ca_location
end

Instance Attribute Details

#caObject

Returns the value of attribute ca.



19
20
21
# File 'lib/puppet/ssl/host.rb', line 19

def ca
  @ca
end

#certificateObject



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/puppet/ssl/host.rb', line 156

def certificate
  unless @certificate
    generate_key unless key

    # get the CA cert first, since it's required for the normal cert
    # to be of any use.
    return nil unless Certificate.find("ca") unless ca?
    return nil unless @certificate = Certificate.find(name)

    unless certificate_matches_key?
      raise Puppet::Error, "Retrieved certificate does not match private key; please remove certificate from server and regenerate it with the current key"
    end
  end
  @certificate
end

#certificate_requestObject



137
138
139
# File 'lib/puppet/ssl/host.rb', line 137

def certificate_request
  @certificate_request ||= CertificateRequest.find(name)
end

#keyObject



119
120
121
# File 'lib/puppet/ssl/host.rb', line 119

def key
  @key ||= Key.find(name)
end

#nameObject (readonly)

Returns the value of attribute name.



18
19
20
# File 'lib/puppet/ssl/host.rb', line 18

def name
  @name
end

Class Method Details

.ca_nameObject

This is the constant that people will use to mark that a given host is a certificate authority.



36
37
38
# File 'lib/puppet/ssl/host.rb', line 36

def self.ca_name
  CA_NAME
end

.configure_indirection(terminus, cache = nil) ⇒ Object

Configure how our various classes interact with their various terminuses.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/puppet/ssl/host.rb', line 45

def self.configure_indirection(terminus, cache = nil)
  Certificate.terminus_class = terminus
  CertificateRequest.terminus_class = terminus
  CertificateRevocationList.terminus_class = terminus

  if cache
    # This is weird; we don't actually cache our keys, we
    # use what would otherwise be the cache as our normal
    # terminus.
    Key.terminus_class = cache
  else
    Key.terminus_class = terminus
  end

  if cache
    Certificate.cache_class = cache
    CertificateRequest.cache_class = cache
    CertificateRevocationList.cache_class = cache
  else
    # Make sure we have no cache configured.  puppet master
    # switches the configurations around a bit, so it's important
    # that we specify the configs for absolutely everything, every
    # time.
    Certificate.cache_class = nil
    CertificateRequest.cache_class = nil
    CertificateRevocationList.cache_class = nil
  end
end

.destroy(name) ⇒ Object

Remove all traces of a given host



96
97
98
# File 'lib/puppet/ssl/host.rb', line 96

def self.destroy(name)
  [Key, Certificate, CertificateRequest].collect { |part| part.destroy(name) }.any? { |x| x }
end

.search(options = {}) ⇒ Object

Search for more than one host, optionally only specifying an interest in hosts with a given file type. This just allows our non-indirected class to have one of indirection methods.



104
105
106
107
108
109
110
111
112
# File 'lib/puppet/ssl/host.rb', line 104

def self.search(options = {})
  classlist = [options[:for] || [Key, CertificateRequest, Certificate]].flatten

  # Collect the results from each class, flatten them, collect all of the names, make the name list unique,
  # then create a Host instance for each one.
  classlist.collect { |klass| klass.search }.flatten.collect { |r| r.name }.uniq.collect do |name|
    new(name)
  end
end

Instance Method Details

#ca?Boolean

Is this a ca host, meaning that all of its files go in the CA location?

Returns:

  • (Boolean)


115
116
117
# File 'lib/puppet/ssl/host.rb', line 115

def ca?
  ca
end

#certificate_matches_key?Boolean

Returns:

  • (Boolean)


172
173
174
175
176
177
# File 'lib/puppet/ssl/host.rb', line 172

def certificate_matches_key?
  return false unless key
  return false unless certificate

  certificate.content.check_private_key(key.content)
end

#generateObject

Generate all necessary parts of our ssl host.



180
181
182
183
184
185
186
187
188
189
190
# File 'lib/puppet/ssl/host.rb', line 180

def generate
  generate_key unless key
  generate_certificate_request unless certificate_request

  # If we can get a CA instance, then we're a valid CA, and we
  # should use it to sign our request; else, just try to read
  # the cert.
  if ! certificate and ca = Puppet::SSL::CertificateAuthority.instance
    ca.sign(self.name)
  end
end

#generate_certificate_requestObject

Our certificate request requires the key but that’s all.



142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/puppet/ssl/host.rb', line 142

def generate_certificate_request
  generate_key unless key
  @certificate_request = CertificateRequest.new(name)
  @certificate_request.generate(key.content)
  begin
    @certificate_request.save
  rescue
    @certificate_request = nil
    raise
  end

  true
end

#generate_keyObject

This is the private key; we can create it from scratch with no inputs.



125
126
127
128
129
130
131
132
133
134
135
# File 'lib/puppet/ssl/host.rb', line 125

def generate_key
  @key = Key.new(name)
  @key.generate
  begin
    @key.save
  rescue
    @key = nil
    raise
  end
  true
end

#public_keyObject

Extract the public key from the private key.



199
200
201
# File 'lib/puppet/ssl/host.rb', line 199

def public_key
  key.content.public_key
end

#ssl_store(purpose = OpenSSL::X509::PURPOSE_ANY) ⇒ Object

Create/return a store that uses our SSL info to validate connections.



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/puppet/ssl/host.rb', line 205

def ssl_store(purpose = OpenSSL::X509::PURPOSE_ANY)
  unless @ssl_store
    @ssl_store = OpenSSL::X509::Store.new
    @ssl_store.purpose = purpose

    # Use the file path here, because we don't want to cause
    # a lookup in the middle of setting our ssl connection.
    @ssl_store.add_file(Puppet[:localcacert])

    # If there's a CRL, add it to our store.
    if crl = Puppet::SSL::CertificateRevocationList.find(CA_NAME)
      @ssl_store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK if Puppet.settings[:certificate_revocation]
      @ssl_store.add_crl(crl.content)
    end
    return @ssl_store
  end
  @ssl_store
end

#wait_for_cert(time) ⇒ Object

Attempt to retrieve a cert, if we don’t already have one.



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/puppet/ssl/host.rb', line 225

def wait_for_cert(time)
  begin
    return if certificate
    generate
    return if certificate
  rescue SystemExit,NoMemoryError
    raise
  rescue Exception => detail
    puts detail.backtrace if Puppet[:trace]
    Puppet.err "Could not request certificate: #{detail}"
    if time < 1
      puts "Exiting; failed to retrieve certificate and waitforcert is disabled"
      exit(1)
    else
      sleep(time)
    end
    retry
  end

  if time < 1
    puts "Exiting; no certificate found and waitforcert is disabled"
    exit(1)
  end

  while true
    sleep time
    begin
      break if certificate
      Puppet.notice "Did not receive certificate"
    rescue StandardError => detail
      puts detail.backtrace if Puppet[:trace]
      Puppet.err "Could not request certificate: #{detail}"
    end
  end
end