Getting Firefox/Chrome to use system-wide certificates

While browsing the web, I was getting quite annoyed that firefox, chrome and console tools (wget, ...) each maintained a separate list of trusted certificates.

Which led me to dive into the arcane world of NSS, the "Network Security Services".

Note

This is a rather long post, which will walk you through setting up a single, user-wide certificate database.

Default behavior

A short overview of the NSS/Firefox/Chrome/ca-certificates world:

  • Most Linux distribution provide a system-wide trusted certificate store in /etc/ssl/certs, also available as a concatenated trust list at /etc/ssl/certs/ca-certificates.crt.
  • The Mozilla-maintained NSS library can store a list of trusted certificates, with various levels of (dis)trust, in a SQLite database (recent versions)
  • Mozilla programs use the NSS library, and store its database in the profile directory (see ~/.mozilla/firefox/XXXXXXX.default/)
  • Chrome uses a user-wide NSS store at the standard location of ~/.pki/nssdb

All this means that:

  • Certificates added to the system-wide store by administrators must be added again in each browser
  • Firefox and Chrome don't share their database: each certificate must be added to both browsers

Using the system NSS database

Reading through Mozilla website, I discovered how to force Chrome/Firefox to use the system-wide NSS store at /etc/pki/nssdb: https://wiki.mozilla.org/NSS_Shared_DB_Howto

Note

If you use both Chrome and Firefox and wish to merge the certificate datastore of both browsers, please follow the instructions in the above page.

The process is the following:

  • First, we'll force Firefox to upgrade its database:
    • Type the following at a shell prompt: export NSS_DEFAULT_DB_TYPE=sql
    • Run Firefox
    • Add the above command to your .profile file — or in the system-wide environment defaults of your distribution
  • Now, copy the certificate database to your user-wide store:
    • mkdir -p ~/.pki/nssdb
    • cp ~/.mozilla/firefox/*.default/{key4.db,cert9.db,pkcs11.txt} ~/.pki/nssdb
  • We can now force Firefox to use the system-wide store. This is controlled in the .mozilla/firefox/XXXXXXXX.default/pkcs11.txt file. Edit it with your favorite editor to apply the following diff (the important part is the library= change and the Flags=moduleDBOnly,... addition):
--- .pki/nssdb/pkcs11.txt.orig   2012-08-14 00:09:48.257725993 +0200
+++ .pki/nssdb/pkcs11.txt        2012-08-13 23:48:41.620938375 +0200
@@ -1,5 +1,5 @@
-library=
+library=libnsssysinit.so
 name=NSS Internal PKCS #11 Module
 parameters=configdir='sql:/home/xelnor/test_pki/nssdb' certPrefix='' keyPrefix='' secmod='secmod.db' flags= updatedir='' updateCertPrefix='' updateKeyPrefix='' updateid='' updateTokenDescription=''
-NSS=Flags=internal,critical trustOrder=75 cipherOrder=100 slotParams=(1={slotFlags=[RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512] askpw=any timeout=30})
+NSS=Flags=moduleDBOnly,internal,critical trustOrder=75 cipherOrder=100 slotParams=(1={slotFlags=[RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512] askpw=any timeout=30})

Filling the system database

Great, Firefox and Chrome now share the same NSS database, and will also read certificate from the system datastore.

Now, let's create, fill and update that database.

Creation

We only need a couple of commands (as root):

# Create the folder storing the database
$ mkdir -p /etc/pki/nssdb
# Create the database
$ certutil -N -d sql:/etc/pki/nssdb
# Make sure everyone can read it
$ chmod -R a+r /etc/pki/

Filling

We'll now have to walk through the list of system-wide certificates and add them to our system-wide NSS database.

Most distributions provide a /etc/ca-certificates.conf configuration file, holding the list of certificates to trust and to distrust (lines starting with a !).

This can be in two simple shell loops (there is also a better script on GitHub): :

# Insert trusted certificates
$ egrep -v '^(#|!|$)' /etc/ca-certificates.conf | while read cert; do \
    certutil -A -d sql:/etc/pki/nssdb -n $(basename $cert) -t C,C,C -i /usr/share/ca-certificates/$cert; \
  done;
#
# Insert distrusted certificates
$ egrep '^!' /etc/ca-certificates.conf | while read cert; do \
    certutil -A -d sql:/etc/pki/nssdb -n $(basename $cert) -t p,p,p -i /usr/share/ca-certificates/$cert; \
  done

The -n option provides a nickname for the certificate, while -i indicates the path to the PEM file.

The -t C,C,C part means that the certificate should be trusted for signing server certificates, whereas -t p,p,p explicitly distrusts the certificate.

Updating

Now that the database has been filled, we want to keep it updated whenever new certificates are added or removed.

This will be done through the ca-certificates update hooks, located under /etc/ca-certificates/update.d. When /etc/ca-certificates.conf is updated, each script in that folder is executed, with its input containing updated certificate paths. Lines for new (trusted) certificates begin with a +, lines for removed certificates with a -.

A sample script for this is available on the GitHub NSS-SystemCerts repository.

Issues

The system is now working in a satisfactory manner. I still find a couple of issues with the current behaviour:

  • There is no way to provide NSS with a per-user config template (either enabling the system-wide and user-wide databases, or using different arguments)
  • There is no way to use both the system-wide database (along with the user-wide version) and an application-specific database: updating the library= line in a pkcs11.txt file will disable this database and force reading /etc/pki/nssdb and ~/.pki/nssdb