OS X : deploying Firefox in an enterprise environment

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 with people at Adobe some kind of weird disease. 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.

La phrase (intelligente) du jour n°4

Cette loi est une altération intolérable de nos libertés individuelles. Il est effrayant que la société s'habitue à les négocier.

Luz, dessinateur pour Charlie Hebdo, à propos du Projet de Loi pour le Renseignement, 2015.

L'État de surveillance

Je suis sur écoute

L’État de police est celui dans lequel l'autorité administrative peut, d'une façon discrétionnaire et avec une liberté de décision plus ou moins complète, appliquer aux citoyens toutes les mesures dont elle juge utile de prendre par elle-même l’initiative, en vue de faire face aux circonstances et d’atteindre à chaque moment les fins qu’elle se propose : ce régime de police est fondé sur l’idée que la fin suffit à justifier les moyens. À L’État de police s’oppose l’État de droit...

Raymond Carré de Malberg, Contribution à la théorie générale de l'État, 1920

Ce 15 avril, 25 députés (sur 577) ont voté le Frenchioct-Act, la loi dite sur le Renseignement, mettant potentiellement un terme à l'État de droit en France. Tout, TOUT le trafic Internet passera désormais par des boîtes noires gouvernementales dont le fonctionnement précis est bien évidemment tenu secret. L'État s'octroie aussi le droit d'enregistrer et d'analyser les conversations téléphoniques, les SMS, etc.

Banksy - Dustbin Surveillance

Contrairement à ce que crient haut et fort MM. Valls et Cazeneuve, il s'agit sans nul doute de surveillance de masse : l'ensemble des communications seront analysées (par des algorithmes) afin, je cite, de détecter, par un traitement automatique, une succession suspecte de données de connexion. Bien entendu, la loi se garde bien de préciser ce qu'est une succession suspecte de données de connexion : chiffrer ses e-mails ? Consulter une carte de la Syrie sur Maps ? Se renseigner sur des techniques de hacking pour mettre son propre site web à l'épreuve ? Tout ceci nous amènera-t-il à être suspecté ?

Les méta-données de connexion seront aussi enregistrées par ces boîtes noires.... et conservées. Ah oui, mais ces méta-données sont anonymes ! Le ministre l'a certifié. Sauf que le but des méta-données est justement d'identifier les personnes et de dresser des cartes sociales (untel est entré en relation avec untel à x reprises, à telles dates, pendant tant de temps, par tels moyens). Quelle ironie.

On parle bien entendu de lutter contre le terrorisme, de défendre les intérêts essentiels économiques de la France, de sécurité nationale, etc. Malheureusement, et comme l'a notamment souligné Marc Trévidic, placée entre de mauvaises mains, cette loi permettra aussi de faire du renseignement social et ainsi de surveiller (au mieux) les opposants politiques et les militants (ONG, associations, syndicats, ...), instaurant ainsi un État policier. Rappelez-vous, grâce aux boîtes noires, l'État saura que votre ordinateur s'est connecté 5 fois cette semaine au site Jacky & Michelle. Il saura aussi que vous consultez régulièrement le site d'associations anti-nucléaire, par exemple. Et il saura aussi que vous vous cachez derrière Paul Bismuth ;) Il le saura, et finira, un jour ou l'autre, par l'utiliser. C'est dans ses intérêts.

Ayez confiaaanceee

Nous l'avons vu précédemment, le simple fait de se savoir surveillé pousse à ne plus agir de la même façon. Cela suffit à ne plus s'engager de la même façon. Cela suffit à ne plus penser de la même façon. Surveiller, c'est censurer à priori.

Comme le dit Glenn Greenwald :

Une société dans laquelle les gens peuvent être surveillés à tout moment pousse à la conformité, l’obéissance et la soumission.

La liste des opposants à cette loi est longue et franchement impressionnante. Certains mettent en avant les dangers pour la démocratie, d'autres pour la liberté d'expression et la liberté de la presse, d'autres pour la vie privée, d'autres pour l'économie... Tous ont raison.

Et pourtant, en France, ce 15 avril 2015, 572 hommes et femmes (25 ont voté pour, 547 n'étaient pas présents) qui savent tout mieux que tout le monde ont balayé toutes ces oppositions d'un revers de "fantasmes !", "Facebook et Google le font déjà !", "c'est pas de la surveillance de masse, d'abord !", sans oublier l'argument massue : "c'est pour lutter contre le terrorisme !".

Le pire dans tout ça reste sans doute que ces 572 gus se sentent tout à fait dans leur bon droit puisqu'ils ont été élus. Et bien sûr, aucun ne sera tenu pour responsable lorsque le système dérapera. C'est ça le système politique français de 2015.

Le salut viendra peut-être de l'association FDN, qui a courageusement déposé une question prioritaire de constitutionnalité. Mais j'ai peu d'espoir.

Dans tous les cas, vous le savez désormais : en France, l'État vous surveille.

Dans tous les cas, vous le savez désormais : en France, si vous ne pensez pas comme on vous a dit de le faire, vous êtes suspects.

Dans tous les cas, vous le savez désormais : en France, le terrorisme a gagné.

Street Art : MadC

MadC, de son vrai nom Claudia Walde est une street-artist allemande. Elle réalise en 2010 son désormais célèbre 700 Wall, une fresque de près de 700m2 qui la propulse sur le devant de la scène graffiti. Renommée internationalement pour ses énormes fresques (Jurassic Park Wall, 500 Wall, ...) qu'elle a l'habitude de réaliser seule, je lui préfère pourtant son travail sur les couleurs et la transparence, qu'elle maîtrise à la perfection.

Pour en savoir plus sur MadC : madc.tv

MadC - The Transparent Wall, 2010

MadC - The Neon Wall, 2011

MadC - Twenty Forty-Three (canvas)

MadC - Allemagne, 2012

MadC - Landsberg, 2015

OSX : Let's play with security

I have a few shellscripts that need username and passwords to accomplish some tasks. For example, I wrote a shellscript to backup some data in an encrypted sparsebundle file that is stored on an AFP share. So I have a username and a password to mount the share, and some others credentials to mount the sparsebundle.

When it comes to automating the backup process, I had to face a simple issue : where do I store these usernames and passwords ?

Hard-code them in the script ? Naaah.

Create some kind of read-only file with proper permissions ? Sounds rather risky.

So, what would be the right way to do this with OSX ?

The answer is kind of obvious : OSX ships with a nice tool called Keychain Access.app. This service is intended to store and retrieve credentials, keys, passwords, certificates,... in a secure fashion. But how can I access it from the shell ?

This is where security comes to the rescue ! security is a Command Line Interface that allows you to administer and manipulate keychains, keys and certificates. In other words, security allows you to access Keychain (the app) from the shell (local shell ! SSH ! shellscript !). Wonderful, isn't it ?

Although it comes with a manpage, let's see some examples.

Adding items to a keychain

First, let's add a generic password to the default keychain. Here is what I'm using to store the credentials for my sparsebundle file :

security add-generic-password -a "username" -D "Image Disk Password" -s "MyFile.sparsebundle" -w "THis_IS_so_s3crEt_!"

The -a option allows you to specify the account name. -D is for the description. -s allows you to specify the service name (here I chose to set it to the sparsebundle filename but you can put whatever you want). And -w allows you to specify the password.

Adding an Internet password is a little bit different :

security add-internet-password -a "username" -D "AFP Share Password" -s "myserver.example.com" -p "share" -r "afp " -l "My AFP Share" -w "THis_IS_4lso_very_s3crEt_!" /Library/Keychains/System.keychain

This time we had to specify the server with -s, a path (here, the name of the shared point) with -p, a label with -l and the protocol (could have been http, ftp, or whatever) with -r.

WARNING : the argument for the -r option MUST be exactly 4 characters long. So you have to write "afp ", not "afp".

Please also notice that we decided to add this entry to the System keychain which is located in /Library/Keychains/System.keychain. We could have chosen to add it to another existing keychain by providing the path to this keychain, or we could have created a brand new one with :

security create-keychain /path/to/my/new/Personnal.keychain

Retrieving items

Retrieving an item in a keychain is pretty easy. Here I choose to retrieve the item by service (remember the -s option we used when we created the entry ?) :

security find-generic-password -g -s "MyFile.sparsebundle"

Which outputs the following :

keychain: "/Users/francoiskubler/Library/Keychains/login.keychain"
class: "genp"
attributes:
    0x00000007 <blob>="MyFile.sparsebundle"
    0x00000008 <blob>=<NULL>
    "acct"<blob>="username"
    "cdat"<timedate>=0x32303134313232393130323735365A00  "20141229102756Z\000"
    "crtr"<uint32>=<NULL>
    "cusi"<sint32>=<NULL>
    "desc"<blob>="Image Disk Password"
    "gena"<blob>=<NULL>
    "icmt"<blob>=<NULL>
    "invi"<sint32>=<NULL>
    "mdat"<timedate>=0x32303134313232393130323735365A00  "20141229102756Z\000"
    "nega"<sint32>=<NULL>
    "prot"<blob>=<NULL>
    "scrp"<sint32>=<NULL>
    "svce"<blob>="MyFile.sparsebundle"
    "type"<uint32>=<NULL>
password: "THis_IS_so_s3crEt_!"

But I could have chosen to retrieve it by account name (remember the -a option ?) :

security find-generic-password -g -a "username"

Or by account name AND service AND description in a specific keychain file :

security find-generic-password -g -a "username" -s "MyFile.sparsebundle" -D "Image Disk Password" /path/to/my/new/Personnal.keychain

If you have more than one entry that matches your criteria, security will only output the first one that matches. So, be careful when you add a new entry in a keychain, and make sure you'll be able to retrieve it easily later by specifying a different label (-l option) or a comment (-j option) or whatever you prefer.

Parsing the output

Obviously, the output of the command is kind of useless and needs to be parsed. On OSX prior to 10.9, when retrieving a password with the -g option (which asks security to print the password), the password was printed on stderr, so you had to play with shell redirections and write something like the following :

my_precious=$(security 2>&1 > /dev/null find-generic-password -g -s "MyFile.sparsebundle" | cut -d "\"" -f 2)

Starting with OSX 10.9, you can drop the -g flag and use -w instead :

my_precious=$(security find-generic-password -w -s "MyFile.sparsebundle")

The cool part here is that you can retrieve more than just the password. You can also retrieve the username (tested on OSX >= 10.6) :

output=$(security 2>&1 find-internet-password -s "myserver.example.com" -r "afp " -g)
username=$(head -n 7 <<< "$output" | tail -n 1 | cut -d "\"" -f 4)
my_precious=$(head -n 1 <<< "$output" | cut -d "\"" -f 2)

Of course, depending on your needs, you'll have to modify the commands, but you should now get the idea. The most important part here is to remember that when called with the -g option, security will print the password to stderr instead of stdout.

More : Certificates, Keys, ...

There are a bunch of option that allows you to manage certificates, keys and identities (certificate + private key). I've never had to deal with that stuff so I can't provide any decent example. But they do exist and a quick look at the manpages should be enough to help you.

#JeSuisCharlie

Les évènements de cette semaine montrent à quel point nous avons échoué en tant que société.

← Billets de l'année 2014 - 2015