NixOS Firefox configuration with policies, preferences, extensions, search engines and cookie exceptions

As this is pretty hard to find and involves quite some guesswork, I want to share my nix Firefox configuration for you all.

Basically what you need to do is translate Firefox JSON into Nix, which is weird and not really documented. The most important quirks:

  • Numbers have to be set as numbers, not as strings.
    • For example, setting "widget.use-xdg-desktop-portal.file-picker" = "1"; resulted in “2” being set and not changeable. No idea why. Not putting the number in quotes solved it.
  • There is no comment support (nix bypasses that!)
  • There is a blocklist or an allowlist (unsure) of preferences that can be set in a policy. So I dont use this at all, and instead use the feature to set preferences directly (does this use autoconfig?)

The config contains most of the generally usable settings from arkenfox user.js, as well as preferences from the documentation

{ config, pkgs, ... }:

let
  # Check about:support for extension/add-on ID strings.
  extensions = [
    "uBlock0@raymondhill.net"                   # uBlock Origin
    "plasma-browser-integration@kde.org"        # Plasma browser integration
    "keepassxc-browser@keepassxc.org"           # KeePassXC Integration
    "{b11bea1f-a888-4332-8d8a-cec2be7d24b9}"    # Snowflake
    #"firefox@tampermonkey.net"                 # TamperMonkey
    "{531906d3-e22f-4a6c-a102-8057b88a1a63}"    # SingleFile
    "jid1-QoFqdK4qzUfGWQ@jetpack"               # Dark Background and Light Text
  ];
in
{
  programs.firefox = {
    enable = true;
    languagePacks = [ "en-US" "de" "en-GB" ];

    # Check about:policies#documentation for options.
    policies = {


        #### DEBLOAT ###
        DisableFirefoxStudies = true;
        #DisableFirefoxScreenshots = true;
        DontCheckDefaultBrowser = true;
        UserMessaging = {
            ExtensionRecommendations = false;
            UrlbarInterventions = false;
            SkipOnboarding = true;
            MoreFromMozilla = false;
            FirefoxLabs = true;
        };
        FirefoxSuggest = {
            WebSuggestions = false;
            SponsoredSuggestions = false;
            ImproveSuggest = false;
            Locked = true;
        };

        #### SECURITY ###
        AutofillAddressEnabled = false;
        AutofillCreditCardEnabled = false;
        HttpsOnlyMode = "force_enabled";
        SSLVersionMin = "tls1.2";
        PostQuantumKeyAgreementEnabled = true;
        HttpAllowlist = [
            "http://localhost"
            "http://127.0.0.1"
        ];

        #### PRIVACY ###
        DisableTelemetry = true;
        EnableTrackingProtection = {
            Value = true;
            Locked = true;
            Cryptomining = true;
            Fingerprinting = true;
            Exceptions = [
                "https://netflix.com"
                "https://amazon.de"
                "https://spotify.com"
            ];
        };
        DisablePocket = true;
        NetworkPrediction = false;

        # Delete data on shutdown
        SanitizeOnShutdown = {
            Cache = true;
            FormData = true;
            SiteSettings = true;
            OfflineApps = true;
        };

        Cookies = {
            Allow = [
                "https://github.com"
                "https://gitlab.com"
                "https://codeberg.org"
                "https://sr.ht"
                "http://127.0.0.1"
                "https://127.0.0.1"
                "http://localhost"
                "https://localhost"
                "https://192.168.1.1"

                # specific sites
                "..."
            ];
        };

      SearchEngines = {
        Remove = [
            "eBay"
            "Google"
            "Bing"
            "Ecosia"
            "Wikipedia"
            "Perplexity"
        ];
        Add = [
            {
                "Name" = "Brave Search";
                "URLTemplate" = "https://search.brave.com/search?q={searchTerms}&summary=0";
                "IconURL" = "https://cdn.search.brave.com/serp/v1/static/brand/eebf5f2ce06b0b0ee6bbd72d7e18621d4618b9663471d42463c692d019068072-brave-lion-favicon.png";
                "Alias" = "brave";
            }
            {
                "Name" = "DuckDuckGo";
                "URLTemplate" = "https://duckduckgo.com/?q={searchTerms}&ia=web&assist=false";
                "IconURL" = "https://duckduckgo.com/favicon.ico";
                "Alias" = "ddg";
                "Description" = "Duckduckgo without AI integrations";
            }
            {
                "Name" = "OpenStreetMap";
                "URLTemplate" = "https://www.openstreetmap.org/search?query={searchTerms}";
                "IconURL" = "https://www.openstreetmap.org/favicon.ico";
                "Alias" = "osm";
            }
            {
                "Name" = "Wikipedia";
                "URLTemplate" = "https://en.wikipedia.org/wiki/Special:Search?go=Go&search={searchTerms}";
                "IconURL" = "https://en.wikipedia.org/favicon.ico";
                "Alias" = "wiki";
            }
        ];
        Default = "DuckDuckGo";
      };
      SearchSuggestEnabled = false;

      ExtensionSettings = builtins.listToAttrs (builtins.map (id: {
        name = id;
        value = {
          install_url = "https://addons.mozilla.org/firefox/downloads/latest/${id}/latest.xpi";
          installation_mode = "force_installed";
        };
      }) extensions);
    };

    preferences = {

        #### FEATURES ###
        "layout.spellcheckDefault" = 1;
        # Use the systems native filechooser portal
        "widget.use-xdg-desktop-portal.file-picker" = 1;
        # allow adblockers to act everywhere. WARNING this is a security hole.
        "extensions.webextensions.restrictedDomains" = "";
        "media.webrtc.camera.allow-pipewire" = true;
        "browser.download.always_ask_before_handling_new_types" = true;


        #### DEBLOAT ###
        "browser.discovery.enabled" = false;
        "app.shield.optoutstudies.enabled" = false;
        "browser.topsites.contile.enabled" = false;
        "browser.urlbar.suggest.quicksuggest.sponsored" = false;
        "browser.urlbar.trending.featureGate" = false;
        "browser.newtabpage.activity-stream.feeds.section.topstories" = false;
        "browser.newtabpage.activity-stream.feeds.snippets" = false;
        "browser.newtabpage.activity-stream.section.highlights.includePocket" = false;
        "browser.newtabpage.activity-stream.section.highlights.includeBookmarks" = false;
        "browser.newtabpage.activity-stream.section.highlights.includeDownloads" = false;
        "browser.newtabpage.activity-stream.section.highlights.includeVisited" = false;
        "browser.newtabpage.activity-stream.showSponsored" = false;
        "browser.newtabpage.activity-stream.system.showSponsored" = false;
        "browser.newtabpage.activity-stream.showSponsoredTopSites" = false;
        # Privacy: Disable automatic opening in new windows (manually still works)
        # https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/9881
        "browser.link.open_newwindow" = 3;
        # Privacy: Set all window open modes to abide above method
        "browser.link.open_newwindow.restriction"= 0;

        #### PRIVACY ###
        "privacy.resistFingerprinting" = "true";
        # disable sending downloaded files to the internet
        "browser.safebrowsing.downloads.remote.enabled" = false;
        "network.dns.disablePrefetch" = false;
        # redundancy: disable network prefetching
        "network.predictor.enabled" = false;
        # disable preloading websites when hovering over links
        "network.http.speculative-parallel-limit" = 0;
        # disable connecting to bookmarks when hovering over them
        "browser.places.speculativeConnect.enabled" = "false";
        "privacy.globalprivacycontrol.enabled" = true;
        "privacy.clearOnShutdown_v2.cookiesAndStorage" = true;
        "privacy.fingerprintingProtection" = true;

        "browser.contentblocking.category" = "strict";
        "extensions.pocket.enabled" = false;
        "browser.search.suggest.enabled" = false;
        "browser.search.suggest.enabled.private" = false;
        "browser.urlbar.suggest.searches" = false;
        # store media in cache only on private browsing
        "browser.privatebrowsing.forceMediaMemoryCache" = true;
        "network.http.referer.XOriginTrimmingPolicy" = 2;
        # Privacy: Disable CSP reporting
        # https://bugzilla.mozilla.org/show_bug.cgi?id=1964249
        "security.csp.reporting.enabled" = false;

        #### SECURITY ###
        #"browser.formfill.enable" = false;
        "pdfjs.enableScripting" = false;
        #"signon.autofillForms" = false
        # UNCLEAR
        "signon.formlessCapture.enabled" = false;
        # prevent scripts from moving or resizing windows
        "dom.disable_window_move_resize" = true;
        # Security: Disable remote debugging feature
        # https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/16222
        "devtools.debugger.remote-enabled" = false;
        # Security: Restrict directories from which extensions can be loaded (Unclear)
        # https://archive.is/DYjAM
        "extensions.enabledScopes" = 5;

        #### SSL ###
        # Security: Require safe SSL negotiation to avoid potentially MITMed sites
        "security.ssl.require_safe_negotiation" = true;
        # Security: Disable TLS1.3 0-RTT as key encryption may not be forward secret
        # https://github.com/tlswg/tls13-spec/issues/1001
        "security.tls.enable_0rtt_data" = 2;
        # Security: Enable strict public key pinning, prevents some MITM attacks
        "security.cert_pinning.enforcement_level" = 2;
        # Security: Enable CRLite to ensure that revoked certificates are detected
        "security.pki.crlite_mode" = 2;
        # Security: Treat unsafe negotiation as broken
        # https://wiki.mozilla.org/Security:Renegotiation
        # https://bugzilla.mozilla.org/1353705
        "security.ssl.treat_unsafe_negotiation_as_broken" = true;
        #  Security: Display more information on Insecure Connection warning pages
        # Test: https://badssl.com
        "browser.xul.error_pages.expert_bad_cert" = true;
        };
    };
}

Other posts

Open question

  • policies allow setting any pref as user-changeable. Does Nix allow this too? Should I also just try nixifying the json syntax? (preferences docs)
3 Likes

:nerd_face: erm, i have many many options in my

programs.firefox.policies.Preferences

but none of the

apz.*
beacon.*
cookiebanners.*
devicesensors.*
devtools.*
experiments.*
findbar.*
identify.*
image.*
security.*
services.*
sidebar.*
webgl.*

have applied at all to about:config?

edit: ah, i cant read, there are only a few from your list, like devtools.* and security.* - do they really set for you..? i should really use the autoConfig for this, huh (which supposedly solves my problem). next thing i am looking for is a declarative userChrome.css, which i have seen someone do inside of autoConfig (?)

so, um, again, what is exactly different in

programs.firefox.preferences

that seem to have worked for you, completely ignoring the list of “allowed” preferences? thats not fair… :wilted_flower: but maybe it is because i am substituting these options for pkgs.librewolf by just simply changing

programs.firefox.package = pkgs.librewolf;

…which then wouldnt symlink, obviously, so i had to

environment.etc."firefox/policies/policies.json".target = "librewolf/policies/policies.json";

which is a mess and i am not sure if i still need it, and im afraid to do anything, otherwise all of my browser profiles will get wiped for the 9000th time… so, do these preferences only work for pkgs.firefox, then?

also, none of these seem to work as well

programs.firefox.languagePacks
programs.firefox.policies.RequestedLocales
programs.firefox.policies.Preferences."intl.accept_languages"
programs.firefox.policies.Preferences."intl.locale.requested"

which is, again, probably a pkgs.librewolf’s intentional prevention thing…

anyway, i like your little extensions simplification… mine has grown into a couple thousand lines… =)