Ich verwende Firefox als Webbrowser und schon eh und je ist er eine besondere Anwendung, bei der ich zum Beispiel früher am OOM-Killer-Wert gedreht habe, damit Firefox vor anderen wichtigen Programmen beendet wird. Aber auch heute noch hat Firefox einiges Verhalten, weshalb ich ihn besonders behandele.

Verwaltet von Systemd

Mit systemd-run kann man Prozesse von Systemd überwachen lassen. Mit der Option --user wird dafür der Benutzerdienst und nicht der Systemdient genutzt. Es ist ähnlich wie man mit Cron Prozesse zu einer bestimmten Zeit ausführen kann, die dann von Cron überwacht werden.

Der Vorteil an der Überwachung mit Systemd sind die vielen Funktionen, die Systemd bietet. Unter anderem werden die Ausgaben der Prozesse ins Journal geschrieben und verschwinden nicht im Nirgendwo oder landen ohne Zuordnung in ~/.xsession-errors.

srun

Als Hilfe habe ich mir ein Skript srun in ~/bin angelegt, um die Optionen besser kontrollieren und das Skript auch für andere Programme einsetzen zu können. Das gute an systemd-run ist, dass es die Prozesse in einer bereinigten Prozessumgebung startet. Daher werden bei mir jedoch auch einige wichtige Variablen nicht übernommen, die ich mithilfe der Option übergebe:

#!/bin/sh

if test -n "$MANAGERPID$INVOCATION_ID"
then
    # we are already under control of Systemd
    exec "$@"
fi

exec systemd-run --user --quiet --collect \
  --setenv=PATH=$HOME/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
  --setenv=GDK_SCALE=$GDK_SCALE --setenv=GDK_DPI_SCALE=$GDK_DPI_SCALE \
  --setenv=QT_AUTO_SCREEN_SCALE_FACTOR=$QT_AUTO_SCREEN_SCALE_FACTOR \
  ${DESKTOP_STARTUP_ID:+--setenv=DESKTOP_STARTUP_ID=$DESKTOP_STARTUP_ID} \
  ${XDG_RUNTIME_DIR:+--setenv=XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR} \
  "$@"

Die Variablen mit GDK und QT sind bei mir für den HiRes-Display notwendig. Die DESKTOP_STARTUP_ID ist notwendig, damit der Fenster-Manager AwesomeWM die Fenster einem bestimmten Prozess zuordnen kann. Durch --collect bleiben fehlerhaft beendete Prozesse nicht als Eintrag bei Systemd hängen und würden zukünftige Aufrufe blockieren.

ff

Zum Starten von Firefox verwende ich bereits das Skript ff, um besser meine Browser-Profile verwalten zu können. Dieses Skript habe ich jetzt um den Aufruf von srun erweitert. Um besser im Journal die Firefox-Instanzen unterscheiden zu können, habe ich den Units, die automatisch von systemd-run erzeugt werden, entsprechende Namen gegeben. Die normale Firefox-Instanz heißt ff, die für die Bank ff-bank u. s. w.

Da eine Unit nur ein Mal gestartet werden darf und ein weiterer Aufruf von Firefox nur den Hauptprozess kontaktiert und eine Seite öffnen lässt, soll nur der erste Aufruf tatsächlich mit srun geschehen:

if systemctl --user is-active $unit >/dev/null
then
    exec firefox "$@"
else
    exec srun --unit=$unit --property=SyslogIdentifier=$unit firefox "$@"
fi

Möglichkeiten mit AppArmor begrenzen

Systemd bietet die Option, für eine Unit ein AppArmor-Profil festzulegen. Da ich unterschiedliche Browser-Profile nutze, um Online-Bank, Matrix, Entwicklung und Nightly vom normalen Surfen zu trennen, benötige ich auch unterschiedliche AppArmor-Regeln.

Die Entwickler- und die Nightly-Version habe ich in eigenen Verzeichnissen abgelegt, die in den Profilen variieren. Jede Instanz soll dabei nur auf ihr eigenen Firefox-Profilverzeichnis zugreifen können und in einigen Profilen ist Sound und Drucken erlaubt.

Anfangs habe ich mehrere Dateien für die Profile verwendet, gemeinsame Bestandteile ausgelagert und über include jeweils eingebunden. Jedoch entwickelten sich die Anforderungen an die Profile so komplex, dass ich das Skript gen-ff-aa-profiles zur Erzeugung der Profile geschrieben habe.

Darin wird eine Funktion ff_profile definiert, der der Profilname, optional das Programmverzeichnis von Firefox, Parameter für bestimmte Regeln und am Ende weitere Einträge für das Profil übergeben werden.

ff_profile default deny_pinger '
  include <abstractions/cups-client>

  owner @{HOME}/.mozilla/native-messaging-hosts/* r,
  /home/joerg/git/keepassxc/build/src/proxy/keepassxc-proxy Ux,

  owner @{HOME}/.config/.keysnail.js r,
  owner @{HOME}/Downloads/{,**} rw,

  deny $ff_dir/update.test{,/} w,
'

In meinem Online-Bank-Profil will ich weder Musik hören, noch drucken und nur Zugriff auf das Verzeichnis ~/Bank haben, damit ich dort Dateien speichern kann.

ff_profile bank deny_audio deny_pinger deny_fs_browsing '
  owner @{HOME}/Bank/{,**} rw,
'

Alle Profile lasse ich gemeinsam in eine Datei schreiben und lade diese mit AppArmor. Mit der zusätzlichen Option -C bei für AppArmor werden die Regeln nicht erzwungen, aber Meldungen bei Verstößen ausgegeben – ein Testmodus.

gen-ff-aa-profiles > /etc/apparmor.d/firefox && apparmor_parser -r /etc/apparmor.d/firefox

Beim Aufruf von srun in ff musste ich nur noch die Option -p AppArmorProfile=$unit ergänzen.

Für die Entwicklerversion und die Nightly-Version sehen die Profile ähnlich aus. Zu der Entwicklerversion ist noch zu sagen, dass Firefox immer versucht ein zusätzliches Profil anzulegen, was man aber durch die leere Datei ~/.mozilla/firefox/ignore-dev-edition-profile unterbinden kann.

ff_profile dev /home/joerg/kein_Backup/firefox-dev allow_updater '
  owner @{HOME}/.mozilla/native-messaging-hosts/* r,
  /home/joerg/git/keepassxc/build/src/proxy/keepassxc-proxy Ux,

  owner @{HOME}/Downloads/{,**} rw,
'

Fazit

Ein Browser wie Firefox ist durch das Laden von unbekannten Inhalten aus dem Internet und die Ausführung von Javascript einer sehr großen Bedrohung ausgesetzt. Um dem Nutzer viele Funktionen bieten zu können, braucht der Browser andererseits viele Rechte: Dateisystemzugriff, Zugriff auf Sound- und Video-Geräte, Ausführung von Programmen.

Wer jedoch seine Nutzung des Browsers und der Profile genauer spezifiziert, kann mit systemd-run und AppArmor Firefox besser einhegen, um im Falle eines Fehlverhaltens – das im Web nicht abwegig ist – den Schaden zu begrenzen.