Anonymize hostname hardened template automatic installation of browser

To reduce 90% of common post here i decided to learn to use salt on Qubes to provide pre-configured hardened template and browsers. I spend 3-4 days working on this.

What is salt ? Read here Salt (management software) — Qubes OS Documentation but with simple term you can say it’s a “advanced bash script” to setup your own config under Qubes

Once you have installed everything i recommend you to switch to this guide 🛡 Qubes OS live mode. dom0 in RAM. Non-persistent Boot. RAM-Wipe. Protection against forensics. Tails mode. Hardening dom0. Root read‑only. Paranoid Security. Ephemeral Encryption

The guide target advanced Qubes users (for now) i will provide the same setup for noob users soon

The guide is using debian-13-minimal and fedora-43-minimal template so you will need to install those templates before starting

The debian/kicksecure salt file require sys-whonix to setup the template but the fedora salt file do not require sys-whonix

To use the “automatic installation” file you need to move the file inside dom0 with this command

qvm-run --pass-io <src-vm> 'cat /path/to/file_in_src_domain' > /path/to/file_name_in_dom0

To be more clear the command must look like this


qvm-run --pass-io fedora-43 'cat /home/user/Downlods/firefox.sls' > firefox.sls

Then do

sudo mv (name of the file) /srv/salt/

or you can use this guide to copy paste easily

The file must be put at /srv/salt/(name of the file).sls

The name of the file doesn’t matter you can name the file ex.sls it will work

In dom0 open a terminal and do

sudo qubesctl state.apply librewolf

(don’t put “sls” at the end of the command)

sudo qubesctl state.apply x

where x = the name of the file in /srv/salt/

Information needed to know :

  1. If you do not have enough ram the installation process will abort so don’t open too much vm at the same time
  2. This is possible codeberg block your Tor or VPN IP address in that case the installation will fail you will have to delete the template then restart sys-whonix or change your VPN ip and re-start the command.
  3. The installation packages with apt or dnf could fail because of Tor or your vpn you will have to do the steps 2 to solve the issue
  4. I could move the repository to somewhere else to avoid the n2 issue but this will be useless i think a lot of tor/vpn ip is blocked in famous hosting services like gitlab, github etc…

Features :

:white_check_mark: Kernel hardening from Tails & Secureblue
:white_check_mark: Selinux automatic installation & enabled
:white_check_mark: Deny read access to root file system , home directories except Downloads and machine-id for browsers using my apparmor profiles
:white_check_mark: Dark theme enabled by default
:white_check_mark: Anonymize hostname at boot for Template, Appvm, Dispvm
:white_check_mark: Every installation of browsers will be done by using apt-transport-tor (for deb based template) because fedora do not provide this feature
:white_check_mark: Firefox is using arkenfox user.js and brave is hardened by default the browser profile is coming from here no telemetry , no ai enabled etc…
:white_check_mark: qvm-firewall enabled by default for the Appvm we allow only dns traffic , https , http trafic everything else will be drop
:white_check_mark: Minimize debian even more thanks to qubist post
:white_check_mark: Automatically install Ublock origin when firefox start (like librewolf)
:white_check_mark: Automatically setup wget proxy and curl proxy to install software in the template (works only in debian for unknow reason)
:white_check_mark: Apparmor enabled by default the browser , Nautilus and qubes features will use my apparmor profiles

debian-brave-apparmor.sls
start-debian-minimal:
  qvm.start:
    - name: debian-13-minimal

update-debian-minimal:
  qvm.run:
    - name: debian-13-minimal
    - cmd: |
        /bin/bash -c 'apt-get update && apt-get -y full-upgrade'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: start-debian-minimal

shutdown-debian-minimal:
  qvm.shutdown:
    - name: debian-13-minimal
    - require:
      - qvm: update-debian-minimal

setup-transport:
  qvm.start:
    - name: debian-13-minimal
    - require:
      - qvm: shutdown-debian-minimal    

fix-locale:
  qvm.run:
    - name: debian-13-minimal
    - cmd: |
        /bin/bash -c "sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && /usr/sbin/locale-gen" && \
        sed -i 's/^# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
        /usr/sbin/locale-gen
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: setup-transport


setup-tor:
  qvm.run:
    - name: debian-13-minimal
    - cmd: |
        /bin/bash -c 'apt-get -y install apt-transport-tor alacritty'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: fix-locale

minimize-debian:
  qvm.run:
    - name: debian-13-minimal
    - cmd: |
        /bin/bash -c 'apt-get -y purge \
        apt-transport-https \
        apt-utils \
        cpio \
        cron \
        cron-daemon-common \
        debconf-i18n \
        dhcpcd-base \
        eatmydata \
        fdisk \
        ifupdown \
        iproute2 \
        iputils-ping \
        less \
        libcap2-bin \
        libeatmydata1 \
        libfdisk1 \
        libgcrypt20 \
        libidn2-0 \
        libjansson4 \
        libk5crypto3 \
        libkeyutils1 \
        libkrb5support0 \
        libmnl0 \
        libnewt0.52 \
        libp11-kit0 \
        libsemanage-common \
        libsepol2 \
        libslang2 \
        libtext-iconv-perl \
        libtirpc-common \
        libxtables12 \
        logrotate \
        nftables \
        tasksel \
        whiptail \
        xterm && apt-get -y autoremove'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: setup-tor

shutdown-after-tor:
  qvm.shutdown:
    - name: debian-13-minimal
    - require:
      - qvm: minimize-debian


clone-base-apparmor:
  qvm.clone:
    - name: base-apparmor
    - source: debian-13-minimal
    - require:
      - qvm: shutdown-after-tor

config-apt:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'cat <<EOF >/etc/apt/sources.list
        deb tor+https://deb.debian.org/debian trixie main contrib non-free-firmware
        deb tor+https://deb.debian.org/debian-security trixie-security main contrib non-free-firmware
        deb tor+https://deb.debian.org/debian trixie-backports main
        EOF'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: clone-base-apparmor


enable-dark-theme:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'cat <<EOF >/etc/environment
         GTK_THEME=Adwaita:dark
         QT_QPA_PLATFORMTHEME=qt5ct
         QT_STYLE_OVERRIDE=Adwaita-dark               
        EOF' 
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: config-apt

random-hostname:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'cat > /etc/rc.local << '"'"'EOF'"'"'
        #!/bin/bash
        # Anonymize hostname with random name at boot
        
        RANDOM_HOSTNAME=$(cat /dev/urandom | tr -dc "a-z" | head -c 12)
        echo "$RANDOM_HOSTNAME" > /etc/hostname
        hostname "$RANDOM_HOSTNAME"
        sed -i "s/^127\.0\.0\.1.*/127.0.0.1 $RANDOM_HOSTNAME localhost/" /etc/hosts
        hostnamectl set-hostname "$RANDOM_HOSTNAME" 2>/dev/null || true
        EOF'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: enable-dark-theme


random-hostname-exec:
  qvm.run:
    - name: base-apparmor
    - cmd: chmod +x /etc/rc.local
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: random-hostname


config-apt-onion-qubes:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'cat <<EOF >/etc/apt/sources.list.d/qubes-r4.list 
        deb [arch=amd64 signed-by=/usr/share/keyrings/qubes-archive-keyring-4.3.gpg] tor+http://deb.qubesosfasa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion/r4.3/vm trixie main
        # Backup HTTPS repository (commented)
        #deb [arch=amd64 signed-by=/usr/share/keyrings/qubes-archive-keyring-4.3.gpg ] https://deb.qubes-os.org/r4.3/vm trixie main"
        EOF'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: random-hostname-exec


set-pinning-backports:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'cat <<EOF >/etc/apt/preferences.d/backports
        Package: *
        Pin: release n=trixie-backports
        Pin-Priority: 900
        EOF'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: config-apt-onion-qubes

set-pinning-stable:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'cat <<EOF >/etc/apt/preferences.d/stable
        Package: *
        Pin: release n=stable
        Pin-Priority: 500
        EOF'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: set-pinning-backports

update-base-apparmor:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'apt-get update && apt-get -y full-upgrade'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: set-pinning-backports
      - qvm: set-pinning-stable

restart:
  qvm.shutdown:
    - name: base-apparmor
    - require:
      - qvm: update-base-apparmor
 
start-again:
  qvm.start:
    - name: base-apparmor
    - require:
      - qvm: restart

add-unstable-repo:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'cat <<EOF >>/etc/apt/sources.list
        deb tor+https://deb.debian.org/debian/ unstable main
        EOF'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: start-again

create-apparmor-pinning:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'cat <<EOF >/etc/apt/preferences.d/unstable-pin
        Package: apparm* python*-appar* python*-libappar*
        Pin: release a=unstable
        Pin-Priority: 990
        Package: *
        Pin: release a=unstable
        Pin-Priority: 1
        EOF'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: add-unstable-repo

update-indexes-after-unstable:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'apt update'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: create-apparmor-pinning

install-apparmor:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'apt install -y -t unstable apparmor apparmor-utils apparmor-profiles apparmor-profiles-extra'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: update-indexes-after-unstable

shutdown-before-kernelopts:
  qvm.shutdown:
    - name: base-apparmor
    - require:
      - qvm: install-apparmor

set-apparmor-kernelopts:
  qvm.prefs:
    - name: base-apparmor
    - kernelopts: 'swiotlb=2048 apparmor=1 security=apparmor'
    - require:
      - qvm: shutdown-before-kernelopts

restart-base-apparmor:
  qvm.start:
    - name: base-apparmor
    - require:
      - qvm: set-apparmor-kernelopts

minimize-git:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'apt-get install --no-install-recommends -y git'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: restart-base-apparmor

speed-boost-apparmor:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c '
        tee -a /etc/apparmor/parser.conf > /dev/null <<EOF
        write-cache
        cache-loc /etc/apparmor/earlypolicy/
        Optimize=compress-fast
        EOF
        '
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: minimize-git

shutdown-base-apparmor-before-clone:
  qvm.shutdown:
    - name: base-apparmor
    - require:
      - qvm: speed-boost-apparmor


cloning-apparmor:
  qvm.clone:
    - name: debian-apparmor-brave
    - source: base-apparmor
    - flags:
      - shutdown

start-brave-template:
  qvm.start:
    - name: debian-apparmor-brave
    - require:
      - qvm: cloning-apparmor

# we install the tor package you will need it to install package in appvm or dispvm 
install-curl-tor:
  qvm.run:
    - name: debian-apparmor-brave
    - cmd: 'apt-get -y install tor curl'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: start-brave-template

# by default the tor package will enable the tor service but we are already using sys-whonix it's useless
disable-tor-service:
  qvm.run:
    - name: debian-apparmor-brave
    - cmd: 'systemctl disable tor'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: install-curl-tor

download-brave-key:
  qvm.run:
    - name: debian-apparmor-brave
    - cmd: |
        bash -c "export all_proxy=http://127.0.0.1:8082/ && \
        curl -fsSLo /usr/share/keyrings/brave-browser-archive-keyring.gpg \
        https://brave-browser-apt-release.s3.brave4u7jddbv7cyviptqjc7jusxh72uik7zt6adtckl5f4nwy2v72qd.onion/brave-browser-archive-keyring.gpg"
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: disable-tor-service

add-brave-repo:
  qvm.run:
    - name: debian-apparmor-brave
    - cmd: |
        bash -c "export all_proxy=http://127.0.0.1:8082/ && \
        curl -fsSLo /etc/apt/sources.list.d/brave-browser-release.sources \
        https://brave-browser-apt-release.s3.brave4u7jddbv7cyviptqjc7jusxh72uik7zt6adtckl5f4nwy2v72qd.onion/brave-browser.sources"
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: download-brave-key


force-brave-onion-apt:
  qvm.run:
    - name: debian-apparmor-brave
    - cmd: | 
        /bin/bash -c 'cat > /etc/apt/sources.list.d/brave-browser-release.sources << EOF
        Types: deb
        URIs: https://brave-browser-apt-release.s3.brave4u7jddbv7cyviptqjc7jusxh72uik7zt6adtckl5f4nwy2v72qd.onion
        Suites: stable
        Components: main
        Architectures: amd64 arm64
        Signed-By: /usr/share/keyrings/brave-browser-archive-keyring.gpg
        EOF'
    - user: root
    - flags:
      - pass-io
      - nogui
    - require:
      - qvm: add-brave-repo

install-brave:
  qvm.run:
    - name: debian-apparmor-brave
    - cmd: |
        bash -c 'apt-get update && apt-get install -y brave-browser zenity ffmpeg libavcodec-extra pipewire-qubes qubes-core-agent-networking nautilus qubes-core-agent-nautilus && \ 
        apt-get -y purge gnome-keyring && \
        apt-get -y autoremove' 
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: force-brave-onion-apt


setup-apparmor-profiles-dkzkz:
  qvm.run:
    - name: debian-apparmor-brave
    - cmd: |
        /bin/bash -c 'export all_proxy=http://127.0.0.1:8082/ && \
        cd /tmp && \
        git clone https://codeberg.org/dkzkz/apparmor-qubes && \
        cd /tmp/apparmor-qubes/stable/browser/brave && \ 
        mv bra* /etc/apparmor.d/ && \ 
        mv /tmp/apparmor-qubes/stable/file-manager/nautilus /etc/apparmor.d/ && \ 
        mv /tmp/apparmor-qubes/stable/display-vm/Xorg /etc/apparmor.d/Xorg && \ 
        cd /tmp/apparmor-qubes/stable/qubes-scripts/ && \ 
        mv q* /etc/apparmor.d/ && \
        cd /tmp/apparmor-qubes/stable/xdg-open/ && \ 
        mv open /etc/apparmor.d/'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: install-brave

enable-brave-apparmor:
  qvm.run:
    - name: debian-apparmor-brave
    - cmd: |
        /bin/bash -c 'apparmor_parser -r /etc/apparmor.d/brav* && \
        apparmor_parser -r /etc/apparmor.d/Xorg && \
        apparmor_parser -r /etc/apparmor.d/qubes* && \
        apparmor_parser -r /etc/apparmor.d/nautilus && \ 
        apparmor_parser -r /etc/apparmor.d/open'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: setup-apparmor-profiles-dkzkz

cleanup-apparmor-repo:
  qvm.run:
    - name: debian-apparmor-brave 
    - cmd: 'rm -rf /tmp/apparmor-qubes'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: enable-brave-apparmor

create-brave-dir:
  qvm.run:
    - name: debian-apparmor-brave
    - cmd: 'mkdir -p /opt/brave.com/brave'
    - user: root
    - flags:
      - nogui		
      - pass-io
    - require:
      - qvm: cleanup-apparmor-repo

setup-proxy-wget:
  qvm.run:
    - name: debian-apparmor-brave
    - cmd: |
        /bin/bash -c '
        echo "proxy = http://127.0.0.1:8082" >> ~/.curlrc
        echo "use_proxy = on" >> ~/.wgetrc
        echo "http_proxy = 127.0.0.1:8082" >> ~/.wgetrc
        echo "https_proxy = 127.0.0.1:8082" >> ~/.wgetrc'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: create-brave-dir

write-initial-preferences:
  qvm.run:
    - name: debian-apparmor-brave
    - cmd: |
        /bin/bash -c 'cd /tmp && \
        wget https://codeberg.org/dkzkz/apparmor-qubes/raw/branch/main/install/salt/debian/apparmor/brave-browser/brave-browser.sls/initial_preferences && \
        mv ini* /opt/brave.com/brave/' 
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: setup-proxy-wget

create-policy-dir:
  qvm.run:
    - name: debian-apparmor-brave
    - cmd: 'mkdir -p /etc/brave/policies/managed'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: write-initial-preferences

write-group-policy:
  qvm.run:
    - name: debian-apparmor-brave
    - cmd: |
        /bin/bash -c 'cat <<EOF >/etc/brave/policies/managed/GroupPolicy.json
        {
          "MetricsReportingEnabled": false
        }
        EOF'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: create-policy-dir

make-skel-dir:
  qvm.run:
    - name: debian-apparmor-brave
    - cmd: 'mkdir -p /etc/skel/.config/BraveSoftware/Brave-Browser'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: write-group-policy

write-skel-local-state:
  qvm.run:
    - name: debian-apparmor-brave
    - cmd: |
        /bin/bash -c 'cat <<EOF | sudo tee /etc/skel/.config/BraveSoftware/Brave-Browser/Local\ State > /dev/null
        {
        "background_mode": {"enabled": false},
        "hardware_acceleration_mode": {"enabled": false},
        "brave": {
        "p3a": {"enabled": false},
        "stats": {"reporting_enabled": false}
        },
        "default_apps": {"extensions": {}},
        "extensions": {
        "theme": {"colors": {}}
        },
        "sync_promo": {"user_skipped": true}
        }
        EOF'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: make-skel-dir

shutdown-template:
  qvm.shutdown:
    - name: debian-apparmor-brave
    - require:
      - qvm: write-skel-local-state

set-template-colour:
  qvm.prefs:
    - name: debian-apparmor-brave
    - label: black
    - require:
      - qvm: shutdown-template

create-dvm:
  qvm.present:
    - name: brave-browser-dvm
    - template: debian-apparmor-brave
    - label: yellow
    - require:
      - qvm: set-template-colour
      
config-firewall:
  cmd.run:
    - name: |
        printf "action=accept specialtarget=dns
        action=accept proto=tcp dstports=443
        action=accept proto=tcp dstports=80
        action=accept proto=tcp dstports=9050
        action=drop" | qubesd-query dom0 admin.vm.firewall.Set brave-browser-dvm

config-brave-dvm:
  qvm.prefs:
    - name: brave-browser-dvm
    - memory: 300
    - maxmem: 3000
    - template-for-dispvms: true
    - require:
      - qvm: create-dvm

enable-brave-appmenus-dispvm:
  qvm.features:
    - name: brave-browser-dvm
    - enable:
      - service.apparmor
      - appmenus-dispvm
    - require:
      - qvm: config-brave-dvm

hardening-dvm:
  qvm.features:
    - name: brave-browser-dvm
    - set:
      - anon-timezone: '1'
      - menu-items: brave-browser.desktop Alacritty.desktop 
    - require:
      - qvm: enable-brave-appmenus-dispvm
debian-mullvad-apparmor.sls
start-debian-minimal: 
  qvm.start:
    - name: debian-13-minimal

update-debian-minimal:
  qvm.run:
    - name: debian-13-minimal
    - cmd: |
        /bin/bash -c 'apt-get update && apt-get -y full-upgrade'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: start-debian-minimal

shutdown-debian-minimal:
  qvm.shutdown:
    - name: debian-13-minimal
    - require:
      - qvm: update-debian-minimal

setup-transport:
  qvm.start:
    - name: debian-13-minimal
    - require:
      - qvm: shutdown-debian-minimal    

fix-locale:
  qvm.run:
    - name: debian-13-minimal
    - cmd: |
        /bin/bash -c "sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && /usr/sbin/locale-gen" && \
        sed -i 's/^# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
        /usr/sbin/locale-gen
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: setup-transport


setup-tor:
  qvm.run:
    - name: debian-13-minimal
    - cmd: |
        /bin/bash -c 'apt-get -y install apt-transport-tor alacritty'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: fix-locale

minimize-debian:
  qvm.run:
    - name: debian-13-minimal
    - cmd: |
        /bin/bash -c 'apt-get -y purge \
        apt-transport-https \
        apt-utils \
        cpio \
        cron \
        cron-daemon-common \
        debconf-i18n \
        dhcpcd-base \
        eatmydata \
        fdisk \
        ifupdown \
        iproute2 \
        iputils-ping \
        less \
        libcap2-bin \
        libeatmydata1 \
        libfdisk1 \
        libgcrypt20 \
        libidn2-0 \
        libjansson4 \
        libk5crypto3 \
        libkeyutils1 \
        libkrb5support0 \
        libmnl0 \
        libnewt0.52 \
        libp11-kit0 \
        libsemanage-common \
        libsepol2 \
        libslang2 \
        libtext-iconv-perl \
        libtirpc-common \
        libxtables12 \
        logrotate \
        nftables \
        tasksel \
        whiptail \
        xterm && apt-get -y autoremove'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: setup-tor

shutdown-after-tor:
  qvm.shutdown:
    - name: debian-13-minimal
    - require:
      - qvm: minimize-debian

clone-base-apparmor:
  qvm.clone:
    - name: base-apparmor
    - source: debian-13-minimal
    - require:
      - qvm: shutdown-after-tor

config-apt:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'cat <<EOF >/etc/apt/sources.list
        deb tor+https://deb.debian.org/debian trixie main contrib non-free-firmware
        deb tor+https://deb.debian.org/debian-security trixie-security main contrib non-free-firmware
        deb tor+https://deb.debian.org/debian trixie-backports main
        EOF'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: clone-base-apparmor

enable-dark-theme:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'cat <<EOF >/etc/environment
         GTK_THEME=Adwaita:dark
         QT_QPA_PLATFORMTHEME=qt5ct
         QT_STYLE_OVERRIDE=Adwaita-dark               
        EOF' 
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: config-apt

random-hostname:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'cat > /etc/rc.local << '"'"'EOF'"'"'
        #!/bin/bash
        # Anonymize hostname with random name at boot
        
        RANDOM_HOSTNAME=$(cat /dev/urandom | tr -dc "a-z" | head -c 12)
        echo "$RANDOM_HOSTNAME" > /etc/hostname
        hostname "$RANDOM_HOSTNAME"
        sed -i "s/^127\.0\.0\.1.*/127.0.0.1 $RANDOM_HOSTNAME localhost/" /etc/hosts
        hostnamectl set-hostname "$RANDOM_HOSTNAME" 2>/dev/null || true
        EOF'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: enable-dark-theme


random-hostname-exec:
  qvm.run:
    - name: base-apparmor
    - cmd: chmod +x /etc/rc.local
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: random-hostname


config-apt-onion-qubes:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'cat <<EOF >/etc/apt/sources.list.d/qubes-r4.list 
        deb [arch=amd64 signed-by=/usr/share/keyrings/qubes-archive-keyring-4.3.gpg] tor+http://deb.qubesosfasa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion/r4.3/vm trixie main
        # Backup HTTPS repository (commented)
        #deb [arch=amd64 signed-by=/usr/share/keyrings/qubes-archive-keyring-4.3.gpg ] https://deb.qubes-os.org/r4.3/vm trixie main"
        EOF'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: random-hostname-exec


set-pinning-backports:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'cat <<EOF >/etc/apt/preferences.d/backports
        Package: *
        Pin: release n=trixie-backports
        Pin-Priority: 900
        EOF'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: config-apt-onion-qubes

set-pinning-stable:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'cat <<EOF >/etc/apt/preferences.d/stable
        Package: *
        Pin: release n=stable
        Pin-Priority: 500
        EOF'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: set-pinning-backports

update-base-apparmor:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'apt-get update && apt-get -y full-upgrade'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: set-pinning-backports
      - qvm: set-pinning-stable

restart:
  qvm.shutdown:
    - name: base-apparmor
    - require:
      - qvm: update-base-apparmor
 
start-again:
  qvm.start:
    - name: base-apparmor
    - require:
      - qvm: restart

add-unstable-repo:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'cat <<EOF >>/etc/apt/sources.list
        deb tor+https://deb.debian.org/debian/ unstable main
        EOF'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: start-again

create-apparmor-pinning:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'cat <<EOF >/etc/apt/preferences.d/unstable-pin
        Package: apparm* python*-appar* python*-libappar*
        Pin: release a=unstable
        Pin-Priority: 990
        Package: *
        Pin: release a=unstable
        Pin-Priority: 1
        EOF'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: add-unstable-repo

update-indexes-after-unstable:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'apt update'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: create-apparmor-pinning

install-apparmor:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'apt install -y -t unstable apparmor apparmor-utils apparmor-profiles apparmor-profiles-extra'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: update-indexes-after-unstable

shutdown-before-kernelopts:
  qvm.shutdown:
    - name: base-apparmor
    - require:
      - qvm: install-apparmor

set-apparmor-kernelopts:
  qvm.prefs:
    - name: base-apparmor
    - kernelopts: 'swiotlb=2048 apparmor=1 security=apparmor'
    - require:
      - qvm: shutdown-before-kernelopts

restart-base-apparmor:
  qvm.start:
    - name: base-apparmor
    - require:
      - qvm: set-apparmor-kernelopts

minimize-git:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c 'apt-get install --no-install-recommends -y git'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: restart-base-apparmor

speed-boost-apparmor:
  qvm.run:
    - name: base-apparmor
    - cmd: |
        /bin/bash -c '
        tee -a /etc/apparmor/parser.conf > /dev/null <<EOF
        write-cache
        cache-loc /etc/apparmor/earlypolicy/
        Optimize=compress-fast
        EOF
        '
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: minimize-git

shutdown-base-apparmor-before-clone:
  qvm.shutdown:
    - name: base-apparmor
    - require:
      - qvm: speed-boost-apparmor


clone-debian-13-minimal-to-mullvad-template:
  qvm.clone:
    - name: debian-apparmor-mullvad
    - source: base-apparmor
    - flags:
      - shutdown
    - require:
      - qvm: shutdown-base-apparmor-before-clone


# Start the cloned template
start-mullvad-template:
  qvm.start:
    - name: debian-apparmor-mullvad
    - require:
      - qvm: clone-debian-13-minimal-to-mullvad-template


basic-tool-needed:
  qvm.run:
    - name: debian-apparmor-mullvad
    - cmd: 'apt-get -y install curl wget'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: start-mullvad-template 


setup-proxy-wget:
  qvm.run:
    - name: debian-apparmor-librewolf
    - cmd: |
        /bin/bash -c '
        echo "proxy = http://127.0.0.1:8082" >> ~/.curlrc
        echo "use_proxy = on" >> ~/.wgetrc
        echo "http_proxy = 127.0.0.1:8082" >> ~/.wgetrc
        echo "https_proxy = 127.0.0.1:8082" >> ~/.wgetrc'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: basic-tool-needed


# Download Mullvad signing key
download-mullvad-key:
  qvm.run:
    - name: debian-apparmor-mullvad
    - cmd: 'bash -c "export all_proxy=http://127.0.0.1:8082/ && curl -fsSLo /usr/share/keyrings/mullvad-keyring.asc https://repository.mullvad.net/deb/mullvad-keyring.asc"'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: setup-proxy-wget

# Add Mullvad repository
add-mullvad-repository:
  qvm.run:
    - name: debian-apparmor-mullvad
    - cmd: |
        /bin/bash -c 'echo "deb [signed-by=/usr/share/keyrings/mullvad-keyring.asc arch=$( dpkg --print-architecture )] tor+https://repository.mullvad.net/deb/stable stable main" | tee /etc/apt/sources.list.d/mullvad.list'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: download-mullvad-key

# Update package indexes for Mullvad
update-for-mullvad:
  qvm.run:
    - name: debian-apparmor-mullvad
    - cmd: 'apt update'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: add-mullvad-repository

# Install Mullvad Browser, nautilus, and dependencies, then remove gnome-keyring
install-mullvad-browser-nautilus:
  qvm.run:
    - name: debian-apparmor-mullvad
    - cmd: |
        /bin/bash -c 'apt-get -y install mullvad-browser ffmpeg libavcodec-extra pipewire-qubes nautilus qubes-core-agent-networking zenity qubes-core-agent-nautilus && \
        apt-get -y purge gnome-keyring && \
        apt-get -y autoremove'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: update-for-mullvad


setup-apparmor-profiles-dkzkz:
  qvm.run:
    - name: debian-apparmor-mullvad
    - cmd: |
        /bin/bash -c 'export all_proxy=http://127.0.0.1:8082/ && \
        cd /tmp && \
        git clone https://codeberg.org/dkzkz/apparmor-qubes && \
        cd /tmp/apparmor-qubes/stable/browser/mullvad-browser && \ 
        mv mullv* /etc/apparmor.d/ && \ 
        mv /tmp/apparmor-qubes/stable/file-manager/nautilus /etc/apparmor.d/ && \ 
        mv /tmp/apparmor-qubes/stable/display-vm/Xorg /etc/apparmor.d/Xorg && \ 
        cd /tmp/apparmor-qubes/stable/qubes-scripts/ && \ 
        mv q* /etc/apparmor.d/ && \
        cd /tmp/apparmor-qubes/stable/xdg-open/ && \ 
        mv open /etc/apparmor.d/'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: install-mullvad-browser-nautilus

enable-mullvad-apparmor:
  qvm.run:
    - name: debian-apparmor-mullvad
    - cmd: |
        /bin/bash -c 'apparmor_parser -r /etc/apparmor.d/mullvad* && \
        apparmor_parser -r /etc/apparmor.d/Xorg && \
        apparmor_parser -r /etc/apparmor.d/qubes* && \
        apparmor_parser -r /etc/apparmor.d/nautilus && \ 
        apparmor_parser -r /etc/apparmor.d/open'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: setup-apparmor-profiles-dkzkz


remove-cloned-repository:
  qvm.run:
    - name: debian-apparmor-mullvad
    - cmd: 'rm -rf /tmp/apparmor-qubes'
    - user: root
    - flags:
      - nogui
      - pass-io
    - require:
      - qvm: enable-mullvad-apparmor

# Shutdown template after AppArmor configuration
shutdown-mullvad-template:
  qvm.shutdown:
    - name: debian-apparmor-mullvad
    - require:
      - qvm: remove-cloned-repository

# Set template color to black
set-template-color-black:
  qvm.prefs:
    - name: debian-apparmor-mullvad
    - label: black
    - require:
      - qvm: shutdown-mullvad-template

# Create AppVM from template as a disposable template
create-apparmor-mullvad-browser-dvm:
  qvm.present:
    - name: mullvad-browser-dvm
    - template: debian-apparmor-mullvad
    - label: yellow
    - require:
      - qvm: set-template-color-black


config-firewall:
  cmd.run:
    - name: |
        printf "action=accept specialtarget=dns
        action=accept proto=tcp dstports=443
        action=accept proto=tcp dstports=80
        action=drop" | qubesd-query dom0 admin.vm.firewall.Set mullvad-browser-dvm

# Set as disposable template
configure-mullvad-dvm:
  qvm.prefs:
    - name: mullvad-browser-dvm
    - template-for-dispvms: true
    - memory: 300
    - maxmem: 3000
    - require:
      - qvm: create-apparmor-mullvad-browser-dvm

# Enable appmenus-dispvm feature and set menu-items
enable-mullvad-appmenus-dispvm:
  qvm.features:
    - name: mullvad-browser-dvm
    - enable:
      - appmenus-dispvm
      - service.apparmor
    - require:
      - qvm: configure-mullvad-dvm

# Add mullvad-browser.desktop and xterm + hardening features to the menu
add-mullvad-browser-menu:
  qvm.features:
    - name: mullvad-browser-dvm
    - set:
      - anon-timezone: '1'
      - menu-items: mullvad-browser.desktop Alacritty.desktop 
    - require:
      - qvm: enable-mullvad-appmenus-dispvm

I provide only two files because there is more than ~ 3 500 lines for all salt files and if i have to edit the post to update the content it would be painful for me almost impossible

To get the kicksecure version only brave for now go on my repo
To get the debian version go on my codeberg repo
To get the fedora version go on my codeberg repo
To get the fedora Selinux version go on my codeberg repo

Technical details information

Every files have the same logic.

  1. Update the original template , shutdown the original template clone the template , install packages and config inside the cloned template , shutdown the cloned template , create dvm
  2. Debian and kicksecure files is using the onion repository of Qubes to update the system
  3. To allow users to use tor in brave-browser i added the tcp 9050 ports in the firewall for the brave file
  4. Debian is using the backports repository mixed with the stable repository to get recent packages and fix
  5. Debian and kicksecure is using the unstable version of apparmor because of this issue
  6. The brave hardened value and arkenfox js profile is fetched from my codeberg repo i didn’t touch anything to the original files
  7. Qubist provided a list of packages that can be removed in debian-13 i didn’t removed nano , vim perl, gnupg
  8. IMCP request will not work due to the qvm-firewall rules i applied (90% of software doesn’t need it anyway)
  9. In debian template i removed xterm in favor of Alacritty terminal because xterm is ugly , slow , and hard to deal with. But you can remove alacritty in the installation process if you want
  10. I probably forget some details but you can inspect the salt content if details are missing i will add them later

Know issue :

Missing :

:x: A salt file to automatically update the apparmor profile from my repository to the template (i will do it soon)
:x: Librewolf is not in dark mode on fedora or debian i forget which one
:x: I’m not really sure if the qvm-features anon-timezone 1 is properly working in my testing i get the same issue as Anon-timezone doesnt work for me i enabled anon-timezone 1 for every salt file but my timezone didn’t change when i tested on website like https://browserleaks.com/

Feel free to contact me on element @aatrfs76519:nope.chat for any suggestion about the guide

The curl and wget proxy
The dark theme
Brave browser hardened is coming from here
To setup arkenfox js and ublock for firefox i used my own guide
The qvm-firewall is coming from this discussion

9 Likes

Always good to see some salt in the Forum. I think you could simplify
and clarify this by using native salt states.

I dont think you need to start and stop the targets - qubesctl
will take care of that, although there may be cases where you want to
explicitly do this.

Instead of using cmd.run to call apt, there is native states.pkg

install-apparmor: 
  pkg.installed:
    - name: base-apparmor
    - refresh: True
    - pkgs:
      - apparmor
      - apparmor-utils
      -  apparmor-profiles
      -  apparmor-profiles-extra

Using refresh: True will refresh the package database before
installing.

You can also use

update:
  pkg.uptodate:
    - refresh: True

instead of apt update and apt upgrade

Salt also allows you to manipulate files using file states
When you’re changing the repository definitions, you can simply comment
and uncomment lines in the Qubes definitions, like this:

onion:
  file.uncomment:
    - name: /etc/apt/sources.list.d/qubes-r4.list
    - regex: "^deb\ .*onion.*\ trixie\ main"

There are a host of existing salt states to manipulate repositories,
files,etc, as well as the Qubes modules. If you need any pointers just
ask.

6 Likes

Oh wow thanks you for your suggestion to improve the salt i really appreaciate i didn’t know about a lot of things you said i have so much to learn !

In my testing qubesctl do not do it by default or maybe i did something wrong ?

Yes please i’d like you do it

Excellent idea. I was also thinking about creating a script for automatically building hardened templates: a hardened Debian or Kicksecure‑minimal with Mullvad browser, and a hardened Fedora 43 (minimal) with the Trivalent browser.
I will keep an eye on your work. Good luck to you. Later I will add your guide to my live‑hardening guide.
Continue creating simple scripts for your guides. Beginners really like that, and it boosts the number of guide users.
By the way, You can also publish and modify my hardened live‑dom0 script on Codeberg and you can always use it in your scripts. I’d be happy about it :slightly_smiling_face:

1 Like

I also confirm that this command doesn’t work for me on debian 13 (only fedora 43 and kicksecure)

Thanks for the feedback i completely forget to do the same thing for trivalent browser i will do it ! If you want we can discuss about how we can improve the salt setup in element it would be great.

Oh but this is already done. In the wiki of my repository, i have put a list of useful guides from the Qubes Forum your guide is there but maybe i should add more useful guide Pages - dkzkz/apparmor-qubes - Codeberg.org

Wait there is a template for kicksecure minimal ? I was looking for this

Thanks for the feedback again now it confirm the problem is coming somewhere else i hope the dev will fix the bug

1 Like

Adding the Trivalent browser is great - thank you! But I also need to sort out adding SELinux, which is a core part of Fedora’s hardening (I’m not strong in that area; I’ve used Debian my whole life :slightly_smiling_face:).
Salt is still new to me. I’m studying it and will continue learning, including through your scripts :slightly_smiling_face:

There is no Kicksecure‑minimal template. My idea was to create such a template by morph a debian‑minimal Debian template morph to Kicksecure. Hardening Debian template

1 Like

To enable selinux you can follow the guide here How to install & activate SELinux on minimal fedora templates? - #2 by Gandalfen it’s easy to do i tested it on fedora-42 it was working but i didn’t test on fedora-43

I could add a selinux version in my salt but i don’t know how stable is selinux under Qubes… i don’t know if it’s worth it to do that

2 Likes

I created a selinux version of the installation here

This will install the browser with selinux enabled

I also added the trivalent browser from secureblue and, i fixed the hostname script now it work correctly in fedora

Now i think i almost finished with salt the only thing i could do is improving the syntax and add the same installation file but for noob users but that’s all

1 Like

Excellent work! I will testing it tomorrow or the day after

1 Like

Add a new features in the salt installation kernel hardening from tails & secureblue

Now when you use the salt installation the AppVM will provide you three choices of boot mode

  1. Default boot mode without any hardening features
  2. Tails kernel hardening
  3. Secureblue kernel hardening

By using the boot mode from tails or secureblue you must except some performance issue in favor of improving the security of your VM

If you want to be sure it’s working open a terminal in your vm and enter :

cat /proc/cmdline

I didn’t touch anything from the original values i copy and paste the same value from tails & secureblue but i think we can remove some value in secureble (?)

In case your AppVM doesn’t work you can easily switch the kernel boot mode in the settings like in the screen (don’t forget to click on “Apply

ISSUE : The tails kernel hardening boot mode doesn’t work yet i need to investigate where the issue is coming from in /var/log/xen/console/guest-test.log

ISSUE n2 : Sounds is not playing on trivalent i don’t know if it’s coming from fedora, selinux , the kernel hardening , or the browser himself i will investigate this later it’s not a priority

Only secureblue kernel hardening works perfectly for Debian & Fedora

If you don’t want to reinstall everything and want the secureblue kernel in dom0 enter this :


 qvm-features (name of your vm) boot-mode.kernelopts.secureblue "hash_pointers=always init_on_alloc=1 init_on_free=1 iommu=force intel_iommu=on iommu.passthrough=0 iommu.strict=1 kvm_amd.sev=1 kvm_amd.sev_es=1 kvm_amd.sev_snp=1 kvm-intel.vmentry_l1d_flush=always kvm.mitigate_smt_rsb=1 l1d_flush=on l1tf=full,force lockdown=confidentiality loglevel=0 mitigations=auto,nosmt module.sig_enforce=1 page_alloc.shuffle=1 proc_mem.force_override=ptrace pti=on random.trust_bootloader=off random.trust_cpu=off randomize_kstack_offset=on rd.shell=0 rd.emergency=halt slab_debug=FZ slab_nomerge spec_store_bypass_disable=on spectre_v2=on ssbd=force-on systemd.ssh_auto=no vdso32=0 vsyscall=none"

The issue with the tails kernel hardening will be fixed probably today or tomorrow it’s not that hard to fix

2 Likes

Tails boot mode has been fixed the issue was coming from qvm-features for some reason qvm wouldn’t accept the first syntax i provided

To use the tails kernel hardening boot mode in dom0 enter this :

 qvm-features (name of your vm) boot-mode.kernelopts.tails "randomize_kstack_offset=on net.core.bpf_jit_harden=2 spec_store_bypass_disable=on mds=full,nosmt kernel.kexec_load_disabled=1 init_on_free=1 slab_nomerge slub_debug=FZ vsyscall=none mce=0 page_alloc.shuffle=1 kernel.kptr_restrict=2 vm.mmap_rnd_bits=32 vm.mmap_rnd_compat_bits=16" 

Still missing in the repository

  1. Salt files for noob users
  2. A hook script in the fedora mullvad template to fix the exec for dvm after each update of mullvad browser
  3. Improve the syntax of every salt files
  4. I think i could harden a little bit the template by adding some sysctl value and some other things i need to think about it carefully