shopping24 tech blog

s is for shopping

November 14, 2013 / by Torsten Bøgh Köster / CTO / @tboeghk

Provisioning Vagrant using a Puppet master - the reliable way

Vagrant is a great tool to set up local test machines. Puppet is a great tool to deliver unified server configuration to a lot of machines. A Puppet master centralizes the provisioning process. Unfortunately combining Vagrant with a Puppet master will lead you directly into SSL certificate hell. Here’s how we got back out of hell …

A problem of pain

When developing in a team and iteratively launching and destroying your local Vagrant testing machine, you’ll hit a limitation of the puppet_master provisioner in Vagrant. When talking to the Puppet master, the client generates a SSL certificate based on the local machine fingerprint to sign it’s requests. This certificate is stored on the Puppet master server.

You can see where we’re heading: As soon as you destroy and up your local Vagrant machine (even on the same physical hardware), the machine fingerprint and thus the SSL cert changes as well. All requests to the Puppet master are rejected.

Another limitation we’re hitting is that the puppet_master provisioner does not allow to supply custom facts for Facter, who we depend on in the Vagrant machine.

The idea

The solution is (almost) straightforward and inspired by a Github pull request on the Vagrant project:

  • We’re going to use a local puppet run to properly place the project’s SSL certificates in the Vagrant machine and to supply some Facter facts.

  • Then we’re going to start the “real” Puppet master run and are tweaking it to overcome our sometimes pretty messy internet connection

Setting up Vagrant using two provisioners

We’re going to set up Vagrant to run two consecutive provisioners (order matters!). The first one is a puppet provisioner, that executes out of the local project source and prepares the needed SSL puppet certificates. Then the additional Facter facts given are put in place for the puppet_master provisioner.

Vagrant.configure("2") do |config|
   # 1) prepare certs to connect to puppet server
   config.vm.provision :puppet do |puppet|
      puppet.manifests_path = "src/test/puppet/manifests"
      puppet.manifest_file  = "prepare-puppetserver.pp"
      puppet.options = "--fileserverconfig=/vagrant/src/test/puppet/fileserver.conf
       	 --templatedir=/vagrant/src/test/puppet/templates 
       	 --debug 
       	 --verbose 
       	 --environment development"
      puppet.facter = {
         "application_name" => "your-project-role",
         "is_vagrant"       => "true"
      }
   end
	
   # 2) Provision services on the system using Puppet Maschta
   config.vm.provision "puppet_server" do |puppet|
      puppet.puppet_server = "your.puppet.master.server.de"
      puppet.puppet_node = "projectname-vagrant.your.fqdn.de"
      puppet.options = "--test 
         --waitforcert=10 
         --verbose 
         --debug 
         --environment development 
         --pluginsync 
         --configtimeout=30m"
   end
end

Setting up Puppet – using Puppet

The prepare-puppetserver.pp syncs the puppet ssl directory and templates facts into a textfile in /etc/facter/facts.d. In our projects, the Vagrantfile resides in the root directory and puppet sources in src/test/puppet.

# place puppetserver certs
notice('Preparing puppet master cert files and facts ...')
file { '/var/lib/puppet/ssl':
  ensure  => 'directory',
  owner   => 'puppet',
  group   => 'puppet',
  source  => 'puppet:///files/ssl',
  mode    => '0600',
  recurse => true;
} ->
# place puppet facts
file { ['/etc/facter', '/etc/facter/facts.d']:
  ensure => 'directory',
  owner  => 'root',
  group  => 'root',
  mode   => '0644',
} ->
file { '/etc/facter/facts.d/s24_vagrant_facts.txt':
  ensure  => file,
  owner   => 'root',
  group   => 'root',
  content => template('s24_vagrant_facts.txt.erb'),
  mode    => '0644',
} ->
# stop puppet agent
exec { 'stop-puppet-agent': command => '/etc/init.d/puppet stop', }

The get the file source working, you need to place a src/test/puppet/fileserver.conf in your project, that resolves the files mountpoint:

[files]
    path /vagrant/src/test/puppet/files
    allow *

To get it activated, set the --fileserverconfig and --templatedir properly in your Vagrant setup.

Creating Puppet certs on your first run

To obtain the SSL certs to configure through Puppet, your local Vagrant box needs to contact the Puppet master on it’s very first run. On your first run, comment out your local puppet provisioner and let the puppet_master provisioner connect to the Puppet master and create a local cert cache in /var/lib/puppet/ssl.

Copy those script to your project directory conveniently mounted at /vagrant/src/test/puppet/files. Put your local puppet provisioner back into place and you’re ready to run.