OS X : deploying Firefox in an enterprise environment

how-to

Mozilla : doing good is part of our code

A few months ago, I found myself fighting against Firefox, my web browser of choice since 2006. It was a long and painful journey through outdated doc, web pages, how-tos, tests and all. Eventually, I succeeded and the journey ended with a ready-to-deploy package. Hurray !

This was almost 4 years ago, with Firefox 5. We are now at Firefox 38 ! And you know what ? It seems like people at Mozilla are now suffering from the same disease as people at Adobe. Yup, Mozillians now also do their own stuff in their cave, and I even suspect them to harbour some grudge about sysadmins.

I could have whined about a lot of things that I just don't understand about Mozilla :

  • Thunderbird let down,
  • Hidding the protocol in the URL bar in Firefox,
  • Including DRMs in Firefox,
  • The fact that Firefox kinda lost its innovative momentum in favor of a Google's Chrome photocopier,
  • FirefoxOS,
  • Or even the recent decision to include Pocket, a centralized and proprietary service (have you Mozillians read your Manifesto ?)

But that would be too easy ! Remember : I'm french, so whining is written in my DNA :o) I'm rather going to focus on getting things done.

So, yesterday I decided that it would be nice for my users to enjoy the brand new Firefox 38.0.1. I downloaded this new baby, mounted the .dmg, copied the .app and launched my little script that allows me to prepare the app for deployment. It's a pretty simple script : it copies a few files to some specific locations and changes a few permissions, nothing fancy. It worked pretty well since june 2011.

I had to update the script with the release of Firefox 34 because the devs suddenly decided that putting the autoconfig.js file in Firefox.app/Contents/Resources/defaults/prefs would be a better idea than in Firefox.app/Contents/MacOS/defaults/prefs. Of course, there is no doc about it, so people like me have to guess it.

I've also discovered that Firefox can load the autoconfig.js file from another directory : Firefox.app/Contents/Resources/browser/defaults/preferences. Since it's (probably ?) going to be the official-supported-standard-way in the future (See this page), and since it's already working, we'll stick to that one.

Furthermore, with Firefox 38 (or maybe sooner), they also decided that the .cfg file should now be stored in Firefox.app/Contents/Resources rather than in Firefox.app/Contents/MacOS where it used to be. Of course, the doc doesn't help here and still points to the old paths. I wouldn't be surprised to see this change again in several months. Da!n Adobe disease !

So yeah, it was time to write something about all that stuff. I'll try to keep it up-to-date... or not (Mozilla has got some surprise for us, sysadmins :D )...

Prerequisites

Download Firefox from the official channel. Mount the DMG file and copy/paste Firefox.app on your Desktop or wherever you want.

Also download the XPI files for the extensions you want to bundle. That's all, you're done.

Customizing settings

Let's start with the easy part ! Firefox comes with its own settings management system. There is no .plist file in the standard path for you to play with. There will be no .mobileconfig file to send to your clients. So, it's a bit more painful, but we still can do things.

Okay, let's start by creating a new file called autoconfig.js in Firefox.app/Contents/Resources/browser/defaults/preferences :

pref("general.config.filename", "mozilla.cfg");
pref("general.config.obscure_value", 0);

You may need to create the directories if they don't exist.

This basically tells Firefox that you want it to load another set of preferences from the mozilla.cfg file. You can name the file differently if you want, that's not a problem (mine is called meyeretpartenaires.cfg). Just remember to put the good name in the autoconfig.js file.

Firefox comes with 4 directives that allow us to set values for preferences :

  • defaultPref : sets a preference to a specific value, and allow the user to change it ;
  • pref : sets a preference to a specific value each time the user opens a new session. If the user changes the value, it will be erased on restart ;
  • lockPref : locks a preference to a specific value (user can't change it) ;
  • clearPref : blanks a preference.

Create the mozilla.cfg file in Firefox.app/Contents/Resources and put your directives in it. Here is mine :

// Disable autoupdate :
lockPref("browser.search.update", false);
lockPref("app.update.enabled", false);
lockPref("app.update.auto", false);
lockPref("app.update.mode", 0);
lockPref("app.update.service.enabled", false);

// Disable plugins check :
lockPref("plugins.hide_infobar_for_outdated_plugin", true);
clearPref("plugins.update.url");

// Disable telemetry and health report :
lockPref("toolkit.telemetry.enabled", false);
lockPref("toolkit.telemetry.prompted", 2);
lockPref("toolkit.telemetry.rejected", true);
lockPref("datareporting.healthreport.service.enabled", false);

// Disable the 'Know your rights' thing :
lockPref("browser.rights.3.shown", true);

// Disable the 'What's new' thing :
lockPref("browser.startup.homepage_override.mstone", "ignore");

// Do not hide 'http://' :
lockPref("browser.urlbar.trimURLs", false);

// Disable Pocket (but let the user enable it) :
defaultPref("browser.pocket.enabled", false);

// Set the default cookies policy to something less intrusive (but let the user change it) :
defaultPref("network.cookie.cookieBehavior", 3);

Just have a look at those pages to get a list of available preferences and build your own set of prefs.

Done ? Great ! You should know that any syntax error will prevent Firefox from starting. So you'd better check it and test it before you deploy it.

Integrating add-ons

This is where the fun begins. Firefox has different kind of add-ons, different locations to store them, different ways to load them and...almost no doc about all that stuff.

This how-to will only cover one case : include some add-ons, ensure they are enabled and prevent users from removing them.

The easy way : distribution/bundles

This first method will ONLY WORK if the add-on you want to include meet the followings :

  • It isn't a restartless add-on,
  • It isn't a SDK add-on,
  • It doesn't depend on AddonManager API.

Please also note that the user won't be able to disable nor remove the addon. In fact, the user won't even see the add-on in about:addons. If the user installs the same add-on in a more recent version, Firefox will use the most recent one (that would be the user's).

When ditributing an add-on using this method, Firefox loads it in a special way, so it might not work. If it doesn't, jump to the second method.

Ready ? First, rename the .xpi file into a .zip one. Extract it wherever you want.

Open the install.rdf file contained in the directory you just extracted and find the <em:id> entry. The id can be either a GUID or a string formatted like so : extensionname@example.org. You can even have both of them. Copy the id and close the file. Keep the extracted files.

Create the following directory : Firefox.app/Contents/Resources/distribution/bundles/id-of-the-add-on

If your add-on id is <em:id>{daf44bf7-a45e-4450-979c-91cf07434c3d}</em:id>, you would :

mkdir -p Firefox.app/Contents/Resources/distribution/bundles/{daf44bf7-a45e-4450-979c-91cf07434c3d}

If your add-on id is <em:id>extensionname@example.org</em:id>, you would :

mkdir -p Firefox.app/Contents/Resources/distribution/bundles/extensionname@example.org

Allright ? Now just put the previously extracted files in that directory and voilà !

The not-so-easy way : browser/extensions

This second method is basically the same as the previous one, except that :

  • It (should) works with all kind of add-ons,
  • User will be able to disable the add-on,
  • The add-on is disabled by default.

All you have to do is follow the previous method, but put the files in Firefox.app/Contents/Resources/**browser/extensions**/id-of-the-add-on instead of Firefox.app/Contents/Resources/**distribution/bundles**/id-of-the-add-on.

It's a little bit more complicated to enable the add-on. Before dealing with that particular matter, there are a few things that you have to understand.

First, Firefox can load add-ons from 4 locations (called scopes). You should have a look at the doc to understand what they are : you will need them.

Second, for obvious security reasons, Firefox comes with a mechanism that automatically disables add-ons that were not explicitly installed by the user. So if you just put an add-on in a scope, Firefox will include it, but it will also disable it.

Since this is our case, we'll have to disable this feature. This can be done by setting the preference extensions.autoDisableScopes to SCOPE_ALL - SCOPE_APPLICATION, which is 11 (basically, you tell Firefox not to auto-disable add-ons that are in SCOPE_APPLICATION). So, in your mozilla.cfg file, you would add :

lockPref("extensions.autoDisableScopes", 11);

There is another preference that you may need/look at. It's called extensions.enabledScopes and does just the opposite of extensions.autoDisableScopes.

As always, remember to test your customized Firefox.app before deploying it everywhere.

Further considerations

Caution : troll

Do you remember I told you Mozilla has a surprise for us ? Well, here it is : https://bugzilla.mozilla.org/show_bug.cgi?id=1144127

TL;DR : Starting with Firefox 40, support for distribution/bundles is dropped. This means that we'll have to either stick with the second method (and, remember, the user is then able to disable the included add-ons) or... well... find another way (which will probably consists in an awful hack).

But, you know what, maybe we should just stop deploying Firefox in enterprise. For a big player like Mozilla, it's a bit of a shame that they neither support MCX (for OSX) nor GPO (for Windows). It's a bit of a shame to see such a big player always disdain the enterprise/educational world.

Remember what Asa Dotzler wrote back in 2011 ? Here it is, just in case you don't :

Enterprise has never been (and I'll argue, shouldn't be) a focus of ours. Until we run out of people who don't have sysadmins and enterprise deployment teams looking out for them, I can't imagine why we'd focus at all on the kinds of environments you care so much about.

At least, we've been warned. And the solution might be way easier than we think.