Chapter 1. Getting Started with Puppet

Puppet is an open source framework and toolset for managing the configuration of computer systems. In this book, we're going to look at how you can use Puppet to manage your configuration. As the book progresses, we'll introduce Puppet's features and then show you how to integrate Puppet into your provisioning and management lifecycle. To do this, we'll take you through configuring a real-world scenario that we'll introduce in Chapter 2.

In this chapter, we start with a quick overview of Puppet, what it is, how it works, and which release to use, and then we show you how to install Puppet and its inventory tool, Facter. We show you how to install it on Red Hat, Debian, Ubuntu, Solaris, Microsoft Windows, and via a Ruby gem. We'll then configure it and show you how create your first configuration items. We'll also introduce you to the concept of "modules," Puppet's way of collecting and managing bundles of configuration data. We'll then show you how to apply one of these modules to a host using the Puppet agent.

What Is Puppet?

Puppet is Ruby-based, licensed as GPLv2 and can run in either client-server or stand-alone modes. Puppet is principally developed by Luke Kanies and his company, Puppet Labs (formerly Reductive Labs). Kanies has been involved with Unix and systems administration since 1997 and developed Puppet from that experience. Unsatisfied with existing configuration management tools, Kanies began working with tool development in 2001 and in 2005 he founded Puppet Labs, an open source development house focused on automation tools. Shortly after this, Puppet Labs released their flagship product, Puppet.

Puppet can be used to manage configuration on UNIX (including OSX) and Linux platforms, and recently Microsoft Windows platforms as well. Puppet is often used to manage a host throughout its lifecycle: from initial build and installation, to upgrades, maintenance, and finally to end-of-life, when you move services elsewhere. Puppet is designed to continuously interact with your hosts, unlike provisioning tools which build your hosts and leave them unmanaged.

Puppet has a simple operating model that is easy to understand and implement. The model is made up of three components:

  • Deployment

  • Configuration Language and Resource Abstraction Layer

  • Transactional Layer

The Puppet model

Figure 1.1. The Puppet model

Deployment

Puppet is usually deployed in a simple client-server model (Figure 1-2). The server is called a "Puppet master", the Puppet client software is called an agent and the host itself is defined as a node.

The Puppet master runs as a daemon on a host and contains the configuration required for your environment. The Puppet agents connect to the Puppet master via an encrypted and authenticated connection using standard SSL, and retrieve or "pull" any configuration to be applied.

Importantly, if the Puppet agent has no configuration available or already has the required configuration then Puppet will do nothing. This means that Puppet will only make changes to your environment if they are required. The whole process is called a configuration run.

Each agent can run Puppet as a daemon via a mechanism such as cron, or the connection can be manually triggered. The usual practice is to run Puppet as a daemon and have it periodically check with the master to confirm that its configuration is up-to-date or to retrieve any new configuration. However, many people find being able to trigger Puppet via a mechanism such as cron, or manually, better suits their needs. By default, the Puppet agent will check the master for new or changed configuration once every 30 minutes. You can configure this period to suit your environment.

Puppet client-server model

Figure 1.2. Puppet client-server model

Other deployment models also exist. For example, Puppet can also run in a stand-alone mode where no Puppet master is required. Configuration is installed locally on the host and the puppet binary is run to execute and apply that configuration. We discuss this mode later in the book.

Configuration Language and Resource Abstraction Layer

Puppet uses a declarative language to define your configuration items, which Puppet calls "resources." This declarative nature creates an important distinction between Puppet and many other configuration tools. A declarative language makes statements about the state of your configuration - for example, it declares that a package should be installed or a service should be started.

Most configuration tools, such as a shell or Perl script, are imperative or procedural. They describe HOW things should be done rather than the desired end state - for example, most custom scripts used to manage configuration would be considered imperative.

This means Puppet users just declare what the state of their hosts should be: what packages should be installed, what services should be running, etc. With Puppet, the system administrator doesn't care HOW this state is achieved – that's Puppet's problem. Instead, we abstract our host's configuration into resources.

Configuration Language

What does this declarative language mean in real terms? Let's look at a simple example. We have an environment with Red Hat Enterprise Linux, Ubuntu, and Solaris hosts and we want to install the vim application on all our hosts. To do this manually, we'd need to write a script that does the following:

  • Connects to the required hosts (including handling passwords or keys)

  • Checks to see if vim is installed

  • If not, uses the appropriate command for each platform to install vim, for example on Red Hat the yum command and on Ubuntu the apt-get command

  • Potentially reports the results of this action to ensure completion and success

Note

This would become even more complicated if you wanted to upgrade vim (if it was already installed) or apply a particular version of vim.

Puppet approaches this process quite differently. In Puppet, we define a configuration resource for the vim package. Each resource is made up of a type (what sort of resource is being managed: packages, services, or cron jobs), a title (the name of the resource), and a series of attributes (values that specify the state of the resource - for example, whether a service is started or stopped).

You can see an example of a resource in Listing 1-1.

Example 1-1. A Puppet Resource

package { "vim":
    ensure => present,
}

The resource in Listing 1-1 specifies that a package called vim should be installed. It is constructed like:

type { title:
    attribute => value,
}

In Listing 1-1, the resource type is the package type. Puppet comes with a number of resource types by default, including types to manage files, services, packages, and cron jobs, among others.

Note

You can see a full list of the types Puppet can currently manage (and their attributes) at http://docs.puppetlabs.com/references/stable/type.html. You can also extend Puppet to support additional resource types, as we'll discuss in Chapter 10.

Next is the title of the resource, here the name of the package we want to install, vim. The type and title of the resource can be combined together to allow Puppet to create a reference to the resource. For example, our resource would be called Package["vim"]. We'll see this a lot more in later chapters when we build relationships between resources, allowing us to create structure in our configuration, for example installing a package before starting its associated service.

Lastly, we've specified a single attribute, ensure, with a value of present. Attributes tell Puppet about the required state of our configuration resource. Each type has a series of attributes available to configure it. Here the ensure attribute specifies the state of the package: installed, uninstalled, etc. The present value tells Puppet we want to install the package. To uninstall the package we would change the value of this attribute to absent.

Resource Abstraction Layer

With our resource created, Puppet takes care of the details of how to manage that resource when our agents connect. Puppet handles the "how" by knowing how different platforms and operating systems manage certain types of resources. Each type has a number of "providers." A provider contains the "how" of managing packages using a particular package management tool. For the package type, for example, for there are more than 20 providers covering a variety of tools including yum, aptitude, pkgadd, ports, and emerge.

When an agent connects, Puppet uses a tool called "Facter" to return information about that agent, including what operating system it is running. Puppet then chooses the appropriate package provider for that operating system and uses that provider to check if the vim package is installed. For example, on Red Hat it would execute yum, on Ubuntu it would execute aptitude, and on Solaris it would use the pkg command. If the package is not installed, then Puppet will install it. If the package is already installed, Puppet does nothing.

Puppet will then report its success or failure in applying the resource back to the Puppet master.

Transactional Layer

Puppet's transactional layer is its engine. A Puppet transaction encompasses the process of configuring each host including:

  • Interpret and compile your configuration

  • Communicate the compiled configuration to the agent

  • Apply the configuration on the agent

  • Report the results of that application to the master

The first step Puppet takes is to analyze your configuration and calculate how to apply it to your agent. To do this, Puppet creates a graph showing all resources, their relationships to each other and to each agent. This allows Puppet to work out in what order, based on relationships you create, to apply each resource to your host. This model is one of Puppet's most powerful features.

Puppet then takes the resources and compiles them into a "catalog" for each agent. The catalog is sent to the host and applied by the Puppet agent. The results of this application are then sent back to the master in the form of a report.

The transaction layer allows configurations to be created and applied repeatedly on the host. Puppet calls this idempotent, meaning multiple applications of the same operation will yield the same results. Puppet configuration can be safely run multiple times with the same outcome on your host and hence ensuring your configuration stays consistent.

Puppet is not fully transactional though; your transactions aren't logged (other than informative logging) and hence you can't roll back transactions as you can with some databases. You can, however, model transactions in a "noop," or no operation mode, that allows you to test the execution of your changes without making any actual changes.

Selecting the Right Version of Puppet

The best version of Puppet to use is usually the latest release, which at the time of writing is the 2.6.x branch of releases; newer ones are currently in development. The biggest advantage of the 2.6.x branch of releases onward is their replacement of XML-RPC as a transport layer. The 2.6.x releases instead use REST APIs, resulting in greatly improved performance. The 2.6.x releases are also stable, perform well, and contain a wide of variety of new features and functions unavailable in earlier releases.

Older releases of Puppet, especially releases before the 0.24.x branch of releases, tend to be very poorly featured and contain a number of bugs and issues. They are largely unsupportable and requests for help for with 0.20.x, 0.22.x, and 0.23.x or earlier releases will be largely met with suggestions that you upgrade. We do not recommend you use any of these releases.

Note

This book assumes you are using either a 2.6.x or later release, although most of the material (except where specifically indicated) is supported back to release 0.24.7. It is important to remember that if you use the 0.24.7 or 0.24.8 releases, you will not get the benefit of the performance improvements in the 0.25.x and later releases.

There are a variety of releases, some older than others, packaged for operating systems. The 0.24.x releases are broadly packaged. The 2.6.x and 0.25.x releases are packaged and distributed in newer versions of operating systems and platforms. If you can't find later Puppet releases packaged for your distribution you have the option of rolling your own packages, backporting, or installing from source (though we don't recommend the latter - see below).

Can I mix releases of Puppet?

The most common deployment model for Puppet is client-server. Many people ask if you can have differing releases of Puppet on the master and as agents. The answer is yes, with some caveats. The first caveat is that the master needs to be a later release than the agents. For example, you can have a version 0.24.8 agent connected to a version 2.6.0 master but not a version 2.6.0 agent connected to a 0.24.8 master.

The second caveat is that the older the agent release, the less likely it will function correctly with a newer release of the master. It is highly unlikely that a version 0.20.0 agent will correctly work with a version 2.6.0 master. Generally, the 0.24.x branch of agents will happily connect to and function with 2.6.x and 0.25.x-based masters. Later versions of masters may not be so forgiving of earlier agents and some functions and features may not behave correctly.

Lastly, mixing 2.6.x and later release masters with 0.24.x and earlier agents will mean you won't get the full performance enhancements available in 2.6.x. The 0.24.x agents will still communicate with the slower XML-RPC transport layer rather than taking advantage of the newer REST interface.

Installing Puppet

Puppet can be installed and used on a variety of different platforms, including the following:

  • Red Hat Enterprise Linux, CentOS, Fedora & Oracle Enterprise Linux

  • Debian and Ubuntu

  • Mandrake and Mandriva

  • Gentoo

  • Solaris and OpenSolaris

  • MacOS X and MacOS X Server

  • *BSD

  • AIX

  • HP UX

  • Microsoft Windows hosts (in versions after 2.6.0 and with only limited support for file resources)

On these platforms, Puppet manages a variety of configuration items, including (but not limited to):

  • Files

  • Services

  • Packages

  • Users

  • Groups

  • Cron jobs

  • SSH keys

  • Nagios configuration

For Puppet, the agent and master server installations are very similar, although most operating systems and distribution packaging systems divide the master and agent functions into separate packages. On some operating systems and distributions, you'll also need to install Ruby and its libraries and potentially some additional packages. Most good packaging systems will have most of the required packages, like Ruby, as prerequisites of the Puppet and Facter packages. For other features (i.e., some types of reporting that I'll demonstrate later in this book), you may also need to install additional packages. When we look at these functions, I'll let you know what additional packages you'll need to install.

We'll also demonstrate how to install Puppet from source, but we don't recommend this approach. It is usually operationally easier and simpler to use your operating system's package management system, especially if you are installing Puppet on a large number of hosts.

Installing on Red Hat Enterprise Linux and Fedora

On Red Hat Enterprise Linux and Red Hat based-derivatives, you need to install some prerequisites (such as the Ruby programming language, the Ruby libraries and the Ruby Shadow library) to allow Puppet to manage users and groups. You can do this with Red Hat's package management tool, Yum.

# yum install ruby ruby-libs ruby-shadow

Next, to get the latest releases of Puppet, you will need to add the EPEL repository (see sidebar) to your host and then install packages from that repository. You can add the EPEL repository by adding the epel-release RPM (.rpm package manager).

# rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/
Installing on Red Hat Enterprise Linux and Fedora
epel-release-5-3.noarch.rpm

Note

The EPEL repository is a volunteer-based community effort from the Fedora project to create a repository of high-quality add-on packages for Red Hat Enterprise (RHEL) and its compatible spinoffs such as CentOS, Oracle Enterprise Linux or Scientific Linux. You can find more details on EPEL including how to add it to your host at http://fedoraproject.org/wiki/EPEL and http://fedoraproject.org/wiki/EPEL/FAQ#howtouse.

On the master, you need to install the puppet, puppet-server, and facter packages from the EPEL repository.

# yum install puppet puppet-server facter

The puppet package contains the agent, the puppet-server package contains the master, and the facter package contains the system inventory tool Facter. As mentioned earlier, Facter gathers information, or "facts," about your hosts that are used to help customize your Puppet configuration.

On the agent, you only need to install the prerequisites and the puppet and facter packages.

# yum install puppet facter

Installing on Debian and Ubuntu

On Debian and Ubuntu we also need to install the Ruby packages as a prerequisite:

# apt-get install ruby libshadow-ruby1.8

Then you can install the required packages for Puppet: puppet, puppetmaster, and facter. The puppet package contains the Puppet agent, the puppetmaster package contains the master, and the facter package contains the Facter system inventory tool.

On the master, you need to install this:

# apt-get install puppet puppetmaster facter

On the agent, you only need to install the following packages:

# apt-get install puppet facter

Note

Installing the puppet, puppetmaster, and facter packages will also install some prerequisite packages, such as Ruby itself, if they are not already installed.

Installing on OpenSolaris

Installing Puppet on OpenSolaris requires installing Ruby first. Then install Puppet and Facter via a RubyGem. Start by using the pkg command to install Ruby.

# pkg install -q SUNWruby18

Once Ruby is installed (it can take a little while to download and install), there are two ways to install Puppet. The first is to use the RubyGems packaging system. RubyGems is installed by default when the SUNWruby18 package is installed. You can use the gem command to install the required Gems.

# gem install puppet facter

Alternatively, if you use Blastwave packages, Puppet and Facter are also available from the Blastwave repositories at http://www.blastwave.org and can be added using the pkgutil command. Further instructions are available on the Puppet wiki at http://projects.puppetlabs.com/projects/puppet/wiki/Puppet_Solaris.

Installing from Source

You can also install Puppet and Facter from source tarballs. We don't recommend this approach because it makes upgrading, uninstalling and generally managing Puppet across a lot of hosts difficult. To do this you'll need to ensure some prerequisites are installed, for example Ruby and its libraries, using the appropriate packages for your host or via source again. First, download the Facter tarball from the Puppet Labs site.

$ cd /tmp
$ wget http://puppetlabs.com/downloads/facter/facter-1.5.7.tar.gz

Unpack the tarball and run the install.rb script to install Facter.

$ tar -zxf facter-1.5.7.tar.gz
$ cd facter-1.5.7
# ./install.rb

This will install Facter into the default path for Ruby libraries on your host, for example /usr/lib/ruby/ on many Linux distributions.

Next, we need to download and install Puppet using the same process:

$ cd /tmp
$ wget http://puppetlabs.com/downloads/puppet/puppet-2.6.1.tar.gz
$ tar -zxf puppet-2.6.1.tar.gz
$ cd puppet-2.6.1
# ./install.rb

Like the Facter steps, this will install Puppet into the default path for Ruby libraries on your host.

Note

You can find the latest Puppet and Facter releases at http://projects.puppetlabs.com/projects/puppet/wiki/Downloading_Puppet.

Installing on Microsoft Windows

Since version 2.6.0, Puppet has supported running on Microsoft Windows. For the 2.6.0 release Puppet only manages a limited subset of configuration, primarily managing files, but other configuration types should be available in later releases.

Installing Puppet on Microsoft Windows can be achieved a couple of different ways, but the first step of both methods is to install Ruby. The easiest way to do this is with the Ruby One-Click Installer available at http://rubyinstaller.rubyforge.org/wiki/wiki.pl?RubyInstaller. You can also download binaries at http://www.ruby-lang.org/en/downloads/ if you wish.

We're going to use the One-Click installer. Download the latest version, which at the time of writing is at http://rubyforge.org/frs/download.php/47082/ruby186-27_rc2.exe. Run the downloaded executable and install Ruby. During the installation, select the Use RubyGems tick box.

Once Ruby is installed, start the RubyGems Package Manager from the start menu:

Programs -> Ruby-186-27 -> RubyGems -> RubyGems Package Manager

From the command window that opens, you can then install the Facter and Puppet gems.

C:gem install puppet facter

Installing on other Platforms

We've just explained how to install Puppet on some popular platforms. Puppet can also be installed on a wide variety of other platforms. Puppet is also available in varying versions on:

  • MacOS X via MacPorts and from https://sites.google.com/a/explanatorygap.net/puppet/

  • Solaris via Blastwave

  • SLES/OpenSuSE via http://software.opensuse.org/

  • Gentoo via Portage

  • Mandrake and Mandriva via the Mandriva contrib repository

  • FreeBSD via ports tree

  • NetBSD via pkgsrc

  • OpenBSD via ports tree

  • ArchLinux via ArchLinux AUR

Note

You can find a full list of additional operating systems and specific instructions at http://projects.puppetlabs.com/projects/puppet/wiki/Downloading_Puppet.

Puppet's tarball also contains some packaging artifacts in the conf directory, for example an RPM spec file and OS X build scripts, that can allow you to create your own packages for compatible operating systems. Now you've installed Puppet on your chosen platform, we can start configuring it.

Configuring Puppet

Let's start by configuring a Puppet master that will act as our configuration server. We'll look at Puppet's configuration files, how to configure networking and firewall access and how to start the Puppet master. Remember that we're going to be looking at Puppet in its client-server mode. Here, the Puppet master contains our configuration data, and Puppet agents connect via SSL and pull down the required configuration.

On most platforms, Puppet's configuration will be located under the /etc/puppet directory. Puppet's principal configuration file is called puppet.conf and is stored at /etc/puppet/puppet.conf. It is likely that this file has already been created when you installed Puppet, but if it hasn't, then you can create a simple file using the following command:

# puppetmasterd --genconfig > puppet.conf

Note

We're assuming your operating system uses the /etc/ directory to store its configuration files, as most Unix/Linux operating systems and distributions do. If you're on a platform that doesn't, for example Microsoft Windows, substitute the location of your puppet.conf configuration file.

The puppet.conf configuration file is constructed much like an INI-style configuration file and divided into sections. Each section configures a particular element of Puppet. For example, the [agent] section configures the Puppet agent, and the [master] section configures the Puppet master binary. There is also a global configuration section called [main]. All components of Puppet will set options specified in the [main] section.

Note

On releases before 2.6.0, each section was named for the Puppet binary command rather than the function, for example the [master] section was called [puppetmasterd] and the [agent] section was [puppetd]. If you have this older style configuration, then Puppet 2.6.0 and later versions will prompt you to update your configuration file when you start Puppet.

At this stage, we're only going to add one entry, certname, to the puppet.conf file. The certname option specifies the name of the Puppet master. We'll add the certname value to the [master] section (if the section doesn't already exist in your file, then create it).

[master]
certname=puppet.example.com

Replace puppet.example.com with the fully qualified domain name of your host.

Note

We'll look at other options in the puppet.conf file in later chapters.

Adding the certname option and specifying our fully qualified domain name does two things: it makes troubleshooting certificate issues easier, and it addresses a bug with the Ruby SSL code present on many Linux-based hosts. This bug requires that we manually specify the name used by your Puppet master's SSL certificates. You can read more about the precise bug at http://projects.puppetlabs.com/projects/puppet/wiki/Ruby_Ssl_2007_006.

We recommend you also create a DNS CNAME for your Puppet host, for example puppet.example.com, and add it to your /etc/hosts file and your DNS configuration:

# /etc/hosts
127.0.0.1 localhost
192.168.0.1 puppet.example.com puppet

Once we've configured appropriate DNS for Puppet we need to add the site.pp file which holds the basics of the configuration items we want to manage.

The site.pp file

The site.pp file tells Puppet where and what configuration to load for our clients. We're going to store this file in a directory called manifests under the /etc/puppet directory.

Note

"Manifest" is Puppet's term for files containing configuration information. Manifest files have a suffix of .pp.

This directory and file is often already created when the Puppet packages are installed. If it hasn't already been created, then create this directory and file now:

# mkdir /etc/puppet/manifests
# touch /etc/puppet/manifests/site.pp

Tip

Puppet will not start without the site.pp file being present.

We'll add some configuration to this file later in this chapter, but now we just need the file present.

Note

You can also override the name and location of the manifests directory and site.pp file using the manifestdir and manifest configuration options, respectively. These options are set in the puppet.conf configuration file in the [master] section. See http://docs.puppetlabs.com/references/stable/configuration.html for a full list of configuration options. We'll talk about a variety of other options throughout this book.

Firewall Configuration

The Puppet master runs on TCP port 8140. This port needs to be open on your master's firewall (and any intervening firewalls and network devices), and your client must be able to route and connect to the master. To do this, you need to have an appropriate firewall rule on your master, such as the following rule for the Netfilter firewall:

-A INPUT -p tcp -m state --state NEW --dport 8140 -j ACCEPT

The preceding line allows access from everywhere to TCP port 8140. If possible, you should lock this down to only networks that require access to your Puppet master. For example:

-A INPUT -p tcp -m state --state NEW -s 192.168.0.0/24 --dport 8140 -j ACCEPT

Here we've restricted access to port 8140 to the 192.168.0.0/24 subnet.

Note

You can create similar rules for other operating systems' firewalls such as pf or the Windows Firewall.

Starting the Puppet Master

The Puppet master can be started via an init script on most Linux distributions. On Red Hat, we would run the init script with the service command, like so:

# service puppetmaster start

On Debian or Ubuntu, we run it using the invoke-rc.d command:

# invoke-rc.d puppetmaster start

Other platforms should use their appropriate service management tools.

Note

Output from the daemon can be seen in /var/log/messages on Red Hat-based hosts and /var/log/daemon.log on Debian and Ubuntu hosts. Puppet will log via the daemon facility to Syslog by default on most operating systems. You will find output from the daemons in the appropriate location and files for your operating system.

Starting the daemon will initiate your Puppet environment, create a local Certificate Authority, certificates and keys for the master, and open the appropriate network socket to await client connections. You can see Puppet's SSL information and certificates in the /etc/puppet/ssl directory.

# ls -l /etc/puppet/ssl
drwxrwx--- 5 puppet puppet 4096 2009-11-16 22:36 ca
drwxr-xr-x 2 puppet root   4096 2009-11-16 22:36 certificate_requests
drwxr-xr-x 2 puppet root   4096 2009-11-16 22:36 certs
-rw-r--r-- 1 puppet root    361 2009-11-16 22:36 crl.pem
drwxr-x--- 2 puppet root   4096 2009-11-16 22:36 private
drwxr-x--- 2 puppet root   4096 2009-11-16 22:36 private_keys
drwxr-xr-x 2 puppet root   4096 2009-11-16 22:36 public_keys

The directory on the master contains your Certificate Authority, certificate requests from your clients, a certificate for your master and certificates for all your clients.

Note

You can override the location of the SSL files using the ssldir option.

You can also run the Puppet master from the command line to help test and debug issues. I recommend doing this when testing Puppet initially. To do this we start the Puppet master daemon like so:

# puppet master --verbose --no-daemonize

The --verbose option outputs verbose logging and the --no-daemonize option keeps the daemon in the foreground and redirects output to standard out. You can also add the --debug option to produce more verbose debug output from the daemon.

Connecting Our First Agent

Once you have the Puppet master configured and started, we can configure and initiate your first agent. On the agent, as we mentioned earlier, you need to install the appropriate packages, usually puppet and facter, using your operating system's package management system. We're going to install a client on a host called node1.example.com and then connect to our puppet.example.com master.

When connecting our first client, we want to run the Puppet agent from the command line rather than as a service. This will allow us to see what is going on as we connect. The Puppet agent daemon is run using puppet agent (or in versions previous to 2.6.0, using the puppetd binary) and you can see a connection to the master initiated in Listing 1-2.

Tip

You can also run a Puppet client on the Puppet master, but we're going to start with the more traditional client server. And yes, that means you can use Puppet to manage itself!

Example 1-2. Puppet Client Connection to the Puppet Master

node1# puppet agent --server=puppet.example.com --no-daemonize --verbose
info: Creating a new certificate request for node1.example.com
info: Creating a new SSL key at /var/lib/puppet/ssl/private_keys/node1.example.com
Puppet Client Connection to the Puppet Master
.pem warning: peer certificate won't be verified in this SSL session notice: Did not receive certificate

In Listing 1-2, we executed the Puppet agent with three options. The first option, --server, specifies the name or address of the Puppet master to connect to.

Tip

If we don't specify a server, Puppet will look for a host called "puppet." It's often a good idea to create a CNAME for your Puppet master, for example puppet.example.com.

We can also specify this in the main section of the /etc/puppet/puppet.conf configuration file on the client.

[main]
server=puppet.example.com

Your client must be able to resolve the hostname of the master to connect to (this is why it is useful to have a Puppet CNAME and to specify your Puppet master in the /etc/hosts file on your client).

The --no-daemonize option runs the Puppet client in the foreground and outputs to standard out. By default, the Puppet client runs as a daemon.

Tip

The --verbose option enables verbose output from the client. Adding the --debug option can provide further output that is useful for troubleshooting.

In Listing 1-1, you can see the output from our connection. The agent has created a certificate signing request and a private key to secure our connection. Puppet uses SSL certificates to authenticate connections between the master and the agent. The agent sends the certificate request to the master and waits for the master to sign and return the certificate.

At this point, the agent is still running and awaiting the signed certificate. It will continue to check for a signed certificate every two minutes until it receives one or is canceled (using Ctrl-C, for example).

Note

You can change the time the Puppet agent will wait by using the --waitforcert option. You can specify a time in seconds or 0 to not wait for a certificate, in which case the agent will exit.

Completing the Connection

To complete the connection and authenticate our agent we now need to sign the certificate the agent has sent to the master. We do this using puppet cert (or the puppetca binary) on the master.

puppet# puppet cert --list
node1.example.com

Tip

You can find a full list of the binaries that come with Puppet at http://puppetlabs.com/trac/puppet/wiki/PuppetExecutables.

The --list option displays all the certificates waiting to be signed. We can then sign our certificate using the --sign option.

puppet# puppet cert --sign node1.example.com
Signed node1.example.com

You can sign all waiting certificates with the puppet cert --sign --all command.

Note

Rather than signing each individual certificate, you can also enable "autosign" mode. In this mode, all incoming connections from specified IP addresses or address ranges are automatically signed. This obviously has some security implications and should only be used if you are comfortable with it. You can find more details at http://puppetlabs.com/trac/puppet/wiki/FrequentlyAskedQuestions#why-shouldn-t-i-use-autosign-for-all-my-clients.

On the client, two minutes after we've signed our certificate, we should see the following entries (or you can stop and restart the Puppet agent rather than waiting two minutes):

notice: Got signed certificate
notice: Starting Puppet client version 2.6.1

The agent is now authenticated with the master, but we have another message present:

err: Could not retrieve catalog: Could not find default node or by name with 
Completing the Connection
'node1.example.com, node1' on node node1.example.com

The agent has connected and our signed certificate has authenticated the session with the master. The master, however, doesn't have any configuration available for our puppet node, node1.example.com, and hence we have received an error message. We now have to add some configuration for this agent on the master.

Warning

It is important that the time is accurate on your master and agent. SSL connections rely on the clock on hosts being correct. If the clocks are incorrect then your connection may fail with an error indicating that your certificates are not trusted. You should use something like NTP (Network Time Protocol) to ensure your host's clocks are accurate.

Creating Our First Configuration

Let's get some more understanding of Puppet's components, configuration language and capabilities. We learned earlier that Puppet describes the files containing configuration data as manifests. Puppet manifests are made up of a number of major components:

  • Resources – Individual configuration items

  • Files – Physical files you can serve out to your agents

  • Templates – Template files that you can use to populate files

  • Nodes – Specifies the configuration of each agent

  • Classes – Collections of resources

  • Definitions – Composite collections of resources

These components are wrapped in a configuration language that includes variables, conditionals, arrays and other features. Later in this chapter we'll introduce you to the basics of the Puppet language and its elements. In the next chapter, we'll extend your knowledge of the language by taking you through an implementation of a multi-agent site managed with Puppet.

Note

In addition to these components, Puppet also has the concept of a "module," which is a portable collection of manifests that contain resources, classes, definitions, files, and templates. We'll see our first module shortly.

Extending the site.pp file

Our first step in creating our first agent configuration is defining and extending the site.pp file. See an example of this file in Listing 1-3.

Example 1-3. The site.pp File

import 'nodes.pp'
$puppetserver = 'puppet.example.com'

Note

Puppet manifest files are traditionally suffixed with .pp. If your manifest file has the .pp suffix, you can drop the suffix when importing files.

The import directive tells Puppet to load a file called nodes.pp. This directive is used to include any Puppet configuration we want to load. For example, if we specify resources in a file called resources.pp, we would need to import it this way:

import 'resources.pp'

When Puppet starts, it will now load the nodes.pp file and process the contents. In this case, this file will contain the node definitions we create for each agent we connect. You can also import multiple files like so:

import 'nodes/*'
import 'classes/*'

The import statement will load all files with a suffix of .pp in the directories nodes and classes.

The $puppetserver statement sets a variable. In Puppet, configuration statements starting with a dollar sign are variables used to specify values that you can use in Puppet configuration.

In Listing 1-3, we've created a variable that contains the fully qualified domain name of our Puppet master, enclosed in double quotes.

Note

In Puppet manifests, strings with double-quotes are subject to variable interpolation and strings with single quotes are not. If you want to use a variable in a string, you should enclose it in double-quotes, for example: "This is a $variable string". You can also add braces, { }, to variables in strings to define them more clearly, "This is a ${variable} string". You can find quoting rules for Puppet at http://docs.puppetlabs.com/guides/more_language.html#quoting.

Agent Configuration

Let's add our first agent definition to the nodes.pp file we've just asked Puppet to import. In Puppet manifests, agents are defined using node statements.

# touch /etc/puppet/manifests/nodes.pp.

You can see the node definition we're going to add in Listing 1-4.

Example 1-4. Our Node Configuration

node 'node1.example.com' {
    include sudo
}

For a node definition we specify the node name, enclosed in single quotes, and then specify the configuration that applies to it inside curly braces { }. The client name can be the hostname or the fully qualified domain name of the client. At this stage, you can't specify nodes with wildcards (e.g., *.example.com) but you can use regular expressions, such as:

node /^wwwd+.example.com/ {
    include sudo
}

This example will match all nodes from the domain example.com with the hostname www1, www12, www123, etc.

Note

We'll see more of node regular expressions in Chapter 3.

Next, we specify an include directive in our node definition. The include directive specifies a collection of configuration that we want to apply to our host. There are two types of collections we can include in a node:

  • Classes – a basic collection of resources

  • Modules – an advanced, portable collection of resources that can include classes, definitions, and other supporting configuration

You can include multiple collections by using multiple include directives or separating each collection with commas.

include sudo
include sshd
include vim, syslog-ng

In addition to including collections of resources, you can also specify individual resources to a node, like so:

node 'node1.example.com' {
include sudo
    package { 'vim': ensure => present }
}

In this case however, as we've seen in Listing 1-4, we're just going to add a single collection of resources: the sudo module.

Note

Puppet also has an inheritance model in which you can have one node inherit values from another node. You can read about node inheritance at http://docs.puppetlabs.com/guides/language_tutorial.html#nodes; we'll talk more about it in Chapter 3.

Creating our first module

The next step is to create the sudo module. A module is a collection of manifests, resources, files, templates, classes, and definitions. A single module would contain everything required to configure a particular application. For example, it could contain all the resources (specified in manifest files), files and associated configuration to configure Apache or the sudo command on a host.

Each module needs a specific directory structure and a file called init.pp. This structure allows Puppet to automatically load modules. To perform this automatic loading, Puppet checks a series of directories called the module path. The module path is configured with the modulepath configuration option in the [main] section of the puppet.conf file. By default, Puppet looks for modules in the /etc/puppet/modules and /var/lib/puppet/modules directories, but you can add additional locations if required:

[main]
moduledir = /etc/puppet/modules:/var/lib/puppet/modules:/opt/modules

The automatic loading of modules means, unlike our nodes.pp file, modules don't need to be loaded into Puppet using the import directive.

Module Structure

Let's start by creating a module directory and file structure in Listing 1-5. We're going to create this structure under the directory /etc/puppet/modules. We will name the module sudo. Modules (and classes) must be normal words containing only letters, numbers, underscores and dashes.

Example 1-5. Module Structure

# mkdir -p /etc/puppet/modules/sudo/{files,templates,manifests}
# touch /etc/puppet/modules/sudo/manifests/init.pp

The manifests directory will hold our init.pp file and any other configuration. The init.pp file is the core of your module and every module must have one. The files directory will hold any files we wish to serve as part of our module. The templates directory will contain any templates that our module might use.

The init.pp file

Now let's look inside our sudo module, starting with the init.pp file, which we can see in Listing 1-6.

Example 1-6. The sudo module's init.pp file

class sudo {
    package { sudo:
         ensure => present,
    }

    if $operatingsystem == "Ubuntu" {
         package { "sudo-ldap":
            ensure => present,
            require => Package["sudo"],
        }
    }

    file { "/etc/sudoers":
        owner => "root",
        group => "root",
        mode => 0440,
        source => "puppet://$puppetserver/modules/sudo/etc/sudoers",
        require => Package["sudo"],
    }
}

Our sudo module's init.pp file contains a single class, also called sudo. There are three resources in the class, two packages and a file resource.

The first package resource ensures that the sudo package is installed, ensure => present. The second package resource uses Puppet's if/else syntax to set a condition on the installation of the sudo-ldap package.

Note

Puppet also has two other conditional statements, a case statement and a selector syntax. You can see more details of Puppet's conditional syntaxes at http://docs.puppetlabs.com/guides/more_language.html#conditionals.

Puppet will check the value of the operatingsystem fact for each connecting client. If the value of the $operatingsystem fact is Ubuntu, then Puppet should install the sudo-ldap package.

Note

We discovered Facter and its values earlier in this chapter. Each fact is available as a variable, the fact name prefixed with a $ sign, in your Puppet manifests.

Lastly, in this resource we've also specified a new attribute, require. The require attribute is a metaparameter. Metaparameters are resource attributes that are part of Puppet's framework rather than belonging to a specific type. They perform actions on resources and can be specified for any type of resource.

The require metaparameter creates a dependency relationship between the Package["sudo-ldap"] resource and the Package["sudo"] resource. In this case, adding the require metaparameter to the resource tells Puppet that the Package["sudo"] is required by the Package["sudo-ldap"] resource. Hence, the Package["sudo"] resource must and will be installed first.

Relationships are an important part of Puppet. They allow you to instantiate real world relationships between configuration components on your hosts. A good example of this is networking. A number of resources on your hosts, for example a Web server or an MTA (Mail Transfer Agent), would rely on your network being configured and active before they can be activated. Relationships allow you to specify that certain resources, for example those configuring your network, are processed before those resources that configure your Web server or MTA.

The usefulness of relationships does not end there. Puppet can also build triggering relationships between resources. For example, if a file resource changes, then you can tell Puppet to restart a service resource. This means you can change a service's configuration file and have that change trigger a restart of that service to ensure it is running with the updated configuration. We'll see a lot more of these relationships and other metaparameters in Chapter 3.

Note

You can see a full list of the available metaparameters at http://docs.puppetlabs.com/references/stable/metaparameter.html.

The last resource in the sudo class is a file resource, File["/etc/sudoers"], which manages the /etc/sudoers file. Its first three attributes allow us to specify the owner, group and permissions of the file. In this case, the file is owned by the root user and group and has its mode set to 0440 (currently the mode can only be set using octal notation).

The next attribute, source, allows Puppet to retrieve a file from the Puppet file server and deliver it to the client. The value of this attribute is the name of the Puppet file server and the location and name of the file to retrieve.

puppet://$puppetserver/modules/sudo/etc/sudoers

Let's break down this value. The puppet:// part specifies that Puppet will use the Puppet file server protocol to retrieve the file.

Note

Currently, the Puppet file server protocol is the only protocol available. In future versions of Puppet, the file server will support other protocols, such as HTTP or rsync.

The $puppetserver variable contains the hostname of our Puppet server. Remember that we created this variable and placed it in our site.pp file earlier? Instead of the variable, you could also specify the host name of the file server here.

puppet://puppet.example.com/modules/sudo/etc/sudoers

Tip

One handy shortcut is to just remove the server name. Then Puppet will use whatever server the client is currently connected to, for example our source line would look like: puppet:///modules/sudo/etc/sudoers.

The next portion of our source value tells Puppet where to look for the file. This is the equivalent of the path to a network file share. The first portion of this share is modules, which tells us that the file is stored in a module. Next we specify the name of the module the file is contained in, in this case sudo. Finally, we specify the path inside that module to find the file.

All files in modules are stored under the files directory; this is considered the "root" of the module's file "share." In our case, we would create the directory etc under the files directory and create the sudoers file in this directory.

puppet$ mkdir -p /etc/puppet/modules/sudo/files/etc
puppet$ cp /etc/sudoers /etc/puppet/manifests/files/etc/sudoers

Applying Our First Configuration

We've created our first Puppet module! Let's step through what will happen when we connect an agent that includes this module.

  1. It will install the sudo package.

  2. If it's an Ubuntu host, then it will also install the sudo-ldap package

  3. Lastly, it will download the sudoers file and install it into /etc/sudoers.

Now let's see this in action and include our new module on the agent we've created, node1.example.com. Remember we created a node statement for our host in Listing 1.4:

node 'node1.example.com' {
    include sudo
}

When the agent connects it will now include the sudo module. To do this we run the Puppet agent again, as shown in Listing 1-7.

Example 1-7. Applying Our First Configuration

puppet# puppet agent --server=puppet.example.com --no-daemonize --verbose --onetime

Note

Puppet has a handy mode called "noop." The "noop" mode runs Puppet but doesn't make any changes on your host. It allows you to see what Puppet would do, as a dry run. To run in "noop" mode, specify --noop on the command line.

In Listing 1-7, we've run the Puppet agent and connected to the master. We've run the agent in the foreground, in verbose mode and with the --onetime option that tells the Puppet agent to only run once and then stop. We can see a configuration run commence on our host:

Tip

In Puppet, the combined configuration to be applied to a host is called a "catalog" and the process of applying it is called a "run." You can find a glossary of Puppet terminology at http://projects.puppetlabs.com/projects/puppet/wiki/Glossary_Of_Terms.

notice: Starting Puppet client version 2.6.1
info: Caching catalog for node1.example.com
info: Applying configuration version '1272631279'
notice: //sudo/Package[sudo]/ensure: created
notice: //sudo/File[/etc/sudoers]/checksum: checksum changed
Applying Our First Configuration
'{md5}9f95a522f5265b7e7945ff65369acdd2' to '{md5}d657d8d55ecdf88a2d11da73ac5662a4' info: Filebucket[/var/lib/puppet/clientbucket]: Adding
Applying Our First Configuration
/etc/sudoers(d657d8d55ecdf88a2d11da73ac5662a4) info: //sudo/File[/etc/sudoers]: Filebucketed /etc/sudoers to puppet with sum
Applying Our First Configuration
d657d8d55ecdf88a2d11da73ac5662a4 notice: //sudo/File[/etc/sudoers]/content: content changed
Applying Our First Configuration
'{md5}d657d8d55ecdf88a2d11da73ac5662a4' to '{md5}9f95a522f5265b7e7945ff65369acdd2' notice: Finished catalog run in 10.54 seconds

Let's look at what has happened during our run. First we see that the agent has cached the configuration for the host. By default, Puppet uses this cache if it fails to connect to the master during a future run.

Next, we can see our resources being applied. First the sudo package is installed and then the /etc/sudoers file is copied across. We can see that during the copy process Puppet has backed up the old file, a process Puppet calls file bucketing. This means that if we've made a mistake and overwritten the file incorrectly we can always recover it.

Tip

Puppet can back up files remotely to our master using the filebucket type. See http://docs.puppetlabs.com/references/stable/type.html#filebucket. We'll show you how to do this in Chapter 3.

The last line of the catalog run tells us this process took 10.54 seconds to complete.

If we look on the Puppet master, we can see the results of the run logged there too.

notice: Starting Puppet server version 2.6.1
info: Autoloaded module sudo
info: Expiring the node cache of node1.example.com
info: Not using expired node for node1.example.com from cache; expired at Fri Apr 30
Applying Our First Configuration
08:44:46 -0400 2010 info: Caching node for node1.example.com notice: Compiled catalog for node1.example.com in 0.02 seconds

Here we can see that Puppet has loaded our sudo module and compiled the catalog for node1.example.com. This catalog is then sent down to the agent and applied on the target host.

If the Puppet agent is running as a daemon, it would then wait 30 minutes and then connect to the master again to check if the configuration has changed on our host or if a new configuration is available from the master. We can adjust this run interval using the runinterval option in the /etc/puppet/puppet.conf configuration file on the agent host.

[agent]
runinterval=3600

Here we've adjusted the run interval to 3600 seconds, or 60 minutes.

Summary

So that's it - we've used Puppet to configure our first agent. You've also been introduced to the theoretical underpinnings of Puppet and how to:

  • Install Puppet

  • Configure Puppet

  • Use Puppet to manage some simple configuration on a single host

In the next chapter, we'll extend our Puppet configuration to multiple agents, learn more about Puppet's configuration language and learn how to build more complex configurations.

Resources

  • Introduction to Puppet http://docs.puppetlabs.com/guides/introduction.html

  • Installation - http://docs.puppetlabs.com/guides/installation.html

  • Configuration Guide - http://docs.puppetlabs.com/guides/configuring.html

  • Configuration Reference - http://docs.puppetlabs.com/references/stable/configuration.html

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.129.19