I do want to write more about the synergy between Puppet and Ansible; but, several people have asked me for more information on getting started with Puppet. The last time I installed Puppet Server, I took extensive notes. I figured I’d share those here, to help anyone else save time who’s just getting started by installing puppet server.
These instructions are specifically related to Cent OS 7. However, most of these steps pertain to any Linux based OS. Additionally, these instructions cover installing Puppet 4.x, since 5.0 is not yet used in Puppet Enterprise.
Escalating to Root
Most of the commands in this document require you run them as the root user. Using the sudo tool, you can escalate to root for the rest of your session.
[lkanies@puppetlab ~]$ sudo su - [root@puppetlab ~]#
Setting Up Puppetlabs Repositories
If you are running Red Hat, Debian, Ubuntu or SuSe Linux, Puppetlabs provides repositories to easily install their software. Installing in any other system is a little beyond the scope of this article. If you need to install puppet on another system, read the Puppetlabs documentation for more information.
Start by installing the software collection RPM from Puppetlabs.
[root@puppetlab ~]# rpm -Uvh https://yum.puppetlabs.com/puppetlabs-release-pc1-el-7.noarch.rpm Retrieving https://yum.puppetlabs.com/puppetlabs-release-pc1-el-7.noarch.rpm Preparing... ################################# [100%] Updating / installing... 1:puppetlabs-release-pc1-1.1.0-5.el################################# [100%]
Installing Puppet Server
Since you have installed the repositories, use your package manager to install the following packages: puppetserver, puppetdb and puppetdb-termini. While puppetdb and puppetdb-termini are not required, I recommend you install them unless you have a separate puppetdb server already in place.
[root@puppetlab ~]# yum install puppetserver puppetdb puppetdb-termini puppetdb-terminus
Configuring and Starting Puppet Server
Previously, Puppet master servers ran on Ruby inside a Rack server configuration. Because this required manual configuration and didn’t perform as well under load, Puppetlabs wrote the puppetserver application to run the same code in a Java process. When the puppetserver process is started for the first time, it creates a certificate. That TLS certificate will be used to sign any agent that connects to it. The common name of the certificate derives from the hostname of the server. Check now to make sure the host name is what you prefer it to be.
[root@puppetlab ~]# hostname puppetlab.example.com
You must ensure that this matches the FQDN your nodes will be using to contact this server. Before configuring the server, make this change now if you need to.
Next, you edit /etc/puppetlabs/puppet/puppet.conf
to set up your server names.
[main] dns_alt_names = <fqdn of puppet server> [agent] server = <fqdn of puppet server>
If you have additional dns names on which this server may be contacted, add them to the dns_alt_names
entry (comma-separated). In addition, the server
entry informs the puppet agent process what machine to contact as its puppet master. (In this case, the server is its own master.)
Finally, start the puppetserver
service.
[root@puppetlab ~]# systemctl start puppetserver
As a result of the puppetserver service, a cert is generated. Use the following command to print the certificate and verify its details. (We’ll need to source the profile that adds the puppet binary to the path first.)
[root@puppetlab puppet]# . /etc/profile.d/puppet-agent.sh [root@puppetlab puppet]# puppet cert list --all
Adding the First Puppet Agent – the Server Itself
Because puppet can control services, you should configure puppet to keep its own services running. When writing puppet code, you start with one or more manifests, or a collection of resources that are controlled by puppet. Most of the time, those manifests are organized into modules, which are smaller chunks of code meant to control one particular technology or set of configuration. For any mature technology, it’s very possible that someone else has already done much of the work for you in writing a module. Modules can be shared and downloaded from a single repository called the Puppet Forge.
Because of the limited scope of this post, I’m going to put all of my code directly into a node (client) definition on the manifest. This is generally a bad practice; but, it will serve to demonstrate puppet more succinctly. If you want to read more on modules and best practices, you should check the documentation on Puppetlabs’s website and Gary Larizza’s blog on the roles and profiles pattern.
Your First Manifest
First, we’re going to create a file called /etc/puppetlabs/code/environments/production/manifests/nodes.pp
. This is a manifest- a collection of resources that will be directly applied to puppet agents. Note that we’re in a folder structure called environments/production
. Puppet allows you to split your code into different environments so that you can test manifest changes without potentially breaking existing servers. We’re going to start with just the default production environment.
Open /etc/puppetlabs/code/environments/production/manifests/nodes.pp
in your favorite editor. Below, I’m going to use the hostname puppetlab.example.com
; but, you should replace that with the FQDN of your puppet server.
node 'puppetlab.example.com' { notify { 'hello world' : } }
You have just written a node definition. Node definitions describe some resources that should only be applied on the matching node. Typically, the global manifests (any .pp
files located in the manifests
folder of an environment) contain mostly node definitions. So, to be clear, we’ve just informed our puppet server to print the ‘hello world’ message when it contacts itself as a client. Run puppet agent with the -t
flag to see it in action:
[root@puppetlab manifests]# puppet agent -t Info: Using configured environment 'production' Info: Retrieving pluginfacts Info: Retrieving plugin Info: Caching catalog for puppetlab.example.com Info: Applying configuration version '1512101298' Notice: hello world Notice: /Stage[main]/Main/Node[puppetlab.example.com]/Notify[hello world]/message: defined 'message' as 'hello world' Notice: Applied catalog in 0.03 seconds
Controlling Services
Hello world examples are great and all; but, shouldn’t we do something more meaningful? Most puppet nodes run an agent daemon that checks in with the puppet server from time to time to make sure the configuration hasn’t drifted. Change your node definition manifest to match the one below to ensure this service (daemon) is running.
node 'puppetlab.example.com' { service { 'puppet' : ensure => 'running', } }
Now, instead of a ‘notify,’ we have a ‘service’ declaration in our node definition. Both notify and service are examples of resources. Puppet code is written in resources which are collected into a catalog to be applied by the agent. Run puppet agent again to see your service start.
[root@testpuppet manifests]# puppet agent -t Info: Using configured environment 'production' Info: Retrieving pluginfacts Info: Retrieving plugin Info: Caching catalog for puppetlab.example.com Info: Applying configuration version '1512101760' Notice: /Stage[main]/Main/Node[puppetlab.example.com]/Service[puppet]/ensure: ensure changed 'stopped' to 'running' Info: /Stage[main]/Main/Node[puppetlab.example.com]/Service[puppet]: Unscheduling refresh on Service[puppet] Notice: Applied catalog in 0.12 seconds
For more information on the types of resources you can declare, review the puppet documentation.
Adding Another Node
While getting a server to be its own client is stimulating, it seems like now would be a good time to create another client. We started with a server called puppetlab.example.com
, so I’m going to work on another CentOS 7 box called testnode.example.com
. Add another node definition to your manifest just like the first, except target your new client node instead of your puppet server.
node 'puppetlab.example.com' { service { 'puppet' : ensure => 'running', } } node 'testnode.example.com' { service { 'puppet' : ensure => 'running', }
Installing and Configuring the Puppet Agent
After setting up your puppet server manifest, you want to install and configure the puppet agent on your client machine. Log in, escalate to root, and run the following commands to install the agent.
[root@testnode ~]# rpm -Uvh https://yum.puppetlabs.com/puppetlabs-release-pc1-el-7.noarch.rpm Retrieving https://yum.puppetlabs.com/puppetlabs-release-pc1-el-7.noarch.rpm Preparing... ################################# [100%] Updating / installing... 1:puppetlabs-release-pc1-1.1.0-5.el################################# [100%] [root@testnode ~]# yum install puppet-agent
Once puppet is installed, we need to tell the agent the name of the server to whom it should connect. Open /etc/puppetlabs/puppet/puppet.conf
in your favorite editor and add the following (note that you should replace puppetlab.example.com
with your puppet server’s name).
[agent] server = puppetlab.example.com
Generating a Certificate Request
Puppet clients and agents authenticate each other with a TLS certificate. When you first create a new node and run puppet agent, a certificate request is generated and sent to the server to be signed. Once that’s complete, you can sign the request on the server to generate a client certificate. Consequently, the next time the agent runs against the server it will cache its new certificate and use it to authenticate every time puppet runs.
Start by running puppet agent on the new client to generate a request (note that you probably will have to source the puppet profile first).
[root@testnode ~]# . /etc/profile.d/puppet-agent.sh [root@testnode ~]# puppet agent -t Info: Creating a new SSL key for testnode.example.com Info: Caching certificate for ca Info: csr_attributes file loading from /etc/puppetlabs/puppet/csr_attributes.yaml Info: Creating a new SSL certificate request for testnode.example.com Info: Certificate Request fingerprint (SHA256): AF:E9:E3:D6:3F:9A:0F:CC:83:01:DD:66:55:87:B9:4B:03:9C:C1:1C:7E:BB:12:CE:8B:21:93:6B:83:B3:E4:33 Info: Caching certificate for ca Exiting; no certificate found and waitforcert is disabled
Note, if you see “no route to host,” the CentOS firewall may be blocking port 8140. Run the following commands on the puppet server to open the port and then try the agent run again.
[root@puppetlab manifests]# firewall-cmd --zone=public --add-port=8140/tcp --permanent success [root@puppetlab manifests]# firewall-cmd --reload success
Your command created a certificate request from the new client and sent that request to the master. Take a note of that fingerprint.
Signing a Certificate Request
On your puppet server, you’ll now want to sign the certificate request for your new node so that the client and server trust each other. Start by running the following command, which prints out all of the certificates that haven’t yet been signed.
[root@puppetlab manifests]# puppet cert list "testnode.example.com" (SHA256) AF:E9:E3:D6:3F:9A:0F:CC:83:01:DD:66:55:87:B9:4B:03:9C:C1:1C:7E:BB:12:CE:8B:21:93:6B:83:B3:E4:33
You can see our request from testnode and its fingerprint. Check that fingerprint against the one that the client gave you and make certain they match. If they do, you can sign the request and issue the certificate with the following command.
[root@puppetlab manifests]# puppet cert sign testnode.example.com Signing Certificate Request for: "testnode.eample.com" (SHA256) AF:E9:E3:D6:3F:9A:0F:CC:83:01:DD:66:55:87:B9:4B:03:9C:C1:1C:7E:BB:12:CE:8B:21:93:6B:83:B3:E4:33 Notice: Signed certificate request for testnode.example.com Notice: Removing file Puppet::SSL::CertificateRequest testnode.example.com at '/etc/puppetlabs/puppet/ssl/ca/requests/testnode.example.com.pem'
Running the Agent for the First Time With a Signed Certificate
Now that our certificate is in order, go back to your test node and run puppet agent one last time. You should see it start the puppet daemon as expected.
[root@testnode ~]# puppet agent -t Info: Caching certificate for testnode.example.com Info: Caching certificate_revocation_list for ca Info: Caching certificate for testnode.example.com Info: Using configured environment 'production' Info: Retrieving pluginfacts Info: Retrieving plugin Info: Caching catalog for testnode.example.com Info: Applying configuration version '1512104337' Notice: /Stage[main]/Main/Node[testnode.example.com]/Service[puppet]/ensure: ensure changed 'stopped' to 'running' Info: /Stage[main]/Main/Node[testnode.example.com]/Service[puppet]: Unscheduling refresh on Service[puppet] Info: Creating state file /opt/puppetlabs/puppet/cache/state/state.yaml Notice: Applied catalog in 0.22 seconds
Assuming everything ran properly, you should see puppet start the puppet service as you declared in your node definition.
Aside: Why Did I Need to Generate a Cert Only on the New Node
It may seem odd that you only created a certificate for your second client and not for the puppet server itself. The reason for this is because the puppet server uses its own CA certificate to connect via the agent. Since this is a valid certificate that matches the client (puppetlab.example.com
) and the certificate is signed by the CA (self-signed, in this case), the server accepts it as trusted.
Further Reading
This is just a base tutorial on how to get bootstrapped with a puppet server in a CentOS environment. Hopefully, working through this has whetted your appetite. If so, I’d suggest reading the puppet documentation, especially the sections on the main manifest(s), environments, module fundamentals, and the puppet language. If you’re looking for more real world examples, you can also review the essential configuration quick start guides for real world scenarios and how puppet helps solve them.
In conclusion, remember that your servers are cattle and not pets. Having a puppet server and agent model in place in your environment will help you avoid configuration problems that are hard to solve in the future.