From 367b0c114f38d5c332f5ee971ad13dd69e302dec Mon Sep 17 00:00:00 2001 From: tilpner Date: Mon, 15 Jun 2020 09:53:06 +0200 Subject: WIP towards module based configuration --- addons/default.nix | 90 +++++++ addons/noscript/config.json | 101 ++++++++ addons/qwantjunior/content-script.js | 34 +++ addons/qwantjunior/favicon.ico | Bin 0 -> 2285 bytes addons/qwantjunior/managed_storage.json | 20 ++ addons/qwantjunior/manifest.json | 40 +++ addons/ublock/config.json | 84 +++++++ default.nix | 120 ++------- firefox-configuration.nix | 59 +++++ nix/lib.nix | 307 +++++++++++++---------- nix/options.nix | 66 +++++ profiles/addons/default.nix | 90 ------- profiles/addons/noscript/config.json | 101 -------- profiles/addons/qwantjunior/content-script.js | 34 --- profiles/addons/qwantjunior/favicon.ico | Bin 2285 -> 0 bytes profiles/addons/qwantjunior/managed_storage.json | 20 -- profiles/addons/qwantjunior/manifest.json | 40 --- profiles/addons/ublock/config.json | 84 ------- profiles/defaults.nix | 2 +- profiles/disableAccounts.nix | 18 ++ profiles/disableClutter.nix | 21 ++ profiles/disableLocation.nix | 22 ++ profiles/disableMedia.nix | 30 +++ profiles/disableNormandy.nix | 23 ++ profiles/disablePasswordManager.nix | 37 +++ profiles/disablePocket.nix | 21 ++ profiles/disableStudies.nix | 25 ++ profiles/disableTunnels.nix | 27 ++ profiles/disableUpdates.nix | 44 ++++ profiles/distrustUser.nix | 2 +- profiles/enableFission.nix | 7 + profiles/fewerSimplifications.nix | 5 + profiles/forgetActivity.nix | 26 +- profiles/lessFingerprinting.nix | 2 +- profiles/minimalConnections.nix | 4 +- profiles/minimalHome.nix | 2 +- profiles/noAccounts.nix | 14 -- profiles/noClutter.nix | 15 -- profiles/noLocation.nix | 18 -- profiles/noMedia.nix | 26 -- profiles/noNormandy.nix | 19 -- profiles/noPasswords.nix | 27 -- profiles/noPocket.nix | 15 -- profiles/noStudies.nix | 19 -- profiles/noTunnels.nix | 23 -- profiles/noUpdates.nix | 38 --- profiles/ocsp.nix | 40 +-- profiles/replaceAllUrls.nix | 35 +-- profiles/restrict.nix | 2 +- profiles/safebrowsing.nix | 40 +-- profiles/trackingprotection.nix | 7 + 51 files changed, 1060 insertions(+), 886 deletions(-) create mode 100644 addons/default.nix create mode 100644 addons/noscript/config.json create mode 100644 addons/qwantjunior/content-script.js create mode 100644 addons/qwantjunior/favicon.ico create mode 100644 addons/qwantjunior/managed_storage.json create mode 100644 addons/qwantjunior/manifest.json create mode 100644 addons/ublock/config.json create mode 100644 firefox-configuration.nix create mode 100644 nix/options.nix delete mode 100644 profiles/addons/default.nix delete mode 100644 profiles/addons/noscript/config.json delete mode 100644 profiles/addons/qwantjunior/content-script.js delete mode 100644 profiles/addons/qwantjunior/favicon.ico delete mode 100644 profiles/addons/qwantjunior/managed_storage.json delete mode 100644 profiles/addons/qwantjunior/manifest.json delete mode 100644 profiles/addons/ublock/config.json create mode 100644 profiles/disableAccounts.nix create mode 100644 profiles/disableClutter.nix create mode 100644 profiles/disableLocation.nix create mode 100644 profiles/disableMedia.nix create mode 100644 profiles/disableNormandy.nix create mode 100644 profiles/disablePasswordManager.nix create mode 100644 profiles/disablePocket.nix create mode 100644 profiles/disableStudies.nix create mode 100644 profiles/disableTunnels.nix create mode 100644 profiles/disableUpdates.nix create mode 100644 profiles/enableFission.nix create mode 100644 profiles/fewerSimplifications.nix delete mode 100644 profiles/noAccounts.nix delete mode 100644 profiles/noClutter.nix delete mode 100644 profiles/noLocation.nix delete mode 100644 profiles/noMedia.nix delete mode 100644 profiles/noNormandy.nix delete mode 100644 profiles/noPasswords.nix delete mode 100644 profiles/noPocket.nix delete mode 100644 profiles/noStudies.nix delete mode 100644 profiles/noTunnels.nix delete mode 100644 profiles/noUpdates.nix create mode 100644 profiles/trackingprotection.nix diff --git a/addons/default.nix b/addons/default.nix new file mode 100644 index 0000000..2e648ce --- /dev/null +++ b/addons/default.nix @@ -0,0 +1,90 @@ +{ pkgs, lib, fetchurl, runCommand }: + +let + # Extension IDs are used as keys, see .applications.gecko.id in manifest.json + localAddon = { id, src, settings ? null }: { + policies = { + ExtensionSettings.${id} = { + installation_mode = "force_installed"; + install_url = "file://${src}"; + }; + } // (lib.optionalAttrs (settings != null) { + "3rdparty".Extensions.${id} = settings; + }); + }; + + addon = { id, url, sha256, settings ? null }: localAddon { + inherit id settings; + src = fetchurl { + inherit url sha256; + }; + }; +in { + # This can be safe-ish, if extension installation is also disabled + disableExtensionSignatureChecking.preferences = { + xpinstall.signatures.required = false; + }; + + privacybadger = addon { + id = "jid1-MnnxcxisBPnSXQ@jetpack"; + url = "https://addons.mozilla.org/firefox/downloads/file/3509922/privacy_badger-2020.2.19-an+fx.xpi"; + sha256 = "1issggv5wl5x3a4p3q8hrhbkhgsdx9f2qzbscg6y6f75yazswc20"; + settings = { + showIntroPage = false; + }; + }; + + noscript = addon { + id = "{73a6fe31-595d-460b-a920-fcc0f8843232}"; + url = "https://addons.mozilla.org/firefox/downloads/file/3517653/noscript_security_suite-11.0.15-an+fx.xpi"; + sha256 = "0gb0a6pp0rj9jpg1094arqvcwxh1rd2m47ijawlidybm29qmyyay"; + }; + + noscriptFork = localAddon { + id = "{73a6fe31-595d-460b-a920-fcc0f8843232}"; + src = import (pkgs.fetchzip { + url = https://git.tx0.co/firefox-profiles/noscript/snapshot/noscript-63e23c676fb86f33e70d7362bf625b534ce65346.tar.xz ; + sha256 = "0f0hna8fjj7vdjgmnjkd567smg8gda8jp9sl21nz3dx0s5d0vw8f"; + }); + settings.defaultSettings = builtins.readFile ./noscript/config.json; + }; + + ublock = addon { + id = "uBlock0@raymondhill.net"; + url = "https://addons.mozilla.org/firefox/downloads/file/3509800/ublock_origin-1.25.0-an+fx.xpi"; + sha256 = "0pyna4c2b2ffh8ifjj4c8ga9b73g37pk432nyinf8majyb1fq6rc"; + settings.adminSettings = builtins.readFile ./ublock/config.json; + }; + + qwantjunior = localAddon { + id = "qwantjunior@search.mozilla.org"; + src = runCommand "addon.xpi" { nativeBuildInputs = [ pkgs.zip ]; } '' + SRC=${./qwantsearch} + cd $SRC + zip -r $out . + ''; + + settings.settings = { + searchRegionKey = "DE"; + searchLanguageKey = "de"; + interfaceLanguageKey = "de_de"; + }; + }; + + qwantjuniorSystem.policies = { + "3rdparty".Extensions."qwantjunior@search.mozilla.org".settings = { + searchRegionKey = "DE"; + searchLanguageKey = "de"; + interfaceLanguageKey = "de_de"; + }; + }; + +/* + borderify.policies = { + ExtensionSettings."borderify@example.com" = { + installation_mode = "force_installed"; + install_url = "file://${../../borderify.xpi}"; + }; + }; +*/ +} diff --git a/addons/noscript/config.json b/addons/noscript/config.json new file mode 100644 index 0000000..57e121d --- /dev/null +++ b/addons/noscript/config.json @@ -0,0 +1,101 @@ +{ + "policy": { + "DEFAULT": { + "capabilities": [ + "frame", + "fetch", + "other", + "script", + "object", + "font", + "media", + "webgl", + "ping" + ], + "temp": false + }, + "TRUSTED": { + "capabilities": [ + "script", + "object", + "media", + "frame", + "font", + "webgl", + "fetch", + "ping", + "other" + ], + "temp": false + }, + "UNTRUSTED": { + "capabilities": [], + "temp": false + }, + "sites": { + "trusted": [ + "§:addons.mozilla.org", + "§:afx.ms", + "§:ajax.aspnetcdn.com", + "§:ajax.googleapis.com", + "§:bootstrapcdn.com", + "§:code.jquery.com", + "§:firstdata.com", + "§:firstdata.lv", + "§:gfx.ms", + "§:google.com", + "§:googlevideo.com", + "§:gstatic.com", + "§:hotmail.com", + "§:live.com", + "§:live.net", + "§:maps.googleapis.com", + "§:mozilla.net", + "§:netflix.com", + "§:nflxext.com", + "§:nflximg.com", + "§:nflxvideo.net", + "§:noscript.net", + "§:outlook.com", + "§:passport.com", + "§:passport.net", + "§:passportimages.com", + "§:paypal.com", + "§:paypalobjects.com", + "§:securecode.com", + "§:securesuite.net", + "§:sfx.ms", + "§:tinymce.cachefly.net", + "§:wlxrs.com", + "§:yahoo.com", + "§:yahooapis.com", + "§:yimg.com", + "§:youtube.com", + "§:ytimg.com", + "§:qwantjunior.com", + "§:qwant.com" + ], + "untrusted": [], + "custom": {} + }, + "enforced": true, + "autoAllowTop": false + }, + "local": { + "debug": false, + "showCtxMenuItem": true, + "showCountBadge": true, + "showFullAddresses": false, + "storage": "local", + "uuid": "82ea8b50-5fb6-45c2-8748-0dc29c8d2e49" + }, + "sync": { + "global": false, + "xss": true, + "cascadeRestrictions": false, + "overrideTorBrowserPolicy": false, + "clearclick": true, + "storage": "sync" + }, + "xssUserChoices": {} +} \ No newline at end of file diff --git a/addons/qwantjunior/content-script.js b/addons/qwantjunior/content-script.js new file mode 100644 index 0000000..01b3976 --- /dev/null +++ b/addons/qwantjunior/content-script.js @@ -0,0 +1,34 @@ +"use strict"; + +function changeSettings(managed) { + var preferred = managed.settings; + var userStorage = JSON.parse(localStorage.getItem('user')); + var needsReload = false; + + if (userStorage == null) { + localStorage.setItem('user', JSON.stringify({ + userSetting: preferred + })); + + needsReload = true; + } else if (userStorage.version == "2") { + var setting = userStorage.userSetting; + + for (const [key, value] of Object.entries(preferred)) { + if (setting[key] != preferred[key]) { + setting[key] = preferred[key]; + needsReload = true; + } + } + + localStorage.setItem('user', JSON.stringify(userStorage)); + } + + if (needsReload) { + location.reload(); + } +} + +browser.storage.managed + .get("settings") + .then(changeSettings); diff --git a/addons/qwantjunior/favicon.ico b/addons/qwantjunior/favicon.ico new file mode 100644 index 0000000..5e44aac Binary files /dev/null and b/addons/qwantjunior/favicon.ico differ diff --git a/addons/qwantjunior/managed_storage.json b/addons/qwantjunior/managed_storage.json new file mode 100644 index 0000000..00afb5a --- /dev/null +++ b/addons/qwantjunior/managed_storage.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-03/schema#", + "type": "object", + "properties": { + "settings": { + "type": "object", + "properties": { + "searchRegionKey": { + "type": "string" + }, + "searchLanguageKey": { + "type": "string" + }, + "interfaceLanguageKey": { + "type": "string" + } + } + } + } +} diff --git a/addons/qwantjunior/manifest.json b/addons/qwantjunior/manifest.json new file mode 100644 index 0000000..f38a086 --- /dev/null +++ b/addons/qwantjunior/manifest.json @@ -0,0 +1,40 @@ +{ + "name": "QwantJunior", + "description": "Search Qwant Junior", + "manifest_version": 2, + "version": "1.0", + "applications": { + "gecko": { + "id": "qwantjunior@search.mozilla.org" + } + }, + "hidden": true, + "icons": { + "16": "favicon.ico" + }, + "web_accessible_resources": [ + "favicon.ico" + ], + "chrome_settings_overrides": { + "search_provider": { + "is_default": true, + "name": "Qwant Junior", + "search_url": "https://www.qwantjunior.com", + "search_form": "https://www.qwantjunior.com/?q={searchTerms}", + "search_url_get_params": "q={searchTerms}", + "suggest_url": "https://api.qwant.com/egp/suggest/", + "suggest_url_get_params": "q={searchTerms}&client=opensearch" + } + }, + + "permissions": [ + "storage", + "https://www.qwantjunior.com/*" + ], + + "content_scripts": [ + { "matches": [ "https://www.qwantjunior.com/*" ], + "run_at": "document_idle", + "js": [ "content-script.js" ] } + ] +} diff --git a/addons/ublock/config.json b/addons/ublock/config.json new file mode 100644 index 0000000..67ba236 --- /dev/null +++ b/addons/ublock/config.json @@ -0,0 +1,84 @@ +{ + "timeStamp": 1583409728051, + "version": "1.25.0", + "userSettings": { + "advancedUserEnabled": true, + "alwaysDetachLogger": true, + "autoUpdate": true, + "cloudStorageEnabled": false, + "collapseBlocked": true, + "colorBlindFriendly": false, + "contextMenuEnabled": true, + "dynamicFilteringEnabled": true, + "externalLists": "", + "firewallPaneMinimized": true, + "hyperlinkAuditingDisabled": true, + "ignoreGenericCosmeticFilters": false, + "largeMediaSize": 50, + "parseAllABPHideFilters": true, + "prefetchingDisabled": true, + "requestLogMaxEntries": 1000, + "showIconBadge": true, + "tooltipsDisabled": false, + "webrtcIPAddressHidden": false + }, + "selectedFilterLists": [ + "user-filters", + "easylist", + "easyprivacy", + "malware-0", + "malware-1", + "plowe-0" + ], + "hiddenSettings": { + "allowGenericProceduralFilters": false, + "assetFetchTimeout": 30, + "autoCommentFilterTemplate": "{{date}} {{origin}}", + "autoUpdateAssetFetchPeriod": 120, + "autoUpdateDelayAfterLaunch": 180, + "autoUpdatePeriod": 7, + "blockingProfiles": "11111/#F00 11011/#C0F 11001/#00F 00001", + "cacheStorageAPI": "unset", + "cacheStorageCompression": true, + "cacheControlForFirefox1376932": "no-cache, no-store, must-revalidate", + "cnameIgnoreList": "unset", + "cnameIgnore1stParty": true, + "cnameIgnoreExceptions": true, + "cnameIgnoreRootDocument": true, + "cnameMaxTTL": 120, + "cnameReplayFullURL": false, + "cnameUncloak": true, + "consoleLogLevel": "unset", + "debugScriptlets": false, + "debugScriptletInjector": false, + "disableWebAssembly": false, + "extensionUpdateForceReload": false, + "ignoreRedirectFilters": false, + "ignoreScriptInjectFilters": false, + "filterAuthorMode": false, + "loggerPopupType": "popup", + "manualUpdateAssetFetchPeriod": 500, + "popupFontSize": "unset", + "requestJournalProcessPeriod": 1000, + "selfieAfter": 3, + "strictBlockingBypassDuration": 120, + "suspendTabsUntilReady": "unset", + "uiFlavor": "unset", + "updateAssetBypassBrowserCache": false, + "userResourcesLocation": "unset" + }, + "whitelist": [ + "about-scheme", + "chrome-extension-scheme", + "chrome-scheme", + "moz-extension-scheme", + "opera-scheme", + "vivaldi-scheme", + "wyciwyg-scheme" + ], + "netWhitelist": "about-scheme\nchrome-extension-scheme\nchrome-scheme\nmoz-extension-scheme\nopera-scheme\nvivaldi-scheme\nwyciwyg-scheme", + "dynamicFilteringString": "behind-the-scene * * noop\nbehind-the-scene * inline-script noop\nbehind-the-scene * 1p-script noop\nbehind-the-scene * 3p-script noop\nbehind-the-scene * 3p-frame noop\nbehind-the-scene * image noop\nbehind-the-scene * 3p noop", + "urlFilteringString": "", + "hostnameSwitchesString": "no-large-media: behind-the-scene false", + "userFilters": "" +} \ No newline at end of file diff --git a/default.nix b/default.nix index 57c27c5..fa5420e 100644 --- a/default.nix +++ b/default.nix @@ -1,111 +1,33 @@ -{ pkgs ? import {} }: - -with pkgs; - -# Where to find documentation: -# http://kb.mozillazine.org/About:config_entries -# https://www.privacy-handbuch.de/download/moderat/user.js -# https://github.com/mozilla/policy-templates, about:policies#documentation +{ configuration ? import ./firefox-configuration.nix }: let - directory = callPackage nix/directory.nix {}; - ffLib = callPackage nix/lib.nix {}; - search = callPackage nix/search.nix {}; - - callProfile = p: lib.callPackageWith (pkgs // { - inherit ffLib; - }) p {}; -in rec { - profiles = directory.listDirectory callProfile ./profiles; - - disableErrorReporting.preferences = { - breakpad.reportUrl = ""; + pkgs = import (builtins.fetchTarball { + # 2020-06-15 nixos-unstable, pinned to make sure this project still works without maintenance + url = "https://github.com/NixOS/nixpkgs/archive/0a146054bdf6f70f66de4426f84c9358521be31e.tar.gz"; + sha256 = "154ypjfhy9qqa0ww6xi7d8280h85kffqaqf6b6idymizga9ckjcd"; + }) { + config = {}; + overlays = []; }; - activeProfiles = with profiles; [ - defaults - - addons.disableExtensionSignatureChecking - addons.privacybadger - addons.noscriptFork - addons.qwantjuniorSystem - - minimalConnections - minimalHome - noAccounts - noClutter - noMedia - noNormandy - noUpdates - noStudies - noPocket - noTunnels - noLocation - safebrowsing.disableAll - trackingprotection.disableAll - - replaceAllUrls - # restrict - # distrustUser - - ocsp.disabled + ff = pkgs.callPackage nix/lib.nix {}; - forgetActivity - - { policies.RequestedLocales = [ "de-DE" "en-US" ]; } - ]; - - config = ffLib.mergeProfiles activeProfiles; - bundleConfig = { - inherit (config) policies; - preferences = ffLib.flattenAttrs config.preferences; + eval = ff.eval configuration; +in rec { + inherit (eval) options config; + inherit (eval.config) policies preferences; - # This is very hacky, but Firefox really resists setting the default search engine - # by any other means. Only builtin search engines are allowed to make themselves default - # without prompting for user consent. - patchOmniJaCommand = - let searchEnginesInfo = { - default = { - searchDefault = "qwantjunior"; - searchOrder = [ "qwantjunior" "ddg" ]; - visibleDefaultEngines = [ "qwantjunior" "ddg" ]; - }; - regionOverrides = {}; - locales = {}; - }; - in '' - pushd chrome/browser/search-extensions - cp -r ${profiles/addons/qwantjunior} ./qwantjunior - cp ${builtins.toFile "list.json" (builtins.toJSON searchEnginesInfo)} list.json - popd - ''; + bundle = ff.bundle { + inherit policies preferences; + patchOmniJaCommand = config.omnija.browser.patchCommand; }; - bundle = ffLib.bundle bundleConfig; - launcher = ffLib.launcher bundle; + launcher = ff.launcher bundle; - export = ffLib.export { - inherit (bundleConfig) policies preferences; + export = ff.export { + inherit policies preferences; + # feel free to change this selfPath = "/opt/firefox"; }; - -/* - bundle = profiles.bundle { - policies = { - Preferences = profiles.flattenAttrs { - dom.event.contextmenu.enabled = false; - - extensions = { - blocklist.enabled = false; - getAddons.showPane = false; - htmlaboutaddons.recommendations.enabled = false; - }; - - security.ssl.errorReporting.enabled = false; - }; - - SearchBar = "separate"; - }; - }; - */ + manual = ff.docs options; } diff --git a/firefox-configuration.nix b/firefox-configuration.nix new file mode 100644 index 0000000..1e850f5 --- /dev/null +++ b/firefox-configuration.nix @@ -0,0 +1,59 @@ +{ ... }: { +/* activeProfiles = with profiles; [ + defaults + + addons.disableExtensionSignatureChecking + addons.privacybadger + addons.noscriptFork + addons.qwantjuniorSystem + + minimalConnections + minimalHome + +];*/ + + + ocsp.enable = false; + safebrowsing.disableAll = true; + + features = { + disableAccounts = true; + disableMedia = true; + disableLocation = true; + disablePocket = true; + disableStudies = true; + disableNormandy = true; + disableUpdates = true; + }; + + ui.removeClutter = true; + + dataFrugality = { + forgetActivity = true; + replaceAllUrls = true; + }; + + trackingProtection.disableAll = true; + + # This is very hacky, but Firefox really resists setting the default search engine + # by any other means. Only builtin search engines are allowed to make themselves default + # without prompting for user consent. + omnija.browser.patchCommand = + let searchEnginesInfo = { + default = { + searchDefault = "qwantjunior"; + searchOrder = [ "qwantjunior" "ddg" ]; + visibleDefaultEngines = [ "qwantjunior" "ddg" ]; + }; + regionOverrides = {}; + locales = {}; + }; + in '' + pushd chrome/browser/search-extensions + cp -r ${addons/qwantjunior} ./qwantjunior + cp ${builtins.toFile "list.json" (builtins.toJSON searchEnginesInfo)} list.json + popd + ''; + + policies.RequestedLocales = [ "de-DE" "en-US" ]; +} diff --git a/nix/lib.nix b/nix/lib.nix index 179223b..0ca057d 100644 --- a/nix/lib.nix +++ b/nix/lib.nix @@ -1,147 +1,176 @@ { pkgs, lib }: with lib; -rec { - mozlz4 = pkgs.callPackage ./mozlz4.nix {}; - - flattenAttrs = set: - let recurse = k: v: - if isAttrs v - then mapAttrsToList (k': recurse "${k}.${k'}") v - else nameValuePair k v; - in listToAttrs (flatten (mapAttrsToList recurse set)); - - mkValueString = v: - if isInt v then toString v - else if isString v then "\"${v}\"" - else if true == v then "true" - else if false == v then "false" - else abort "unsupported value type: ${builtins.typeOf v}"; - - mkPrefs = settings: pkgs.writeText "prefs.js" - ("// dummy line\n" + - (lib.concatStringsSep "\n" - (lib.mapAttrsToList (k: v: "pref(\"${k}\", ${mkValueString v}, locked);") - (flattenAttrs settings)))); - - toSearchConfig = settings: pkgs.runCommand "search.json.mozlz4" {} '' - ${mozlz4.compress} < ${pkgs.writeText "search.json" (builtins.toJSON settings)} > $out - ''; - - profile = { preferences, search }: pkgs.runCommand "profile" {} '' - mkdir $out - ln -s ${toUserPrefs preferences} $out/user.js - # ln -s ${toSearchConfig search} $out/search.json.mozlz4 - cp ${../search-with-qj.json.mozlz4} $out/search.json.mozlz4 - ''; - - mkPolicies = policies: pkgs.writeText "policies.json" (builtins.toJSON { - inherit policies; - }); - - mergeProfiles = profiles: - let - sanitise = args: { - policies = args.policies or {}; - preferences = args.preferences or {}; +let + importProfiles = dir: + mapAttrs' + (name: type: + let + name' = builtins.replaceStrings [".nix"] [""] name; + in if type == "regular" then + nameValuePair name' (import (dir + ("/" + name))) + else if type == "directory" then + nameValuePair name' (pkgs.callPackage (dir + ("/" + name)) {}) + else + builtins.throw "Unexpected file type: ${type}") + (builtins.readDir dir); + + self = rec { + mozlz4 = pkgs.callPackage ./mozlz4.nix {}; + directory = pkgs.callPackage ./directory.nix {}; + + profileList = attrValues (directory.pathDirectory ../profiles); + + flattenAttrs = set: + let recurse = k: v: + if isAttrs v + then mapAttrsToList (k': recurse "${k}.${k'}") v + else nameValuePair k v; + in listToAttrs (flatten (mapAttrsToList recurse set)); + + mkValueString = v: + if isInt v then toString v + else if isString v then "\"${v}\"" + else if true == v then "true" + else if false == v then "false" + else abort "unsupported value type: ${builtins.typeOf v}"; + + mkPrefs = settings: pkgs.writeText "prefs.js" + ("// dummy line\n" + + (lib.concatStringsSep "\n" + (lib.mapAttrsToList (k: v: "pref(\"${k}\", ${mkValueString v}, locked);") + (flattenAttrs settings)))); + + toSearchConfig = settings: pkgs.runCommand "search.json.mozlz4" {} '' + ${mozlz4.compress} < ${pkgs.writeText "search.json" (builtins.toJSON settings)} > $out + ''; + + profile = { preferences, search }: pkgs.runCommand "profile" {} '' + mkdir $out + ln -s ${toUserPrefs preferences} $out/user.js + # ln -s ${toSearchConfig search} $out/search.json.mozlz4 + cp ${../search-with-qj.json.mozlz4} $out/search.json.mozlz4 + ''; + + mkPolicies = policies: pkgs.writeText "policies.json" (builtins.toJSON { + inherit policies; + }); + + optionsModule = import ./options.nix; + eval = root: evalModules { + modules = profileList ++ [ + optionsModule + root + ]; + + specialArgs = { + ff = self; }; - sanitised = map sanitise profiles; - final = lib.foldl lib.recursiveUpdate {} sanitised; - in final; - - patchOmniJa = src: script: pkgs.runCommand "omni.ja" { } '' - ${pkgs.unzip}/bin/unzip -q ${src} - - ${script} - - ${pkgs.zip}/bin/zip -qr9XD $out . - ''; - - bundle = { policies ? {}, preferences ? {}, patchOmniJaCommand ? "" }: - let - firefox = pkgs.firefox-unwrapped; - policies' = mkPolicies policies; - preferences' = mkPrefs preferences; - patchedOmniJa = patchOmniJa "${firefox}/lib/firefox/browser/omni.ja" patchOmniJaCommand; - patched = pkgs.runCommand "firefox-bundle" { - nativeBuildInputs = [ pkgs.nix ]; - disallowedReferences = [ firefox ]; - } '' - cp -r ${firefox} $out - chmod -R +w $out - # correct argv[0], which is used to locate distribution and defaults - substituteInPlace $out/bin/firefox \ - --replace ${firefox} $out - - mkdir $out/lib/firefox/distribution - cp ${policies'} $out/lib/firefox/distribution/policies.json - cp ${preferences'} $out/lib/firefox/defaults/pref/99-custom.js - cp ${patchedOmniJa} $out/lib/firefox/browser/omni.ja + }; + + docs = options: + let + optionsDoc = pkgs.nixosOptionsDoc { inherit options; }; + in pkgs.runCommand "manual.html" { nativeBuildInputs = [ pkgs.asciidoctor ]; } '' + asciidoctor \ + -o $out \ + ${builtins.toFile "manual.asciidoc" optionsDoc.optionsAsciiDoc} ''; - wrapped = (pkgs.wrapFirefox patched { - browserName = "firefox"; - version = "custom"; - }) // { inherit policies preferences; }; - in wrapped; - - # This attempts to provide a start for deploying Nix-configured profiles to - # systems which do not have Nix installed, and where /nix/store is undesired. - # It does this by bundling all referenced store items into $out, and rewriting - # references to those store items. - export = { selfPath, policies ? {}, preferences ? {} }: pkgs.stdenv.mkDerivation rec { - name = "firefox-profile-export"; - nativeBuildInputs = [ pkgs.jq ]; - - allowedRequisites = []; - - buildCommand = + + patchOmniJa = src: script: pkgs.runCommand "omni.ja" { } '' + ${pkgs.unzip}/bin/unzip -q ${src} + + ${script} + + ${pkgs.zip}/bin/zip -qr9XD $out . + ''; + + bundle = { policies ? {}, preferences ? {}, patchOmniJaCommand ? "" }: let - policyFile = mkPolicies policies; - prefsFile = mkPrefs preferences; - - closure = pkgs.closureInfo { - rootPaths = [ - policyFile prefsFile - ]; - }; - in '' - mkdir $out $out/store - - storePaths=$(cat ${closure}/store-paths) - for p in $storePaths; do - cp -a "$p" $out/store/"$(basename "$p" | sed -e 's|\([a-z0-9]\{32\}\)-||')" - done - - mv $out/store/prefs.js $out/ - jq < $out/store/policies.json > $out/policies.json - - find $out -type f -print0 | - xargs -0I{} -- sed -i -e "s|$NIX_STORE/\\([a-z0-9]\{32\}\\)-|${selfPath}/store/|g" "{}" + firefox = pkgs.firefox-unwrapped; + policies' = mkPolicies policies; + preferences' = mkPrefs preferences; + patchedOmniJa = patchOmniJa "${firefox}/lib/firefox/browser/omni.ja" patchOmniJaCommand; + patched = pkgs.runCommand "firefox-bundle" { + nativeBuildInputs = [ pkgs.nix ]; + disallowedReferences = [ firefox ]; + } '' + cp -r ${firefox} $out + chmod -R +w $out + # correct argv[0], which is used to locate distribution and defaults + substituteInPlace $out/bin/firefox \ + --replace ${firefox} $out + + mkdir $out/lib/firefox/distribution + cp ${policies'} $out/lib/firefox/distribution/policies.json + cp ${preferences'} $out/lib/firefox/defaults/pref/99-custom.js + cp ${patchedOmniJa} $out/lib/firefox/browser/omni.ja + ''; + wrapped = (pkgs.wrapFirefox patched { + browserName = "firefox"; + version = "custom"; + }) // { inherit policies preferences; }; + in wrapped; + + # This attempts to provide a start for deploying Nix-configured profiles to + # systems which do not have Nix installed, and where /nix/store is undesired. + # It does this by bundling all referenced store items into $out, and rewriting + # references to those store items. + export = { selfPath, policies ? {}, preferences ? {} }: pkgs.stdenv.mkDerivation rec { + name = "firefox-profile-export"; + nativeBuildInputs = [ pkgs.jq ]; + + allowedRequisites = []; + + buildCommand = + let + policyFile = mkPolicies policies; + prefsFile = mkPrefs preferences; + + closure = pkgs.closureInfo { + rootPaths = [ + policyFile prefsFile + ]; + }; + in '' + mkdir $out $out/store + + storePaths=$(cat ${closure}/store-paths) + for p in $storePaths; do + cp -a "$p" $out/store/"$(basename "$p" | sed -e 's|\([a-z0-9]\{32\}\)-||')" + done + + mv $out/store/prefs.js $out/ + jq < $out/store/policies.json > $out/policies.json + + find $out -type f -print0 | + xargs -0I{} -- sed -i -e "s|$NIX_STORE/\\([a-z0-9]\{32\}\\)-|${selfPath}/store/|g" "{}" + ''; + }; + + launcher = firefox: pkgs.writeShellScriptBin "firefox" '' + # FF doesn't accept ro profiles, tries to create lockfile + TMP_PROFILE="$(mktemp -d)" + echo "$TMP_PROFILE" + + function finish() { + echo deleting profile + # rm -rv "$TMP_PROFILE" + } + trap finish EXIT + # cp -r ''${profile} "$TMP_PROFILE" + chmod u+rwx -R "$TMP_PROFILE" + + export MOZ_LOG=nsHttp:1 + export MOZ_LOG_FILE=ff + # Make FF pay attention to services.settings.server + # https://bugzilla.mozilla.org/show_bug.cgi?id=1598562 + export XPCSHELL_TEST_PROFILE_DIR=1 + + ${firefox}/bin/firefox \ + --no-remote \ + --profile "$TMP_PROFILE" \ + "$@" ''; }; - - launcher = firefox: pkgs.writeShellScriptBin "firefox" '' - # FF doesn't accept ro profiles, tries to create lockfile - TMP_PROFILE="$(mktemp -d)" - echo "$TMP_PROFILE" - - function finish() { - echo deleting profile - # rm -rv "$TMP_PROFILE" - } - trap finish EXIT - # cp -r ''${profile} "$TMP_PROFILE" - chmod u+rwx -R "$TMP_PROFILE" - - export MOZ_LOG=nsHttp:1 - export MOZ_LOG_FILE=ff - # Make FF pay attention to services.settings.server - # https://bugzilla.mozilla.org/show_bug.cgi?id=1598562 - export XPCSHELL_TEST_PROFILE_DIR=1 - - ${firefox}/bin/firefox \ - --no-remote \ - --profile "$TMP_PROFILE" \ - "$@" - ''; -} +in self diff --git a/nix/options.nix b/nix/options.nix new file mode 100644 index 0000000..cbd21a5 --- /dev/null +++ b/nix/options.nix @@ -0,0 +1,66 @@ +{ options, pkgs, lib, ... }: + +with lib; +let + # https://github.com/NixOS/nixpkgs/pull/75584 + json = with lib.types; let + valueType = nullOr (oneOf [ + bool + int + float + str + (attrsOf valueType) + (listOf valueType) + ]) // { + description = "Boolean, Integer, Float, String, or lists or attribute sets of these types"; + }; + in valueType; +in { + options = { + meta.description = mkOption { + type = types.lines; + default = ""; + internal = true; + }; + + preferences = mkOption { + type = json; + default = {}; + + description = '' + These nested values are translated directly into Firefox preferences, as can be + found in user.js, prefs.js, or about:config. + ''; + + example = { + }; + }; + + policies = mkOption { + type = json; + default = {}; + + description = '' + These nested values must follow the pattern of https://github.com/mozilla/policy-templates/, + but compliance is not validated immediately. + ''; + + example = { + FirefoxHome = { + Pocket = false; + Locked = true; + }; + NewTabPage = false; + }; + }; + + omnija.browser.patchCommand = mkOption { + type = types.lines; + default = ""; + + description = '' + This is a shell script to be executed for patching omni.ja + ''; + }; + }; +} diff --git a/profiles/addons/default.nix b/profiles/addons/default.nix deleted file mode 100644 index 2e648ce..0000000 --- a/profiles/addons/default.nix +++ /dev/null @@ -1,90 +0,0 @@ -{ pkgs, lib, fetchurl, runCommand }: - -let - # Extension IDs are used as keys, see .applications.gecko.id in manifest.json - localAddon = { id, src, settings ? null }: { - policies = { - ExtensionSettings.${id} = { - installation_mode = "force_installed"; - install_url = "file://${src}"; - }; - } // (lib.optionalAttrs (settings != null) { - "3rdparty".Extensions.${id} = settings; - }); - }; - - addon = { id, url, sha256, settings ? null }: localAddon { - inherit id settings; - src = fetchurl { - inherit url sha256; - }; - }; -in { - # This can be safe-ish, if extension installation is also disabled - disableExtensionSignatureChecking.preferences = { - xpinstall.signatures.required = false; - }; - - privacybadger = addon { - id = "jid1-MnnxcxisBPnSXQ@jetpack"; - url = "https://addons.mozilla.org/firefox/downloads/file/3509922/privacy_badger-2020.2.19-an+fx.xpi"; - sha256 = "1issggv5wl5x3a4p3q8hrhbkhgsdx9f2qzbscg6y6f75yazswc20"; - settings = { - showIntroPage = false; - }; - }; - - noscript = addon { - id = "{73a6fe31-595d-460b-a920-fcc0f8843232}"; - url = "https://addons.mozilla.org/firefox/downloads/file/3517653/noscript_security_suite-11.0.15-an+fx.xpi"; - sha256 = "0gb0a6pp0rj9jpg1094arqvcwxh1rd2m47ijawlidybm29qmyyay"; - }; - - noscriptFork = localAddon { - id = "{73a6fe31-595d-460b-a920-fcc0f8843232}"; - src = import (pkgs.fetchzip { - url = https://git.tx0.co/firefox-profiles/noscript/snapshot/noscript-63e23c676fb86f33e70d7362bf625b534ce65346.tar.xz ; - sha256 = "0f0hna8fjj7vdjgmnjkd567smg8gda8jp9sl21nz3dx0s5d0vw8f"; - }); - settings.defaultSettings = builtins.readFile ./noscript/config.json; - }; - - ublock = addon { - id = "uBlock0@raymondhill.net"; - url = "https://addons.mozilla.org/firefox/downloads/file/3509800/ublock_origin-1.25.0-an+fx.xpi"; - sha256 = "0pyna4c2b2ffh8ifjj4c8ga9b73g37pk432nyinf8majyb1fq6rc"; - settings.adminSettings = builtins.readFile ./ublock/config.json; - }; - - qwantjunior = localAddon { - id = "qwantjunior@search.mozilla.org"; - src = runCommand "addon.xpi" { nativeBuildInputs = [ pkgs.zip ]; } '' - SRC=${./qwantsearch} - cd $SRC - zip -r $out . - ''; - - settings.settings = { - searchRegionKey = "DE"; - searchLanguageKey = "de"; - interfaceLanguageKey = "de_de"; - }; - }; - - qwantjuniorSystem.policies = { - "3rdparty".Extensions."qwantjunior@search.mozilla.org".settings = { - searchRegionKey = "DE"; - searchLanguageKey = "de"; - interfaceLanguageKey = "de_de"; - }; - }; - -/* - borderify.policies = { - ExtensionSettings."borderify@example.com" = { - installation_mode = "force_installed"; - install_url = "file://${../../borderify.xpi}"; - }; - }; -*/ -} diff --git a/profiles/addons/noscript/config.json b/profiles/addons/noscript/config.json deleted file mode 100644 index 57e121d..0000000 --- a/profiles/addons/noscript/config.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "policy": { - "DEFAULT": { - "capabilities": [ - "frame", - "fetch", - "other", - "script", - "object", - "font", - "media", - "webgl", - "ping" - ], - "temp": false - }, - "TRUSTED": { - "capabilities": [ - "script", - "object", - "media", - "frame", - "font", - "webgl", - "fetch", - "ping", - "other" - ], - "temp": false - }, - "UNTRUSTED": { - "capabilities": [], - "temp": false - }, - "sites": { - "trusted": [ - "§:addons.mozilla.org", - "§:afx.ms", - "§:ajax.aspnetcdn.com", - "§:ajax.googleapis.com", - "§:bootstrapcdn.com", - "§:code.jquery.com", - "§:firstdata.com", - "§:firstdata.lv", - "§:gfx.ms", - "§:google.com", - "§:googlevideo.com", - "§:gstatic.com", - "§:hotmail.com", - "§:live.com", - "§:live.net", - "§:maps.googleapis.com", - "§:mozilla.net", - "§:netflix.com", - "§:nflxext.com", - "§:nflximg.com", - "§:nflxvideo.net", - "§:noscript.net", - "§:outlook.com", - "§:passport.com", - "§:passport.net", - "§:passportimages.com", - "§:paypal.com", - "§:paypalobjects.com", - "§:securecode.com", - "§:securesuite.net", - "§:sfx.ms", - "§:tinymce.cachefly.net", - "§:wlxrs.com", - "§:yahoo.com", - "§:yahooapis.com", - "§:yimg.com", - "§:youtube.com", - "§:ytimg.com", - "§:qwantjunior.com", - "§:qwant.com" - ], - "untrusted": [], - "custom": {} - }, - "enforced": true, - "autoAllowTop": false - }, - "local": { - "debug": false, - "showCtxMenuItem": true, - "showCountBadge": true, - "showFullAddresses": false, - "storage": "local", - "uuid": "82ea8b50-5fb6-45c2-8748-0dc29c8d2e49" - }, - "sync": { - "global": false, - "xss": true, - "cascadeRestrictions": false, - "overrideTorBrowserPolicy": false, - "clearclick": true, - "storage": "sync" - }, - "xssUserChoices": {} -} \ No newline at end of file diff --git a/profiles/addons/qwantjunior/content-script.js b/profiles/addons/qwantjunior/content-script.js deleted file mode 100644 index 01b3976..0000000 --- a/profiles/addons/qwantjunior/content-script.js +++ /dev/null @@ -1,34 +0,0 @@ -"use strict"; - -function changeSettings(managed) { - var preferred = managed.settings; - var userStorage = JSON.parse(localStorage.getItem('user')); - var needsReload = false; - - if (userStorage == null) { - localStorage.setItem('user', JSON.stringify({ - userSetting: preferred - })); - - needsReload = true; - } else if (userStorage.version == "2") { - var setting = userStorage.userSetting; - - for (const [key, value] of Object.entries(preferred)) { - if (setting[key] != preferred[key]) { - setting[key] = preferred[key]; - needsReload = true; - } - } - - localStorage.setItem('user', JSON.stringify(userStorage)); - } - - if (needsReload) { - location.reload(); - } -} - -browser.storage.managed - .get("settings") - .then(changeSettings); diff --git a/profiles/addons/qwantjunior/favicon.ico b/profiles/addons/qwantjunior/favicon.ico deleted file mode 100644 index 5e44aac..0000000 Binary files a/profiles/addons/qwantjunior/favicon.ico and /dev/null differ diff --git a/profiles/addons/qwantjunior/managed_storage.json b/profiles/addons/qwantjunior/managed_storage.json deleted file mode 100644 index 00afb5a..0000000 --- a/profiles/addons/qwantjunior/managed_storage.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-03/schema#", - "type": "object", - "properties": { - "settings": { - "type": "object", - "properties": { - "searchRegionKey": { - "type": "string" - }, - "searchLanguageKey": { - "type": "string" - }, - "interfaceLanguageKey": { - "type": "string" - } - } - } - } -} diff --git a/profiles/addons/qwantjunior/manifest.json b/profiles/addons/qwantjunior/manifest.json deleted file mode 100644 index f38a086..0000000 --- a/profiles/addons/qwantjunior/manifest.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "QwantJunior", - "description": "Search Qwant Junior", - "manifest_version": 2, - "version": "1.0", - "applications": { - "gecko": { - "id": "qwantjunior@search.mozilla.org" - } - }, - "hidden": true, - "icons": { - "16": "favicon.ico" - }, - "web_accessible_resources": [ - "favicon.ico" - ], - "chrome_settings_overrides": { - "search_provider": { - "is_default": true, - "name": "Qwant Junior", - "search_url": "https://www.qwantjunior.com", - "search_form": "https://www.qwantjunior.com/?q={searchTerms}", - "search_url_get_params": "q={searchTerms}", - "suggest_url": "https://api.qwant.com/egp/suggest/", - "suggest_url_get_params": "q={searchTerms}&client=opensearch" - } - }, - - "permissions": [ - "storage", - "https://www.qwantjunior.com/*" - ], - - "content_scripts": [ - { "matches": [ "https://www.qwantjunior.com/*" ], - "run_at": "document_idle", - "js": [ "content-script.js" ] } - ] -} diff --git a/profiles/addons/ublock/config.json b/profiles/addons/ublock/config.json deleted file mode 100644 index 67ba236..0000000 --- a/profiles/addons/ublock/config.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "timeStamp": 1583409728051, - "version": "1.25.0", - "userSettings": { - "advancedUserEnabled": true, - "alwaysDetachLogger": true, - "autoUpdate": true, - "cloudStorageEnabled": false, - "collapseBlocked": true, - "colorBlindFriendly": false, - "contextMenuEnabled": true, - "dynamicFilteringEnabled": true, - "externalLists": "", - "firewallPaneMinimized": true, - "hyperlinkAuditingDisabled": true, - "ignoreGenericCosmeticFilters": false, - "largeMediaSize": 50, - "parseAllABPHideFilters": true, - "prefetchingDisabled": true, - "requestLogMaxEntries": 1000, - "showIconBadge": true, - "tooltipsDisabled": false, - "webrtcIPAddressHidden": false - }, - "selectedFilterLists": [ - "user-filters", - "easylist", - "easyprivacy", - "malware-0", - "malware-1", - "plowe-0" - ], - "hiddenSettings": { - "allowGenericProceduralFilters": false, - "assetFetchTimeout": 30, - "autoCommentFilterTemplate": "{{date}} {{origin}}", - "autoUpdateAssetFetchPeriod": 120, - "autoUpdateDelayAfterLaunch": 180, - "autoUpdatePeriod": 7, - "blockingProfiles": "11111/#F00 11011/#C0F 11001/#00F 00001", - "cacheStorageAPI": "unset", - "cacheStorageCompression": true, - "cacheControlForFirefox1376932": "no-cache, no-store, must-revalidate", - "cnameIgnoreList": "unset", - "cnameIgnore1stParty": true, - "cnameIgnoreExceptions": true, - "cnameIgnoreRootDocument": true, - "cnameMaxTTL": 120, - "cnameReplayFullURL": false, - "cnameUncloak": true, - "consoleLogLevel": "unset", - "debugScriptlets": false, - "debugScriptletInjector": false, - "disableWebAssembly": false, - "extensionUpdateForceReload": false, - "ignoreRedirectFilters": false, - "ignoreScriptInjectFilters": false, - "filterAuthorMode": false, - "loggerPopupType": "popup", - "manualUpdateAssetFetchPeriod": 500, - "popupFontSize": "unset", - "requestJournalProcessPeriod": 1000, - "selfieAfter": 3, - "strictBlockingBypassDuration": 120, - "suspendTabsUntilReady": "unset", - "uiFlavor": "unset", - "updateAssetBypassBrowserCache": false, - "userResourcesLocation": "unset" - }, - "whitelist": [ - "about-scheme", - "chrome-extension-scheme", - "chrome-scheme", - "moz-extension-scheme", - "opera-scheme", - "vivaldi-scheme", - "wyciwyg-scheme" - ], - "netWhitelist": "about-scheme\nchrome-extension-scheme\nchrome-scheme\nmoz-extension-scheme\nopera-scheme\nvivaldi-scheme\nwyciwyg-scheme", - "dynamicFilteringString": "behind-the-scene * * noop\nbehind-the-scene * inline-script noop\nbehind-the-scene * 1p-script noop\nbehind-the-scene * 3p-script noop\nbehind-the-scene * 3p-frame noop\nbehind-the-scene * image noop\nbehind-the-scene * 3p noop", - "urlFilteringString": "", - "hostnameSwitchesString": "no-large-media: behind-the-scene false", - "userFilters": "" -} \ No newline at end of file diff --git a/profiles/defaults.nix b/profiles/defaults.nix index 72315f6..9931452 100644 --- a/profiles/defaults.nix +++ b/profiles/defaults.nix @@ -1,4 +1,4 @@ -{ ffLib }: { +{ ff, ... }: { policies = { /*EnableTrackingProtection = { Cryptomining = true; diff --git a/profiles/disableAccounts.nix b/profiles/disableAccounts.nix new file mode 100644 index 0000000..b074b39 --- /dev/null +++ b/profiles/disableAccounts.nix @@ -0,0 +1,18 @@ +{ config, lib, ... }: with lib; { + options.features.disableAccounts = mkOption { + type = types.bool; + default = false; + description = '' + Disable Firefox Accounts. + + This may inconvenience users who already use them on other devices, + but if we otherwise prevent the local storage of browsing information, + it would be inconsistent to let the user sign in and potentionally + store that same information remotely. + ''; + }; + + config.preferences = mkIf config.features.disableAccounts { + identity.fxaccounts.enabled = false; + }; +} diff --git a/profiles/disableClutter.nix b/profiles/disableClutter.nix new file mode 100644 index 0000000..a19d531 --- /dev/null +++ b/profiles/disableClutter.nix @@ -0,0 +1,21 @@ +{ config, lib, ... }: with lib; { + options.ui.removeClutter = lib.mkOption { + type = types.bool; + default = false; + description = '' + Disable potentionally distracting features, to let the user focus. + + This module does not aim to improve privacy or security. + ''; + }; + + config = lib.mkIf config.ui.removeClutter { + policies = { + Homepage.StartPage = "none"; + }; + + preferences = { + browser.slowStartup.notificationDisabled = true; + }; + }; +} diff --git a/profiles/disableLocation.nix b/profiles/disableLocation.nix new file mode 100644 index 0000000..1592dbb --- /dev/null +++ b/profiles/disableLocation.nix @@ -0,0 +1,22 @@ +{ config, lib, ff, ... }: with lib; { + options.features.disableLocation = mkOption { + type = types.bool; + default = false; + description = '' + Forbid requests for the users precise location. + The remote end will still be able to look up the users IP address in a database, for + an approximate location. + ''; + }; + + config.policies = mkIf config.features.disableLocation { + Permissions.Location = { + BlockNewRequests = true; + Locked = true; + }; + + Preferences = ff.flattenAttrs { + geo.enabled = false; + }; + }; +} diff --git a/profiles/disableMedia.nix b/profiles/disableMedia.nix new file mode 100644 index 0000000..c09efa2 --- /dev/null +++ b/profiles/disableMedia.nix @@ -0,0 +1,30 @@ +{ config, lib, ff, ... }: with lib; { + options.features.disableMedia = mkOption { + type = types.bool; + default = false; + description = '' + This assumes a deployment where video consumption does not occur often. + It disables DRM and other encrypted media, as well as autoplay. + ''; + }; + + config.policies = lib.mkIf config.features.disableMedia { + Preferences = ff.flattenAttrs { + media = { + eme.enabled = false; + gmp-gmpopenh264 = { + enabled = false; + autoupdate = false; + }; + gmp-widevinecdm.enabled = false; + peerconnection.enabled = false; + + autoplay = { + default = 1; + enabled.user-gestures-needed = true; + allow-muted = false; + }; + }; + }; + }; +} diff --git a/profiles/disableNormandy.nix b/profiles/disableNormandy.nix new file mode 100644 index 0000000..1dc2dc9 --- /dev/null +++ b/profiles/disableNormandy.nix @@ -0,0 +1,23 @@ +{ config, lib, ... }: with lib; { + options.features.disableNormandy = mkOption { + type = types.bool; + default = false; + description = '' + Normandy enables Mozilla to push changes to the default settings. + Recently this was used to re-enable TLS 1.0 and 1.1 in FF 74, without releasing + a new update. + + Normandy could be used to improve security, by pushing fixes to the default configuration + after a bad release, but it can also be used to introduce/enable anti-features. + + Past activity can be reviewed at https://normandy.cdn.mozilla.net/api/v1/recipe/ + ''; + }; + + config.preferences = mkIf config.features.disableNormandy { + app.normandy = { + enabled = false; + api_url = ""; + }; + }; +} diff --git a/profiles/disablePasswordManager.nix b/profiles/disablePasswordManager.nix new file mode 100644 index 0000000..cf71db6 --- /dev/null +++ b/profiles/disablePasswordManager.nix @@ -0,0 +1,37 @@ +{ config, lib, ... }: with lib; { + options.features.disablePasswordManager = mkOption { + type = types.bool; + default = false; + description = '' + Prevent the user from storing any passwords in the browser. + This can be justified if the physical security of the device is uncertain, or + if the provider wants to avoid the responsiblity of storing such sensitive data. + + However, the users alternatives must be considered: what will a user do without the + password manager? + + Possible "alternatives" (from user perspective) include: + - Choose much weaker passwords + - Store the passwords in an unencrypted form (e.g. on the desktop) + ''; + }; + + config = mkMerge [ + (mkIf config.features.disablePasswordManager { + policies = { + # TODO: how exactly are passwords stored? + OfferToSaveLogins = false; + PasswordManagerEnabled = false; + }; + }) + + (mkIf (!config.features.disablePasswordManager) { + preferences = { + # Ask for password every 15 minutes + security.ask_for_password = 2; + security.password_lifetime = 15; # minutes + signon.masterPasswordReprompt.timeout_ms = 15 * 60 * 1000; + }; + }) + ]; +} diff --git a/profiles/disablePocket.nix b/profiles/disablePocket.nix new file mode 100644 index 0000000..8014c6e --- /dev/null +++ b/profiles/disablePocket.nix @@ -0,0 +1,21 @@ +{ config, lib, ... }: with lib; { + options.features.disablePocket = mkOption { + type = types.bool; + default = false; + description = '' + Pocket allows saving sites to an external services. + For some reason, it has been integrated into Firefox directly, + instead of being offered as an extension. + ''; + }; + + config = mkIf config.features.disablePocket { + policies = { + DisablePocket = true; + }; + + preferences = { + extensions.pocket.enabled = false; + }; + }; +} diff --git a/profiles/disableStudies.nix b/profiles/disableStudies.nix new file mode 100644 index 0000000..b401fe0 --- /dev/null +++ b/profiles/disableStudies.nix @@ -0,0 +1,25 @@ +{ config, lib, ... }: with lib; { + options.features.disableStudies = mkOption { + type = types.bool; + default = false; + description = '' + Firefox Shield Studies are meant to allow Mozilla to perform A/B testing + on Firefox users. + Studies have been used in the past to introduce surprising behaviour [1], + and we may want to prevent that from happening to our users. + + [1]: https://blog.mozilla.org/firefox/update-looking-glass-add/ + https://mozilla.github.io/normandy/user/end_user_interaction.html#opt-out-preference + ''; + }; + + config = mkIf config.features.disableStudies { + policies = { + DisableFirefoxStudies = true; + }; + + preferences = { + app.shield.optoutstudies.enabled = false; + }; + }; +} diff --git a/profiles/disableTunnels.nix b/profiles/disableTunnels.nix new file mode 100644 index 0000000..bf15485 --- /dev/null +++ b/profiles/disableTunnels.nix @@ -0,0 +1,27 @@ +{ config, lib, ... }: with lib; { + options.features.disableTunnels = mkOption { + type = types.bool; + default = false; + description = '' + Take reasonable precautions against the use of a proxy, or an encrypted DNS tunnel. + + This can make sense if we do DNS-level filtering, and the user does not have full control + over the device they're using. + + If a motivated user has local write and execution privileges, it is unlikely that we can prevent + them from circumventing these restrictions. + ''; + }; + + config.policies = mkIf config.features.disableTunnels { + DNSOverHTTPS = { + Enabled = false; + Locked = true; + }; + + Proxy = { + Mode = "none"; + Locked = true; + }; + }; +} diff --git a/profiles/disableUpdates.nix b/profiles/disableUpdates.nix new file mode 100644 index 0000000..ed5cccb --- /dev/null +++ b/profiles/disableUpdates.nix @@ -0,0 +1,44 @@ +{ config, lib, ff, ... }: with lib; { + options.features.disableUpdates = mkOption { + type = types.bool; + default = false; + description = '' + Disable all automatic updates, including: + - Firefox itself + - Extensions + - Search providers + + If some or all of these are externally managed, we may want to prevent automatic + updates from undoing our changes. + + If any properties of our deployment have been audited, automatic updates may introduce + unaudited components and compromise any guarantees made about the users security or privacy. + ''; + }; + + config = lib.mkIf config.features.disableUpdates { + policies = { + DisableAppUpdate = true; + DisableSystemAddonUpdate = true; + ExtensionUpdate = false; + + Preferences = ff.flattenAttrs { + app.update.auto = false; + browser.search.update = false; + }; + }; + + preferences = { + # try really hard to prevent search engine resets, probably wrong + browser.search = { + update = false; + geoSpecificDefaults = false; + "geoSpecificDefaults.url" = ""; + geoip.url = ""; + suggest.enabled = false; + reset.enabled = false; + reset.whitelist = ""; + }; + }; + }; +} diff --git a/profiles/distrustUser.nix b/profiles/distrustUser.nix index b90175b..0132224 100644 --- a/profiles/distrustUser.nix +++ b/profiles/distrustUser.nix @@ -1,4 +1,4 @@ -{ ffLib }: { +{ ... }: { policies = { BlockAboutAddons = true; BlockAboutConfig = true; diff --git a/profiles/enableFission.nix b/profiles/enableFission.nix new file mode 100644 index 0000000..036375b --- /dev/null +++ b/profiles/enableFission.nix @@ -0,0 +1,7 @@ +{ ... }: { + # https://wiki.mozilla.org/Project_Fission#Enabling_Fission + preferences = { + fission.autostart = true; + gfx.webrender.all = true; + }; +} diff --git a/profiles/fewerSimplifications.nix b/profiles/fewerSimplifications.nix new file mode 100644 index 0000000..798a356 --- /dev/null +++ b/profiles/fewerSimplifications.nix @@ -0,0 +1,5 @@ +{ ... }: { + preferences = { + browser.urlbar.trimURLs = false; + }; +} diff --git a/profiles/forgetActivity.nix b/profiles/forgetActivity.nix index ba45b0b..85e2093 100644 --- a/profiles/forgetActivity.nix +++ b/profiles/forgetActivity.nix @@ -1,14 +1,20 @@ -{ }: { - meta.description = '' - Delete all data accumulated during the users browsing session, - or prevent persistent storage in the first place. - ''; - - policies = { - SanitizeOnShutdown = true; +{ config, lib, ... }: with lib; { + options.dataFrugality.forgetActivity = lib.mkOption { + type = types.bool; + default = false; + description = '' + Delete all data accumulated during the users browsing session, + or prevent persistent storage in the first place. + ''; }; - preferences = { - browser.cache.disk.enabled = false; + config = lib.mkIf config.dataFrugality.forgetActivity { + policies = { + SanitizeOnShutdown = true; + }; + + preferences = { + browser.cache.disk.enabled = false; + }; }; } diff --git a/profiles/lessFingerprinting.nix b/profiles/lessFingerprinting.nix index 71c8be5..2f31574 100644 --- a/profiles/lessFingerprinting.nix +++ b/profiles/lessFingerprinting.nix @@ -1,4 +1,4 @@ -{}: { +{ ... }: { # The specific pattern of anti-fingerprinting measures taken can itself be used for fingerprinting preferences = { dom.battery.enabled = false; diff --git a/profiles/minimalConnections.nix b/profiles/minimalConnections.nix index 8bf3a72..7ca2f1c 100644 --- a/profiles/minimalConnections.nix +++ b/profiles/minimalConnections.nix @@ -1,4 +1,4 @@ -{ ffLib }: { +{ ff, ... }: { meta.description = '' Prevent unnecessary connections while browsing. @@ -17,7 +17,7 @@ NetworkPrediction = false; SearchSuggestEnabled = false; - Preferences = ffLib.flattenAttrs { + Preferences = ff.flattenAttrs { extensions = { blocklist.enabled = false; getAddons.showPane = false; diff --git a/profiles/minimalHome.nix b/profiles/minimalHome.nix index 475a562..3e53249 100644 --- a/profiles/minimalHome.nix +++ b/profiles/minimalHome.nix @@ -1,4 +1,4 @@ -{ ffLib }: { +{ ff, ... }: { meta.description = '' The default start page contains a search field, selected default sites, and past activity. diff --git a/profiles/noAccounts.nix b/profiles/noAccounts.nix deleted file mode 100644 index 3ca523d..0000000 --- a/profiles/noAccounts.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ }: { - meta.description = '' - Disable Firefox Accounts. - - This may inconvenience users who already use them on other devices, - but if we otherwise prevent the local storage of browsing information, - it would be inconsistent to let the user sign in and potentionally - store that same information remotely. - ''; - - preferences = { - identity.fxaccounts.enabled = false; - }; -} diff --git a/profiles/noClutter.nix b/profiles/noClutter.nix deleted file mode 100644 index 42e864a..0000000 --- a/profiles/noClutter.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ }: { - meta.description = '' - Disable potentionally distracting features, to let the user focus. - - This module does not aim to improve privacy or security. - ''; - - policies = { - HomePage.StartPage = "none"; - }; - - preferences = { - browser.slowStartup.notificationDisabled = true; - }; -} diff --git a/profiles/noLocation.nix b/profiles/noLocation.nix deleted file mode 100644 index 83563df..0000000 --- a/profiles/noLocation.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ ffLib }: { - meta.description = '' - Forbid requests for the users precise location. - The remote end will still be able to look up the users IP address in a database, for - an approximate location. - ''; - - policies = { - Permissions.Location = { - BlockNewRequests = true; - Locked = true; - }; - - Preferences = ffLib.flattenAttrs { - geo.enabled = false; - }; - }; -} diff --git a/profiles/noMedia.nix b/profiles/noMedia.nix deleted file mode 100644 index 32c960a..0000000 --- a/profiles/noMedia.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ ffLib }: { - meta.description = '' - This assumes a deployment where video consumption does not occur often. - It disables DRM and other encrypted media, as well as autoplay. - ''; - - policies = { - Preferences = ffLib.flattenAttrs { - media = { - eme.enabled = false; - gmp-gmpopenh264 = { - enabled = false; - autoupdate = false; - }; - gmp-widevinecdm.enabled = false; - peerconnection.enabled = false; - - autoplay = { - default = 1; - enabled.user-gestures-needed = true; - allow-muted = false; - }; - }; - }; - }; -} diff --git a/profiles/noNormandy.nix b/profiles/noNormandy.nix deleted file mode 100644 index 60decd2..0000000 --- a/profiles/noNormandy.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ ffLib }: { - meta.description = '' - Normandy enables Mozilla to push changes to the default settings. - Recently this was used to re-enable TLS 1.0 and 1.1 in FF 74, without releasing - a new update. - - Normandy could be used to improve security, by pushing fixes to the default configuration - after a bad release, but it can also be used to introduce/enable anti-features. - - Past activity can be reviewed at https://normandy.cdn.mozilla.net/api/v1/recipe/ - ''; - - preferences = { - app.normandy = { - enabled = false; - api_url = ""; - }; - }; -} diff --git a/profiles/noPasswords.nix b/profiles/noPasswords.nix deleted file mode 100644 index 9adceb7..0000000 --- a/profiles/noPasswords.nix +++ /dev/null @@ -1,27 +0,0 @@ -{ }: { - meta.description = '' - Prevent the user from storing any passwords in the browser. - This can be justified if the physical security of the device is uncertain, or - if the provider wants to avoid the responsiblity of storing such sensitive data. - - However, the users alternatives must be considered: what will a user do without the - password manager? - - Possible "alternatives" (from user perspective) include: - - Choose much weaker passwords - - Store the passwords in an unencrypted form (e.g. on the desktop) - ''; - - policies = { - # TODO: how exactly are passwords stored? - OfferToSaveLogins = false; - PasswordManagerEnabled = false; - }; - - preferences = { - # Ask for password every 15 minutes - security.ask_for_password = 2; - security.password_lifetime = 15; # minutes - signon.masterPasswordReprompt.timeout_ms = 15 * 60 * 1000; - }; -} diff --git a/profiles/noPocket.nix b/profiles/noPocket.nix deleted file mode 100644 index 7582fd6..0000000 --- a/profiles/noPocket.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ ffLib }: { - meta.description = '' - Pocket allows saving sites to an external services. - For some reason, it has been integrated into Firefox directly, - instead of being offered as an extension. - ''; - - policies = { - DisablePocket = true; - }; - - preferences = { - extensions.pocket.enabled = false; - }; -} diff --git a/profiles/noStudies.nix b/profiles/noStudies.nix deleted file mode 100644 index c6398d5..0000000 --- a/profiles/noStudies.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ ffLib }: { - meta.description = '' - Firefox Shield Studies are meant to allow Mozilla to perform A/B testing - on Firefox users. - Studies have been used in the past to introduce surprising behaviour [1], - and we may want to prevent that from happening to our users. - - [1]: https://blog.mozilla.org/firefox/update-looking-glass-add/ - https://mozilla.github.io/normandy/user/end_user_interaction.html#opt-out-preference - ''; - - policies = { - DisableFirefoxStudies = true; - }; - - preferences = { - app.shield.optoutstudies.enabled = false; - }; -} diff --git a/profiles/noTunnels.nix b/profiles/noTunnels.nix deleted file mode 100644 index 292b034..0000000 --- a/profiles/noTunnels.nix +++ /dev/null @@ -1,23 +0,0 @@ -{ }: { - meta.description = '' - Take reasonable precautions against the use of a proxy, or an encrypted DNS tunnel. - - This can make sense if we do DNS-level filtering, and the user does not have full control - over the device they're using. - - If a motivated user has local write and execution privileges, it is unlikely that we can prevent - them from circumventing these restrictions. - ''; - - policies = { - DNSOverHTTPS = { - Enabled = false; - Locked = true; - }; - - Proxy = { - Mode = "none"; - Locked = true; - }; - }; -} diff --git a/profiles/noUpdates.nix b/profiles/noUpdates.nix deleted file mode 100644 index 921a969..0000000 --- a/profiles/noUpdates.nix +++ /dev/null @@ -1,38 +0,0 @@ -{ ffLib }: { - meta.description = '' - Disable all automatic updates, including: - - Firefox itself - - Extensions - - Search providers - - If some or all of these are externally managed, we may want to prevent automatic - updates from undoing our changes. - - If any properties of our deployment have been audited, automatic updates may introduce - unaudited components and compromise any guarantees made about the users security or privacy. - ''; - - policies = { - DisableAppUpdate = true; - DisableSystemAddonUpdate = true; - ExtensionUpdate = false; - - Preferences = ffLib.flattenAttrs { - app.update.auto = false; - browser.search.update = false; - }; - }; - - preferences = { - # try really hard to prevent search engine resets, probably wrong - browser.search = { - update = false; - geoSpecificDefaults = false; - "geoSpecificDefaults.url" = ""; - geoip.url = ""; - suggest.enabled = false; - reset.enabled = false; - reset.whitelist = ""; - }; - }; -} diff --git a/profiles/ocsp.nix b/profiles/ocsp.nix index d56d8df..d109b54 100644 --- a/profiles/ocsp.nix +++ b/profiles/ocsp.nix @@ -1,4 +1,4 @@ -{ }: { +{ config, lib, ... }: { meta.description = '' The Online Certificate Status Protocol is used to distrust revoked certificates. When a new TLS connection is established, and OCSP stapling is not used, the browser checks with the @@ -6,20 +6,30 @@ It should not be disabled for security-sensitive situations, but it may be disabled for privacy reasons. ''; - enabled.preferences = { - security.OCSP = { - enabled = 1; - # OCSP is useless, if the response is not mandatory - require = true; - }; - - security.ssl = { - enable_ocsp_stapling = true; - enable_ocsp_must_staple = true; - }; + options = { + ocsp.enable = lib.mkEnableOption "OCSP"; }; - disabled.preferences = { - security.OCSP.enabled = 0; - }; + config = lib.mkMerge [ + (lib.mkIf config.ocsp.enable { + preferences = { + security.OCSP = { + enabled = 1; + # OCSP is useless, if the response is not mandatory + require = true; + }; + + security.ssl = { + enable_ocsp_stapling = true; + enable_ocsp_must_staple = true; + }; + }; + }) + + (lib.mkIf (!config.ocsp.enable) { + preferences = { + security.OCSP.enabled = 0; + }; + }) + ]; } diff --git a/profiles/replaceAllUrls.nix b/profiles/replaceAllUrls.nix index 8b71033..4c047ea 100644 --- a/profiles/replaceAllUrls.nix +++ b/profiles/replaceAllUrls.nix @@ -1,30 +1,35 @@ -{ ... }: +{ config, lib, ... }: +with lib; let # Requirements: # - is valid connection target (from FF perspective) # - doesn't have any negative effects on the user privacy or security h = "127.0.0.1"; - u = "http://${h}"; + u = lib.mkForce "http://${h}"; in { - meta.description = '' - Firefox communicates with external services in many different ways. Not all of them - happen on startup, and they can be triggered on different intervals. + options.dataFrugality.replaceAllUrls = mkOption { + type = types.bool; + default = false; + description = '' + Firefox communicates with external services in many different ways. Not all of them + happen on startup, and they can be triggered on different intervals. - This is a desperate effort to prevent as much of that communication as possible, by depriving FF of all configurable - URLs. It will break things, put the users security/privacy at risk, and probably shouldn't be enabled. + This is a desperate effort to prevent as much of that communication as possible, by depriving FF of all configurable + URLs. It will break things, put the users security/privacy at risk, and probably shouldn't be enabled. - Even so, this list may be insufficient. It was created manually from an about:config listing - for the searchterms "url", "uri", "endpoint", and "server", so it will miss hidden (but defaulted) and hardcoded URLs. + Even so, this list may be insufficient. It was created manually from an about:config listing + for the searchterms "url", "uri", "endpoint", and "server", so it will miss hidden (but defaulted) and hardcoded URLs. - Further consideration for the placeholder value is required. + Further consideration for the placeholder value is required. - Known issues: - - This list will get outdated rather quickly - - Typos and renaming of keys are not caught in any way - ''; + Known issues: + * This list will get outdated rather quickly + * Typos and renaming of keys are not caught in any way + ''; + }; - preferences = { + config.preferences = lib.mkIf config.dataFrugality.replaceAllUrls { app = { feedback.baseURL = u; releaseNotesURL = u; diff --git a/profiles/restrict.nix b/profiles/restrict.nix index 60f8516..3d0a26a 100644 --- a/profiles/restrict.nix +++ b/profiles/restrict.nix @@ -1,4 +1,4 @@ -{ ffLib }: { +{ ff, ... }: { meta.description = '' This module assumes the user means to misconfigure the browser, and tries to prevent that. It also attempts to keep the user from giving us any sensitive information in the first place. diff --git a/profiles/safebrowsing.nix b/profiles/safebrowsing.nix index 79f3c82..9a14c2c 100644 --- a/profiles/safebrowsing.nix +++ b/profiles/safebrowsing.nix @@ -1,4 +1,10 @@ -{ ffLib }: rec { +{ config, lib, ... }: + +with lib; +let + cfg = config.safebrowsing; + all = cfg.disableAll; +in { meta.description = '' Safebrowsing is a feature meant to protect the user from malicious websites and downloads. @@ -7,26 +13,24 @@ - https://wiki.mozilla.org/Security/Application_Reputation ''; - disableDownloads.preferences = { - browser.safebrowsing = { - downloads = { - # TODO: does this do offline checks? + options.safebrowsing = { + disableAll = lib.mkEnableOption "Disable all safebrowsing features"; + disableDownloads = lib.mkEnableOption "Disable safebrowsing for downloads"; + disablePhishing = lib.mkEnableOption "Disable safebrowsing regarding phishing"; + disableMalware = lib.mkEnableOption "Disable safebrowsing regarding malware"; + }; + + config.preferences.browser.safebrowsing = { + downloads = lib.mkIf (all || cfg.disableDownloads) { + # TODO: does this do offline checks? + enabled = false; + remote = { enabled = false; - remote = { - enabled = false; - url = ""; - }; + url = ""; }; }; - }; - disablePhishing.preferences = { - browser.safebrowsing.phishing.enabled = false; + phishing.enabled = mkIf (all || cfg.safebrowsing.disableDownloads) false; + malware.enabled = mkIf (all || cfg.safebrowsing.disableMalware) false; }; - - disableMalware.preferences = { - browser.safebrowsing.malware.enabled = false; - }; - - disableAll = ffLib.mergeProfiles [ disableDownloads disablePhishing disableMalware ]; } diff --git a/profiles/trackingprotection.nix b/profiles/trackingprotection.nix new file mode 100644 index 0000000..f729df2 --- /dev/null +++ b/profiles/trackingprotection.nix @@ -0,0 +1,7 @@ +{ config, lib, ... }: { + options.trackingProtection.disableAll = + lib.mkEnableOption "Disable all inbuilt tracking protection"; + + config.preferences.browser.contentblocking.enabled = + lib.mkIf config.trackingProtection.disableAll false; +} -- cgit v1.2.3