Managing SSL certificate symlinks with Puppet – A Defined Resource Type example

Recently I had to work on a Puppet module which needed to handle SSL certificates. The certificates are placed in a directory, and for the software to use the certificates I needed to generate symlinks to these certificates with a hashed name.

Quite trivial to solve with a script, there used to be a packages script called “c_rehash” which does this for you but it doesn’t seem to be available in RHEL 7. I decided to try my luck with Puppet code and learned a few things I thought to share and document so I don’t forget…

First things first, my requirements are:

  • place certificates in a specific folder
  • certificates are managed by Puppet
  • create symlinks to the certificates named with the hash value of the certificate
  • when a certificate changes, the hash needs to be recalculated and a new symlink must be placed
  • remove the old symlink

In this example I’ll name my module “certs”.  Starting with the easy part, create certs/init.pp:

class certs () {

  file { '/etc/mycerts/mycert1.crt':
    ensure => present,
    owner  => 'root',
    group  => 'root',
    source => "puppet:///modules/certs/mycert1.crt",
  }

  file { '/etc/mycerts/mycert2.crt':
    ensure => present,
    owner  => 'root',
    group  => 'root',
    source => "puppet:///modules/certs/mycert2.crt",
  }
}

Easy enough, this class is managing my certificates and places them in /etc/mycerts. Now we need to solve the remainder of the requirements: create hashed symlinks, and update the symlinks when the certificates change.

So I need a piece of Puppet code which can remove and create hashed symlinks, which I can execute multiple times with different values. There is a special kind of Puppet resource which lets you do just that: a Defined Resource Type.

Be sure to look through the official link for more information, but here is how I implemented it. I created a file called certs/refresh_symlink.pp:

define certs::refresh_symlink() {
  exec { "remove_symlink_${title}":
    path        => "/usr/bin",
    command     => "rm -f \$(find -L /etc/mycerts -samefile /etc/mycerts/$title | grep -v $title)",
    refreshonly => true,
  }

  exec { "create_symlink_${title}":
    path        => "/usr/bin",
    command     => "ln -s /etc/mycerts/$title /etc/mycerts/$(openssl x509 -hash -noout -in /etc/mycerts/$title).0",
    refreshonly => true,
  }
}

This makes the “certs::refresh_symlink” resource available. When the resource is called the value of “$title” is automatically filled with the title of the instance.  So, calling

certs::refresh_symlink{ 'foo': }

sets the variable $title to ‘foo’.

One other thing to note in the exec options

refreshonly => true

Setting this option makes sure the exec is only run when it’s called by a notify, subscribe or ~> parameter.  Meaning it won’t execute when you declare a certs::refresh_symlink class, it only executes when the class gets notified.

Now we can include the certs::refresh resource in our main certs/init.pp:

class certs () {

  certs::refresh_symlink { 'mycert1.crt': } # Create the resource, execs do NOT run because of refreshonly
  file { '/etc/mycerts/mycert1.crt':
    ensure => present,
    owner  => 'root',
    group  => 'root',
    source => "puppet:///modules/certs/mycert1.crt",
    notify => Certs::Refresh_symlink["mycert1.crt"], # Notify the resource, execs DO run
  }

  certs::refresh_symlink { 'mycert2.crt': } 
  file { '/etc/mycerts/mycert2.crt':
    ensure => present,
    owner  => 'root',
    group  => 'root',
    source => "puppet:///modules/certs/mycert2.crt",
    notify => Certs::Refresh_symlink["mycert2.crt"],
  }
}

Let’s say mycert1.crt is changed. When Puppet is run it will notify the Certs::Refresh_symlink[‘mycert1.crt’] resource. Because it is notified, it will execute the execs in the Certs::Refresh_symlink[‘mycert1.crt’] and remove old invalid symlinks and create new ones.

 

2 thoughts on “Managing SSL certificate symlinks with Puppet – A Defined Resource Type example

  1. RHEL 7 has a script similar to c_rehash. Call

    cacertdir_rehash /etc/mycerts

    to update the certificate links. It’s part of the authconfig package.

Leave a Reply

Your email address will not be published. Required fields are marked *