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

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

I made salt script for setting up firefox in with policies and firefox.cfg. This might have some use for somebody else too.

Note that you have to provide your own configuration files. Furthermore this script is made for using conventional user.js files as input file. All syntax corrections are handed by this salt script itself.

##
# firefox
# ===================================
#
# configures firefox for usage in disposable template
##

##
{% set folder = "salt://<PATH TO FILES>" %}
{% set userjs_file = "arkenfox_user.js" %}
{% set policy_file = "policies.json" %}

{% if grains['id'] != 'dom0' %}
install-firefox-esr:
  pkg.installed:
    - refresh: true
    - install_recommends: false
    - pkgs:
      - firefox-esr

## https://support.mozilla.org/en-US/kb/customizing-firefox-using-autoconfig
## Firefox Policies: https://github.com/mozilla/policy-templates
## enable firefox configuration via autoconfig
enable-policies:
  file.managed:
    - name: /usr/share/firefox-esr/browser/defaults/preferences/autoconfig.js
    - user: root
    - group: root
    - mode: '0644'
    - makedirs: true
    - content: |
        # /usr/share/firefox-esr/browser/defaults/preferences/autoconfig.js
        pref("general.config.filename", "firefox.cfg");
        pref("general.config.obscure_value", 0);
    - require:
      - install-firefox-esr

## configure firefox polices
deploy-policies:
  file.managed:
    - name: /usr/lib/firefox-esr/distribution/policies.json
    - source: {{ folder }}/{{ policy_file }}
    - user: root
    - group: root
    - mode: '0644'
    - makedirs: true
    - require:
      - enable-policies

## configure user.js in template
deploy-userjs:
  file.managed:
    - name: /usr/lib/firefox-esr/firefox.cfg
    - source: {{ folder }}/{{ userjs_file }}
    - user: root
    - group: root
    - mode: '0644'
    - makedirs: true
    - require:
      - install-firefox-esr

prepend-line:
  file.prepend:
    - name: /usr/lib/firefox-esr/firefox.cfg
    - text: "// IMPORTANT: Start your code on the 2nd line"
    - require:
      - deploy-userjs

replace-adapt-syntax:
  file.replace:
    - name: /usr/lib/firefox-esr/firefox.cfg
    - pattern: "^user_pref"
    - repl: "pref"
    - require:
      - deploy-userjs
{% endif %}