Managing Firefox with Nix home-manager

Tags:  CCE Project

This noweb document constructs a customized Firefox installation:

  • I deploy firefox with settings in both about:config below and enterprise policies further down.
  • userChrome.css and userContent.css loaded from A Custom Firefox User Chrome
  • Extensions are defined below and bundled in to my Nix deployments
programs.firefox = {
  enable = true;
  package = myFirefox;                                          # (ref:myFirefox)
  profiles.default = {
    name = "default";
    isDefault = true;
    settings = {
      "svg.context-properties.content.enabled" = true;          # (ref:svgContextProperties)

      "privacy.trackingprotection.enabled" = true;
      "privacy.donottrackheader.enabled" = true;

      "reader.color_scheme" = "dark";

      "browser.urlbar.placeholderName" = "DuckDuckGo";
      "browser.urlbar.placeholderName.Private" = "DuckDuckGo";

      "layout.css.devPixelsPerPx" =                             # (ref:hidpi)
        "1.5";
      # (if config.hardware.video.hidpi.enable == true then
      #   "1.5"
      # else
      #   "1.0");

      "browser.aboutConfig.showWarning" = false;
      "browser.sessionstore.warnOnQuit" = true;

      "toolkit.legacyUserProfileCustomizations.stylesheets" = true; # for userchrome and usercontent
    };

    userChrome = builtins.readFile ../userChrome.css;
    userContent = builtins.readFile ../userContent.css;
  };
};

hhat bit at the end uses these which are ripped more or less verbatim from home-manager's firefox module but i had to fudge the paths; i guess this is pretty ugly. if i can get an extraPrefix in to the main call of pkgs.buildEnv of those extensions' packages, i guess i could make it work without copy/pasteing code out? I do this instead of using programs.firefox.extensions

(svgContextProperties) is used by the simple tab groups addon

NEXT it would be great if (hidpi) could use config.hardware.video.hidpi.enable in My NixOS configuration.

Disabling Policies with wrapped Firefox

per the docs, we can distribute a Firefox with enterprise policies enabled; you can enable extensions here, but I enable them in my user profile instead up above. This is the (myFirefox) referenced in the above code, included in the let block of home-manager:

myFirefox = pkgs.wrapFirefox pkgs.firefox-unwrapped {
  # nixExtensions = pkgs.callPackage ./firefox {}; # (ref:extensionsImport)
  extraNativeMessagingHosts = [
    pkgs.plasma5Packages.plasma-browser-integration
    pkgs.browserpass
  ];
  extraPolicies = {
    DisableFirefoxStudies = true;
    DisablePocket = true;
    DisableTelemetry = true;
    FirefoxHome = {
      Pocket = false;
      Snippets = false;
    };
    UserMessaging = {
      ExtensionRecommendations = false;
      SkipOnboarding = true;
    };
    OfferToSaveLoginsDefault = false;
    # Handlers can be set here for org-protocol
    # https://github.com/mozilla/policy-templates/blob/master/README.md#Handlers
  };
};

NEXT configure org-protocol in here?

A Custom Firefox User Chrome make sure to tangle these when they're changed!

(org-babel-tangle-file "./firefox_user_chrome.org")

Theoretically-Safe Firefox Extensions with Nix

I have this disabled right now ... it's likely easier to just use Firefox Sync for this since the XPIs are just being downloaded

So, Home Manager has the ability to manager a Firefox profile and in doing so, it can side-load extensions which are compiled and managed by Nix. Very nice.

It's recommended in the home-configuration.nix manpage to pull firefox addons out of Nix User Repositories (external: NUR) but i'm not sure i'm comfortable running NUR as an integrated part of my system, I only want to use the minimal set of it. So my understanding of NUR's support for Firefox addons looks like this: addons.json provide an accessible format for machines to generate a generated-firefox-addons.nix with a script like this generate-firefox-addons.sh which also makes a nixlib/firefox/default.nix for importing at (extensionsImport).

nix run -f "https://github.com/nix-community/NUR/archive/master.tar.gz" repos.rycee.firefox-addons-generator \
    --arg pkgs 'import <nixpkgs> {}' \
    -c nixpkgs-firefox-addons addons.json ./generated.nix

Regularly running shell:pushd nixlib/firefox && ./generate-firefox-addons.sh & is "praxis".

default.nix looks like:

# Derived from: https://github.com/nix-community/nur-combined/blob/master/repos/ijohanne/pkgs/firefox-plugins/default.nix
{ pkgs ? import <nixpkgs> {} }:
let
  buildFirefoxXpiAddon = { pname, url, sha256, meta, ... }: let
    newMeta = if meta ? license
              then meta
              else meta // { license = pkgs.lib.licenses.unfree; };
  in
    (pkgs.fetchFirefoxAddon { inherit url sha256; name = pname; }) // { meta = newMeta;} ;
  generated = pkgs.callPackage ./generated.nix { inherit buildFirefoxXpiAddon; };
in
pkgs.lib.mapAttrsToList
  (name: value: value) 
  (pkgs.lib.filterAttrs                                  # (ref:firefox-filterAttrs)
    (name: value: (name != "override") &&
                  (name != "overrideDerivation") &&
                  (name != "recurseForDerivations"))
    (generated))

The most complicated part of this is the map and filter functions in (firefox-filterAttrs) above: programs.firefox.extensions wants a list, the generated addons module returns a set with some overrides attached to it. I just want this to rip 'em out and map it to a list.

And here's an addons.json specifying some of my favorite slugs:

[
  {"slug": "awesome-rss"},
  {"slug": "better-timing-for-no-race"},
  {"slug": "browserpass-ce"},
  {"slug": "clearurls"},
  {"slug": "darkreader"},
  {"slug": "decentraleyes"},
  {"slug": "duckduckgo-for-firefox"},
  {"slug": "facebook-container"},
  {"slug": "https-everywhere"},
  {"slug": "laboratory-by-mozilla"},
  {"slug": "leechblock-ng"},
  {"slug": "multi-account-containers"},
  {"slug": "plasma-integration"},
  {"slug": "privacy-badger17"},
  {"slug": "simple-tab-groups"},
  {"slug": "smart-amazon-smile"},
  {"slug": "styl-us"},
  {"slug": "tab-unload-for-tree-style-tab"},
  {"slug": "tampermonkey"},
  {"slug": "tree-style-tab"},
  {"slug": "twemex-sidebar-for-twitter"},
  {"slug": "ublock-origin"},
  {"slug": "wallabagger"},
  {"slug": "web-scrobbler"},
  {"slug": "yet-another-hints-extension"},
  {"slug": "youtube-feeds"}
]

This still fetches and repackages the XPIs! I'd like to build these more and more from source … but they won't auto-update to something that'll super-own me for now. See An Undeveloped Thought on Web Extensions and the untenable position of powerful user agents below…

NEXT Add changelogs or git commits for these extensions to Universal Aggregator Software Release Feeds

An Undeveloped Thought on Web Extensions and the untenable position of powerful user agents

With regards to the security model of web extensions:

I really don't think the browser extension model is great, but I think user-agents are powerful and powerful user agents are good. There has to be a balance. It's clear that the cloud-services-as-extension model like Grammarly or even AdBlock is ripe for abuse, or change of terms, and the nature of the ecosystem is that when open source developers burn out they can turn around and sell their module to a firm with no trust, and plenty of things to scrutinize.

When subscribing to the changelogs, I see limited authorship information as well as commit patterns: if the module suddenly changes authorship or the commits change from being a very verbose style to only dumping version updates, I need to ask why before it's too late.

The NUR stuff is all based around some automation in that repo which is just managed ad-hoc by a bunch of folks as their personal playgrounds, but I think I can co-opt that and define my own extension set using the same pattern. If I developed that way, this would just be a readme in a forked NUR repo, but here we are, I want to speak through the code, sorry.

By only managing and installing the core open source extensions I rely on, maybe I can keep myself from getting Super Owned by some extension getting sold to some amoral surveillance apparatus, while managing the risk of security issues injected by the relatively lax NUR security model. The extensions I use largely don't interact with cloud services or even update very often themselves, and it's as easy to track the commit feeds or changelog for them in my News feed pipeline.


This is Referenced

''

in "The Complete Computing Environment"

in "Generate a Dynamic Home Manager Configuration"