Skip to content

Instantly share code, notes, and snippets.

@ahpook
Created August 30, 2011 22:14
Show Gist options
  • Save ahpook/1182243 to your computer and use it in GitHub Desktop.
Save ahpook/1182243 to your computer and use it in GitHub Desktop.
Use a generic client certificate with puppet

The problem

There's enough trouble with puppet's ssl model (mandatory client certs) that people go and do odd things to get around it. The primary problem is that for lab/preproduction environments, if you reinstall machines frequently, you lose access to the private key that generated the original cert but (absent some puppet cert --clean [node] operation) the cert still exists, leading to the dreaded Retrieved certificate doesn't match private key error.

A solution

Generate a single client certificate which all your nodes use, and have the master determine node names from facter rather than the SSL DN. This way you can re-install nodes with impunity and as long as your bootstrap plops down the correct config and the cert+key, you don't have any more SSL issues.

The caveats

If you have autosign turned on, this change represents a shift in security tradeoffs: you can turn off autosign and therefore more tightly control which clients can talk to your server because they need to have your client cert+private key on them, but at the cost that any node with the cert+key can request any other node's catalog. So think through the trade-offs, but IMO if you are autosigning anyway, heavily firewalled, and dealing with rapid host recycling, this is a viable approach.

Note that this depends on puppetlabs bug#2128 being implemented, so you need to be running at least version 2.6.9 on client and server.

The procedure

  • generate a certificate+keypair on the master:
     puppet cert --generate generic-hostcert.mydomain.com
  • transfer the resulting $ssldir/{private_keys,ca/signed}/generic-hostcert.mydomain.com.pem to the client's $ssldir/{private_keys,certs}/ directories (in that order!)

  • set up a server instance with the following changes to puppet conf:

     [master]
       node_name = facter
  • Make the following change to auth.conf (so the generic cert is allowed to retrieve anyone's catalog):
    *** /etc/puppet-test/auth.conf  2010-09-29 10:58:34.000000000 -0700
    --- ./auth.conf 2011-08-30 13:53:39.000000000 -0700
    ***************
    *** 51,58 ****
    --- 51,59 ----
    # allow nodes to retrieve their own catalog (ie their configuration)
    path ~ ^/catalog/([^/]+)$
    method find
    allow $1
    # allow the generic cert to retrieve any node's catalog
    + allow generic-hostcert.mydomain.com
    #allow *
  • invoke the client with the following arguments/commands:
    [agent]
       certname = generic-hostcert.mydomain.com
       node_name = facter
       node_name_fact = fqdn
  • run puppet against the server instance, note that the catalog request comes in for /production/catalogs/hostname.mydomain.com, external nodes is invoked correctly, etc even though the cert name is the generic one.
@jude
Copy link

jude commented Oct 5, 2011

(Maybe add a note about the minimum version of puppet this should work with. It looks like the node_name_fact was added until http://projects.puppetlabs.com/issues/2128, so it looks like the minimum is at least 2.6.9)

@ahpook
Copy link
Author

ahpook commented Oct 6, 2011

@jude -- Yeah, good point. Updated.

@b1tw1se
Copy link

b1tw1se commented Apr 6, 2012

Just want to point out that the latest versions of puppet master and puppet client have a little different syntax now. Note the node_name_fact option. If you don't specify the following options correctly the facter/node yaml data on the master will all show up as generic-hostcert.mydomain.com.

Latest working syntax:

[agent]
# use a generic certificate when negotiating with the puppet master
certname = generic-hostcert.mydomain.com
node_name = facter
node_name_fact = fqdn

@raphink
Copy link

raphink commented Jul 10, 2013

@AFriemann
Copy link

I wish this would work but I am having trouble implementing it. Unfortunately when trying to register a node with the server I get lot's of errors:

Error: Could not retrieve catalog from remote server: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed: [self signed certificate in certificate chain for /CN=Puppet CA: ip-10-0-1-172.eu-west-1.compute.internal]

10.0.1.172 is the master node. What am I doing wrong here?

@adrianlzt
Copy link

Just adding

    path ~ ^/catalog/([^/]+)$
    method find
    allow $1
    # allow the generic cert to retrieve any node's catalog
    + allow generic-hostcert.mydomain.com

Warning: Unable to fetch my node definition, but the agent run will continue:
Warning: Error 403 on SERVER: Forbidden request: generic-hostcert.inet(127.0.0.1) access to /node/nodename1.inet [find] authenticated at :71

Error: Could not send report: Error 403 on SERVER: Forbidden request: generic-hostcert.inet(127.0.0.1) access to /report/nodename2.inet [save] authenticated at :81

So I added two more lines to auth.conf

# allow nodes to retrieve their own node definition
path ~ ^/node/([^/]+)$
method find
allow $1
# allow the generic cert to retrieve any node's catalog
allow generic-hostcert.dsn.inet
...
# allow all nodes to store their own reports
path ~ ^/report/([^/]+)$
method save
allow $1
# allow the generic cert to retrieve any node's catalog
allow generic-hostcert.dsn.inet

@digglife
Copy link

digglife commented Apr 8, 2015

I agree with @adrianlzt

Info: Retrieving pluginfacts
Info: Caching certificate_revocation_list for ca
Info: Retrieving plugin
Info: Loading facts
Warning: Unable to fetch my node definition, but the agent run will continue:
Warning: Error 403 on SERVER: Forbidden request: generic.mydomain.com(127.0.0.1) access to /node/tcentos [find] authenticated  at :70
Info: Caching catalog for tcentos
Warning: The package type's allow_virtual parameter will be changing its default value from false to true in a future release. If you do not want to allow virtual packages, please explicitly set allow_virtual to false.
   (at /usr/lib/ruby/site_ruby/1.8/puppet/type/package.rb:430:in `default')
Info: Applying configuration version '1428487674'
Notice: Finished catalog run in 5.12 seconds
Error: Could not send report: Error 403 on SERVER: Forbidden request: generic.mydomain.com(127.0.0.1) access to /report/tcentos [save] authenticated  at :79

@lmayorga1980
Copy link

Is there a new version of this for Puppet 4? or PE 2015.2

@bernhardjt
Copy link

For puppetserver2.2 do not forget:

--- /etc/puppetlabs/puppetserver/conf.d/puppetserver.conf.old   2016-02-11 10:33:24.032837550 +0000
+++ /etc/puppetlabs/puppetserver/conf.d/puppetserver.conf   2016-02-11 10:16:27.864607159 +0000
@@ -46,7 +46,7 @@
     # (optional) Authorize access to Puppet master endpoints via rules specified
     # in the legacy Puppet auth.conf file (if true or not specified) or via rules
     # specified in the Puppet Server HOCON-formatted auth.conf (if false).
-    #use-legacy-auth-conf: false
+    use-legacy-auth-conf: false
 }

 # settings related to HTTP client requests made by Puppet Server

@lucasdiedrich
Copy link

For the record, i had to use the option below to work:

use-legacy-auth-conf: true

@dannykansas
Copy link

A huge shot in the dark here, but if this cert method was implemented years ago and the cert has now expired, what is a good method for updating the generic cert?

CA is already taken care of (that's well documented elsewhere, see: puppetlabs/certregen and flyinglotus.io's article, if anyone runs across this needing help on the CA aspect).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment