I like the idea of desired state management systems. You define a set of criteria, and your devices will indefinitely match that criteria, returning to the "Desired State" if required. I've done this a few times with Jamf Pro, using smart groups, extension attributes and policies to "mimic" the functionality of a desired state management system (which was nice) but I wanted to really get to grips with a system that's explicitly designed for this purpose. Enter the king of "Desired State" - Puppet.
In an effort to get to know Puppet, I need to build out some infrastructure. Yeah, I know there's a learning VM (that's really awesome, by the way), but I learn by experience. What better way than to build my own.
Assumptions
I'm not going to get into the nitty gritty of what puppet actually is and does, and I'm going to assume that if you're reading this post, you:
- Understand what Desired State is
- Understand (roughly) what Puppet is and can do
- Are poor / nuts, and would rather build and spin up your own servers, rather than using Puppet's Enterprise version (which is still totally viable, but I'm a masochist)
For the purposes of this example, I will setup 3 VMs and use the following defaults:
- VMware Fusion 8.5.6 to host the VMs
- Network set to custom "vmnet2" network
- NAT
- "Connect Host Mac to this network"
- Provide DHCP, subnet 10.0.1.0/24
- Network set to custom "vmnet2" network
- Puppet Server
- Ubuntu 16.04
- 2 x CPU Cores
- 3072MB Memory
- 50GB HDD (thin provisioned)
- Hostname
puppet-server
- IP Address
10.0.1.129
- Puppet Database Server
- Ubuntu 16.04
- 2 x CPU Cores
- 3072MB Memory
- 50GB HDD (thin provisioned)
- Hostname
puppet-db
- IP Address
10.0.1.130
- Puppet Test Client
- macOS 10.12.4
- 2 x CPU Cores
- 8192MB Memory
- 50GB HDD (thin provisioned)
- Hostname
puppet-client
- IP Address
10.0.1.128
Obviously, if you're following along with me, feel free to change any of the above defaults, but please make sure you're changing them in the instructions below.
Network Configuration
-
Spin up your boxes with the above config, and set each device up to allow access via SSH (cause it's easier like)
-
Rename both servers by editing the hostname file on each device, and adding the desired hostname
$ sudo vi /etc/hostname
-
In addition, add the hostname into the hosts file, pointing to the loopback (
127.0.0.1
) address under theblog.wegotoeleven.co.uk
entry$ sudo vi /etc/hosts
127.0.0.1 blog.wegotoeleven.co.uk 127.0.0.1 puppet-db
-
In an environment without proper DNS, do the following:
4. Set each device with a static IP address. I'm using the IP addresses specified above.```bash $ sudo vi /etc/network/interfaces ``` ``` # The primary network interface auto eth0 iface eth0 inet static address 10.0.0.41 netmask 255.255.255.0 network 10.0.0.0 broadcast 10.0.0.255 gateway 10.0.0.1 dns-nameservers 10.0.0.1 8.8.8.8 ```
-
Add each device into the each device's hosts file
$ sudo vi /etc/hosts
10.0.1.129 puppet-server 10.0.1.130 puppet-db 10.0.1.128 puppet-client
-
Puppet Server Configuration
-
Install the NTP service on the Puppet server, because Puppet uses an NTP source when issuing certificates to it's nodes
$ sudo apt install ntp
-
Add the Puppet repositories to the Puppet Server
$ wget https://apt.puppetlabs.com/puppetlabs-release-pc1-xenial.deb $ sudo dpkg -i puppetlabs-release-pc1-xenial.deb $ sudo apt update
-
Install
puppetserver
on the Puppet Server (duh)$ sudo apt install puppetserver
-
In addition, install the "puppetdb-termini" package to enable the Puppet server to talk to the Puppet DB server
$ sudo /opt/puppetlabs/puppet/bin/puppet resource package puppetdb-termini ensure=latest
-
Create the required configuration files on the Puppet server for the connections to the PuppetDB:
-
routes.yaml:
$ sudo vi /etc/puppetlabs/puppet/routes.yaml
--- master: facts: terminus: puppetdb cache: yaml
-
puppetdb.conf:
$ sudo vi /etc/puppetlabs/puppet/puppetdb.conf
[main] server_urls = https://puppet-db:8081
-
-
Edit puppet.conf
$ sudo vi /etc/puppetlabs/puppet/puppet.conf
[master] storeconfigs = true storeconfigs_backend = puppetdb
-
Ensure the permissions are correct on the Puppet configuration folder
$ sudo chown -R puppet:puppet $(sudo /opt/puppetlabs/puppet/bin/puppet config print confdir)
-
Set
puppetserver
to start automatically when the system starts$ sudo /opt/puppetlabs/puppet/bin/puppet resource service puppetserver ensure=running enable=true
-
Restart the Server to pickup the above changes.
Puppet DB Server Configuration
-
Add the Puppet repositories to the Puppet DB Server
$ wget https://apt.puppetlabs.com/puppetlabs-release-pc1-xenial.deb $ sudo dpkg -i puppetlabs-release-pc1-xenial.deb
-
Install the Database backend for PuppetDB
$ sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main" >> /etc/apt/sources.list.d/pgdg.list' $ wget -q https://www.postgresql.org/media/keys/ACCC4CF8.asc -O - | sudo apt-key add - $ sudo apt update $ sudo apt install postgresql postgresql-contrib
-
Setup PostgreSQL on the Puppet DB Server
$ sudo -u postgres sh
createuser -DRSP puppetdb createdb -E UTF8 -O puppetdb puppetdb psql puppetdb -c 'create extension pg_trgm' exit
-
Restart the PostgreSQL service
$ sudo service postgresql restart
-
Test PostgreSQL. If this is successful, you'll be dropped into PostgreSQL. Use
\q
to exit.$ psql -h blog.wegotoeleven.co.uk puppetdb puppetdb
-
To configure PuppetDB to use this database, add the following details to the
puppetdb
database configuration file. In this example, I'm using super secret usernames and passwords. Don't do this if you're planning on setting this up in prod.$ sudo vi /etc/puppetlabs/puppetdb/conf.d/database.ini
subname = //blog.wegotoeleven.co.uk:5432/puppetdb username = puppetdb password = puppetdb
For the
subname
, replace the hostname with the DB server’s hostname (orblog.wegotoeleven.co.uk
if the PostgreSQL service is running on the same server), replace the port with the port on which PostgreSQL is listening (usually 5432), and replace the database with the name of the database you’ve created for use with PuppetDB. In addition, uncomment the following sections:classname subprotocol gc-interval
Connecting the servers together
-
Install the Puppet Agent on the Puppet DB Server
$ sudo apt install puppet-agent
-
On the Puppet DB server, set the name of the server that the agent should connect to, replacing the server with the required hostname of the Puppet server
$ sudo vi /etc/puppetlabs/puppet/puppet.conf
server = puppet-server
-
Enrol the Puppet DB Server into Puppet
$ sudo /opt/puppetlabs/puppet/bin/puppet agent --test --waitforcert 60
-
On the Puppet Server, check to see whether the Puppet DB server has requested a certificate
$ sudo /opt/puppetlabs/puppet/bin/puppet cert --list
-
You should see the name of the Puppet DB server, followed by some other stuff. Accept the cert request
$ sudo /opt/puppetlabs/puppet/bin/puppet cert --sign puppet-db
-
On the Puppet DB server, install the
puppetdb
service$ sudo /opt/puppetlabs/puppet/bin/puppet resource package puppetdb ensure=latest
-
Stop the
puppetdb
service and setup SSL$ sudo service puppetdb stop && sudo /opt/puppetlabs/bin/puppetdb ssl-setup
-
Ensure the
puppetdb
service runs on reboot$ sudo /opt/puppetlabs/puppet/bin/puppet resource service puppetserver ensure=running enable=true
-
Restart the Puppet DB server
-
Check the PuppetDB logs on the Puppet DB server to verify that it's working
$ sudo cat /var/log/puppetlabs/puppetdb/puppetdb.log
Enrolling a macOS client
-
Install the puppet agent onto the Puppet Client, by downloading the specific version as per the OS version, from here
-
Install the download pkg file. When asked, specify the hostname of the server and the client
-
Open Terminal
-
Edit the Puppet configuration file and add the following contents
$ sudo vi /etc/puppetlabs/puppet/puppet.conf
[main] logdir=/var/log/puppet vardir=/var/lib/puppet ssldir=/var/lib/puppet/ssl #rundir=/var/run/puppet factpath=$vardir/lib/facter templatedir=$confdir/templates [master] ssl_client_header = SSL_CLIENT_S_DN ssl_client_verify_header = SSL_CLIENT_VERIFY [agent] server=puppet-server certname=puppet-client report=true pluginsync=true
-
Request a cert from the Puppet Server
$ sudo /opt/puppetlabs/puppet/bin/puppet agent --waitforcert 60 --test
-
On the Puppet DB server, run the log file and keep an eye on it
$ sudo tail -f /var/log/puppetlabs/puppetdb/puppetdb.log
-
Then, on the Puppet Server, check and sign the cert request from
puppet-client
$ sudo /opt/puppetlabs/puppet/bin/puppet cert --list $ sudo /opt/puppetlabs/puppet/bin/puppet cert --sign puppet-client