Web browsers are critical to pretty much any organization. For many people, the browser is everything, right down to their email client. Since the browser is probably the most used piece of software, and users are putting all kinds of private information into it, it’s vital browsers are kept patched.
Fortunately our default browser is Google Chrome, and Chrome is really good at keeping itself updated. Unfortunately it completely sucks at letting the user know that there is an update to install. I mean really, we’re just going to leave it at three tiny lines changing from grey to orange?
So what are our options?
Like most people, we used our software deployment tool to patch it initially. In our case it was Munki. So the process was we merge in the update into our Munki repo, roughly an hour rolls by and Managed Software Center pops up and asks the user to quit Chrome to update it. All done, right?
Well, not quite. We noticed high numbers of pending updates sitting there in Sal. So your intrepid author took a trip to the help desk to have a listen in on some of the users.
Turns out people are really protective about their tabs. It’s the modern equivalent of not wanting to restart because they will “lose their windows”.
If a user by some random chance find their way to finding Google’s built in update button, Chrome will restart gracefully and preserve their tabs. So we set about working out how we could do this ourselves.
Won’t someone just think of the tabs?
chrome://restart has been around for a while, but for obvious reasons doesn’t work anywhere outside of typing it into Chrome’s location bar, which isn’t exactly user friendly.
After various attempts to trigger this Mike Lynn mentioned on MacAdmins Slack that they had found a way to do it - and it wasn’t pretty, but it worked.
It involved (shudder) AppleScript.
So, we had a method to restart Chrome and keep our user’s tabs safe. We just needed a method to let our users know about it.
Which version is running?
When Chrome’s auto update runs, they actually replace the app bundle from underneath the user. It took me a while (and some help on Slack from Tim Sutton) to work out what was going on. Google places a copy of the binary that is named the same as the version. This means that we can have multiple copies of the app in the same place.
Now to work out if there is an update to install, we simply need to read the Info.plist in the app bundle (the version that should be there) and compare it with the version that is actually running. If the version in the Info.plist is newer than the version running, the user has an update to perform.
I’m a big fan of Shea Craig’s Yo. We have our own version that we have branded with our company logo so users know the notification is coming from us - we’ve used it in the past to let users know our anti-malware tool has cleaned something up, or that they are going to need to update their operating system soon. It’s a nice way of giving the user information without getting in their face.
I have packaged up a generalized version of the script and put it on Github. This script will also only notify the user once for each version so we don’t spam them.
I’m sure everyone who didn’t have an MDM a few weeks ago is scrambling to get one set up - I’m not going to go into anything about MDM, since it really isn’t that interesting. They install profiles and packages - all very unexciting.
This article will take you through some of the decisions we made when developing our DEP enrollment package.
If you are of the open source management tool persuasion, chances are that like me, you are very happy with what you have already and see MDM merely as a method for delivering those tools. Before we considered MDM, our deployment workflow was essentially:
As part of that configuration, Puppet installs Munki
Munki installs the software
So on the face of it, it looked pretty simple for us to use our existing Plan B package with InstallApplication via an MDM.
DEPNotify is a great tool by Joel Rennich - you can pass in various commands and it will let your users know what is going on. So we would open up DEPNotify and then kick off our Plan B installation. Which could sit there for 10 minutes without letting the user know what was going on other than “something is happening”. Whilst this obviously wasn’t a great experience for our users, it got the job done.
Rather than make our users sit there and twiddle their thumbs whilst their computer sorted it’s life out, stopped and though about what our users needed to do first off. From our perspective, we really wanted the computer encrypyed before they did anything, and we needed them to get going with our SSO solution and change their password, set up 2FA etc. So this boiled down to two basic requirements:
Install Chrome - this is where the majority of ‘IT Time’ is spent during onboarding, so there was no need to wait for Munki to finally put it there.
Install and configure Crypt - let’s get the disruptive logout out of the way and let the user use their computer undisturbed.
In macOS 10.13 High Sierra, Apple is introducing Secure Kernel Extension Loading (or SKEL for short). This takes the changes introduced with SIP (requiring all KEXTs to be signed) one step further to requiring the user to enable them manually. Whilst this is great for home users, this absolutely sucks for those of us who manage macOS in the enterprise (who need things like VPN clients and anti malware tools running).
What can we do?
Obviously the long term future for managing macOS is MDM only, and Apple has taken the first step of adding an MDM only management feature for macOS - if the Mac is enrolled in an MDM, SKEL will be disabled, with the promise of more fine grained control in the future. But what if you don’t have an MDM yet?
Apple has added functionality to spctl to allow you to manage this. At it’s most basic you can turn the functionality on or off - this probably isn’t what you really want to do. What most people will want to do is to whitelist the extensions they actually use.
Get the IDs
The first thing you will want to do is to get a clean install of High Sierra (not an upgrade) and install the KEXTs you need. Click ok on the prompt telling you the world will fall over if you enable it and then ignore that and head over to System Preferences -> Security and click on the Allow button.
Once all of your KEXTs are loaded, fire up Terminal and open up the database that actually stores all of this information.
Your button clicking will result in a Team ID being whitelisted in the kext_policy table. Let’s have a look in there:
You will see the Team ID, the bundle ID for each individual extension and the display name of the developer. Note down the Team ID (the first item) - you will need all the IDs for the extensions you wish to whitelist.
Apple has allowed us to interact with this database only when booted from Recovery HD - or a Recovery-like operating system - of which a NetInstall is one. If you are using either the default NBI from Imagr or one created with NBICreator, you will be running a NetInstall. This means you can script the addition of these Team IDs during your provisioning workflow.
Create a script like the following, adding a line for each of the Team IDs you want to whitelist.
And if you are using Imagr, you will want to add a component like the following - note that it has had the first_boot option set to false - we need this to run during the NetInstall session.
What can be better here?
Obviously this is a short term fix. We all need to get an MDM up and running to manage this in the future, I fully expect this functionality to disappear in the future release. And we can obviously only manage this during deployment time. This workflow wouldn’t work if we had DEP workflow, or if we needed to deploy a new kernel extension to the existing fleet (retrieving and NetBooting several thousand laptops isn’t practical for anybody). BUT Apple hasn’t yet provided any mechanism to manage these via MDM other than completely turning the feature off, so we this is as good as we can do right now.
And one last thing - this whitelist is stored in NVRAM. If resetting the PRAM / NVRAM is part of your standard troubleshooting runbook, you should delete that part now.
It’s probably no secret that we use Puppet to configure our macOS fleet at Airbnb. And whilst we have given several talks about how we use Puppet on macOS, there was still a lot of hand waving about how we had our modules set up.
So, after several months of me saying “I should open source that”, here is our first open source Puppet module: puppet-munki.
What does it do?
As you may have guessed, it is a Puppet module that installs and configures Munki. More specifically:
It will install your specified version of Munki, making sure all of its services are loaded (so no need to reboot when upgrading!)
It will generate and install a configuration profile that covers all (probably!) of Munki’s preferences
Supports local only manifests so managed installs and uninstalls can be specified with the module
Is fully configurable with Hiera, so configuration can be specified as generally or as granularly as you need
Supports an optional background auto run after Munki has been installed, allowing Munki to get to work installing things whilst your initial Puppet run continues
Will repair (re-install Munki and the profile) if Munki hasn’t run successfully after a configurable number of days
Includes some Facts about the Munki version installed, the packages installed and a function to determine whether Munki has installed an item (useful if you are installing a package with Munki but need to perform additional configuration with Puppet)
Is something broken? Want to help make this better?
Whilst this module has been in development here for several months, there is always room for improvement. Please file issues and pull requests if you have any suggestions or problems.
This isn’t the only module we have planned for release, so watch this space.