Class: Puppet::SSL::Host Deprecated

Inherits:
Object show all
Defined in:
lib/puppet/ssl/host.rb

Overview

Deprecated.

Use SSLProvider instead.

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name = nil, device = false) ⇒ Host


228
229
230
231
232
233
234
235
236
# File 'lib/puppet/ssl/host.rb', line 228

def initialize(name = nil, device = false)
  @name = (name || Puppet[:certname]).downcase
  @device = device
  Puppet::SSL::Base.validate_certname(@name)
  @key = @certificate = @certificate_request = nil
  @crl_usage = Puppet.settings[:certificate_revocation]
  @crl_path = Puppet.settings[:hostcrl]
  Puppet.deprecation_warning(_("Puppet::SSL::Host is deprecated and will be removed in a future release of Puppet."));
end

Instance Attribute Details

#certificateObject


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

def certificate
  unless @certificate
    generate_key unless key

    # get CA and optional CRL
    sm = Puppet::SSL::StateMachine.new(onetime: true)
    sm.ensure_ca_certificates

    cert = get_host_certificate
    return nil unless cert

    validate_certificate_with_key(cert)
    @certificate = cert
  end
  @certificate
end

#certificate_requestPuppet::SSL::CertificateRequest?

Search for an existing CSR for this host either cached on disk or stored by the CA. Returns nil if no request exists.


182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/puppet/ssl/host.rb', line 182

def certificate_request
  unless @certificate_request
    csr = load_certificate_request_from_file
    if csr
      @certificate_request = csr
    else
      csr = download_csr_from_ca
      if csr 
        @certificate_request = csr
      end
    end
  end
  @certificate_request
end

#crl_pathObject (readonly)

Returns the value of attribute crl_path


21
22
23
# File 'lib/puppet/ssl/host.rb', line 21

def crl_path
  @crl_path
end

#crl_usage=(value) ⇒ Object (writeonly)

Sets the attribute crl_usage


23
24
25
# File 'lib/puppet/ssl/host.rb', line 23

def crl_usage=(value)
  @crl_usage = value
end

#deviceObject (readonly)

Returns the value of attribute device


21
22
23
# File 'lib/puppet/ssl/host.rb', line 21

def device
  @device
end

#keyObject


72
73
74
# File 'lib/puppet/ssl/host.rb', line 72

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

#nameObject (readonly)

Returns the value of attribute name


21
22
23
# File 'lib/puppet/ssl/host.rb', line 21

def name
  @name
end

Class Method Details

.configure_indirection(terminus, cache = nil) ⇒ Object

Configure how our various classes interact with their various terminuses.


38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/puppet/ssl/host.rb', line 38

def self.configure_indirection(terminus, cache = nil)
  Certificate.indirection.terminus_class = terminus
  CertificateRequest.indirection.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.indirection.terminus_class = cache
  else
    Key.indirection.terminus_class = terminus
  end

  if cache
    Certificate.indirection.cache_class = cache
    CertificateRequest.indirection.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.indirection.cache_class = nil
    CertificateRequest.indirection.cache_class = nil
  end
end

.from_data_hash(data) ⇒ Object


64
65
66
67
68
69
70
# File 'lib/puppet/ssl/host.rb', line 64

def self.from_data_hash(data)
  instance = new(data["name"])
  if data["desired_state"]
    instance.desired_state = data["desired_state"]
  end
  instance
end

.localhostObject


25
26
27
28
29
30
31
# File 'lib/puppet/ssl/host.rb', line 25

def self.localhost
  return @localhost if @localhost
  @localhost = new
  @localhost.generate unless @localhost.certificate
  @localhost.key
  @localhost
end

.resetObject


33
34
35
# File 'lib/puppet/ssl/host.rb', line 33

def self.reset
  @localhost = nil
end

Instance Method Details

#check_for_certificate_on_disk(cert_name) ⇒ Puppet::SSL::Certificate?

Checks for the requested certificate on disc, at a location determined by this host's configuration.

Raises:

  • (Puppet::Error)

    if contents of certificate file is invalid and could not be loaded


419
420
421
422
423
424
425
426
427
428
# File 'lib/puppet/ssl/host.rb', line 419

def check_for_certificate_on_disk(cert_name)
  file_path = certificate_location(cert_name)
  if Puppet::FileSystem.exist?(file_path)
    begin
      Puppet::SSL::Certificate.from_s(Puppet::FileSystem.read(file_path))
    rescue OpenSSL::X509::CertificateError
      raise Puppet::Error, _("The certificate at %{file_path} is invalid. Could not load.") % { file_path: file_path }
    end
  end
end

#clean_paramsObject

The puppet parameters for commands output by the validate_ methods depend upon whether this is an agent or a device.


141
142
143
# File 'lib/puppet/ssl/host.rb', line 141

def clean_params
  @device ? "--target #{Puppet[:certname]}" : ''
end

#download_certificate_from_ca(cert_name) ⇒ Puppet::SSL::Certificate?

Attempts to download this host's certificate from the CA server. Returns nil if the CA does not yet have a signed cert for this host.

Raises:

  • (Puppet::Error)

    if response from the CA does not contain a valid certificate


437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/puppet/ssl/host.rb', line 437

def download_certificate_from_ca(cert_name)
  begin
    cert = Puppet::Rest::Routes.get_certificate(
      cert_name,
      Puppet::SSL::SSLContext.new(store: ssl_store)
    )
    begin
      Puppet::SSL::Certificate.from_s(cert)
    rescue OpenSSL::X509::CertificateError
      raise Puppet::Error, _("Response from the CA did not contain a valid certificate for %{cert_name}.") % { cert_name: cert_name }
    end
  rescue Puppet::Rest::ResponseError => e
    if e.response.code.to_i == 404
      Puppet.debug _("No certificate for %{cert_name} on CA") % { cert_name: cert_name }
      nil
    else
      raise Puppet::Rest::ResponseError, _("Could not download host certificate: %{message}") % { message: e.message }
    end
  end
end

#download_host_certificateObject


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

def download_host_certificate
  cert = download_certificate_from_ca(name)
  return nil unless cert

  validate_certificate_with_key(cert)
  save_host_certificate(cert)
  cert
end

#generateObject

Generate all necessary parts of our ssl host.


198
199
200
201
202
203
204
205
206
207
208
# File 'lib/puppet/ssl/host.rb', line 198

def generate
  generate_key unless key

  existing_request = certificate_request

  # if CSR downloaded from master, but the local keypair was just generated and
  # does not match the public key in the CSR, fail hard
  validate_csr_with_key(existing_request, key) if existing_request

  generate_certificate_request unless existing_request
end

#generate_certificate_request(options = {}) ⇒ Object

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


91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/puppet/ssl/host.rb', line 91

def generate_certificate_request(options = {})
  generate_key unless key

  # If this CSR is for the current machine...
  if name == Puppet[:certname].downcase
    # ...add our configured dns_alt_names
    if Puppet[:dns_alt_names] and Puppet[:dns_alt_names] != ''
      options[:dns_alt_names] ||= Puppet[:dns_alt_names]
    end
  end

  csr_attributes = Puppet::SSL::CertificateRequestAttributes.new(Puppet[:csr_attributes])
  if csr_attributes.load
    options[:csr_attributes] = csr_attributes.custom_attributes
    options[:extension_requests] = csr_attributes.extension_requests
  end

  @certificate_request = CertificateRequest.new(name)
  @certificate_request.generate(key.content, options)
  begin
    submit_certificate_request(@certificate_request)
    save_certificate_request(@certificate_request)
  rescue
    @certificate_request = nil
    raise
  end

  true
end

#generate_keyObject

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


78
79
80
81
82
83
84
85
86
87
88
# File 'lib/puppet/ssl/host.rb', line 78

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

#public_keyObject

Extract the public key from the private key.


239
240
241
# File 'lib/puppet/ssl/host.rb', line 239

def public_key
  key.content.public_key
end

#puppet_paramsObject


145
146
147
# File 'lib/puppet/ssl/host.rb', line 145

def puppet_params
  @device ? "device -v --target #{Puppet[:certname]}" : 'agent -t'
end

#save_host_certificate(cert) ⇒ Object

Saves the given certificate to disc, at a location determined by this host's configuration.


296
297
298
299
300
301
# File 'lib/puppet/ssl/host.rb', line 296

def save_host_certificate(cert)
  file_path = certificate_location(name)
  Puppet::Util.replace_file(file_path, 0644) do |f|
    f.write(cert.to_s)
  end
end

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

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


253
254
255
256
257
258
# File 'lib/puppet/ssl/host.rb', line 253

def ssl_store(purpose = OpenSSL::X509::PURPOSE_ANY)
  if @ssl_store.nil?
    @ssl_store = build_ssl_store(purpose)
  end
  @ssl_store
end

#use_crl?Boolean


243
244
245
# File 'lib/puppet/ssl/host.rb', line 243

def use_crl?
  !!@crl_usage
end

#use_crl_chain?Boolean


247
248
249
# File 'lib/puppet/ssl/host.rb', line 247

def use_crl_chain?
  @crl_usage == true || @crl_usage == :chain
end

#validate_certificate_with_key(cert) ⇒ Object

Validate that our private key matches the specified certificate.

Raises:


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

def validate_certificate_with_key(cert)
  raise Puppet::Error, _("No certificate to validate.") unless cert
  raise Puppet::Error, _("No private key with which to validate certificate with fingerprint: %{fingerprint}") % { fingerprint: cert.fingerprint } unless key
  unless cert.content.check_private_key(key.content)
    raise Puppet::Error, _("The certificate retrieved from the master does not match the agent's private key. Did you forget to run as root?\nCertificate fingerprint: %{fingerprint}\nTo fix this, remove the certificate from both the master and the agent and then start a puppet run, which will automatically regenerate a certificate.\nOn the master:\npuppetserver ca clean --certname %{cert_name}\nOn the agent:\n1. puppet ssl clean %{clean_params}\n2. puppet %{puppet_params}\n") % { fingerprint: cert.fingerprint, cert_name: Puppet[:certname], clean_params: clean_params, puppet_params: puppet_params }
  end
end

#wait_for_cert(time) ⇒ Object

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


261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/puppet/ssl/host.rb', line 261

def wait_for_cert(time)
  begin
    return if certificate
    generate
    return if certificate
  rescue StandardError => detail
    Puppet.log_exception(detail, _("Could not request certificate: %{message}") % { message: detail.message })
    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

  loop do
    sleep time
    begin
      break if certificate
      Puppet.notice _("Did not receive certificate")
    rescue StandardError => detail
      Puppet.log_exception(detail, _("Could not request certificate: %{message}") % { message: detail.message })
    end
  end
end