Book Image

Learning Puppet Security

Book Image

Learning Puppet Security

Overview of this book

Table of Contents (17 chapters)
Learning Puppet Security
About the Author
About the Reviewers

Preparing the environment for examples

As mentioned in the preface, we're going to use Vagrant to run our examples. In case you missed it, Vagrant is a tool that helps you automate the creation of virtual machines for testing. In this case, it's a great tool for us to use to quickly build-out our build and example environments.

We'll be using CentOS 6 in these examples, but most of them should run without much modification on other platforms. You will need to adjust the package names and perhaps configure the filenames for other operating systems. Many community modules, which we will explore in later chapters, support multiple flavors of Linux as well as other Unix-like systems. The powerful descriptive language of Puppet makes this easy to do.

While the use of Vagrant is not required, it will help us to maintain a clean environment for each of the examples we run, and will also ease the creation of virtual machines. If you choose not to use Vagrant for this, you can still run the examples using the manifests and modules provided with the source accompanying this book.

Installing Vagrant and VirtualBox

In order for us to use Vagrant, we must first install it. To do this, we need to install the required dependencies followed by Vagrant itself. We'll be using VirtualBox to host the virtual machines in these examples, since it is the most supported virtual machine provider.

VirtualBox can be downloaded from On this site, you will find packages for installing a variety of operating systems. You simply need to pick the package for your chosen operating system and install it using the instructions found on the site.

Once we have VirtualBox installed, we can approach installing Vagrant. Vagrant has several methods of installation. These methods include OS packages for Linux, as well as installers for OS X and Windows. Older versions of Vagrant supported installation via the Ruby gem utility, but this has been removed in later versions.

Vagrant can be found at Once you're there, you can download the package or installer for your OS. Once downloaded, you can install the package using your operating system's package manager, or by executing the downloaded package. In Windows and OS X, this is sufficient to have a working installation of Vagrant.

More in-depth installation instructions can be found on the Documentation tab on the Vagrant website; however, the package or installer will do most of the work.

It is worth noting that if you are using Windows, you will perform most of the work we're doing in a command shell on the DOS command box. However, if you use a local editor, you should be able to follow along with no issues.

Creating our first Vagrantfile

Now that we have Vagrant installed, we'll create our first Vagrant configuration. Vagrant uses a file called Vagrantfile to control its operation.

First, we start by creating a directory for our project. In this case, we'll call it puppetbook. We'll end up building on this setup in later chapters to automate configuration of our examples. This will allow us to focus on the Puppet tasks, and not so much on getting our test systems into the desired state.

Inside this directory, we'll create a directory called master_manifests. The purpose of this directory is to hold the Puppet manifests that we'll use to provision the base VM.

We'll be using the Puppet provisioner to do our work. This is one of a handful of methods you can use to provision a Vagrant virtual machine. Using this provisioner, we'll write a Puppet manifest that will describe the desired state of our machine. Vagrant will then use this manifest to run Puppet locally and configure the system.

Next, we'll create a Vagrantfile. In your favorite editor, go ahead and open Vagrantfile. Add the following contents. We'll cover what each one does in a moment:

Vagrant.configure(2) do |config|
  config.vm.define :puppetmaster do |master| = "centos65-x64-puppet"
    master.vm.box_url = ""
    master.vm.hostname = "" "private_network", ip: "", netmask: ""

    master.vm.provision "shell", inline: "yum –y update puppet"

    master.vm.provision "puppet" do |puppet|
      puppet.manifests_path = "master_manifests"
      puppet.manifest_file = "init.pp"



It's possible that by the time you read this, the Vagrant box referenced in the preceding code will be deprecated. This book was written using the Puppet Labs CentOS 6 machine images. You can go to and find a replacement. You want a CentOS 6 x86_64 box with Puppet (called plain there) and VirtualBox addons.

Go ahead and save the file. We'll cover what each file does here:

Vagrant.configure(2) do |config|

This line sets up Vagrant using configuration version 2. It uses Ruby blocks to create a Vagrant configuration with the config variable:

config.vm.define :puppetmaster do |master|

This line defines a virtual machine called puppetmaster. Vagrant supports multimachine setups, which is a feature we'll use later on in the book. For now, we'll define a single machine. Much like the preceding code, we use a block called master: = "centos65-x64-puppet"

This defines the box we'll use for our Puppet Master. It is a symbolic name, but it makes sense to name it according to what it is. If you refer to the same box later, it'll use the same base and not download the box files an additional time:

master.vm.box_url = ""

This defines the URL we'll download our box file from. In this case, we're grabbing it from the hosted Puppet Vagrant boxes on Puppet Labs. We could get a box from any number of other places, but the Puppet Labs boxes come with the Puppet agent preinstalled and the Puppet repository is already available and ready for use. If you wish to explore other box options, there is a directory of them available at

master.vm.hostname = ""

This command simply sets the host name of our machine. It is important for the master as it influences the certificate name that gets created at installation: "private_network", ip: "", netmask: ""

This line creates a private network for our virtual machines to use. We assign it the IP address (78 is PU on a phone dial pad):

master.vm.provision "shell", inline: "yum –y update puppet

"Wait," you say, "I thought we were using the Puppet provisioner?"

As it turns out, the Puppet Labs base box comes with Puppet 3.4 installed. The current version we wish to use in this book is 3.7.3. We use the yum statement to upgrade Puppet before the provisioner starts. Otherwise, we get issues when the Puppet run updates the agent:

master.vm.provision "puppet" do |puppet|

Here, we tell Vagrant we're going to use the Puppet provisioner, and open a block called puppet to do so:

puppet.manifests_path = "master_manifests"

Here, we give the path to the manifest directory. This is relative to the path that the Vagrantfile is in. As you can recall, we created this directory earlier:

puppet.manifest_file = "init.pp"

We define the Puppet manifest to be called init.pp. This is the default name of a Puppet manifest. Vagrant defaults to default.pp if it's not specified:


These lines undo each of the preceding blocks and close out the file.

If we run Vagrant now, it will throw an error because it cannot find the init.pp file, so let's go ahead and create it inside the master_manifests directory. To save space, we'll call out each block and describe its function rather than giving the entire file and explaining it:

package { 'puppet-server':
  ensure => 'present',

The preceding resource declaration will install the Puppet Master. By specifying the ensure value of present, we make sure it's installed; however, we tell Puppet that we do not care about the version and do not wish to upgrade it:

file { '/etc/puppet/puppet.conf':
  ensure  => 'present',
  owner   => 'root',
  group   => 'root',
  mode    => '0644',
  source  => '/vagrant/master_manifests/files/puppet.conf',
  require => Package['puppet-server'],

The preceding resource declaration has a good amount more going on. Here, we're going to manage a file called /etc/puppet/puppet.conf. We ensure that it is present, then set the owner, group, and mode to set the values. Using the source parameter, we source the file from the local filesystem. Vagrant, by default, will mount the directory containing the Vagrantfile as /vagrant, so we can take advantage of that mount to get the file without otherwise copying it.

The last line here shows off the explicit dependency management of Puppet. We require that the puppet-server package is installed before we install the configuration file. This will ensure that the directory is created, and the package installation does not overwrite the configuration file:

service { 'puppetmaster':
  ensure  => 'running',
  require => File['/etc/puppet/puppet.conf'],

This last resource declaration ensures that the Puppet Master service is running. It depends on the configuration file being there.

In a real-world example, we're likely to use subscribe instead of require here. This would restart the service if the configuration file changed. However, since we're using the local Puppet provisioner and not running this code under a Puppet Master, this code will only be run once, so it is unnecessary to use subscribe.

We need one last file to make the system work. The file resource depends on a file called master_manifests/files/puppet.conf. We've covered the contents of this file in the Puppet installation section, so we will not repeat them here. You simply need to copy the file to the directory for the provisioner to use.

When we're done, the complete directory structure of this setup will look as follows:

├── Vagrantfile
└── master_manifests
    ├── files
    │   └── puppet.conf
    └── init.pp

Once we're set up, we're in a good position to run the examples that we'll present in this book. As these examples get more complex, we'll add the necessary data to this structure to add things such as client machines.