Class: Puppet::SSL::Host
- Extended by:
- Indirector
- 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 => [:disabled_ca] }
Constants included from Indirector
Class Attribute Summary collapse
-
.ca_location ⇒ Object
Returns the value of attribute ca_location.
Instance Attribute Summary collapse
-
#ca ⇒ Object
Returns the value of attribute ca.
- #certificate ⇒ Object
- #certificate_request ⇒ Object
-
#desired_state ⇒ Object
This accessor is used in instances for indirector requests to hold desired state.
- #key ⇒ Object
-
#name ⇒ Object
readonly
Returns the value of attribute name.
Class Method Summary collapse
-
.ca_name ⇒ Object
This is the constant that people will use to mark that a given host is a certificate authority.
-
.configure_indirection(terminus, cache = nil) ⇒ Object
Configure how our various classes interact with their various terminuses.
-
.destroy(name) ⇒ Object
Puppet::SSL::Host is actually indirected now so the original implementation has been moved into the certificate_status indirector.
- .from_data_hash(data) ⇒ Object
- .localhost ⇒ Object
- .reset ⇒ Object
-
.search(options = {}) ⇒ Object
Puppet::SSL::Host is actually indirected now so the original implementation has been moved into the certificate_status indirector.
Instance Method Summary collapse
-
#ca? ⇒ Boolean
Is this a ca host, meaning that all of its files go in the CA location?.
-
#generate ⇒ Object
Generate all necessary parts of our ssl host.
-
#generate_certificate_request(options = {}) ⇒ Object
Our certificate request requires the key but that’s all.
-
#generate_key ⇒ Object
This is the private key; we can create it from scratch with no inputs.
-
#initialize(name = nil) ⇒ Host
constructor
A new instance of Host.
-
#public_key ⇒ Object
Extract the public key from the private key.
-
#ssl_store(purpose = OpenSSL::X509::PURPOSE_ANY) ⇒ Object
Create/return a store that uses our SSL info to validate connections.
- #state ⇒ Object
-
#suitable_message_digest_algorithms ⇒ Object
eventually we’ll probably want to move this somewhere else or make it configurable –jeffweiss 29 aug 2012.
- #to_data_hash ⇒ Object
- #validate_certificate_with_key ⇒ Object
-
#wait_for_cert(time) ⇒ Object
Attempt to retrieve a cert, if we don’t already have one.
Methods included from Indirector
Constructor Details
#initialize(name = nil) ⇒ Host
Returns a new instance of Host.
262 263 264 265 266 267 |
# File 'lib/puppet/ssl/host.rb', line 262 def initialize(name = nil) @name = (name || Puppet[:certname]).downcase Puppet::SSL::Base.validate_certname(@name) @key = @certificate = @certificate_request = nil @ca = (name == self.class.ca_name) end |
Class Attribute Details
.ca_location ⇒ Object
Returns the value of attribute ca_location.
52 53 54 |
# File 'lib/puppet/ssl/host.rb', line 52 def ca_location @ca_location end |
Instance Attribute Details
#ca ⇒ Object
Returns the value of attribute ca.
26 27 28 |
# File 'lib/puppet/ssl/host.rb', line 26 def ca @ca end |
#certificate ⇒ Object
195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/puppet/ssl/host.rb', line 195 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.indirection.find("ca", :fail_on_404 => true) unless ca? return nil unless @certificate = Certificate.indirection.find(name) validate_certificate_with_key end @certificate end |
#certificate_request ⇒ Object
159 160 161 |
# File 'lib/puppet/ssl/host.rb', line 159 def certificate_request @certificate_request ||= CertificateRequest.indirection.find(name) end |
#desired_state ⇒ Object
This accessor is used in instances for indirector requests to hold desired state
31 32 33 |
# File 'lib/puppet/ssl/host.rb', line 31 def desired_state @desired_state end |
#key ⇒ Object
141 142 143 |
# File 'lib/puppet/ssl/host.rb', line 141 def key @key ||= Key.indirection.find(name) end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
25 26 27 |
# File 'lib/puppet/ssl/host.rb', line 25 def name @name end |
Class Method Details
.ca_name ⇒ Object
This is the constant that people will use to mark that a given host is a certificate authority.
47 48 49 |
# File 'lib/puppet/ssl/host.rb', line 47 def self.ca_name CA_NAME end |
.configure_indirection(terminus, cache = nil) ⇒ Object
Configure how our various classes interact with their various terminuses.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/puppet/ssl/host.rb', line 56 def self.configure_indirection(terminus, cache = nil) Certificate.indirection.terminus_class = terminus CertificateRequest.indirection.terminus_class = terminus CertificateRevocationList.indirection.terminus_class = terminus host_map = {:ca => :file, :disabled_ca => nil, :file => nil, :rest => :rest} if term = host_map[terminus] self.indirection.terminus_class = term else self.indirection.reset_terminus_class end 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 CertificateRevocationList.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 CertificateRevocationList.indirection.cache_class = nil end end |
.destroy(name) ⇒ Object
Puppet::SSL::Host is actually indirected now so the original implementation has been moved into the certificate_status indirector. This method is in-use in ‘puppet cert -c <certname>`.
117 118 119 |
# File 'lib/puppet/ssl/host.rb', line 117 def self.destroy(name) indirection.destroy(name) end |
.from_data_hash(data) ⇒ Object
121 122 123 124 125 126 127 |
# File 'lib/puppet/ssl/host.rb', line 121 def self.from_data_hash(data) instance = new(data["name"]) if data["desired_state"] instance.desired_state = data["desired_state"] end instance end |
.localhost ⇒ Object
33 34 35 36 37 38 39 |
# File 'lib/puppet/ssl/host.rb', line 33 def self.localhost return @localhost if @localhost @localhost = new @localhost.generate unless @localhost.certificate @localhost.key @localhost end |
.reset ⇒ Object
41 42 43 |
# File 'lib/puppet/ssl/host.rb', line 41 def self.reset @localhost = nil end |
.search(options = {}) ⇒ Object
Puppet::SSL::Host is actually indirected now so the original implementation has been moved into the certificate_status indirector. This method does not appear to be in use in ‘puppet cert -l`.
132 133 134 |
# File 'lib/puppet/ssl/host.rb', line 132 def self.search( = {}) indirection.search("*", ) end |
Instance Method Details
#ca? ⇒ Boolean
Is this a ca host, meaning that all of its files go in the CA location?
137 138 139 |
# File 'lib/puppet/ssl/host.rb', line 137 def ca? ca end |
#generate ⇒ Object
Generate all necessary parts of our ssl host.
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 260 |
# File 'lib/puppet/ssl/host.rb', line 228 def generate generate_key unless key # ask indirector to find any existing requests and download them 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 if !existing_request.nil? && (key.content.public_key.to_s != existing_request.content.public_key.to_s) raise Puppet::Error, _(<<ERROR_STRING) % { fingerprint: existing_request.fingerprint, csr_public_key: existing_request.content.public_key.to_text, agent_public_key: key.content.public_key.to_text, cert_name: Puppet[:certname], ssl_dir: Puppet[:ssldir], cert_dir: Puppet[:certdir].gsub('/', '\\') } The CSR retrieved from the master does not match the agent's public key. CSR fingerprint: %{fingerprint} CSR public key: %{csr_public_key} Agent public key: %{agent_public_key} To fix this, remove the CSR from both the master and the agent and then start a puppet run, which will automatically regenerate a CSR. On the master: puppet cert clean %{cert_name} On the agent: 1a. On most platforms: find %{ssl_dir} -name %{cert_name}.pem -delete 1b. On Windows: del "%{cert_dir}\\%{cert_name}.pem" /f 2. puppet agent -t ERROR_STRING end generate_certificate_request unless existing_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, {allow_dns_alt_names: true}) end end |
#generate_certificate_request(options = {}) ⇒ Object
Our certificate request requires the key but that’s all.
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/puppet/ssl/host.rb', line 164 def generate_certificate_request( = {}) 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] != '' [:dns_alt_names] ||= Puppet[:dns_alt_names] elsif Puppet::SSL::CertificateAuthority.ca? and fqdn = Facter.value(:fqdn) and domain = Facter.value(:domain) [:dns_alt_names] = "puppet, #{fqdn}, puppet.#{domain}" end end csr_attributes = Puppet::SSL::CertificateRequestAttributes.new(Puppet[:csr_attributes]) if csr_attributes.load [:csr_attributes] = csr_attributes.custom_attributes [:extension_requests] = csr_attributes.extension_requests end @certificate_request = CertificateRequest.new(name) @certificate_request.generate(key.content, ) begin CertificateRequest.indirection.save(@certificate_request) rescue @certificate_request = nil raise end true end |
#generate_key ⇒ Object
This is the private key; we can create it from scratch with no inputs.
147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/puppet/ssl/host.rb', line 147 def generate_key @key = Key.new(name) @key.generate begin Key.indirection.save(@key) rescue @key = nil raise end true end |
#public_key ⇒ Object
Extract the public key from the private key.
270 271 272 |
# File 'lib/puppet/ssl/host.rb', line 270 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.
276 277 278 279 280 281 |
# File 'lib/puppet/ssl/host.rb', line 276 def ssl_store(purpose = OpenSSL::X509::PURPOSE_ANY) if @ssl_store.nil? @ssl_store = build_ssl_store(purpose) end @ssl_store end |
#state ⇒ Object
359 360 361 362 363 364 365 366 367 368 369 370 |
# File 'lib/puppet/ssl/host.rb', line 359 def state if certificate_request return 'requested' end begin Puppet::SSL::CertificateAuthority.new.verify(name) return 'signed' rescue Puppet::SSL::CertificateAuthority::CertificateVerificationError return 'revoked' end end |
#suitable_message_digest_algorithms ⇒ Object
eventually we’ll probably want to move this somewhere else or make it configurable –jeffweiss 29 aug 2012
322 323 324 |
# File 'lib/puppet/ssl/host.rb', line 322 def [:SHA1, :SHA256, :SHA512] end |
#to_data_hash ⇒ Object
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/puppet/ssl/host.rb', line 283 def to_data_hash my_cert = Puppet::SSL::Certificate.indirection.find(name) result = { 'name' => name } my_state = state result['state'] = my_state result['desired_state'] = desired_state if desired_state thing_to_use = (my_state == 'requested') ? certificate_request : my_cert # this is for backwards-compatibility # we should deprecate it and transition people to using # json[:fingerprints][:default] # It appears that we have no internal consumers of this api # --jeffweiss 30 aug 2012 result['fingerprint'] = thing_to_use.fingerprint # The above fingerprint doesn't tell us what message digest algorithm was used # No problem, except that the default is changing between 2.7 and 3.0. Also, as # we move to FIPS 140-2 compliance, MD5 is no longer allowed (and, gasp, will # segfault in rubies older than 1.9.3) # So, when we add the newer fingerprints, we're explicit about the hashing # algorithm used. # --jeffweiss 31 july 2012 result['fingerprints'] = {} result['fingerprints']['default'] = thing_to_use.fingerprint .each do |md| result['fingerprints'][md.to_s] = thing_to_use.fingerprint md end result['dns_alt_names'] = thing_to_use.subject_alt_names result end |
#validate_certificate_with_key ⇒ Object
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/puppet/ssl/host.rb', line 209 def validate_certificate_with_key raise Puppet::Error, _("No certificate to validate.") unless certificate raise Puppet::Error, _("No private key with which to validate certificate with fingerprint: %{fingerprint}") % { fingerprint: certificate.fingerprint } unless key unless certificate.content.check_private_key(key.content) raise Puppet::Error, _(<<ERROR_STRING) % { fingerprint: certificate.fingerprint, cert_name: Puppet[:certname], ssl_dir: Puppet[:ssldir], cert_dir: Puppet[:certdir].gsub('/', '\\') } The certificate retrieved from the master does not match the agent's private key. Certificate fingerprint: %{fingerprint} To fix this, remove the certificate from both the master and the agent and then start a puppet run, which will automatically regenerate a certificate. On the master: puppet cert clean %{cert_name} On the agent: 1a. On most platforms: find %{ssl_dir} -name %{cert_name}.pem -delete 1b. On Windows: del "%{cert_dir}\\%{cert_name}.pem" /f 2. puppet agent -t ERROR_STRING end end |
#wait_for_cert(time) ⇒ Object
Attempt to retrieve a cert, if we don’t already have one.
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
# File 'lib/puppet/ssl/host.rb', line 327 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. }) 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 Puppet.log_exception(detail, _("Could not request certificate: %{message}") % { message: detail. }) end end end |