Journal Sandboxer des applications avec bubblewrap (3/3) : Script de gestion

Posté par  . Licence CC By‑SA.
Étiquettes :
25
8
jan.
2024

(Écrit par moi en anglais, traduit à 95% par ChatGPT)

Précédemment dans cette série, nous avons découvert comment utiliser bubblewrap pour sandboxer des applications simples. Ensuite, nous sommes passés à des applications plus complexes, et nous avons conclu que, même si cela fonctionne, les lignes de commande longues devenaient très encombrantes.

Je vais maintenant vous présenter le script (peu imaginativement appelé sandbox) que j'utilise pour sandboxer mes applications. Son fichier de configuration se trouve à ~/.config/sandbox.yml.

Il commence par les ressources : principalement des liaisons de chemins, mais aussi des variables d'environnement et des services D-Bus. Un préréglage est un ensemble nommé de ressources. Vous pouvez ensuite associer des préréglages aux applications à l'aide de règles.

Commençons par un préréglage très basique, reflétant la toute première commande que nous avons sandboxée, plus quelques éléments de base de la deuxième partie :

presets:
  common:
    - args: [--clearenv, --unshare-pid, --die-with-parent, --proc, /proc, --dev, /dev, --tmpfs, /tmp, --new-session]
    - setenv: [PATH, LANG, XDG_RUNTIME_DIR, XDG_SESSION_TYPE, TERM, HOME, LOGNAME, USER]
    - ro-bind: /etc
    - ro-bind: /usr
    - args: [--symlink, usr/bin, /bin, --symlink, usr/bin, /sbin, --symlink, usr/lib, /lib, --symlink, usr/lib, /lib64, --tmpfs, "{env[XDG_RUNTIME_DIR]}"]
    - bind: /run/systemd/resolve

Rien ne devrait vous surprendre ici à ce stade. Utilisons ce préréglage :

$ sandbox -p common zsh
$

Juste après cela, nous avons créé un home sandboxé par application. Faisons également un préréglage pour cela :

  private-home:
    - bind: ["{env[HOME]}/sandboxes/{executable}/", "{env[HOME]}"]
      bind-create: true
    - dir: "{env[HOME]}/.config"
    - dir: "{env[HOME]}/.cache"
    - dir: "{env[HOME]}/.local/share"

Vous pouvez l'essayer :

$ sandbox -p common -p private-home zsh
$

(notez que toutes les instances de zsh partageront le même home sandboxé)

Définissons maintenant quelques préréglages supplémentaires pour les applications de bureau. Encore une fois, rien de surprenant si vous avez suivi les articles précédents (sauf que nous n'avons pas à gérer explicitement la variable d'environnement DBUS_SESSION_BUS_ADDRESS ou le socket xdg-dbus-proxy : le script s'en charge pour nous dès que nous utilisons une ressource dbus-*) :

  x11:
    - setenv: [DISPLAY]
    - ro-bind: /tmp/.X11-unix/
  wayland:
    - setenv: [WAYLAND_DISPLAY]
    - ro-bind: "{env[XDG_RUNTIME_DIR]}/{env[WAYLAND_DISPLAY]}"
  pulseaudio:
    - ro-bind: "{env[XDG_RUNTIME_DIR]}/pulse/native"
    - ro-bind-try: "{env[HOME]}/.config/pulse/cookie"
    - ro-bind-try: "{env[XDG_RUNTIME_DIR]}/pipewire-0"
  drm:
    - dev-bind: /dev/dri
    - ro-bind: /sys
  portal:
    - file: ["", "{env[XDG_RUNTIME_DIR]}/flatpak-info"]
    - file: ["", "/.flatpak-info"]
    - dbus-call: "org.freedesktop.portal.*=*"
    - dbus-broadcast: "org.freedesktop.portal.*=@/org/freedesktop/portal/*"

Maintenant que nous avons terminé avec les préréglages, nous pouvons définir des règles. Les applications peuvent être associées implicitement, à partir du nom de l'exécutable lancé, ou explicitement. Commençons par une règle implicite (basée sur le nom de l'exécutable), par exemple pour firefox :

rules:
  - match:
      bin: firefox
    setup:
      - setenv:
          MOZ_ENABLE_WAYLAND: 1
      - use: [common, private-home, wayland, portal]
      - dbus-own: org.mozilla.firefox.*
      - bind: "{env[HOME]}/Downloads"
      - bind: ["{env[HOME]}/.config/mozilla", "{env[HOME]}/.mozilla"]

Maintenant, exécuter sandbox firefox reliera ~/Downloads et configurera les préréglages communs, private-home, wayland et portal. Je relie également sa configuration (~/.mozilla dans l'environnement sandboxé) à mon répertoire ~/.config/firefox (car j'essaie de garder mon répertoire ~/sandboxes jetable, sans y conserver des choses importantes).

Présentons une règle explicite :

  - match:
      name: shell
    setup:
      - use: [common, private-home]

Cette règle ne sera jamais associée automatiquement ; elle doit être associée manuellement par sandbox --name shell zsh. Elle aura son propre home privé dans ~/.sandboxes/shell. J'ai tendance à l'utiliser pour les commandes CLI que je veux exécuter en sandbox, mais pour lesquelles je ne me soucie pas de leur état persistant (et que je n'exécute pas souvent).

Un ensemble intéressant de règles est celui que j'utilise pour node/npm, où je veux que la commande puisse utiliser le répertoire courant, mais presque rien d'autre :

  - match:
      bin: node
    setup:
      - use: [common, private-home]
      - bind-cwd: {}
      - cwd: true

  - match:
      bin: npx
    setup:
      - use: [common, private-home]
      - bind-cwd: {}
      - cwd: true

  - match:
      bin: npm
    setup:
      - use: [common, private-home]
      - bind-cwd: {}
      - cwd: true

De cette façon, je peux simplement exécuter sandbox npm ci ou sandbox npm run ... dans mon répertoire actuel, et les choses fonctionneront simplement (tout en étant sandboxées).

Vous pouvez également définir une règle par défaut, de secours :

  - match:
      name: none

  # Fallback: anything else fall backs to a sandboxed empty home
  - setup:
    - use: [common, private-home, x11, wayland, pulseaudio, portal]

La règle none, c'est si vous ne voulez pas utiliser la règle par défaut pour une commande inconnue ; dans ce cas, vous pouvez faire sandbox --name none -p common --tmpfs ~ ...

C'est à peu près tout. Une chose à retenir est d'utiliser -- pour séparer les arguments de sandbox des arguments sandboxés : sandbox -- npm --save install ...

Je vous présente ce script sous forme d'un simple Gist avec uniquement ce billet de blog comme documentation. Il y a probablement suffisamment de choses à faire ici pour l'opportunité de développer tout un projet sur la base de ce script. Je ne serai pas celui qui le fera. Si une âme courageuse et motivée souhaite entreprendre cette tâche, allez-y, vous avez ma bénédiction. Tout ce que j'ai publié ici (sur le blog ou le script) est publié sous la licence CC-0.

Si vous ne voulez pas utiliser un tel script, l'alternative la plus proche est probablement Firejail. Les principales différences sont :

  • Beaucoup de choses codées comme préréglages dans le fichier de configuration ou dans le script (comme la gestion de D-Bus ou le préréglage pulseaudio) sont codées en dur dans le binaire principal (suid) de Firejail. Je suis d'accord avec l'auteur de bubblewrap que cela représente une surface d'attaque assez large.

  • Firejail est livré avec beaucoup de règles prédéfinies. La plupart des applications devraient fonctionner directement sans avoir à travailler dur pour les faire fonctionner.

  • Il n'y a pas d'équivalent à --bind brut dans Firejail, donc si vous voulez faire des choses comme --bind /opt/projects/my-project ~/workspace ou --bind ~/.config/mozilla ~/.mozilla, vous ne pourrez pas le faire.

  • Firejail a quelques fonctionnalités supplémentaires intéressantes comme les règles de pare-feu par sandbox.

  • Si vous entrez dans une sandbox par défaut avec firejail --no-profile zsh, vous verrez que la sandbox par défaut dans Firejail permet tout — vous devez explicitement mettre des choses sur liste noire. Nous avons vu la toute première fois que nous avons lancé bubblewrap que cela prend l'approche opposée — par défaut rien n'est partagé, vous devez explicitement mettre des choses sur liste blanche.

  • # héritage

    Posté par  . Évalué à 2.

    je n'ai rien vu concernant l'héritage. On pourrait supposer que les sections x11, wayland, … héritent toute de common.

    On peut le faire soit par la mise en place d'un mot clé spécifique, ou alors via le mécanisme interne à YAML.

    Qu'en dites-vous ? Pour le reste, c'est génial ; bravo pour l'idée.

    • [^] # Re: héritage

      Posté par  . Évalué à 3. Dernière modification le 12 janvier 2024 à 22:13.

      Les préréglages peuvent aussi utiliser "use", ce qui donne une forme d’héritage.

  • # Excellents articles

    Posté par  (site web personnel) . Évalué à 1.

    J’ai apprécié les 3 articles, avec les explications et les démonstrations, et pas seulement le résultat final.
    Merci pour tout !

    Comme tu le dis toi-même : rien de surprenant. Mais quel temps gagné pour nous ! Comme toi, je n’ai pas accroché à Firejail, qui reste cependant un précurseur méritant :-)

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.