Tag Archives: Puppet

Installing Certificates on macOS with Puppet

I have lots of internal web servers that use self signed certs for HTTPS encryption. Every time I try to login to theses server I get the “Your connection is not private” message.

Screen Shot 2017-10-13 at 6.41.26 PM.png

It’s not that it’s a big deal, but it’s annoying since I use multiple computers for admin work. Let’s get puppet to solve this problem.

I started with an external fact that I placed in my module, in the facts.d folder (you have to create this folder). It simply checks to see if I can find the certificate in the system keychain and sets a variable … either “cert_installed=true” or “cert_installed=false”.

#!/usr/bin/ruby
check_cert="security find-certificate -a server.company.edu.cer -k /Library/Keychains/System.keychain | grep \"server.company.edu\" >/dev/null"

if system(check_cert) == true
  puts "cert_installed=true"
else
  puts "cert_installed=false"
end

The in the manifest … I check if the cert is installed and if it’s false, I copy the cert to the /tmp folder and then run a command to install it and trust it.

 class cert {
	if $facts['cert_installed'] == 'false' {
		file { "/tmp/server.company.edu.cer":
			mode => "0777",
	  		owner => 'root',
		  	group => 'wheel',
		  	source => "puppet:///modules/cert/server.company.edu.cer",
		}
		exec { "server.company.edu.cer":
	  		command => 'sudo security add-trusted-cert -d -r trustAsRoot -k /Library/Keychains/System.keychain "/tmp/server.company.edu.cer"',
			path	=> ["/bin", "/usr/bin", "/sbin", "/usr/sbin"],
			require => File["/tmp/server.company.edu.cer"]
		}
	}
}

This has been working beautifully.

Using Puppet To Manage License Files

The life of an admin in Higher Ed inevitably means, now that classes have started, your software is expiring. This is an annual problem. So I wanted to see how Puppet can help me.

In order for this to work, you need an application that can be licensed with a static file on the machine. Something that can be updated on a master machine and then copied to the clients. My examples involve Mathematica … insert your software in it’s place.

Puppet is perfect at enforcing configuration, so the idea of enforcing a license file is an ideal task. Take this file and put it there .. no questions asked.

class mathematica_license {
  file { "/Library/Mathematica/Licensing/mathpass":
    ensure => 'present',    
    mode   => "0644",
    owner  => 'root',
    group  => 'wheel',
    source => "puppet:///modules/mathematica_license/mathpass",
  }
}

This works and if that’s all you need, great. You take your new license file, and put it in the files folder in your module, and use a file resource to say .. if this file doesn’t exist on the client … then copy it.

But a simple check would be great. What if I run this on all Lab machines, some of which don’t run Mathematica. An If statement makes a lot of sense. If Mathematica exist in the Applications folder, then copy new license file over.  Sounds simple, but this isn’t always Puppets strongest skill. You can try to use an “unless” statement or “onlyif” … but I usually find myself fighting with the syntax.

class mathematica_license {
  exec {"mathematica_check":
    command => "true",
    path    => ["/usr/bin","/usr/sbin", "/bin"],
    unless  => "ls /Applications/ | grep Mathematica.app",
  }

  file { "/Library/Mathematica/Licensing/mathpass":
    ensure => 'present',
    mode   => '0644',
    owner  => 'root',
    group  => 'wheel',
    require => Exec["mathematica_check"],
    source  => "puppet:///modules/mathematica_license/mathpass",
  }
}

It works, but maybe not in the way you want. The “unless” statement says, if results come back from the “ls” command, and grep finds “Mathematica ,then execute the “command” statement, which just evaluates to true. Then puppet runs the file resource below it. But it only runs the “file” resource if the “require” statement is true .. which is the “mathematica_check” above it.

If you’re just running puppet on the client side, you get the results you want. If the Application is installed, the file gets copied over. But if the Application isn’t installed, nothing happens.

If you’re reporting back to a dashboard like the Foreman … a side effort of a client not having your Application installed, is lots of errors. All those errors get reported back to your dashboard, causing a lot of false positives you need to over look. Your client reports back in an error state, even though it did exactly what you wanted it to do .. not copy the file if Mathematica isn’t installed.

A better solution seems to be, to create a custom fact. A fact that checks for the existence of the application. Then use an “If” to qulalify your resource in the manifest. Now the code only runs, if “Mathematica” is installed. If it can’t find the Application … the code exits and moves on. No errors in your log.

class mathematica_license {
  if $mathematica == true {
    file { "/Library/Mathematica/Licensing/mathpass":
      mode   => "0644",
      owner  => 'root',
      group  => 'wheel',
      source => "puppet:///modules/mathematica_license/mathpass",
    }
  }
}

This does involve you creating a module with custom facts and deploying it to your clients. But to be honest, that can be easier then trying to manage puppets onlyif or unless statement in exec resources. Just my opinion

Creating A Custom OS X Puppet Installer

puppet_installer_icon

Getting puppet on OSX is getting better and simpler. You now have 3 packages to install, the puppet user is created for you and it just works … for the most part. In my environment, I have a couple extra steps. Like installing the Launch Daemon when I also install the packages, and adding my puppet master to the hosts file on each client. Instead of  touching each client 5 to 6 times to get puppet setup. I rolled it into a custom package.

I’m using PackageMaker but certainly you could use any flavor of package management (packages, iceberg, etc) and my client machines are on OSX 10.9.5.

01I downloaded Facter, Hiera, and Puppet from the puppetlabs website and pulled out the pkgs. Then added them to my PackageMaker project.

_ _ _ _

02
I created the com.puppetlabs.puppet.plist Launch Daemon and added the file to the project. Just make sure to change the “Destination” to “/Library/LaunchDaemons”

For good measure, here’s the com.puppetlabs.puppet.plist. Just copied from the website.

<!--?<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Label</key>
        <string>com.puppetlabs.puppet</string>
        <key>OnDemand</key>
        <false/>
        <key>ProgramArguments</key>
        <array>
                <string>/usr/bin/puppet</string>
                <string>agent</string>
                <string>--no-daemonize</string>
                <string>--logdest</string>
                <string>syslog</string>
                <string>--color</string>
                <string>false</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>ServiceDescription</key>
        <string>Puppet agent service</string>
        <key>ServiceIPC</key>
        <false/>
</dict>
</plist>

_ _ _ _

03I also added a Postinstall script to handle a few extra things like installing some dependencies, making root the owner of the Launch Daemon, setting the permission on the Launch Daemon, and then adding my puppet server to the hosts file.

#!/bin/sh

#need for property_list_key
gem install CFPropertyList -v 2.2.6

#need for managedmac
gem install sqlite3

#change owner and permissions of puppet launchdaemon
sudo chown root:wheel /Library/LaunchDaemons/com.puppetlabs.puppet.plist
sudo chmod 644 /Library/LaunchDaemons/com.puppetlabs.puppet.plist

#edit hosts file with puppet server IP. Change xxx to match your server IP.
echo "\nxxx.xxx.xxx.xxx    puppet" >> /etc/hosts

Next … build package and go forth and puppetize!

Managing OSX Mavericks Power Settings With Puppet

One of the things I want Puppet to do for my lab of iMac’s is to make sure the computers never go to sleep. That way when I need remote access to them, I know I have it.

Ideally, I don’t want puppet to run the same command every time it runs. It would be much better if it checked for a “sleep” status and then ran on the out coming of the check.

I’ve been testing this back and forth for a day or so and the code I currently have … seems to be doing the trick.

exec { "set_power_settings":
        path   => ["/usr/bin", "/usr/sbin", "/bin", "/sbin"],
        command => "pmset -c displaysleep 30 sleep 0 disksleep 0 womp 1 autorestart 1",
        unless => "pmset -g | grep -w sleep | awk '{print $2}' | grep -w 0 >/dev/null",
        }

The gist of it is… run the “Command“, which set’s my desired power settings. “unless” the current state of “sleep” is already set to “0” (never sleep).

Manage OS X Firewall with Puppet

MAC OS X FIrewall

With the help of glarizza’s module, Property_List_Key, I’m testing managing OS X plist with Puppet. This turns the built-in firewall on and ensures it stays on. Tested on OS X 10.9.5

 property_list_key { 'globalstate':
      ensure => present,
      path   => '/Library/Preferences/com.apple.alf.plist',
      key    => 'globalstate',
      value  => '1',
      value_type    => 'integer',
    }

This get’s me thinking all sorts of creative things that can be managed and enforced in a lab setting, where user’s might have admin rights and make undesirable changes.

Using Puppet to manage Java auto updates on Mac OS X

I’ve been toying with a virtual setup of puppet open source over the last week. My Puppet Master is Ubuntu 12.04, my Puppet Agent is Mac OS X 10.9.

I’ve been looking at puppet for over two years and have tried twice in the past to stand up a virtual machine and make it do something!!!. It usually ends with some crisis being more important and by the time I come back to puppet … I’m lost. I get frustrated and delete the VM’s. Rinse and repeat.

This time I”m up and running and my two big goals, both of which I accomplished, were too manage a local admin account, and install and manage printers for Mac OS X. Two things I can manage in several different ways, but I’d like to see if Puppet’s way is better/different. My thoughts are still out on that. I’m trying to consider how puppet could fit into my current workflows. But it is neat to watch puppet work. Which got me thinking. What kind of creative things can puppet do to manage a Mac OS X desktop?

If one of the things Puppet can manage are file resources, and plist are just formatted xml files. Can puppet manage plist for Mac OS X? So I decided to try something very simple. I want to turn off Java 7’s auto update feature. No matter how many times you turn it off, when you install a new version of Java, it’s re-checked. This is a good test because the plist only contains 1 entry.

Typically to disable auto updates, you can just open terminal and run:

sudo defaults write /Library/Preferences/com.oracle.java.Java-Updater.plist JavaAutoUpdateEnabled -false

But with puppet, I want to make sure of a couple things:

– The file “com.oracle.java.Java-Updater.plist” exist at /Library/Preferences/

– That the mode is set to 644

– The contents of the file contains the entire plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>JavaAutoUpdateEnabled</key>
    <false/>
</dict>
</plist>

so my puppet code looks like this:

Screen Shot 2014-08-15 at 9.06.00 AM

node pup{
        file {'com.oracle.java.Java-Updater.plist':
                path    => '/Library/Preferences/com.oracle.java.Java-Updater.plist',
                ensure  => present,
                mode    => 644,
                content => '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>JavaAutoUpdateEnabled</key>
        <false/>
</dict>
</plist>',
        }

And after I run puppet on my node, I did a restart for good measure and then checked the auto update box. Which appears to have worked.

Screen Shot 2014-08-15 at 9.04.54 AM

Pretty neat for a quick test. It has me thinking what else might Puppet be able to do?