Enable Google two-factor authentication for SSH connections on OS X

Note: this post was updated with additional security concerns regarding Git and the method of installing the required tools needed for compiling the PAM module. Thanks to @marczak and @Magervalp for the feedback.

Two-factor authentication (2FA) is fairly mainstream these days, which is a good thing. It would be nifty if Mac Admins could add the increased security 2FA offers to remote (SSH) logins on OS X. There are existing commercial solutions like Duo Security (a local Ann Arbor business I heartily endorse) that offer tools to accomplish this but if you are already using Google Authenticator for other services it may make sense to use that instead. As part of the Google Authenticator open source code Google provides a PAM module which, with some effort, can be compiled and configured for use with OS X’s own PAM system. In order to compile the GA PAM module the Xcode CLI tools are required as well as automake and autoconf. The easiest way to install the latter two is either through Homebrew, a popular OS X package manager or using ready-made PKG installers from the Rudix project.


In order to prepare the required tools follow these steps. First, we’ll need the Xcode command line tools:

$ xcode-select --install
xcode-select: note: install requested for command line developer tools

This will prompt the user to install the command line developer tools.

Before we continue a quick note regarding the Git client that ships with OS X – this is a post about security after all. A few weeks ago it was announced that all shipping Git clients had a serious security issue on case-insensitive filesystems that could allow for malicious repositories to overwrite the .git/config file and cause arbitrary command execution. Apple shipped a patch for the issue with Xcode 6.2 beta 3 which I would strongly suggest downloading from Apple’s Developer site and installing.

All that is left now is to install automake and autoconf which are the only required tools that do not ship with Xcode. As noted by one commenter it was necessary for him to also install libtool. I’ve added it to the list for reference, it may or may not be needed for everyone but won’t hurt to install alongside the other two. If you are a current Homebrew user all you should have to do is:

$ brew install autoconf automake libtool

Or, if you use Rudix as your package manager it should be as simple as:

$ sudo rudix install automake autoconf libtool

If you would like to use either Homebrew or Rudix package managers but don’t have them installed yet you must do so first. As noted by Ed Marczak the recommended installation method for both Homebrew and Rudix involves directly piping and executing code from the Internet, in good faith. I agree with him that this is not necessarily a habit you want to get too comfortable with. It takes as little as one line of code inserted either accidentally or maliciously to cause data loss, install malware and so on. I’m not implying that either of these tools will, but other less scrupulous persons may take advantage of the trust you previously put into legitimate install processes. I recommend that you examine the code executed by any pipe-curl-to-interpreter install like Homebrew or Rudix beforehand. Both Homebrew and Rudix have Github repositories.

If you just want to install the required tools without the added weight of a packaging tool you can opt to install the self-contained PKG installers for automake, autoconf and libtool provided by the Rudix project. If you decide to use the Rudix PKG installers I recommend that you examine them using something like Pacifist prior to installation. Pacifist is by far the best OS X package inspection tool and you should consider paying for a license. On with the show, shall we?

Installing Homebrew, followed by an installation of automake, autconf and libtool:

$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
$ brew install autoconf automake libtool

Installing Rudix package manager, followed by an installation of automake, autoconf and libtool:

$ curl -s https://raw.githubusercontent.com/rudix-mac/rpm/2014.10/rudix.py | sudo python - install rudix
$sudo rudix install automake autoconf libtool

Installing automake and autoconf using Rudix PKG installers:

Download the automake PKG installer (10.6-10.10)
Download the autoconf PKG installer (10.6-10.10)
Download the libtool PKG installer (10.6-10.10)

Installation and configuration

With the prerequisites out of the way, compiling the PAM module should now go smoothly:

$ git clone https://github.com/google/google-authenticator.git
$ cd google-authenticator/libpam
$ autoreconf -ivf
$ automake --add-missing
$ ./configure
$ sudo make install
$ sudo cp /usr/local/lib/security/pam_google_authenticator.so /usr/lib/pam/
$ sudo vi /etc/pam.d/sshd

The last command above opens up the SSH daemon PAM configuration file in vim, where we will add the following line:

auth required pam_google_authenticator.so nullok

Adding the line makes the Google Authenticator PAM module required for all authentication requests. This means that in order to perform a successful SSH login the remote user must provide both their account password and a one-time code generated by Google Authenticator or other compatible 2FA app. Note the ‘nullok’ option which will cause the Google Authenticator module to be skipped for users who have not yet been setup using the google-authenticator tool, which we will discuss next.

Setting up users for two-factor authentication

As part of the ‘make install’ process an executable was installed to /usr/local/bin/google-authenticator which is used to set up a user for GA authentication. Running google-authenticator without any options will prompt the user to select the type of token to create (HOTP or TOTP) and a few other additional security options. Running it with the -h flag will display the full usage:

$ google-authenticator -h
google-authenticator [<options>]
 -h, --help               Print this message
 -c, --counter-based      Set up counter-based (HOTP) verification
 -t, --time-based         Set up time-based (TOTP) verification
 -d, --disallow-reuse     Disallow reuse of previously used TOTP tokens
 -D, --allow-reuse        Allow reuse of previously used TOTP tokens
 -f, --force              Write file without first confirming with user
 -l, --label=<label>      Override the default label in "otpauth://" URL
 -i, --issuer=<issuer>    Override the default issuer in "otpauth://" URL
 -q, --quiet              Quiet mode
 -Q, --qr-mode={NONE,ANSI,UTF8}
 -r, --rate-limit=N       Limit logins to N per every M seconds
 -R, --rate-time=M        Limit logins to N per every M seconds
 -u, --no-rate-limit      Disable rate-limiting
 -s, --secret=<file>      Specify a non-standard file location
 -w, --window-size=W      Set window of concurrently valid codes
 -W, --minimal-window     Disable window of concurrently valid codes

We will use option flags to perform a non-interactive configuration, the output of which is shown below. The options we’re using are -t (create a TOTP token, the more secure option), -d (disallow reuse), -r 3 (number of logins per time window), -R 30 (duration of time window), -w 90 (token validity window) and -f (force writing configuration to ~/.google_authenticator).

$ google-authenticator  -t -d -r 1 -R 30 -w 90 -f


Your new secret key is: SECRET_KEY
Your verification code is VERIFICATION_CODE
Your emergency scratch codes are:

The output contains a few important bits of data. The first bit is the google.com URL which is a link to a QR code used to add the token for your user and host to Google Authenticator or other compatible app. Open the URL by command-clicking it in Terminal.app which should open your default web browser and show a QR code, ready to be added to a 2FA app. Instructions on how to add a token to Google Authenticator or Authy using QR codes are here:

Adding a new token to Google Authenticator
Adding a new token to Authy

The second bit (or bits) of info are the five emergency scratch codes which can be used as one-time emergency codes in case you lose access to your 2FA application. It is a good idea to store these emergency codes someplace safe.

With the setup of the Google Authenticator PAM module and configuring of our 2FA app out of the way we can now attempt a Google Authenticator 2FA-enabled SSH login:

$ ssh demo@localhost

Verification code: 
Last login: Tue Jan  6 23:05:58 2015 from localhost
myhost:~ demo$

Success! As seen above the SSH login process first prompts for the regular user password and then prompts for a verification code. The six-digit code is retrieved from the 2FA app we added our token to and once entered at the prompt it is accepted and login is complete. Huzzah!

Even though this post describes how to enable Google Authenticator 2FA for SSH on OS X it should work much the same for non-OS X hosts. The README found on the Github repository contains further detailed information on configuration as well.

MacTech Conference 2014 Docker slides are up

I spoke at MacTech Conference 2014 about Docker earlier this week, the slides for which are now up at https://db.tt/mSWzOHnb

In the talk I cover Docker and application containerization specific to Mac admins. The content is purposely not an all-encompassing dive into Docker but aims to introduce Mac admins to the concept of containerization and how it makes their lives easier deploying Mac management-centric services.

Thanks to everyone who showed up and asked questions during my talk. The MacTech Conference organization usually also makes the session videos available, for a fee. I am not involved in the sale of the videos so check out the Conference video page after the Conference to find out more.

Creating a signed Java Deployment Rule Set with Windows Server CA


With the release of Oracle’s Java 7 Update 51 came heightened security measures that affect unsigned and self-signed Java applets. At its standard “High” security setting the Java web plugin and standalone JVM will refuse to run unsigned or self-signed applets unless they have been explicitly added to a user-level whitelist which is a newly added security feature in Java 7 Update 51.
To allow large organizations to better manage security for their users Oracle previously introduced the Deployment Rule Set feature in Java 7 Update 40. The Deployment Rule Set consists of a single signed JAR file named “DeploymentRuleSet.jar” deployed in the Java system path “/Library/Application Support/Oracle/Java/Deployment”. Given the new security measures in Java 7 Update 51 it is a good time to start using a Deployment Rule Set since it provides:

  • The ability to use wildcard exception rules, unlike the user exception site list (https://mydomain.myorg.com/*)
  • No Java security warnings when accessing a whitelisted Java applet, unlike the user exception site list
  • Easy system-wide installation and updating of the ruleset

This post deals with a common scenario for Mac admins: you’re in an established Windows Server Active Directory environment that offers Certificate Authority services. Clients may already have your domain’s CA in their trusted cert store so extending this to sign a Java Deployment Rule Set JAR may make sense. The process of deploying the DeploymentRuleSet.jar file is outside the scope of this article although I did include a postinstall script as an addendum to assist with the installation of the signed certificate chain that this article will help you create. With that said, let’s get underway.

The process

Generate a new keystore and key

To perform the various key requests and code signing operations, a way to keep it simple is to create a fresh Java keystore file using the same password as the Java default JKS password. You’re free to play around with the -keyalg and -keysize settings as needed.

$ keytool -genkey -alias mykey -keyalg RSA -keysize 2048 -keystore keystore.jks -storepass changeit

Generate a Certificate Signing Request

In order to verify and sign the code signing certificate the Windows CA is going to need a certificate signing request (CSR) to process. This command creates one based on the key we generated in the previous step.

$ keytool -certreq -alias mykey -file csr.csr -keystore keystore.jks -storepass changeit

Extract the private key from the keystore

To submit a signing request, we’ll need the private key as well as the public one. The easiest way to get the private key out of an existing keystore is to import the keystore into a newly-created keystore, selecting only the key we are interested in and storing it as PKCS#12. The Windows tool we’ll use later can process PKCS#12 keys so we don’t need to do any further conversion.

$ keytool -v -importkeystore -srckeystore keystore.jks -srcalias mykey -destkeystore myp12file.p12 -deststoretype PKCS12

Rename private key file

Windows Server likes certain things to be a certain way and dealing with certificates is no different, so we must rename our PKCS#12 file to have a .pfx extension to allow certreq.exe to play nice.

$ mv myp12file.p12 myp12file.pfx

Sign the CSR using Windows Server

The files you will need to process the signing request are “mykey.csr”, “mykey.cer” and “myp12file.pfx”. Sign your generated signing request using a user account that has Read and Enroll rights to a template configured for code signing on the Windows Server CA. In the example here we’re using a template named “MyCodeSigningTemplate”. See here for more info on how to create a code signing Certificate Template with Windows Server: http://technet.microsoft.com/en-us/library/cc730826(v=ws.10).aspx

Allow certreq.exe to overwrite mykey.cer and mykey.csr when prompted by the “certreq” command.

C:\Windows\system32&amp;gt;certreq -submit -attrib &amp;quot;CertificateTemplate:MyCodeSigningTemplate&amp;quot; mykey.csr mykey.cer myp12file.pfx

Import signed key and CA into keystore

We need to add the signed key and signing CA (and any intermediate CA certs) back into our keystore so we can use it for code signing.

$ keytool -importcert -keystore keystore.jks -file ca-certificate.pem -alias CARoot -storepass changeit
Certificate was added to keystore
$ keytool –importcert –keystore keystore.jks –file mykey.cer –alias mykey -storepass changeit -trustcacerts
Certificate reply was installed in keystore

Create DeploymentRuleSet.jar and sign it with the newly signed key

Now we can get down to the business of creating a .jar file and signing it with our shiny new key. We stash ruleset.xml into a JAR using the “jar” command. Next, we use “jarsigner” to sign “DeploymentRuleSet.jar” with our key which is retrieved from our Java keystore using the “mykey” alias.

$ jar -cvf DeploymentRuleSet.jar ruleset.xml
added manifest
adding: ruleset.xml(in = 266) (out= 225)(deflated 15%)
$ jarsigner -keystore keystore.jks -storepass changeit DeploymentRuleSet.jar mykey

Combine signing key and associated root CA certificates

In order to easily distribute our public signing cert as well as those of our CA and any intermediate CAs they should be concatenated into one single file. The order to concatenate them in is CA -> Intermediate -> (Optional intermediates) -> mykey.pem.

$ cat rootCA.pem (intermediateCert1.pem, intermediateCert2.pem) mykey.pem &amp;gt; mychain.pem

Import mychain.pem into Java keystore on client(s)

In order for the Java browser plugin to accept our Deployment Rule Set without complaining, we need to add its code signing key public key certificate to the Java keystore.
The Java home path for the browser plugin is different from system Java so we need to import our certificate chain into the browser plugin-specific keystore located at “/Library/Internet Plug-Ins/Contents/Home/lib/security/cacerts”. To make the certificate chain available to standalone Java applications as well it must be imported into the system Java keystore at “/Library/Java/Home/lib/security/cacerts”.

Both keystores use the same default password: “changeit”. For enhanced security, it may be a good idea to change the password for the individual keystores to a new one after importing the certificate chain. This is optional, but a security note worth mentioning.

$ keytool -importcert -keystore /Library/Internet\ Plug-Ins/Contents/Home/lib/security/cacerts -storepass changeit -alias mykey -file mychain.pem -noprompt
$ keytool -importcert -keystore /Library/Java/Home/lib/security/cacerts -storepass changeit -alias mykey -file mychain.pem -noprompt

Testing the Deployment Rule Set

Excelsior! We should now be able to place our DeploymentRuleSet.jar file into its designated path and load a web page we whitelisted in the ruleset.xml file. If all went well the Java application on the web page will load without any warnings from the JVM about the DRS using an untrusted self-signed certificate or the application being blocked because of its unsigned or self-signed status. You can verify the presence of an active Deployment Rule Set by navigating to the “Java” preference pane in System Preferences and clicking the “Security” tab. If active, the tab will contain a line of blue text that says “View the active Deployment Rule Set”. The blue text can be clicked to view the current rule set in a new window. The Deployment Rule Set will also allows inspection of the signing certificate and its associated root certificates. These should match the code signing key’s certificate and any root and intermediary CA certificates used in our previous steps.


Java 7u51 Security tab


Java 7u51 Deployment Rule Set

Java 7u51 DRS Certificates

Java 7u51 DRS Certificates


Hopefully this will help a few Mac admins with deploying a self-signed DeploymentRuleSet.jar file using their organization’s local CA. If you have questions or comments leave them at the end of this post, find me on Twitter or on Freenode IRC in ##osx-server.

Addendum: Package postflight

To make distribution of the signing certificate chain a little easier I’ve included a postinstall script that can be added to an installer package. The postinstall script will check the browser plugin and system keystores for the presence of the “my_chain” alias and if it is not found in either one of the keystores it adds the certificate chain. The script expects the installer to drop “my_chain.pem” into /tmp and it securely removes the file after completion of the script. You are free to change file names and aliases as needed.


# Check Java Plugin and System keystores for existence of the signing cert.
#   If found, we skip installation and report success. If not found, tag the
#   keystore as needing installation and proceed with installation. Check result
#   of installation afterwards and log the result for reporting later.

# Executable and keystore file statics
KEYTOOL_LIST=&amp;quot;/usr/bin/keytool -list -storepass changeit -keystore &amp;quot;
KEYTOOL_IMPORT=&amp;quot;/usr/bin/keytool -importcert -storepass changeit -trustcacerts -file /tmp/my_chain.pem -alias my_chain -noprompt -keystore &amp;quot;
JAVA_PLUGIN=&amp;quot;/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/security/cacerts&amp;quot;
LOGGER=&amp;quot;/usr/bin/logger -t JAVADRSINSTALL&amp;quot;
REMOVE_PEM=&amp;quot;/usr/bin/srm /tmp/my_chain.pem 2&amp;gt;&amp;amp;1 &amp;gt;/dev/null&amp;quot;

# Initialize reporting variables. I like string comparisons, deal with it.

# Check whether the signing cert is installed in the Java plugin keystore
if ! `${KEYTOOL_LIST} &amp;quot;${JAVA_PLUGIN}&amp;quot; | grep my_chain 2&amp;gt;&amp;amp;1 &amp;gt;/dev/null`; then
    ${LOGGER} &amp;quot;Cert chain for Java DRS must be installed in Java Plugin.&amp;quot;
# Check whether the signing cert is installed in the System Java keystore
if ! `${KEYTOOL_LIST} &amp;quot;${JAVA_SYSTEM}&amp;quot; | grep my_chain 2&amp;gt;&amp;amp;1 &amp;gt;/dev/null`; then
    ${LOGGER} &amp;quot;Cert chain for Java DRS must be installed in System Java Home.&amp;quot;

# If we didn't find the signing key in the keystores we need to install them.

# Install into Java plugin keystore
if [[ $keystore_plugin == '1' ]]; then
    echo $keystore_status
    ${LOGGER} &amp;quot;Installing cert chain for Java DRS into JavaAppletPlugin&amp;quot;
    ${KEYTOOL_IMPORT} &amp;quot;${JAVA_PLUGIN}&amp;quot; 2&amp;gt;&amp;amp;1 &amp;gt;/dev/null

    # Check whether our signing key is now in the keystore
    if `${KEYTOOL_LIST} &amp;quot;${JAVA_PLUGIN}&amp;quot; | grep my_chain 2&amp;gt;&amp;amp;1 &amp;gt;/dev/null`; then

# Install into System Java keystore
if [[ $keystore_system == '1' ]]; then
    ${LOGGER} &amp;quot;Installing cert chain for Java DRS into System Java Home&amp;quot;
    ${KEYTOOL_IMPORT} &amp;quot;${JAVA_SYSTEM}&amp;quot; 2&amp;gt;&amp;amp;1 &amp;gt;/dev/null

    # Check whether our signing key is now in the keystore
    if `${KEYTOOL_LIST} &amp;quot;${JAVA_SYSTEM}&amp;quot;  | grep my_chain 2&amp;gt;&amp;amp;1 &amp;gt;/dev/null`; then

# Report on status of installs, log any failures and securely remove our key

# No installation needed for either keystore, report it
if [[ ($keystore_plugin == '0') &amp;amp;&amp;amp; ($keystore_system == '0') ]]; then
    ${LOGGER} &amp;quot;Java DRS cert chain install not needed.&amp;quot;

# Both checks came back as failed, report it
elif [[ ($keystore_plugin == '1') &amp;amp;&amp;amp; ($keystore_system == '1') ]]; then
    ${LOGGER} &amp;quot;Java DRS cert chain install into all keystores failed.&amp;quot;

# Both checks came back correctly, report success for all keystores
elif [[ ($keystore_plugin == '2') &amp;amp;&amp;amp; ($keystore_system == '2') ]]; then
    ${LOGGER} &amp;quot;Java DRS cert chain install into all keystores complete.&amp;quot;

# Java Plugin installation not needed, System Java keystore succeeded.
elif [[ ($keystore_plugin == '0') &amp;amp;&amp;amp; ($keystore_system == '2') ]]; then
    ${LOGGER} &amp;quot;Java DRS cert chain install into Java Plugin keystore not needed.&amp;quot;
    ${LOGGER} &amp;quot;Java DRS cert chain install into System Java keystore successful.&amp;quot;

# Java Plugin installation succeeded, System Java keystore not needed.
elif [[ ($keystore_plugin == '2') &amp;amp;&amp;amp; ($keystore_system == '0') ]]; then
    ${LOGGER} &amp;quot;Java DRS cert chain install into Java Plugin keystore successful.&amp;quot;
    ${LOGGER} &amp;quot;Java DRS cert chain install into System Java keystore not needed.&amp;quot;

# Java Plugin installation not needed, System Java keystore failed.
elif [[ ($keystore_plugin == '0') &amp;amp;&amp;amp; ($keystore_system == '1') ]]; then
    ${LOGGER} &amp;quot;Java DRS cert chain install into Java Plugin keystore not needed.&amp;quot;
    ${LOGGER} &amp;quot;Java DRS cert chain install into System Java keystore failed.&amp;quot;

# Java Plugin installation failed, System Java keystore not needed.
elif [[ ($keystore_plugin == '1') &amp;amp;&amp;amp; ($keystore_system == '0') ]]; then
    ${LOGGER} &amp;quot;Java DRS cert chain install into Java Plugin keystore failed.&amp;quot;
    ${LOGGER} &amp;quot;Java DRS cert chain install into System Java keystore not needed.&amp;quot;

# Java Plugin installation failed, System Java keystore succeeded.
elif [[ ($keystore_plugin == '1') &amp;amp;&amp;amp; ($keystore_system == '2') ]]; then
    ${LOGGER} &amp;quot;Java DRS cert chain install into Java Plugin keystore failed.&amp;quot;
    ${LOGGER} &amp;quot;Java DRS cert chain install into System Java keystore successful.&amp;quot;

# Java Plugin installation succeeded, System Java keystore failed.
elif [[ ($keystore_plugin == '2') &amp;amp;&amp;amp; ($keystore_system == '1') ]]; then
    ${LOGGER} &amp;quot;Java DRS cert chain install into Java Plugin keystore successful.&amp;quot;
    ${LOGGER} &amp;quot;Java DRS cert chain install into System Java keystore failed.&amp;quot;

exit 0

Auto-this and Auto-that

Inspired by recent Auto-events I decided I was tired of having to manually roll hardware-specific NetBoot images, such as we’ve had to do recently for both Mountain Lion and Mavericks releases. It took me a while to spelunk into the innards of System Image Utility and its related frameworks and tools but I feel that I was able to write up something half-decent for it.

With that said, please take a look at AutoNBI.py, the latest member of the Auto-family. There’s no fancy GUI, but that’s the point here – integration into your workflow. There’s some first version caveats such as the NBI modification method being pretty basic in that it currently will replace or add only one folder since that is what I needed to be able to do for my needs. I have some additional code in the works that will let AutoNBI ingest a plist file with more complex add and remove configurations, but for now this’ll have to do.

Try it out, let me know what you think. Link to the Bitbucket project page is here and the Readme follows below:


A tool to automate (or not) the building and customization of Apple NetBoot NBI bundles.


  • OS X 10.9 Mavericks – This tool relies on parts of the SIUFoundation Framework which is part of System Image Utility, found in/System/Library/CoreServices in Mavericks.
  • Munki tools installed at /usr/local/munki – needed for FoundationPlist.

Thanks to:

  • Greg Neagle for overall inspiration and code snippets (COSXIP)
  • Per Olofsson for the awesome AutoDMG which inspired this tool
  • Tim Sutton for further encouragement and feedback on early versions

This tool aids in the creation of Apple NetBoot Image (NBI) bundles. It can run either in interactive mode by passing it a folder, installer application or DMG or automatically, integrated into a larger workflow.

Command line options:

  • [–source][-s] The valid path to a source of one of the following types:
  • A folder (such as /Applications) which will be searched for one or more valid install sources
  • An OS X installer application (e.g. “Install OS X Mavericks.app”)
  • An InstallESD.dmg file
  • [–destination][-d] The valid path to a dedicated build root folder:

The build root is where the resulting NBI bundle and temporary build files are written. If the optional –folder arguments is given an identically named folder must be placed in the build root:

./AutoNBI &lt;arguments&gt; -d /Users/admin/BuildRoot –folder Packages -> Causes AutoNBI to look for /Users/admin/BuildRoot/Packages

  • [–name][-n] The name of the NBI bundle, without .nbi extension
  • [–folder] Optional – The name of a folder to be copied onto NetInstall.dmg. If the folder already exists, it will be overwritten. This allows for the customization of a standard NetInstall image by providing a custom rc.imaging and other required files, such as a custom Runtime executable. For reference, see the DeployStudio Runtime NBI.
  • [–auto][-a] Optional – Enable automated run. The user will not be prompted for input and the application will attempt to create a valid NBI. If the input source path results in more than one possible installer source the application will stop. If more than one possible installer source is found in interactive mode the user will be presented with a list of possible InstallerESD.dmg choices and asked to pick one.


To invoke AutoNBI in interactive mode: sudo ./AutoNBI -s /Applications -d /Users/admin/BuildRoot -n Mavericks

To invoke AutoNBI in automatic mode: sudo ./AutoNBI -s ~/InstallESD.dmg -d /Users/admin/BuildRoot -n Mavericks -a

To replace “Packages” on the NBI boot volume with a custom version: sudo ./AutoNBI -s ~/InstallESD.dmg -d ~/BuildRoot -n Mavericks -f Packages -a

Workaround for Konica Minolta (and other) PDEs in Mavericks

Is your organization using those really shiny and fancy Konica Minolta multifunction printers? Did your users start upgrading to Mavericks only to find that none of the custom functionality menus (courtesy of KM PDEs) were available in the Print window? Try this workaround to make them show up again in the print dialogs of sandboxed apps (Preview, TextEdit). Note that the script can easily be modified to provide the same workaround for other vendors’ incompatible PDEs as well. And make sure to use sudo, of course.

Update: To make applying this workaround a little easier I have created a ‘nopkg’ Munki pkginfo that will apply it to any PDEs found in a user’s /Library/Printers folder that are missing the required key in their Info.plist. It is up to the Mac admin to insert the names of the appropriate printer driver install items for which this patch should be an update. Get the pkginfo item right here.

Update 2: For those having trouble figuring out how to apply this workaround or those who don’t use Munki for their patch management I have put up a standalone version of the script. Simply unzip the file and run with sudo:

sudo ./mavericks_pde_fix.py

Changes will only be made to PDEs that are missing a key in their Info.plist required for Mavericks compatibility.

Mavericks tool time – SIU and imagetool

Recently I’ve had to rebuild our customized NBI NetBoot images a number of times due to special OS builds (yay) and needing to test Mavericks DPs. In that process it became obvious that it’s easy to make a mistake adding certain resources, deleting others and making sure that the resulting DMG is resized afterwards. I don’t know about you, but if I have to repeatedly and manually run a bunch of error-prone steps my mind quickly turns towards automating the heck out of it to regain sanity and remove error.

Wanting to cut my teeth on some more Python I figured I would seize this particular itch to do that. Having been generally happy with System Image Utility (SIU) my first approach was to create an SIU workflow and then figure out a way to execute it from Python. This, I surmised, required tapping the power of the Automator framework with PyObjc through things like AMWorkflow.runWorkflowAtURL_withInput_error_(). Not the easiest for a first PyObjc project, admittedly. After some scary times wandering in the PyObjc desert kicking the Automator Framework around and not making a lot of headway I decided to see if perhaps System Image Utility had a CLI mode I could subvert for my needs. Thus I stumbled upon ‘/System/Library/CoreServices/System Image Utility.app/Contents/MacOS/imagetool‘ which I didn’t remember seeing before and indeed appears to be new in Mavericks.

Long story short: imagetool is a CLI tool that can generate NetBoot/Install/Restore NBI bundles ready for deployment as well as regular bootable installer media. Since we already have the “createinstallmedia” tool contained in the Install OS X Mavericks.app bundle (written up so very nicely by Rusty Meyers) I will not go into the –createmedia (-c) function here as it does the same thing.

The usage info for imagetool is as follows:

/System/Library/CoreServices/System\ Image\ Utility.app/Contents/MacOS/imagetool

Usage: imagetool <options>
  where <options> are:
   -c | --createmedia -s <path_to_install_app> -d <path_to_volume>
   -p | --plist <path> path to a property list containing the build specifications
   [--netboot | --netinstall | --netrestore] image kind (required)
   -d | --destination <path> path to save the image (required)
   -s | --source <mountpoint> mountpoint of source volume or path to Install Mac OS X
        application (required)
   -n | --name <string> name of image (required)
   -i | --index <integer> image index (0-65535) (required)

   -h | --help this help

The help text goes on to say:

To create a property list, create a custom workflow in System Image Utility.
Add any customized steps you wish the workflow to perform.
Hold down the option key when clicking the run button in the workflow.
This will save the workflow detail to the specified location instead of executing it.

While it would be perfectly fine to feed imagetool some CLI parameters the –plist option intrigued me. I tested the “option-click Run to save” (so intuitive, Apple) in SIU and it does indeed save a plist file, different in structure from the usual .wflow document.

Upon opening the file we see a pretty straightforward set of configuration items:

<?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">
	<string>NetInstall of OS X 10.9 (13A603) Install (7.14 GB)</string>
	<string>NetInstall of Install OS X Mavericks</string>
	<string>/Path/To/NetInstall of Install OS X Mavericks</string>
			<string>/Path/To//Install OS X Mavericks.app</string>

These are all as one would expect to see – image description, name, index as well as the location it will be written out to and source(s) of the InstallESD.dmg to use. If the “Add Packages and Post-Install Scripts” workflow item is included its settings are recorded as follows:


In addition to the above options the .plist contains a lengthy “userConfigurationOptions” dict containing a “groupID” and “userID” key which are set to that of the user running SIU. A third key named “siuPrefs” (containing a dict of other strings, integers, arrays and dicts) appears to contain a few SIU-specific keys but also many settings originating from the user creating the NetBoot image. In my testing these keys contained ColorSync profile settings, Text Replacement configs and even configurations for third party applications.

The SIU-related keys are:


Since I didn’t want to retain most of the user settings I removed the entire “userConfigurationOptions” dict as a test and subsequent images built and booted without a problem. Taking all of the above into account it is therefore fairly straightforward to put together one or more .plist files that can be fed to imagetool with an invocation like this:

sudo '/System/Library/CoreServices/System Image Utility.app/Contents/MacOS/imagetool' --plist '/Users/admin/Documents/NetRestore Template.plist' --index 3000

This will tell imagetool to use the configuration in “NetRestore Template.plist”, overriding the “imageIndex” key in the plist and substituting it with “3000”. Note that while my example uses Mavericks as an installer source I have been able to verify that substituting a Mountain Lion (10.8) or Lion (10.7) installer app and InstallESD.dmg also works without a hitch and the resulting NBIs were all bootable.

As you can see, some interesting things are possible with this entirely CLI-driven process. I will be posting a tool written in Python in the next few days that leverages imagetool to automate the creation and processing of plists to generate and modify NetBoot images for use in mass deployments of OS X.

AFP548 Episode 2 up now

I recently casted pod for the fine folks at AFP548 and had the privilege of talking to the esteemed Charles Edge for an hour and change. Go check it out here.

Thanks to Charles and Allister for their direct involvement and the AFP548 purveyors at large for having me. It was a blast!

Edit: if iTunes seems wonky, this is the direct link to the episode on SoundCloud.

PSU Mac Admins Conference 2013 slides are up

First of all I want to thank all those brave souls who stuck around until this year’s conference’s bitter end to come to listen to me talk about Munki, Munkiserver, Puppet and the combination of the three. The video of mine and other speaker’s sessions can be found on the PSU Mac Admins YouTube page somewhere in the latter part of June.

After some minor modifications (thanks @Allister) I have posted my slide deck as a PDF. The PSU folks will also be sending out links to attendees of the slides of all the other speakers.

However if you did not attend the conference you can find my slides right here.

Many thanks to the PSU Mac Admins team for organizing a well-oiled conference, it was a blast!

Adobe Creative Cloud for Teams

Update: since the details that we received did not line up with Creative Cloud Enterprise features I asked for verification from our vendor whether we were given details about the CC  Enterprise product and whether it was given by an Adobe rep. Neither were the case, so I am modifying my post to instead outline the details of the Creative Cloud for Teams program for those SMB admins who are considering it for their users.

I apologize for any confusion the initial post created, it was by no means my intention to do so. A big thanks to Jody Rogers for alerting me about the misinformation. As always he is on top of things.

Adobe Creative Cloud for Teams highlights:

  • For those with security policies that forbid Cloud storage use, Creative Cloud storage must be blocked through firewall port filtering at the customer’s site.
  • Creative Cloud Packager currently does not have a “kill switch” for the CC storage functionality, as it has for EULA suppression, update notifications, etc.
  • Laptop users who use the CC apps at home or anywhere else that is not at their employer’s location will have full access to CC storage.
  • CC Teams admins have the ability to see which users are using CC storage and must police this usage themselves.
  • The ability to retrieve any CC-stored content for users who have been removed from the company’s CC account is in the works.
  • Upon first time deployment of one or more CC apps the end user must register an Adobe ID to then validate the app(s) they were given access to. CC Teams admins must generate email notifications for each new user (and likely also for each new app assigned to an existing user).
  • A user’s computer must make contact with the Adobe CC servers at least once every 30 days or the installed CC app(s) will revert to trial mode.
  • As far as I could understand there’s still the dual-license ability where a user can use the same applications they are licensed for on a desktop and a laptop computer. No clear word on whether this means simultaneously or not.
  • For those of us who need to test deployment, security or end-user functionality Adobe can decide to make short-term (think 3-4 weeks) licenses available.

These are all the points I got out of our 45 minute call. Anyone out there who has more solid details that either confirm or contradict any of the information presented here is encouraged to respond in the comments, email or Twitter.

Link – Booting multiple NBIs using ISC DHCPD

Brandon Penglase wrote up a very helpful wiki article on his site outlining how to configure ISC’s DHCP server to serve multiple NetBoot images as opposed to the single image, methods for which have been available for a number of years now. Noted caveats are that Startup Disk will not be able to display the available NBIs as it uses a custom port to receive the list of images back and the inability to use thin NetBoot images that require server-side storage for the client.

Go read it now.

%d bloggers like this: