[Guide] Automatically install extensions and configure new (dispvm) hardened Firefox profiles with arkenfox user.js and policies

I mean theoretically you can harden and resist any fingerprinting algorithm. It’s just that I don’t have the time and effort do dedicate for that and probably the target audience of my advise doesn’t also.

It all depends on your threat model and how far are you comfort on spending resources/times on that…

I was able, last night, to get an Arkenfox-originated set of user prefs plugged into split-browser. This was a file I had generated with a “medium” set of things enabled–including his fingerprint resisting section (4500? 5000? I can’t recall the number). It also has about twelve additional prefs in it that I use to configure the UI, so who knows how much fingerprinting that entails.

I was still able to call up the bookmarks list with Alt-B so what I did, did not break split browser. In essence, split browser has a user.js file (I believe it’s in /usr/share/split-browser but I’m not on my qubes machine so I can’t check that); just append what you get from Step One in the first post, to that.

The split-browser directory structure is different from standard Firefox, so I still haven’t figured out where the profiles file (from step 2) should go. Since that’s the one that causes extensions to be loaded, and which can actually set the default search engine, I really want to solve that problem. (It’s a bit tedious having to reinstall ublock every time I fire up a browser!) My first guess yesterday did not work. If I can solve that, I’ll just start a new topic.

1 Like

I’ll look into that. Another, less ideal, solution could be to install ublock in the template with apt, however the version is likely not up to date.

Actually @SteveC in the guide I mention /usr/lib but could you check if /usr/share/firefox-esr exists? One should be a link of the other but still worth checking.

1 Like

I missed something guaranteed, but doesn’t something like

$ sudo dnf install mozilla-ublock-origin mozilla-https-everywhere

in a browser’s template work?

On my “arkenfox” VM, /usr/lib/firefox-esr is a directory, it’s not a link. But inside it is a link distribution->…/…/share/firefox-esr/distribution, which is to say /usr/lib/firefox-esr/distribution/ links to /usr/share/firefox-esr/distribution/. The only other link in that directory is firefox-bin->firefox-esr.

The same is true on the split-browser VM.

Differences between split and arkenfox
arkenfox

OK, so your guide mentions, basically, four files. The first is (1) user.js, Arkenfox’s file with my relatively minor changes to it, which is full of “user_pref()” calls. This for me lives on dom0. It gets converted into (2) firefox.cfg, which is basically the same content except the calls are pref() calls (the conversion also strips away comments).

Firefox.cfg gets installed to /usr/lib/firefox-esr/firefox.cfg. There is no such file on split browser.

(3) autoconfig.js of course references (2)firefox.cfg; it goes to /usr/share/firefox-esr/browser/defaults/preferences/autoconfig.js. there is no such file on split browser but it contains other files like firefox.js, vendor.js, firefox-branding.js, and debugger.js

Finally (4)policies.json goes to /usr/lib/firefox-esr/distribution…which, remember is also a link to /usr/share/firefox-esr/distribution. (Distribution, with policies.json in it, is a sibling to browser, buried in which is autoconfig.js). there is no such file on split browser, the only thing in that directory there is a subdirectory named searchplugins.

split browser
/usr/share/split-browser-disp/firefox contains sb.js and sb-load.js. sb-load.js looks a lot like your autoconfig.json file with a reference to general.config.filename (sb.js), and obscure-value. It also sets sandbox_enabled to false. sb.js contains, among other things, his keyboard mappings (so that alt-B opens his bookmark list on his bookmark vm). To this I appended my (2)firefox.cfg file. I didn’t realize until just now that I should have appended the .js file, not the firefox.cfg file. However, it seemed to work anyway even though the calls are to pref rather than user_pref!

/usr/lib/tmpfiles.d contains something called split-browser-disp.conf which looks like it contains a command to run /run/split-browser-disp. I’m not sure exactly what that’s about, honestly, I’m just guessing here.

/etc/split-browser-disp contains a couple of bash files to set environment variables (one for tor and one for firefox).

/etc/qubes-rpc contains split-browser-disp which I assume is the service handler for calls from the bookmark machine.

I tried putting the arkenfox policies.json in /user/share/split-browser-disp/firefox/distribution. That didn’t work. I’m now thinking I should have tried /usr/lib/firefox-esr/distribution (i.e. the identical location to on the arkenfox system). EDIT TO ADD: No, that didn’t work either.

If I can just figure out where split-browser looks for its policies.json file; I can probably create the Grand Unified Browser; split-arkenfox if you will.

EDIT YET AGAIN. I found out I put that file on the arkenfox template, not the split browser template.

Once placed on the split browser template, in /usr/lib/firefox-esr/distribution…it DOES seem to work! HALLELUJAH!!!

1 Like

9 posts were split to a new topic: Split-browser issues on fedora-36

Thanks for your contribution. It made it somewhat clearer for me, what I’am aiming for. I naively tried to to get security and anonimity in one solution. But wouldn’t that be somewhat like a disposable whonix-ws VM in a split browser configuration?

If I use Qubes the way I do, who will pay for it? The premise about something HAS to be payed is wrong in so many ways…

I do not know in which direction your reply goes. At least someone who works in internet businesses HAS to pay his rent and buy some food. What is your idea, where the money should come from?

This is completely offtopic.

@deeplow, please split topic from here into All Around Qubes or elswhere. Possible subject - “Is it moral and/or legal to use addblocks and other SPA tools in Qubes”

@ruedi this forum is exclusively for Qubes-related discussions. Other venues like reddit.com/r/privacy may be more suited.

Hi, since I am new to forum regulations I do make mistakes, but I should have known better.
I deleted the off-topic section. Hope that’s ok.

Thanks BEBF738VD for the guide.
Have any Fedora template users figured out an elegant way to integrate updater.sh and user-overrides.js into this process? It would be nice to have a way to automatically check if user.js is up to date, then update user.js if it is not, and finally apply user-overrides.js. Having to manually maintain each VM sounds like a lot of work. I am still a bit new to linux so I am not sure how to accomplish this.

edit: I might switch to firefox-esr to have more supported options in policies.json and to reduce amount of maintenance required. Please correct me if I am wrong, but it looks like the commands in the OP do not include a method to activate deprecated user.js prefs that ESR uses. edit2: turns out the commands DO activate deprecated user.js prefs for ESR.

@Vr3th @clarinetthelionking The link that Vr3th provided is accurate. I checked it against mozilla’s documentation. I only had issues including my ublock config in policies.json. However this article helped solve these issues. I configured ublock to my liking in an existing browser, then used ublock’s “Back up to file” setting to generate a backup file. Pasted this backup into the adminSetting utility mentioned in the article. Then pasted the output plain string value into policies.json like so:

{
  "3rdparty": {
    "Extensions": {
      "ublock0@raymondhill.net":  {
        "adminSettings": {"userSettings":{"OTHER/SETTINGS/HERE"..."}
      }
    }
  }
}

Now everything is working in all VMs based on the Fedora template.

No worries!

Interesting.

If they are trying to fingerprint based on the speed a GPU renders whatever would a way to mask this be to randomize the clock speed of your GPU? I don’t know much but about this sort of thing but I would assume it would be hard to impossible to make any judgements based of render speed if the clock speed and maybe ram timing of a GPU was randomized or at least changed from time to time.
Just a thought.

How can I make extensions to get “silently” granted with all the necessary permissions not requiring to me to see any splash screens about first use etc?

1 Like

Here’s an addition of caching Firefox addons to install addons using policies.json.

The new DispVMs on boot will request caching VM to get the Firefox addons before Firefox will start.
Caching VM on receiving request will get the latest addon version info and check the addon on disk if it’s the latest version. It’ll download the addon if the version on disk is not the latest. Then it’ll send the latest addon to the requester.

Example for uBlock Origins.
Change sys-whonix in /usr/local/bin/firefox-get-addons in disposable template and in /etc/qubes/policy.d/30-user.policy in dom0 to your desired VM that will be processing the requests to get the Firefox addons.
Change debian-11-dvm in dom0 qvm-tags to your desired disposable template.

To make disposable VMs based on other disposable templates use the caching VM you need to repeat step 1 in new disposable template and add firefox tag to it in dom0:
qvm-tags debian-11-firefox-dvm add tag firefox

1. In disposable template (debian-11-dvm in this example):

Run as user:

mkdir -p /home/user/.config/systemd/user
cat << 'EOF' | sudo tee /usr/local/bin/firefox-get-addons
#!/bin/sh
qubes_vm_name="$(qubesdb-read /name)"
# Check that we're not running inside Disposable Template
if echo "$qubes_vm_name" | grep -q --invert-match "\-dvm" ; then
    /usr/lib/qubes/qrexec-client-vm --filter-escape-chars-stderr -- sys-whonix user.FirefoxGetAddon+uBlockOrigin /usr/lib/qubes/qfile-unpacker
fi
EOF
sudo chmod +x /usr/local/bin/firefox-get-addons 
mkdir -p /home/user/.config/systemd/user
cat << 'EOF' > /home/user/.config/systemd/user/firefox-get-addons.service
[Unit]
Description=Get Firefox Add-ons
Before=basic.target
DefaultDependencies=no

[Service]
Type=oneshot
Environment=QREXEC_REMOTE_DOMAIN=dom0
ExecStart=/usr/local/bin/firefox-get-addons

[Install]
WantedBy=basic.target
EOF
systemctl --user daemon-reload
systemctl --user enable firefox-get-addons.service

Add extension to install in ExtensionSettings in policies.json file:

        "ExtensionSettings": {
            "*": {
                "installation_mode": "blocked"
            },
            "uBlock0@raymondhill.net": {
                "installation_mode": "normal_installed",
                "install_url": "file:///home/user/QubesIncoming/dom0/ublock_origin-latest.xpi"
            }
        },

2. In your caching VM (sys-whonix in this example):

Run as user:

sudo mkdir -p /usr/local/etc/qubes-rpc
cat << 'EOF' | sudo tee /usr/local/etc/qubes-rpc/user.FirefoxGetAddon+uBlockOrigin 
#!/usr/bin/env python3
import os
import sys
import json
import requests
import shutil
import hashlib
import subprocess as s

def download_file(url,proxies,filename):
    with requests.get(url, proxies=proxies, stream=True) as r:
        with open(filename, 'wb') as f:
            shutil.copyfileobj(r.raw, f)

def sha256sum(filename):
    h  = hashlib.sha256()
    b  = bytearray(128*1024)
    mv = memoryview(b)
    with open(filename, 'rb', buffering=0) as f:
        while n := f.readinto(mv):
            h.update(mv[:n])
    return h.hexdigest()

def main():
    # Set Tor proxy if we're running in Whonix VM
    if os.path.exists('/usr/share/whonix/marker'):
        proxy = 'socks5h://127.0.0.1:9050'
    else:
        proxy = ''
    proxies = {
       'http': proxy,
       'https': proxy
    }
    url = 'https://addons.mozilla.org/api/v5/addons/addon/ublock-origin/versions/?page_size=1'
    response = requests.get(url, proxies=proxies)
    addon_latest_json = response.json()['results'][0]
    addon_dest_file = '/home/user/Downloads/firefox-extensions/ublock_origin-latest.xpi'
    os.makedirs(os.path.dirname(addon_dest_file), exist_ok=True)
    if not os.path.exists(addon_dest_file) or sha256sum(addon_dest_file) != addon_latest_json['file']['hash'].split(':')[1]:
        download_file(addon_latest_json['file']['url'], proxies, addon_dest_file)
        if not os.path.exists(addon_dest_file) or sha256sum(addon_dest_file) != addon_latest_json['file']['hash'].split(':')[1]:
            s.call(['notify-send','Failed to get latest uBlock Origin Firefox addon','--icon=dialog-error'])
            sys.exit(1)
    s.call(['/usr/lib/qubes/qfile-agent',addon_dest_file])
    sys.exit(0)
if __name__ == '__main__':
    main()
EOF

3. In dom0:

Run as user:

cat << 'EOF' | sudo tee -a /etc/qubes/policy.d/30-user.policy
user.FirefoxGetAddon +uBlockOrigin @tag:firefox sys-whonix allow
EOF
qvm-tags debian-11-dvm add tag firefox
1 Like

Fixed to use ExtensionSettings instead of Extensions in policies.json:

Updated copy here

Here’s an addition of caching Firefox addons to install addons using policies.json.

The new DispVMs on boot will request caching VM to get the Firefox addons before Firefox will start.
Caching VM on receiving request will get the latest addon version info and check the addon on disk if it’s the latest version. It’ll download the addon if the version on disk is not the latest. Then it’ll send the latest addon to the requester.

Example for uBlock Origins.
Change sys-whonix in /usr/local/bin/firefox-get-addons in disposable template and in /etc/qubes/policy.d/30-user.policy in dom0 to your desired VM that will be processing the requests to get the Firefox addons.
Change debian-11-dvm in dom0 qvm-tags to your desired disposable template.

To make disposable VMs based on other disposable templates use the caching VM you need to repeat step 1 in new disposable template and add firefox tag to it in dom0:
qvm-tags debian-11-firefox-dvm add tag firefox

1. In disposable template (debian-11-dvm in this example):

Run as user:

mkdir -p /home/user/.config/systemd/user
cat << 'EOF' | sudo tee /usr/local/bin/firefox-get-addons
#!/bin/sh
qubes_vm_name="$(qubesdb-read /name)"
# Check that we're not running inside Disposable Template
if echo "$qubes_vm_name" | grep -q --invert-match "\-dvm" ; then
    /usr/lib/qubes/qrexec-client-vm --filter-escape-chars-stderr -- sys-whonix user.FirefoxGetAddon+uBlockOrigin /usr/lib/qubes/qfile-unpacker
fi
EOF
sudo chmod +x /usr/local/bin/firefox-get-addons 
mkdir -p /home/user/.config/systemd/user
cat << 'EOF' > /home/user/.config/systemd/user/firefox-get-addons.service
[Unit]
Description=Get Firefox Add-ons
Before=default.target

[Service]
Type=oneshot
Environment=QREXEC_REMOTE_DOMAIN=dom0
ExecStart=/usr/local/bin/firefox-get-addons

[Install]
WantedBy=default.target
EOF
systemctl --user daemon-reload
systemctl --user enable firefox-get-addons.service

Add extension to install in ExtensionSettings in policies.json file:

        "ExtensionSettings": {
            "*": {
                "installation_mode": "blocked"
            },
            "uBlock0@raymondhill.net": {
                "installation_mode": "normal_installed",
                "install_url": "file:///home/user/QubesIncoming/dom0/ublock_origin-latest.xpi"
            }
        },

2. In your caching VM (sys-whonix in this example):

Run as user:

sudo mkdir -p /usr/local/etc/qubes-rpc
cat << 'EOF' | sudo tee /usr/local/etc/qubes-rpc/user.FirefoxGetAddon+uBlockOrigin 
#!/usr/bin/env python3
import os
import sys
import json
import requests
import shutil
import hashlib
import subprocess as s

def download_file(url,proxies,filename):
    with requests.get(url, proxies=proxies, stream=True) as r:
        with open(filename, 'wb') as f:
            shutil.copyfileobj(r.raw, f)

def sha256sum(filename):
    h  = hashlib.sha256()
    b  = bytearray(128*1024)
    mv = memoryview(b)
    with open(filename, 'rb', buffering=0) as f:
        while n := f.readinto(mv):
            h.update(mv[:n])
    return h.hexdigest()

def main():
    # Set Tor proxy if we're running in Whonix VM
    if os.path.exists('/usr/share/whonix/marker'):
        proxy = 'socks5h://127.0.0.1:9050'
    else:
        proxy = ''
    proxies = {
       'http': proxy,
       'https': proxy
    }
    url = 'https://addons.mozilla.org/api/v5/addons/addon/ublock-origin/versions/?page_size=1'
    response = requests.get(url, proxies=proxies)
    addon_latest_json = response.json()['results'][0]
    addon_dest_file = '/home/user/Downloads/firefox-extensions/ublock_origin-latest.xpi'
    os.makedirs(os.path.dirname(addon_dest_file), exist_ok=True)
    if not os.path.exists(addon_dest_file) or sha256sum(addon_dest_file) != addon_latest_json['file']['hash'].split(':')[1]:
        download_file(addon_latest_json['file']['url'], proxies, addon_dest_file)
        if not os.path.exists(addon_dest_file) or sha256sum(addon_dest_file) != addon_latest_json['file']['hash'].split(':')[1]:
            s.call(['notify-send','Failed to get latest uBlock Origin Firefox addon','--icon=dialog-error'])
            sys.exit(1)
    s.call(['/usr/lib/qubes/qfile-agent',addon_dest_file])
    sys.exit(0)
if __name__ == '__main__':
    main()
EOF

3. In dom0:

Run as user:

cat << 'EOF' | sudo tee -a /etc/qubes/policy.d/30-user.policy
user.FirefoxGetAddon +uBlockOrigin @tag:firefox sys-whonix allow
EOF
qvm-tags debian-11-dvm add tag firefox

Fixed systemd service so Firefox won’t start before all addons were received.

Updated copy here

Here’s an addition of caching Firefox addons to install addons using policies.json.

The new DispVMs on boot will request caching VM to get the Firefox addons before Firefox will start.
Caching VM on receiving request will get the latest addon version info and check the addon on disk if it’s the latest version. It’ll download the addon if the version on disk is not the latest. Then it’ll send the latest addon to the requester.

Example for uBlock Origins.
Change sys-whonix in /usr/local/bin/firefox-get-addons in disposable template and in /etc/qubes/policy.d/30-user.policy in dom0 to your desired VM that will be processing the requests to get the Firefox addons.
Change debian-11-dvm in dom0 qvm-tags to your desired disposable template.

To make disposable VMs based on other disposable templates use the caching VM you need to repeat step 1 in new disposable template and add firefox tag to it in dom0:
qvm-tags debian-11-firefox-dvm add tag firefox

1. In disposable template (debian-11-dvm in this example):

Run as user:

mkdir -p /home/user/.config/systemd/user
cat << 'EOF' | sudo tee /usr/local/bin/firefox-get-addons
#!/bin/sh
qubes_vm_name="$(qubesdb-read /name)"
# Check that we're not running inside Disposable Template
if echo "$qubes_vm_name" | grep -q --invert-match "\-dvm" ; then
    /usr/lib/qubes/qrexec-client-vm --filter-escape-chars-stderr -- sys-whonix user.FirefoxGetAddon+uBlockOrigin /usr/lib/qubes/qfile-unpacker
fi
EOF
sudo chmod +x /usr/local/bin/firefox-get-addons 
mkdir -p /home/user/.config/systemd/user
cat << 'EOF' > /home/user/.config/systemd/user/firefox-get-addons.service
[Unit]
Description=Get Firefox Add-ons
Before=basic.target
DefaultDependencies=no

[Service]
Type=oneshot
Environment=QREXEC_REMOTE_DOMAIN=dom0
ExecStart=/usr/local/bin/firefox-get-addons

[Install]
WantedBy=basic.target
EOF
systemctl --user daemon-reload
systemctl --user enable firefox-get-addons.service

Add extension to install in ExtensionSettings in policies.json file:

        "ExtensionSettings": {
            "*": {
                "installation_mode": "blocked"
            },
            "uBlock0@raymondhill.net": {
                "installation_mode": "normal_installed",
                "install_url": "file:///home/user/QubesIncoming/dom0/ublock_origin-latest.xpi"
            }
        },

2. In your caching VM (sys-whonix in this example):

Run as user:

sudo mkdir -p /usr/local/etc/qubes-rpc
cat << 'EOF' | sudo tee /usr/local/etc/qubes-rpc/user.FirefoxGetAddon+uBlockOrigin 
#!/usr/bin/env python3
import os
import sys
import json
import requests
import shutil
import hashlib
import subprocess as s

def download_file(url,proxies,filename):
    with requests.get(url, proxies=proxies, stream=True) as r:
        with open(filename, 'wb') as f:
            shutil.copyfileobj(r.raw, f)

def sha256sum(filename):
    h  = hashlib.sha256()
    b  = bytearray(128*1024)
    mv = memoryview(b)
    with open(filename, 'rb', buffering=0) as f:
        while n := f.readinto(mv):
            h.update(mv[:n])
    return h.hexdigest()

def main():
    # Set Tor proxy if we're running in Whonix VM
    if os.path.exists('/usr/share/whonix/marker'):
        proxy = 'socks5h://127.0.0.1:9050'
    else:
        proxy = ''
    proxies = {
       'http': proxy,
       'https': proxy
    }
    url = 'https://addons.mozilla.org/api/v5/addons/addon/ublock-origin/versions/?page_size=1'
    response = requests.get(url, proxies=proxies)
    addon_latest_json = response.json()['results'][0]
    addon_dest_file = '/home/user/Downloads/firefox-extensions/ublock_origin-latest.xpi'
    os.makedirs(os.path.dirname(addon_dest_file), exist_ok=True)
    if not os.path.exists(addon_dest_file) or sha256sum(addon_dest_file) != addon_latest_json['file']['hash'].split(':')[1]:
        download_file(addon_latest_json['file']['url'], proxies, addon_dest_file)
        if not os.path.exists(addon_dest_file) or sha256sum(addon_dest_file) != addon_latest_json['file']['hash'].split(':')[1]:
            s.call(['notify-send','Failed to get latest uBlock Origin Firefox addon','--icon=dialog-error'])
            sys.exit(1)
    s.call(['/usr/lib/qubes/qfile-agent',addon_dest_file])
    sys.exit(0)
if __name__ == '__main__':
    main()
EOF

3. In dom0:

Run as user:

cat << 'EOF' | sudo tee -a /etc/qubes/policy.d/30-user.policy
user.FirefoxGetAddon +uBlockOrigin @tag:firefox sys-whonix allow
EOF
qvm-tags debian-11-dvm add tag firefox