====== Replacing Foremans web SSL certificate. ====== [[http://theforeman.org/|Foreman]] does a great job of providing SSL support out-of-the-box, it does this by using the SSL certificates generated by your puppet-ca. Unless your users web browsers all trust the puppet CA (unlikely), any human user of Foreman is going to get SSL warnings. Replacing Foremans SSL certifcate with one that's signed by a default trusted CA requires some care for 2 reasons: * Other components apart from humans using web browers, need to validate the Foreman server. Therefore we need to make sure these components validate the Forman server against the correct CA. * Foreman uses SSL client authentication to authenticate various components via their puppet-ca certificates. We need to make sure we do *not* change the CA that foreman uses to authenticate these components. The following diagram shows the various components talking to the Forman server and which communications require the SSL keys changing. {{:foreman-puppet-ssl.png?400|}} For the following examples, the new certificates live in the following locations: * SSL Certificate: `/etc/pki/tls/certs/puppet.example.com.crt` * SSL Private Key: `/etc/pki/tls/private/puppet.example.com.key` * SSL CA Chain: `/etc/pki/tls/certs/cachain.crt` ==== Foreman ==== Foreman is presented via mod_passenger on Apache, so the SSL keys need to be changed in your apache VirtualHost. For me this was ''/etc/httpd/conf.d/05-foreman-ssl.conf''. The following options need to be changed: ''SSLCertificateFile'', ''SSLCertificateKeyFile'' and ''SSLCertificateChainFile''. It is important that you do **not** change ''SSLCACertificateFile'' or ''SSLCARevocationFile'', as these are used for client authentication. Your final config should look like this: ## SSL directives SSLEngine on SSLCertificateFile "/etc/pki/tls/certs/puppet.example.com.crt" SSLCertificateKeyFile "/etc/pki/tls/private/puppet.example.com.key" SSLCertificateChainFile "/etc/pki/tls/certs/cachain.crt" SSLCACertificatePath "/etc/pki/tls/certs" SSLCACertificateFile "/var/lib/puppet/ssl/certs/ca.pem" SSLCARevocationFile "/var/lib/puppet/ssl/ca/ca_crl.pem" SSLVerifyClient optional SSLVerifyDepth 3 SSLOptions +StdEnvVars If you use the Console feasture for foreman, you will also want to change the ''websockets_ssl_key'' and ''websockets_ssl_cert'' keys in ''/etc/foreman/settings.yaml''. For example: :websockets_ssl_key: /etc/pki/tls/private/puppet.example.com.key :websockets_ssl_cert: /etc/pki/tls/certs/puppet.example.com.crt ==== Smart Proxy ==== If you have your smart proxies running on the same box as foreman, make sure `foreman_ssl_ca` is **not** defined in ''/etc/foreman-proxy/settings.yaml'' and it will read the CA from the main foreman settings. ==== Puppet ==== Change ''ssl_ca'' in ''/etc/puppet/foreman.yaml'', do not change ''ssl_cert'' or ''ssl_key''. For example: :ssl_ca: "/etc/pki/tls/certs/cachain.crt" :ssl_cert: "/var/lib/puppet/ssl/certs/puppet.example.com.pem" :ssl_key: "/var/lib/puppet/ssl/private_keys/puppet.example.com.pem" ==== Managing with puppet ==== If you use manage your foreman and puppet install with the ''theforeman/puppet'' and ''theforeman/foreman'' modules, you can configure all the above with the following hiera data: foreman::ssl: true puppet::server_foreman_ssl_ca: '/etc/pki/tls/certs/cachain.crt' puppet::server_foreman_url: 'https://puppet.example.com' foreman::server_ssl_key: '/etc/pki/tls/private/puppet.example.com.key' foreman::server_ssl_cert: '/etc/pki/tls/certs/puppet.example.com.crt' foreman::server_ssl_chain: '/etc/pki/tls/certs/cachain.crt' foreman::servername: 'puppet.example.com' foreman::foreman_url: 'https://puppet.example.com' foreman::websockets_ssl_key: '/etc/pki/tls/private/puppet.example.key' foreman::websockets_ssl_cert: '/etc/pki/tls/certs/puppet.example.crt' ===== Importing Puppet Classes into Puppet Dashboard ===== [[http://www.puppetlabs.com/puppet/related-projects/dashboard/|Puppet Dashboad]] has a concept of classes, which can be really useful if you make use of [[http://docs.puppetlabs.com/guides/external_nodes.html|external nodes]] and link it to the dashboard. Unfortunately it doesn’t [[http://projects.puppetlabs.com/issues/3503|currently]] have a way to auto-import classes defined in your puppet manifests. The following is a little bit of python hacked together to provide this functionality. It looks at a directory for a list of modules and the database details for puppet dashboard (only works for MySQL). It will add any modules to puppet dashboard that are not already defined and remove any extras that no longer exist in your modules path. #!/usr/bin/python import os import MySQLdb import datetime #Path to Puppet modules directory modulesdir="/etc/puppet/modules/" #MySQL details: user = "" password = "" host = "" database = "" availmodules = [] currentmodules = []`{lang="python"} #Get list of avaible modules from filesystem for item in os.listdir(modulesdir): if os.path.isdir(os.path.join(modulesdir,item)) and not item.startswith('.'): availmodules.append(item) availmodules = set(availmodules) #Get list of current modules from database db = MySQLdb.connect(host=host,user=user,passwd=password,db=database) cursor = db.cursor() cursor.execute("SELECT name FROM node\_classes") for [name] in cursor.fetchall(): currentmodules.append(name) currentmodules = set(currentmodules) extramodules = currentmodules - availmodules print "Availble:\\t"+str(availmodules) print "Current:\\t"+str(currentmodules) print "Extra:\\t\\t"+str(extramodules) #Add or update current availble modules currenttime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') for module in availmodules: if module in currentmodules: #module already exists, just update timestamp cursor.execute("UPDATE node\_classes SET updated\_at = %s WHERE name = %s",(currenttime,module)) else: #module doesn't exist, insert it print "Adding: "+str(module) cursor.execute("INSERT INTO node\_classes (name,created\_at,updated\_at) vALUES (%s,%s,%s)",(module,currenttime,currenttime)) #Delete any extra modules for module in extramodules: print "Deleting:"+str(module) cursor.execute("DELETE from node\_classes WHERE name = %s",(module)) db.commit() cursor.close() db.close() There are better ways to get a list of classes, for example the [[https://github.com/puppetlabs/interface-utils|interface utils]], but this requires puppet 2.6.5 at a minimum and I’m running 2.6.2. It would also be desirable to input data into the dashboard via an API instead of directly into the database, but AFAIK this functionality isn’t available yet. I personally run the above as part of the post-commit hook on the puppet repository. But you could also run it via cron or manually.