diff --git a/core/management/commands/install_xapian.py b/core/management/commands/install_xapian.py index 4be1d907..3f7fe3cc 100644 --- a/core/management/commands/install_xapian.py +++ b/core/management/commands/install_xapian.py @@ -39,12 +39,16 @@ class Command(BaseCommand): return None return xapian.version_string() - def _desired_version(self) -> str: + def _desired_version(self) -> tuple[str, str, str]: with open( Path(__file__).parent.parent.parent.parent / "pyproject.toml", "rb" ) as f: pyproject = tomli.load(f) - return pyproject["tool"]["xapian"]["version"] + return ( + pyproject["tool"]["xapian"]["version"], + pyproject["tool"]["xapian"]["core-sha256"], + pyproject["tool"]["xapian"]["bindings-sha256"], + ) def handle(self, *args, force: bool, **options): if not os.environ.get("VIRTUAL_ENV", None): @@ -53,7 +57,7 @@ class Command(BaseCommand): ) return - desired = self._desired_version() + desired, core_checksum, bindings_checksum = self._desired_version() if desired == self._current_version(): if not force: self.stdout.write( @@ -65,7 +69,12 @@ class Command(BaseCommand): f"Installing xapian version {desired} at {os.environ['VIRTUAL_ENV']}" ) subprocess.run( - [str(Path(__file__).parent / "install_xapian.sh"), desired], + [ + str(Path(__file__).parent / "install_xapian.sh"), + desired, + core_checksum, + bindings_checksum, + ], env=dict(os.environ), check=True, ) diff --git a/core/management/commands/install_xapian.sh b/core/management/commands/install_xapian.sh index 2c97f120..5bfeef07 100755 --- a/core/management/commands/install_xapian.sh +++ b/core/management/commands/install_xapian.sh @@ -1,7 +1,11 @@ #!/usr/bin/env bash # Originates from https://gist.github.com/jorgecarleitao/ab6246c86c936b9c55fd # first argument of the script is Xapian version (e.g. 1.2.19) +# second argument of the script is core sha256 +# second argument of the script is binding sha256 VERSION="$1" +CORE_SHA256="$2" +BINDINGS_SHA256="$3" # Cleanup env vars for auto discovery mechanism unset CPATH @@ -21,9 +25,15 @@ BINDINGS=xapian-bindings-$VERSION # download echo "Downloading source..." -curl -O "https://oligarchy.co.uk/xapian/$VERSION/${CORE}.tar.xz" +curl -O "https://oligarchy.co.uk/xapian/$VERSION/${CORE}.tar.xz" || exit 1 + +echo "${CORE_SHA256} ${CORE}.tar.xz" | sha256sum -c - || exit 1 + curl -O "https://oligarchy.co.uk/xapian/$VERSION/${BINDINGS}.tar.xz" +echo "${BINDINGS_SHA256} ${BINDINGS}.tar.xz" | sha256sum -c - || exit 1 + + # extract echo "Extracting source..." tar xf "${CORE}.tar.xz" diff --git a/docs/howto/xapian.md b/docs/howto/xapian.md new file mode 100644 index 00000000..2ecda0a5 --- /dev/null +++ b/docs/howto/xapian.md @@ -0,0 +1,73 @@ +## Pourquoi Xapian + +Xapian permet de faire de la recherche fulltext. +C'est une librairie écrite en C++ avec des bindings Python +qu'on utilise avec la dépendance `django-haystack` via `xapian-haystack`. + +Elle a les avantages suivants: + +* C'est très rapide et ça correspond très bien à notre échelle +* C'est performant +* Pas besoin de service supplémentaire, c'est une librairie qui utilise des fichiers, comme sqlite + +Mais elle a un défaut majeur: on ne peut pas « juste » la `pip install`, +il faut installer une librairie système et des bindings et ça a toujours été +l'étape la plus frustrante et buggée de notre process d'installation. C'est +aussi la seule raison qui fait que le projet n'es pas compatible windows. + +## Mettre à jour Xapian + +Pour installer xapian le plus simplement possible, on le compile depuis les +sources via la commande `./manage.py install_xapian` comme indiqué dans la +documentation d'installation. + +La version de xapian est contrôlée par le `pyproject.toml` dans la section +`[tool.xapian]`. + +Cette section ressemble à ceci: + +```toml +[tool.xapian] +version = "x.y.z" +core-sha256 = "abcdefghijklmnopqrstuvwyz0123456789" +bindings-sha256 = "abcdefghijklmnopqrstuvwyz0123456789" +``` + +Comme on peut le voir, il y a 3 variables différentes, une variable de version, +qui sert à choisir la version à télécharger, et deux variables sha256. + +Ces variables sha256 permettent de protéger des attaques par supply chain, un +peu comme uv et npm font avec leurs respectifs `uv.lock` et `package-lock.json` +. Elles permettent de vérifier que les fichiers téléchargés n'ont pas été +altérés entre la configuration du fichier et l'installation par l'utilisateur +et/ou le déploiement. + +L'installation de xapian passe par deux fichiers, `xapian-core` et +`xapian-bindings` disponibles sur [https://xapian.org/download](https://xapian.org/download). + +Lorsque le script d'installation télécharge les fichiers, il vérifie leur +signature sha256 contre celles contenues dans ces deux variables. Si la +signature n'est pas la même, une erreur est levée, protégant l'utilisateur +d'une potentielle attaque. + +Pour mettre à jour, il faut donc changer la version ET modifier la signature ! + +Pour récupérer ces signatures, il suffit de télécharger soi-même les archives +du logiciel sur ce site, utiliser la commande `sha256sum` dessus et, enfin, +reporter la valeur sortie par cette commande. + +Pour ce qui est de la correspondance, `core-sha256` correspond à la signature +de `xapian-core` et `bindings-sha256` de `xapian-bindings`. + +Voici un bout de script qui peut faciliter une mise à jour: + +```bash +VERSION="x.y.z" # À modifier avec la bonne version +curl -O "https://oligarchy.co.uk/xapian/${VERSION}/xapian-core-${VERSION}.tar.xz" +sha256sum xapian-core-${VERSION}.tar.xz # Affiche la signature pour `core-sha256` +rm -f xapian-core-${VERSION} + +curl -O "https://oligarchy.co.uk/xapian/${VERSION}/xapian-bindings-${VERSION}.tar.xz" +sha256sum xapian-bindings-${VERSION}.tar.xz # Affiche la signature pour `bindingse-sha256` +rm -f xapian-bindings-${VERSION}.tar.xz +``` \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index ffa4a8b4..f537fa76 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -80,6 +80,7 @@ nav: - Ajouter un logo de promo: howto/logo.md - Ajouter une cotisation: howto/subscriptions.md - Modifier le weekmail: howto/weekmail.md + - Mettre à jour xapian: howto/xapian.md - Terminal: howto/terminal.md - Direnv: howto/direnv.md - Reference: diff --git a/pyproject.toml b/pyproject.toml index 94273bf9..fdcba07c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,7 +92,11 @@ docs = [ default-groups = ["dev", "tests", "docs"] [tool.xapian] -version = "1.4.29" +version = "1.4.31" +# Those hashes are here to protect against supply chains attacks +# See `https://ae-utbm.github.io/sith/howto/xapian/` for more information +core-sha256 = "fecf609ea2efdc8a64be369715aac733336a11f7480a6545244964ae6bc80811" +bindings-sha256 = "a38cc7ba4188cc0bd27dc7369f03906772047087a1c54f1b93355d5e9103c304" [tool.ruff] output-format = "concise" # makes ruff error logs easier to read