Introducing ‘puppetinabox’: bootstrap a lab setup with Puppet

Over the past few days, I’ve been working on a project. I call it puppetinabox and it includes a consolidate controlrepo with a Puppetfile of curated collection of forge modules and config repository. If you have an existing Linux OS template and basic skills with linux and git, you can quickly deploy a network managed by puppet and r10k. This curated collection can be used as is, to learn Puppet or stand up a basic network, or fork the code and start customizing your network, using it as the foundation for a more complex configuration.

This project grew out of the 12 Days of Commitmas and my own desire to puppetize my home network. Over the holiday break, there was a lot of streaming media going on and I started to see failures on my services. Refreshing the services, which were entirely too coupled on mostly one VM, was looking pretty daunting. Puppetizing the network was slightly daunting, but far preferable to doing it by hand. I decided to put what I had learned and practiced to good use and thus, puppetinabox was born!

Puppetinabox includes everything required to provide puppet, puppetdb, r10k, dns and dhcp services, a yum repository, and a build server. The only thing you need to provide is a gateway device and you have a functional network. If you already have a working network and you want to puppetize it, never fear – you can tweak the provided material to fit or use puppetinabox to start refreshing the services one at a time.

I created the puppetinabox GitHub organization to house all the repositories. You can read the documentation here. I’d love to hear what you think, whether this will help you or what improvements might make it better. Let me know in comments, on twitter, or as a GitHub issue. If anyone is graphically inclined, I could really use a nice looking avatar for the organization, too!

If you’d like some more information, I presented on the 1/15/2015 vBrownBag DevOps Series. Watch the video and check out the slides!

Puppet rspec tests with Hiera data

Editor’s note: Please check out the much newer article Configuring Travis CI on a Puppet Module Repo for the new “best practices” around setting up rspec-puppet. You are encouraged to use the newer setup, though everything on this page will still work!

I’ve covered puppet unit tests with rspec and beyond before. What if you need to go even further and test data from hiera? There’s a way to do that with rspec as well, and it only requires a few extra lines to your spec config – plus the hiera data, of course.

Use hiera in your class

We’ve covered a number of ways to use hiera in your class. You can use hiera lookups (hiera(), hiera_hash(), etc.) and automatic parameter lookups with classes. We’ll look specifically at hiera_hash() and the create_resources() it is commonly paired with. You cannot simply test that create_resources() was called because you need to know the resulting title of the generated resource. Here’s a simple DHCP profile class I created:

class profile::dhcp {
  # DHCP service and host reservations
  include dhcp::server
  $dhcp_server_subnets = hiera_hash('dhcp_server_subnets', undef)
  if ($dhcp_server_subnets) {
    create_resources('dhcp::server::subnet', $dhcp_server_subnets)
  }

  $dhcp_server_hosts = hiera_hash('dhcp_server_hosts', undef)
  if ($dhcp_server_hosts) {
    create_resources('dhcp::server::host', $dhcp_server_hosts)
  }
}

Continue reading

Puppet 3.7.3 Updates

I took the plunge this weekend and updated my home lab to Puppet v3.7.3. Before you begin, check out the release notes. Puppet v4.0.0 is on the horizon, so there are a lot of new features and options available to you, along with bug fixes, but also some deprecations. It’s a great time to test some of the new features (like the future parser) while you can still turn them off, but as long as you’re coming from the 3.6.x series, your existing config should just work.

I had one issue with the upgrade. I started getting errors that classes couldn’t be found, regardless of which class I included and whether it was via puppet agent or puppet apply. Turned out, I had a manually installed puppet gem. Whoops! It was version 3.6.1 and I simply removed it and everything started working. You really shouldn’t have that issue, unless you’ve been doing testing on your master like I’ve been doing. Don’t do that.

While I’ve only been using it for about 12 hours now, I have to say I love the changes in 3.7.0. The big performance improvement comes from persistent HTTPS connections. Previously, new connections were initiated for (I believe) just about every file transferred, including plugins (facts, types, providers). This is a huge performance increase for me and I still have pretty small manifests. If you have really large catalogs, especially ones that transfer a lot of files, you should be really happy with this. The best part is, you don’t have to enable this, it’s turned on for you.

If you have the time this holiday season, give Puppet 3.7.3 a shot in your lab. You should be pleasantly surprised and prepared to upgrade production after the holiday!

Publishing Forge Modules

Last week, we looked at an advanced spec helper, puppetlabs_spec_helper, and generated some tests with it. We also looked at the rake targets available with the helper and you may have noticed the build target: “Build puppet module package”. Prior to that, we created a new certs module that is code only, no data, for use in distribution of certificate files for web servers. It seems like a good opportunity to see how this works so we can upload a module to the forge.

Forge Modules

The Puppet Forge is a central repository for shared modules, written by Puppet Labs or by the community. It’s the puppet analog to perl’s CPAN, python’s pip, etc. – tell puppet you want a module and it fetches it from the forge. As the modules are shared, rather than specific to a user’s installation, be sure to use sound fundamentals to create a portable module. Your role and profile modules, which likely reference umpteen other modules and the files and templates they contain, are not good candidates for the forge, but a utility module like the certs module is a good candidate.

Continue reading

Beyond rspec-puppet: puppetlabs_spec_helper

Editor’s note: Please check out the much newer article Configuring Travis CI on a Puppet Module Repo for the new “best practices” around setting up rspec-puppet. You are encouraged to use the newer setup, though everything on this page will still work!

We recently discussed test-driven development for puppet modules in the context of rspec-puppet. That’s a nice, simple introduction to testing, but doesn’t provide everything we need. Rspec-puppet is limited in the matchers available (notably there are no negation tests) and its inability to test dependencies (when a module includes another module), both of which will be necessary eventually. The next step is puppetlabs_spec_helper, a project by Puppet Labs that provides us with more full-fledged specification tests.

Installation

The biggest requirement for puppetlabs_spec_helper is a ruby version of 1.9 or higher. CentOS 6.5, however, only includes v1.8.7. There are numerous ways to upgrade ruby, most of which are horrible. We’ll look at using the Ruby Version Manager, or RVM, to upgrade to 1.9.3. This can be done with puppet via the maestrodev/rvm module. After adding the module to your master, create a class or modify an existing one to provide RVM and some puppet and rspec gems.

Continue reading

Puppet Forge Module rnelson0/certs

I just published my first puppet module on the forge, rnelson0/certs. It provides a single define that installs a pair of SSL files (.crt and .key) from a specified external location to the managed node. This is designed for use with apache::vhost defines that allow you to provide the name of SSL files to the vhost, but requires the files to already exist on the node. I hope you find it useful. Report any issues via the GitHub issues tracker. Thanks!

Introduction to rspec-puppet

Editor’s note: Please check out the much newer article Configuring Travis CI on a Puppet Module Repo for the new “best practices” around setting up rspec-puppet. You are encouraged to use the newer setup, though everything on this page will still work!

Over the course of the Puppet series, one thing I’ve ignored is testing. As vSphere admins, many of us are comfortable with programming but probably not as well versed in some practices as full-time developers. Today we’ll look at an introduction to some test-driven-development with puppet.

Test Driven Development

What is this Test Driven Development, or TDD, that everyone speaks so highly of? In essence, you write tests that fail before you write any code, then you write code to satisfy the tests. Each test typically looks at a specific unit of functionality of a program, such as whether a file is created or has contents, and are called “unit tests.” By testing a specific function, when you have a failure, you can typically narrow down the problem domain to a few lines of code. When all unit tests generate successes, your code works (in theory!). In addition, when you modify the code in the future, these unit tests help ensure that you haven’t broken something that was previously working, also known as a “regression.”

TDD depends, of course, on writing tests that both provide coverage of all your code and that map to the requirements of the program. If you forget to provide a test that covers a vital portion of your code, all your tests can be successful but leave you with a broken program. If you have not been practicing TDD on an existing program, you can still add tests. However, you will not have 100% test coverage (the percent of code that is covered by unit tests) initially, or possibly ever, as all of the existing code was written prior to the unit tests. To keep things simple today, we’ll start writing some new code.

Continue reading

Don’t Disable SELinux, Part 2

Yesterday I warned everyone not to disable SELinux because the fix is almost always a quick one. But, what do you do if there is no selboolean that fixes your problem with a simple one liner?

After yesterday’s article, Tim Meusel shared a message he receives in his audit log when running nginx on his puppet master with SELinux in enforce mode:

type=AVC msg=audit(1415871389.171:787): avc:  denied  { name_connect }
 for  pid=2228 comm="nginx" dest=8080
 scontext=system_u:system_r:httpd_t:s0
 tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket
type=SYSCALL msg=audit(1415871389.171:787): arch=c000003e syscall=42
 success=no exit=-13 a0=19 a1=259e2b0 a2=10 a3=7fffdac559d0 items=0
 ppid=2227 pid=2228 auid=4294967295 uid=996 gid=995 euid=996 suid=996
 fsuid=996 egid=995 sgid=995 fsgid=995 tty=(none) ses=4294967295
 comm="nginx" exe="/usr/sbin/nginx" subj=system_u:system_r:httpd_t:s0
 key=(null)

That’s…. that’s ugly. The important parts have been highlighted. Nginx cannot talk to the tcp_socket at /var/run/puppet/puppetmaster_unicorn.sock. There doesn’t appear to be a selboolean that matches the issue. You could try flipping semi-relevant booleans for hours till you stumble upon some combination that may work, undoubtedly with side effects, and possibly never find the right combination. That could end up being a LOT of time wasted without any guarantee of success.

Instead, use audit2allow. By providing the tool with portions of an audit log, it will build an SELinux policy that will allow everything marked as “denied”. Here’s an example of generating a policy for review, then generating and applying that policy:

grep nginx /var/log/audit/audit.log | audit2allow > nginx.te
more nginx.te
grep nginx /var/log/audit/audit.log | audit2allow -M nginx
semodule -i nginx.pp

You can find more detail on the tool on the web, particularly this article where another nginx user is struggling with SELinux. You may have to repeat this process a few times – nginx stopped running when it failed to attach to the socket, so there could be other SELinux permission issues it would encounter if it had not failed. You won’t see those in the audit.log until it gets past the socket. Keep at it until audit2allow is building the same policy file on consecutive runs, at which point there are no new failures to discover. Your application should be fully working now and encounter no more SELinux permission issues.

Update: Tim continued to struggle after he performed the above steps until he moved the unicorn socket out of /var/run (which is admittedly not the recommended location!) even though he wasn’t seeing any more failures in the audit log. This command forces SELinux to log all failure events and then the new failures showed up and were processed by audit2allow:

semodule --disable_dontaudit --build

See Tim’s blog for more info.

You can apply the policy via puppet using the selmodule type, plus a file resource to put the .pp file in the correct location.

While this takes a lot longer to resolve than touching some selbooleans, you should only have to do it once. This ensures you still have the protections of SELinux and a well defined policy state for your application. If, and only if, this doesn’t resolve your issue, should you even entertain the thought of disabling SELinux, as a temporary resolution until a permanent solution is found.

Don’t Disable SELinux

When developing new web-based solutions on modern Linux distros, inevitably you’ll run into a fun issue – like your webserver throwing database errors when there’s not even any traffic making it to out of the server toward the database – and bang your head against the desk a bit. If you google for the error, you’ll run into the worst advise possible: “If your problem is not solved then disable your SELinux.” That’s right, just disable the whole thing because one part bothers you. The only positive part of this advise is that you may not have even though to look at SELinux before that.

You can verify that SELinux is the issue by taking a look at the audit log (tail -f /var/log/audit/audit.log) and using your web application. You’ll see a ton of crap that is simply undecipherable to human beings. What you’re looking for is the word denied and the application, file, or user that is having an issue. Here’s a deny for the application httpd when trying to talk to that remote database:

type=AVC msg=audit(1415813628.801:628): avc:  denied  { name_connect } for  pid=11911 comm="httpd"
 dest=3306 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=system_u:object_r:mysqld_port_t:s0
 tclass=tcp_socket

The next step is to narrow the issue down. There are a large number of settings for SELinux, known as SELinux Booleans, that may be affecting your application. Take a quick gander at them, find the most likely boolean, set the value to on, and try your application again. If it doesn’t work, set it to off and try another. Here’s a Tips and Trick page that describes the process in more detail and provides a pretty thorough list of booleans. Can’t access files on an NFS share via httpd? Set httpd_use_nfs to true. Talking to a remote database as above? That’s httpd_can_network_connect_db. This is just as simple and more beneficial than disabling SELinux altogether.

Of course, I’d be remiss if I just told you to use setsebool as root. You need to including this setting in your application definition. For example, integrate the setting into your puppet manifests with the selboolean type. Set the value to on and persistent to true. Apply your manifest and getsebool will show the new value. Here’s an example of a manifest I built for the phpMyAdmin application, specifically lines 25-28 where the selbooleans are set. If you’re using a different configuration management tool, you’ll have to do this part yourself, the important part is that you capture the setting.

Take a few minutes to learn how to use SELinux, so you’re aware of when you’re barking up the wrong tree and how to resolve issues, and integrate your findings into your application’s state definition. You’ll benefit by leaving the protection in place.

Migrating away from Puppet’s deprecated import feature

The import keyword in Puppet has been deprecated and will be removed in Puppet v4. That’s good to know, but what can you do about it if you’re using it? Let’s take a look at how it might be used. All directories below are relative to your environment directories, such as /etc/puppet/environments/production, unless a full path is given (starts with /).

Current Setup

Here’s what your site manifest might look like:

#manifests/site.pp
import nodes/*pp

When Puppet starts, it looks at all the *pp files in the nodes directory and loads them. Those files might look like this, one for each node or node-class:

Continue reading