diff options
| author | 2026-05-13 13:43:25 +0100 | |
|---|---|---|
| committer | 2026-05-13 13:43:25 +0100 | |
| commit | 79d68fcc03c1639c1f13343b4b7d5f9f06274295 (patch) | |
| tree | 636a7c3cf2c9d0fe7c0b9027c5ff11756953a846 | |
| parent | 8d557eac140e7437a98a299908c99d7d6772cf1c (diff) | |
| download | dotfiles-79d68fcc03c1639c1f13343b4b7d5f9f06274295.tar.gz dotfiles-79d68fcc03c1639c1f13343b4b7d5f9f06274295.tar.bz2 dotfiles-79d68fcc03c1639c1f13343b4b7d5f9f06274295.zip | |
feat(thunderbird): migrate to flatpak with NMH + PKCS#11 bridges
Move Thunderbird from native pacman to org.mozilla.Thunderbird flatpak,
mirroring the LibreWolf migration. Bubblewrap isolates the mail client from
the rest of $HOME (ssh keys, password store, gpg sockets); intra-process
isolation regression is real but minor (same tradeoff as the browser).
Three cross-sandbox glue points handled in repo:
- run_onchange_after_deploy-thunderbird.sh.tmpl: profile path moves from
~/.thunderbird to ~/.var/app/org.mozilla.Thunderbird/.thunderbird
- run_onchange_after_deploy-pteid-pkcs11.sh.tmpl: refactored to iterate
over (LibreWolf, Thunderbird) instead of hard-coding LibreWolf, so
cartão de cidadão signing/encryption works for S/MIME in TB
- run_onchange_after_deploy-tb-eer.sh.tmpl (new): bridges
external-editor-revived's native messaging host into the sandbox via
a flatpak-spawn --host wrapper + relocated manifest
Other surfaces (Bridge, Radicale, libsecret, mako, OpenPGP) are covered
by Flathub default permissions.
Manual one-shot migration on host (after pulling + just sync): close TB,
copy ~/.thunderbird/. into ~/.var/app/org.mozilla.Thunderbird/.thunderbird/,
chezmoi apply -v, then xdg-mime default org.mozilla.Thunderbird.desktop
x-scheme-handler/mailto. Once verified working, archive the old profile
via mv ~/.thunderbird ~/.thunderbird.pre-flatpak.bak.
| -rw-r--r-- | README.md | 28 | ||||
| -rw-r--r-- | meta/flatpak.txt | 1 | ||||
| -rw-r--r-- | meta/mail.txt | 5 | ||||
| -rw-r--r-- | run_onchange_after_deploy-pteid-pkcs11.sh.tmpl | 61 | ||||
| -rw-r--r-- | run_onchange_after_deploy-tb-eer.sh.tmpl | 77 | ||||
| -rw-r--r-- | run_onchange_after_deploy-thunderbird.sh.tmpl | 2 |
6 files changed, 134 insertions, 40 deletions
@@ -29,8 +29,8 @@ My Arch Linux configuration, managed with [chezmoi](https://www.chezmoi.io/). | Bar / launcher | [waybar](https://github.com/Alexays/Waybar), [fuzzel](https://codeberg.org/dnkl/fuzzel) | | Notifications | [mako](https://github.com/emersion/mako) | | Lock screen | [swaylock](https://github.com/swaywm/swaylock) | -| Browser | [LibreWolf](https://librewolf.net/) (Flathub `io.gitlab.librewolf-community` for bubblewrap host-isolation), hardened via `user-overrides.js` + `userChrome.css` (kept under `firefox/` by name for recognizability) | -| Mail | [Thunderbird](https://www.thunderbird.net/) against [ProtonMail Bridge](https://proton.me/mail/bridge) + Radicale (CalDAV/CardDAV); non-private prefs tracked under `thunderbird/` | +| Browser | [LibreWolf](https://librewolf.net/) (Flathub `io.gitlab.librewolf-community` for bubblewrap host-isolation), hardened via `user-overrides.js` + `userChrome.css` (kept under `firefox/` by name for recognizability) | +| Mail | [Thunderbird](https://www.thunderbird.net/) (Flathub `org.mozilla.Thunderbird`) against [ProtonMail Bridge](https://proton.me/mail/bridge) + Radicale (CalDAV/CardDAV); non-private prefs tracked under `thunderbird/` | | Secrets & identity | [GPG](https://gnupg.org/) (commit signing + SSH auth via gpg-agent), [pass](https://www.passwordstore.org/) | | Media & viewers | [mpv](https://mpv.io/), [zathura](https://pwmt.org/projects/zathura/), [yazi](https://yazi-rs.github.io/) | | Code quality | stylua + [selene](https://github.com/Kampfkarren/selene), [shfmt](https://github.com/mvdan/sh) + [shellcheck](https://www.shellcheck.net/), [ruff](https://github.com/astral-sh/ruff), [taplo](https://taplo.tamasfe.dev/), [prettier](https://prettier.io/) — all wired through `just check` | @@ -75,14 +75,15 @@ chezmoi apply -v Everything is driven by [just](https://just.systems/) recipes against four parallel models: -| Directory | Managed by | Purpose | -| ----------------------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `dot_*`, `private_dot_*` | chezmoi | Dotfiles deployed to `$HOME`. Prefixes: `dot_` → `.`, `private_` → `0600`, `executable_` → `+x`. | -| `meta/*.txt` | `just pkg-apply`, `just pkg-status` | Plain-text package lists (one per line, `#` comments). Groups: `base`, `dev`, `wayland`, etc. | -| `systemd-units/{system,user}/*.txt` | `just unit-apply`, `just unit-status` | Units to enable, split by scope. `system/` files pair by name with `meta/` groups (`system/base.txt` ↔ `meta/base.txt`); `user/` files are standalone. Recipe group token: `<name>` / `system:<name>` / `user:<name>`. | -| `etc/` | `run_onchange_after_deploy-etc.sh.tmpl` | System-level configs deployed to `/etc/` via a chezmoi onchange hook. | -| `firefox/` | `run_onchange_after_deploy-firefox.sh.tmpl` | LibreWolf `user-overrides.js` + `userChrome.css` (kept under the familiar `firefox/` name). | -| (cartão de cidadão) | `run_onchange_after_deploy-pteid-pkcs11.sh.tmpl` | Bridges the `pt.gov.autenticacao` flatpak's PKCS#11 module into the LibreWolf flatpak's NSS DB (filesystem + `--socket=pcsc` override + `modutil -add`). No-op unless both flatpaks are installed. | +| Directory | Managed by | Purpose | +| ----------------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `dot_*`, `private_dot_*` | chezmoi | Dotfiles deployed to `$HOME`. Prefixes: `dot_` → `.`, `private_` → `0600`, `executable_` → `+x`. | +| `meta/*.txt` | `just pkg-apply`, `just pkg-status` | Plain-text package lists (one per line, `#` comments). Groups: `base`, `dev`, `wayland`, etc. | +| `systemd-units/{system,user}/*.txt` | `just unit-apply`, `just unit-status` | Units to enable, split by scope. `system/` files pair by name with `meta/` groups (`system/base.txt` ↔ `meta/base.txt`); `user/` files are standalone. Recipe group token: `<name>` / `system:<name>` / `user:<name>`. | +| `etc/` | `run_onchange_after_deploy-etc.sh.tmpl` | System-level configs deployed to `/etc/` via a chezmoi onchange hook. | +| `firefox/` | `run_onchange_after_deploy-firefox.sh.tmpl` | LibreWolf `user-overrides.js` + `userChrome.css` (kept under the familiar `firefox/` name). | +| (cartão de cidadão) | `run_onchange_after_deploy-pteid-pkcs11.sh.tmpl` | Bridges the `pt.gov.autenticacao` flatpak's PKCS#11 module into the NSS DB of every Mozilla-family flatpak we use (LibreWolf, Thunderbird) — `--filesystem` + `--socket=pcsc` override + `modutil -add` per profile. No-op unless `pt.gov.autenticacao` is installed. | +| (Thunderbird native editor) | `run_onchange_after_deploy-tb-eer.sh.tmpl` | Bridges `external-editor-revived` (host pacman package) into the Thunderbird flatpak: deploys a `flatpak-spawn --host` wrapper into the sandbox's `~/.mozilla/native-messaging-hosts/` and rewrites the manifest `path` to point at it. No-op unless TB flatpak + EER host package are both installed. | ## Recipes at a glance @@ -145,5 +146,10 @@ The repo is enough to rebuild a machine's tooling and configuration, but not its - `~/.password-store/` — the `pass` store that feeds API keys/tokens into the shell at login. - SSH private keys under `~/.ssh/id_*` (only `.pub` / config is in the repo). - LibreWolf profile data (bookmarks, history, extension state) at `~/.var/app/io.gitlab.librewolf-community/.librewolf/` — only the hardening policy lives in `firefox/`. +- Thunderbird profile data (accounts, calendars, OpenPGP keys) at `~/.var/app/org.mozilla.Thunderbird/.thunderbird/` — only non-private prefs live in `thunderbird/`. -Recovery on a fresh install: run `bootstrap.sh`, then `gpg --import` + `pass init <KEYID>`, restore `~/.password-store/`, drop SSH private keys into `~/.ssh/`, and restore the LibreWolf profile. +Recovery on a fresh install: run `bootstrap.sh`, then `gpg --import` + `pass init <KEYID>`, restore `~/.password-store/`, drop SSH private keys into `~/.ssh/`, restore the LibreWolf and Thunderbird profiles, and run once: + +```sh +xdg-mime default org.mozilla.Thunderbird.desktop x-scheme-handler/mailto +``` diff --git a/meta/flatpak.txt b/meta/flatpak.txt index 5fdd5ec..c76b100 100644 --- a/meta/flatpak.txt +++ b/meta/flatpak.txt @@ -12,6 +12,7 @@ io.gitlab.librewolf-community org.chromium.Chromium org.kde.okular org.libreoffice.LibreOffice +org.mozilla.Thunderbird org.torproject.torbrowser-launcher # Portuguese Citizen Card (eID) middleware + GUI. Not on Flathub; ships diff --git a/meta/mail.txt b/meta/mail.txt index 1e65dca..74f6214 100644 --- a/meta/mail.txt +++ b/meta/mail.txt @@ -1,8 +1,9 @@ +# Host-side bits the org.mozilla.Thunderbird flatpak depends on. protonmail-bridge-core -thunderbird # git send-email Perl prereqs (SMTP via local Bridge on 127.0.0.1:1025) perl-authen-sasl perl-mime-tools perl-net-smtp-ssl -# Edit messages in nvim (kernel-style inline patch review without TB mangling) +# Native messaging host binary for External Editor Revived; bridged into the +# TB flatpak by run_onchange_after_deploy-tb-eer.sh.tmpl. external-editor-revived diff --git a/run_onchange_after_deploy-pteid-pkcs11.sh.tmpl b/run_onchange_after_deploy-pteid-pkcs11.sh.tmpl index b6c3b6c..6046bec 100644 --- a/run_onchange_after_deploy-pteid-pkcs11.sh.tmpl +++ b/run_onchange_after_deploy-pteid-pkcs11.sh.tmpl @@ -1,7 +1,7 @@ #!/bin/sh -# Bridge the pt.gov.autenticacao flatpak's PKCS#11 module into the LibreWolf -# flatpak's NSS database so cartão de cidadão authentication works in the -# browser despite the cross-sandbox isolation. +# Bridge the pt.gov.autenticacao flatpak's PKCS#11 module into the NSS +# database of every Mozilla-family flatpak we use, so cartão de cidadão +# authentication / S/MIME signing works despite cross-sandbox isolation. # # Idempotent. Re-runs whenever this script or the pt.gov.autenticacao entry # in meta/flatpak.txt changes. @@ -10,11 +10,9 @@ set -eu PTEID_APP=pt.gov.autenticacao -BROWSER_APP=io.gitlab.librewolf-community MODULE_NAME=pteid-mw flatpak info --user "$PTEID_APP" >/dev/null 2>&1 || exit 0 -flatpak info --user "$BROWSER_APP" >/dev/null 2>&1 || exit 0 PTEID_LOC=$(flatpak info --user --show-location "$PTEID_APP" 2>/dev/null) [ -d "$PTEID_LOC/files" ] || exit 0 @@ -27,36 +25,47 @@ SO_DIR=$(dirname "$SO") SO_IN_SANDBOX="/run/host$SO" SO_DIR_IN_SANDBOX="/run/host$SO_DIR" -flatpak override --user \ +if ! command -v modutil >/dev/null 2>&1; then + echo "pteid-pkcs11: modutil not found (install nss); skipping NSS registration." >&2 + exit 0 +fi + +# Each line: <flatpak_app_id> <profile_subdir> <process_name> +# - profile_subdir is the directory under ~/.var/app/<id>/ that holds the +# per-profile NSS DBs (cert9.db lives in each <profile>/ underneath it) +# - process_name is the binary name to grep for to detect a running instance +APPS="\ +io.gitlab.librewolf-community .librewolf librewolf +org.mozilla.Thunderbird .thunderbird thunderbird" + +echo "$APPS" | while IFS=' ' read -r app profile_subdir proc_name; do + [ -n "$app" ] || continue + flatpak info --user "$app" >/dev/null 2>&1 || continue + + flatpak override --user \ --filesystem="$PTEID_LOC/files:ro" \ --socket=pcsc \ --env="LD_LIBRARY_PATH=$SO_DIR_IN_SANDBOX" \ - "$BROWSER_APP" + "$app" -command -v modutil >/dev/null 2>&1 || { - echo "pteid-pkcs11: modutil not found (install nss); skipping NSS registration." >&2 - exit 0 -} + profiles_dir="$HOME/.var/app/$app/$profile_subdir" + [ -d "$profiles_dir" ] || continue -PROFILES_DIR="$HOME/.var/app/$BROWSER_APP/.librewolf" -[ -d "$PROFILES_DIR" ] || exit 0 - -registered=0 -skipped=0 -for prof in "$PROFILES_DIR"/*/; do + registered=0 + for prof in "$profiles_dir"/*/; do [ -f "$prof/cert9.db" ] || continue if modutil -list -dbdir "sql:$prof" 2>/dev/null | grep -q "^[[:space:]]*Name:[[:space:]]*$MODULE_NAME$"; then - skipped=$((skipped + 1)) - continue + continue fi - if pgrep -u "$(id -u)" -x librewolf >/dev/null 2>&1; then - echo "pteid-pkcs11: LibreWolf is running; close it and re-run 'chezmoi apply' to register the PKCS#11 module." >&2 - exit 0 + if pgrep -u "$(id -u)" -x "$proc_name" >/dev/null 2>&1; then + echo "pteid-pkcs11: $proc_name is running; close it and re-run 'chezmoi apply' to register the PKCS#11 module." >&2 + continue fi modutil -add "$MODULE_NAME" -libfile "$SO_IN_SANDBOX" -dbdir "sql:$prof" -force >/dev/null registered=$((registered + 1)) -done + done -if [ "$registered" -gt 0 ]; then - echo "pteid-pkcs11: registered $MODULE_NAME in $registered LibreWolf profile(s)." -fi + if [ "$registered" -gt 0 ]; then + echo "pteid-pkcs11: registered $MODULE_NAME in $registered $proc_name profile(s)." + fi +done diff --git a/run_onchange_after_deploy-tb-eer.sh.tmpl b/run_onchange_after_deploy-tb-eer.sh.tmpl new file mode 100644 index 0000000..3622e67 --- /dev/null +++ b/run_onchange_after_deploy-tb-eer.sh.tmpl @@ -0,0 +1,77 @@ +#!/bin/sh +# Bridge the External Editor Revived native messaging host into the +# org.mozilla.Thunderbird flatpak. The host binary stays installed via +# pacman (`external-editor-revived`); we relocate the manifest into the +# sandbox and replace its binary path with a wrapper that re-enters the +# host via flatpak-spawn. +# +# Idempotent. Re-runs on script changes or whenever the host-side manifest +# content changes. +# +# host manifest hash: {{ output "sh" "-c" "for p in /usr/lib/mozilla/native-messaging-hosts/external_editor_revived.json /usr/lib/thunderbird/native-messaging-hosts/external_editor_revived.json; do [ -f \"$p\" ] && sha256sum \"$p\" && break; done; true" | sha256sum }} +set -eu + +TB_APP=org.mozilla.Thunderbird +MANIFEST_NAME=external_editor_revived.json +WRAPPER_NAME=external_editor_revived.sh +HOST_BINARY=/usr/lib/external-editor-revived/external-editor-revived + +flatpak info --user "$TB_APP" >/dev/null 2>&1 || exit 0 + +# Locate the host-side manifest. Different PKGBUILDs have used different +# install dirs; prefer the canonical mozilla path, fall back to TB-specific. +HOST_MANIFEST= +for candidate in \ + /usr/lib/mozilla/native-messaging-hosts/$MANIFEST_NAME \ + /usr/lib/thunderbird/native-messaging-hosts/$MANIFEST_NAME \ + /usr/lib64/mozilla/native-messaging-hosts/$MANIFEST_NAME; do + if [ -f "$candidate" ]; then + HOST_MANIFEST=$candidate + break + fi +done +[ -n "$HOST_MANIFEST" ] || { + echo "tb-eer: external-editor-revived manifest not found on host; install the package or skip TB native editor." >&2 + exit 0 +} + +[ -x "$HOST_BINARY" ] || { + echo "tb-eer: $HOST_BINARY not executable on host; skipping." >&2 + exit 0 +} + +# Allow flatpak-spawn --host from inside the sandbox. +flatpak override --user --talk-name=org.freedesktop.Flatpak "$TB_APP" + +# In-sandbox path the manifest will reference. TB looks for user manifests +# at $HOME/.mozilla/native-messaging-hosts/ from inside its sandbox; from +# outside that maps to ~/.var/app/$TB_APP/.mozilla/native-messaging-hosts/. +SANDBOX_NMH_DIR="$HOME/.mozilla/native-messaging-hosts" +HOST_NMH_DIR="$HOME/.var/app/$TB_APP/.mozilla/native-messaging-hosts" +mkdir -p "$HOST_NMH_DIR" + +# Wrapper that re-enters the host to invoke the real binary. +WRAPPER_HOST_PATH="$HOST_NMH_DIR/$WRAPPER_NAME" +WRAPPER_SANDBOX_PATH="$SANDBOX_NMH_DIR/$WRAPPER_NAME" +cat >"$WRAPPER_HOST_PATH" <<EOF +#!/bin/sh +exec flatpak-spawn --host "$HOST_BINARY" "\$@" +EOF +chmod +x "$WRAPPER_HOST_PATH" + +# Rewrite the manifest's "path" field to point at the wrapper as seen from +# inside the sandbox. +TARGET_MANIFEST="$HOST_NMH_DIR/$MANIFEST_NAME" +if command -v jq >/dev/null 2>&1; then + jq --arg p "$WRAPPER_SANDBOX_PATH" '.path = $p' "$HOST_MANIFEST" >"$TARGET_MANIFEST.tmp" + mv "$TARGET_MANIFEST.tmp" "$TARGET_MANIFEST" +else + # Fallback: simple sed on the "path": "..." line. Brittle if the file + # ever becomes minified or contains escaped quotes — jq is preferred. + escaped=$(printf '%s' "$WRAPPER_SANDBOX_PATH" | sed 's/[\/&]/\\&/g') + sed -E "s|(\"path\"[[:space:]]*:[[:space:]]*\")[^\"]*(\")|\1$escaped\2|" \ + "$HOST_MANIFEST" >"$TARGET_MANIFEST.tmp" + mv "$TARGET_MANIFEST.tmp" "$TARGET_MANIFEST" +fi + +echo "tb-eer: External Editor Revived bridged into $TB_APP." diff --git a/run_onchange_after_deploy-thunderbird.sh.tmpl b/run_onchange_after_deploy-thunderbird.sh.tmpl index f19eae0..2b86f49 100644 --- a/run_onchange_after_deploy-thunderbird.sh.tmpl +++ b/run_onchange_after_deploy-thunderbird.sh.tmpl @@ -4,7 +4,7 @@ # thunderbird/ content hash: {{ output "sh" "-c" (printf "cd %q && find thunderbird -type f -exec sha256sum {} + | LC_ALL=C sort" .chezmoi.sourceDir) | sha256sum }} set -eu -PROFILES_DIR="$HOME/.thunderbird" +PROFILES_DIR="$HOME/.var/app/org.mozilla.Thunderbird/.thunderbird" [ -d "$PROFILES_DIR" ] || exit 0 PROFILE=$(find "$PROFILES_DIR" -maxdepth 1 -mindepth 1 -type d -name '*.default-default' | head -1) |
