Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ design-artifacts/
docs/
charts/
.claude/
# Sources des montages par défaut des compose (15-13b, AC 8 e) — hors du
# contexte de build (la sauvegarde pré-import est un secret).
log/
inbox/
documents/
backup/
18 changes: 13 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ KESH_PORT=80
# # KESH_PORT=3000
# Alternatives : (i) lancer `cargo run` avec `sudo` (déconseillé), (ii)
# ajouter la capability via `sudo setcap 'cap_net_bind_service=+ep' target/debug/kesh-api`.
# Hors Docker, poser aussi KESH_ADMIN_BACKUP_DIR sur un dossier inscriptible
# par l'utilisateur (son défaut /data/backup ne l'est pas) : sans quoi tout
# import d'installation est refusé, avant toute suppression.

# --- Compte admin initial (Story v011-5 — double-usage) ---
#
Expand Down Expand Up @@ -179,9 +182,12 @@ KESH_BANK_IMPORT_MAX_MB=10
#KESH_ADMIN_EXPORT_INMEM_MB=50
# Taille max d'un upload .keshbackup à l'import (MiB). Borne [1, 10240], défaut 512.
#KESH_ADMIN_IMPORT_MAX_MB=512
# Répertoire du backup automatique pré-import (filet avant restore destructeur).
# Défaut /tmp (absente ou vide) — PERDU au redémarrage du conteneur ; monter un
# volume persistant.
# Dossier de sauvegarde de sécurité prise avant chaque import d'installation
# (filet avant le restore destructeur ; fichiers SECRETS, en 0600).
# Défaut /data/backup (absente ou vide), monté sur ./backup par les deux compose
# fournis. Une autre valeur doit être un chemin MONTÉ, sans quoi la sauvegarde
# retourne dans le système de fichiers éphémère du conteneur. Hors Docker : un
# dossier inscriptible par l'utilisateur qui lance Kesh.
#KESH_ADMIN_BACKUP_DIR=/data/backup

# --- Chemins internes (hors Docker seulement) ---
Expand Down Expand Up @@ -209,7 +215,8 @@ KESH_BANK_IMPORT_MAX_MB=10
# --- Chemins HOST des bind mounts (docker-compose.yml) ---
# Sans effet avec `docker-compose.prod.yml` (installation Synology du manuel) :
# ses montages sont fixes — `./inbox`, `./documents`, `./log` dans le répertoire
# du compose (issue #558).
# du compose (issue #558), et `./backup` (sauvegarde pré-import, sans variable
# d'hôte).
# Avec `docker-compose.yml` : pour rendre l'inbox accessible depuis votre PC
# (SMB / Synology Drive), montez ces dossiers sur un dossier PARTAGÉ du NAS —
# créez un dossier partagé (ex. « kesh ») avec sous-dossiers inbox/ et
Expand Down Expand Up @@ -242,7 +249,8 @@ RUST_LOG=info
# Chemin HOST du répertoire de logs (bind mount de docker-compose.yml). Pointe
# vers un dossier partagé du NAS pour consulter les logs depuis le PC (SMB/Drive).
# Sans effet avec `docker-compose.prod.yml` : ses montages sont fixes — `./inbox`,
# `./documents`, `./log` dans le répertoire du compose (issue #558).
# `./documents`, `./log` dans le répertoire du compose (issue #558), et `./backup`
# (sauvegarde pré-import, sans variable d'hôte).
#KESH_LOG_HOST_DIR=/volume1/kesh/log
# (défaut sans .env : ./log dans le dossier du projet)
#
Expand Down
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ Thumbs.db
*.log
# Story v011-1 : répertoire de logs fichier monté par docker-compose.prod.yml
log/
# Sources des montages par défaut des compose (15-13b, AC 8 e) : justificatifs,
# factures déposées et sauvegarde pré-import (SECRET) ne se versionnent jamais.
# Motifs ANCRÉS à la racine : `backup/` seul ignorerait aussi la route versionnée
# frontend/src/routes/(app)/admin/backup/. (`log/` reste non ancré : tolérance
# nommée, aucun dossier log versionné — test montages_hors_du_depot.)
/inbox/
/documents/
/backup/

# LaTeX build artifacts (manuels docs/manual/) — ne versionner que .tex + .pdf
# (aligne admin-manual sur la convention marketing-brochure ; Story 17-3f)
Expand Down
10 changes: 8 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,20 @@ Le contenu est rédigé en français à destination des **fiduciaires, PME, ind
- **Une opération qui croise un rapprochement bancaire ne finit plus en erreur interne ([#463](https://github.com/guycorbaz/kesh/issues/463), [#491](https://github.com/guycorbaz/kesh/issues/491), [#536](https://github.com/guycorbaz/kesh/issues/536)).** Un règlement client, l'annulation d'un règlement client, la validation d'une facture, la saisie d'une facture fournisseur et l'enregistrement des réglages de facturation qui croisent une autre opération (un rapprochement bancaire, notamment) ne finissent plus en erreur interne : le serveur rejoue l'opération, comme il le faisait déjà pour l'acceptation d'un rapprochement. Il en va de même de toutes les opérations courantes qui écrivent au journal — hors la restauration d'une sauvegarde et l'effacement des données de démonstration. Seul un interblocage répété trois fois de suite rend encore une erreur ; rien n'est alors écrit et l'opération peut être relancée telle quelle.
- **Les manuels ne prétendent plus que la numérotation des factures se fait dans une transaction `SERIALIZABLE` ([#484](https://github.com/guycorbaz/kesh/issues/484)).** Ils décrivent le verrou du compteur, la contrainte d'unicité et le rejeu.

- **La configuration posée dans `.env` atteint enfin Kesh ([#550](https://github.com/guycorbaz/kesh/issues/550)).** Les deux compose fournis ne transmettaient au conteneur que les variables listées sous `environment:` — Docker Compose lit `.env` pour remplir le compose, il ne le transmet pas. Seize variables que `.env.example` proposait de régler restaient donc **sans effet, sans aucun message** : les e-mails sortants (`KESH_SMTP_*`, `KESH_PUBLIC_BASE_URL`), le mot de passe oublié par e-mail, la sortie de la démonstration, la langue, la longueur minimale des mots de passe, les plafonds d'import et de sauvegarde, et les cookies `Secure` avec `docker-compose.prod.yml`. Elles sont désormais toutes transmises, et un test empêche qu'un compose en oublie une. Le message de démarrage qui renvoyait à une variable inexistante (`KESH_ADMIN_` + `RESET`) nomme le vrai recours (`KESH_ADMIN_USERNAME` / `KESH_ADMIN_PASSWORD`). `docker-compose.yml` s'installe seul : il désigne l'image publiée au lieu d'exiger les sources. Un `.env` sans `KESH_ADMIN_PASSWORD` ne fait plus refuser le démarrage avec `docker-compose.yml`, ni avertir Compose avec `docker-compose.prod.yml` : l'onboarding `/setup` recommandé par le manuel fonctionne, et le retrait des variables après un break-glass aussi. `KESH_JWT_SECRET` reste obligatoire. **Inchangé** : avec `docker-compose.prod.yml`, les montages restent `./documents`, `./inbox`, `./log`, et les `KESH_*_HOST_DIR` y sont sans effet ([#558](https://github.com/guycorbaz/kesh/issues/558)).
- **La configuration posée dans `.env` atteint enfin Kesh ([#550](https://github.com/guycorbaz/kesh/issues/550)).** Les deux compose fournis ne transmettaient au conteneur que les variables listées sous `environment:` — Docker Compose lit `.env` pour remplir le compose, il ne le transmet pas. Seize variables que `.env.example` proposait de régler restaient donc **sans effet, sans aucun message** : les e-mails sortants (`KESH_SMTP_*`, `KESH_PUBLIC_BASE_URL`), le mot de passe oublié par e-mail, la sortie de la démonstration, la langue, la longueur minimale des mots de passe, les plafonds d'import et de sauvegarde, et les cookies `Secure` avec `docker-compose.prod.yml`. Elles sont désormais toutes transmises, et un test empêche qu'un compose en oublie une. Le message de démarrage qui renvoyait à une variable inexistante (`KESH_ADMIN_` + `RESET`) nomme le vrai recours (`KESH_ADMIN_USERNAME` / `KESH_ADMIN_PASSWORD`). `docker-compose.yml` s'installe seul : il désigne l'image publiée au lieu d'exiger les sources. Un `.env` sans `KESH_ADMIN_PASSWORD` ne fait plus refuser le démarrage avec `docker-compose.yml`, ni avertir Compose avec `docker-compose.prod.yml` : l'onboarding `/setup` recommandé par le manuel fonctionne, et le retrait des variables après un break-glass aussi. `KESH_JWT_SECRET` reste obligatoire. **Inchangé** : avec `docker-compose.prod.yml`, les montages restent `./documents`, `./inbox`, `./log` (la 0.13.0 y ajoute `./backup`, sauvegarde pré-import, [#552](https://github.com/guycorbaz/kesh/issues/552)), et les `KESH_*_HOST_DIR` y sont sans effet ([#558](https://github.com/guycorbaz/kesh/issues/558)).

⚠️ **Action requise sur une installation existante.** La nouvelle image seule ne corrige rien : le défaut est dans le fichier compose que vous détenez, et le compose de la 0.13.0 s'emploie avec l'image 0.13.0. **Re-téléchargez le compose** et reportez-y vos seules adaptations locales (port, réseau, **chemins des montages** — un montage modifié à la main et non reporté revient à `./documents`, et justificatifs et PDF paraissent disparus : ils sont toujours dans l'ancien dossier), puis **`docker compose pull`** et **`docker compose up -d`**. Pour qui garde son fichier, le manuel d'administration décrit les gestes, fichier par fichier (§ *Passer à la 0.13.0*). **Relisez votre `.env` avant de mettre à jour** : des lignes jusqu'ici sans effet le deviennent. Avec `docker-compose.yml`, c'est d'abord **Compose** qui refuse, au terminal et avant tout démarrage, si `MARIADB_ROOT_PASSWORD` ou `MARIADB_PASSWORD` manque à `.env` (cf. **Sécurité**, [#551](https://github.com/guycorbaz/kesh/issues/551)). Kesh **refuse de démarrer** si `KESH_SMTP_TLS` ou `KESH_FEATURE_FORGOT_PASSWORD` portent une valeur autre que `true`/`false`/`1`/`0`, si `KESH_COOKIE_SECURE` en porte une avec `docker-compose.prod.yml`, si le mot de passe oublié est activé avec un SMTP incomplet ou mal formé, ou si, activé, le mailer SMTP ne se construit pas, ou encore si `KESH_JWT_SECRET` ou `KESH_ADMIN_PASSWORD` est resté à un placeholder (cf. **Sécurité**) — le message nomme la variable (`docker compose logs kesh-api`). Sans refus : `KESH_PRODUCTION_RESET` réautorise la réinitialisation **d'une installation restée en démonstration** ; les quatre `KESH_SMTP_HOST`/`_USER`/`_PASSWORD`/`_FROM` posées activent l'envoi de factures, et avec `KESH_FEATURE_FORGOT_PASSWORD=true` la réinitialisation du mot de passe par e-mail ; `KESH_COOKIE_SECURE` agit enfin avec `docker-compose.prod.yml`, de même que `KESH_ADMIN_BACKUP_DIR`, `KESH_LANG`… ; une ligne `KESH_LOG_FILE_PATH=` vide désactive désormais le journal fichier. Une valeur qui contient `$` se met entre apostrophes simples (`KESH_SMTP_PASSWORD='pa$word'`), sinon elle est tronquée. Une variable que `.env` ne pose pas, ou laisse vide, prend son défaut sans avertissement — hormis, avec `docker-compose.yml`, `MARIADB_ROOT_PASSWORD` et `MARIADB_PASSWORD`, désormais obligatoires — ; la sauvegarde pré-import reste dans `/tmp`. Un `.env` copié du gabarit sans modification ne change rien d'autre que le refus du placeholder du secret JWT et, avec `docker-compose.yml`, le sort des mots de passe MariaDB : copié du gabarit 0.13.0, où ils sont commentés, il fait refuser Compose ; copié d'un gabarit antérieur, qui posait `kesh_dev`, il fait émettre à Kesh l'avertissement « mot de passe publié ».
⚠️ **Action requise sur une installation existante.** La nouvelle image seule ne corrige rien : le défaut est dans le fichier compose que vous détenez, et le compose de la 0.13.0 s'emploie avec l'image 0.13.0. **Re-téléchargez le compose** et reportez-y vos seules adaptations locales (port, réseau, **chemins des montages** — un montage modifié à la main et non reporté revient à `./documents`, et justificatifs et PDF paraissent disparus : ils sont toujours dans l'ancien dossier), puis **`docker compose pull`** et **`docker compose up -d`**. Pour qui garde son fichier, le manuel d'administration décrit les gestes, fichier par fichier (§ *Passer à la 0.13.0*). **Relisez votre `.env` avant de mettre à jour** : des lignes jusqu'ici sans effet le deviennent. Avec `docker-compose.yml`, c'est d'abord **Compose** qui refuse, au terminal et avant tout démarrage, si `MARIADB_ROOT_PASSWORD` ou `MARIADB_PASSWORD` manque à `.env` (cf. **Sécurité**, [#551](https://github.com/guycorbaz/kesh/issues/551)). Kesh **refuse de démarrer** si `KESH_SMTP_TLS` ou `KESH_FEATURE_FORGOT_PASSWORD` portent une valeur autre que `true`/`false`/`1`/`0`, si `KESH_COOKIE_SECURE` en porte une avec `docker-compose.prod.yml`, si le mot de passe oublié est activé avec un SMTP incomplet ou mal formé, ou si, activé, le mailer SMTP ne se construit pas, ou encore si `KESH_JWT_SECRET` ou `KESH_ADMIN_PASSWORD` est resté à un placeholder (cf. **Sécurité**) — le message nomme la variable (`docker compose logs kesh-api`). Sans refus : `KESH_PRODUCTION_RESET` réautorise la réinitialisation **d'une installation restée en démonstration** ; les quatre `KESH_SMTP_HOST`/`_USER`/`_PASSWORD`/`_FROM` posées activent l'envoi de factures, et avec `KESH_FEATURE_FORGOT_PASSWORD=true` la réinitialisation du mot de passe par e-mail ; `KESH_COOKIE_SECURE` agit enfin avec `docker-compose.prod.yml`, de même que `KESH_ADMIN_BACKUP_DIR`, `KESH_LANG`… ; une ligne `KESH_LOG_FILE_PATH=` vide désactive désormais le journal fichier. Une valeur qui contient `$` se met entre apostrophes simples (`KESH_SMTP_PASSWORD='pa$word'`), sinon elle est tronquée. Une variable que `.env` ne pose pas, ou laisse vide, prend son défaut sans avertissement — hormis, avec `docker-compose.yml`, `MARIADB_ROOT_PASSWORD` et `MARIADB_PASSWORD`, désormais obligatoires — ; pour la sauvegarde pré-import, `./backup` avec le compose de la 0.13.0 ; avec un compose gardé sans ce montage, `/data/backup` du conteneur, éphémère comme l'était `/tmp`. Un `.env` copié du gabarit sans modification ne change rien d'autre que le refus du placeholder du secret JWT et, avec `docker-compose.yml`, le sort des mots de passe MariaDB : copié du gabarit 0.13.0, où ils sont commentés, il fait refuser Compose ; copié d'un gabarit antérieur, qui posait `kesh_dev`, il fait émettre à Kesh l'avertissement « mot de passe publié ».

**Exploitant de `docker-compose.yml` qui construit depuis un clone** : après `git pull`, `docker compose up -d` **tire** `gcorbaz/kesh:latest` au lieu de construire les sources — `docker compose up -d --build` pour construire, et `docker compose pull kesh-api` pour revenir à l'image publiée après un build local qui l'a étiquetée `latest`.

- **La sauvegarde prise avant un import d'installation survit au conteneur ([#552](https://github.com/guycorbaz/kesh/issues/552)).** Elle était écrite dans `/tmp` **du conteneur** et disparaissait à sa recréation — `docker compose up -d` après une modification de `.env`, une mise à jour d'image —, c'est-à-dire au moment même où l'on en a besoin après un import raté. Son défaut devient `/data/backup`, que les deux compose fournis montent sur `./backup` dans le répertoire du compose ; le fichier est créé en mode `0600` (c'est un secret : condensés des mots de passe, jetons de session), écrit sous un nom `.partial` puis renommé, jamais par-dessus un fichier existant. La restaurer demande de la rapatrier en SSH (`sudo cp` puis `sudo chown`, manuel d'administration, § *La sauvegarde de sécurité pré-import*). **Hors Docker**, poser `KESH_ADMIN_BACKUP_DIR` sur un dossier inscriptible : sans quoi tout import est refusé, avant toute suppression. **Avec un compose gardé**, ajouter le montage `./backup:/data/backup` (§ *Passer à la 0.13.0*).

- **Un import dont la sauvegarde n'a pas pu être écrite le dit ([#576](https://github.com/guycorbaz/kesh/issues/576)).** Tout échec survenu avant que la sauvegarde de sécurité soit sur disque — dossier non inscriptible, disque plein, base de données momentanément inaccessible — annonçait « un backup automatique a été créé avant l'opération », alors qu'il n'en existait aucun. Il rend désormais `ADMIN_PRE_IMPORT_BACKUP_FAILED` : aucune sauvegarde n'a été créée, rien n'a été supprimé, et le message nomme les deux pistes, le dossier de sauvegarde et la base de données.

### Sécurité

- **Les dossiers montés par défaut ne se versionnent plus et n'entrent plus dans l'image ([#552](https://github.com/guycorbaz/kesh/issues/552)).** `./backup` (sauvegardes pré-import, un secret), `./inbox` et `./documents` (justificatifs) étaient créés dans le clone par `docker compose up` sans être ignorés : un `git add -A` les aurait versionnés. Ils sont désormais ignorés de git (motifs ancrés à la racine) et du contexte de build, comme `./log`.

- **Le secret JWT et le mot de passe admin laissés au placeholder du gabarit, ou à un gabarit entre chevrons du manuel, sont refusés au démarrage ([#557](https://github.com/guycorbaz/kesh/issues/557)).** Jusqu'ici, `KESH_JWT_SECRET=<GENERATE_ME: openssl rand -hex 32>` recopié tel quel de `.env.example` était **accepté** : les jetons de session étaient signés avec un secret publié dans le dépôt. De même, `KESH_ADMIN_PASSWORD=<GENERATE_ME: openssl rand -base64 24>` décommenté tel quel, ou `KESH_ADMIN_PASSWORD=<mot de passe fort, min. 12 caracteres>` recopié du manuel, créait sur une base vide un administrateur au mot de passe publié. Toute valeur qui contient `GENERATE_ME` (sans égard à la casse) ou qui est de la forme `<…>` est désormais refusée pour ces deux variables, avant le contrôle de longueur. **Action pour une installation existante**, avant la mise à jour, le compose final en place et, avec `docker-compose.yml`, les deux mots de passe MariaDB posés (entrée suivante) : `sudo docker compose config -q && echo 'compose lisible'` doit afficher `compose lisible` — si Compose refuse le fichier, la vérification qui suit ne reçoit rien et reste muette comme un succès —, puis la vérification du manuel d'administration (§ *Passer à la 0.13.0*), `sudo docker compose config | grep -iE 'KESH_(JWT_SECRET|ADMIN_PASSWORD): .? *(<|.*generate_me)'`, doit rester muette. Si elle montre `KESH_JWT_SECRET`, remplacez la valeur par la sortie de `openssl rand -hex 32` (toutes les sessions ouvertes sont invalidées, chacun se reconnecte) ; si elle montre `KESH_ADMIN_PASSWORD`, choisissez un vrai mot de passe ou recommentez la ligne — **et**, si l'administrateur a été créé avec ce placeholder, **changez son mot de passe** dans Kesh (le refus au démarrage ne change pas un compte déjà créé). Puis `docker compose up -d`. Sans cette action, Kesh 0.13.0 refuse de démarrer, avec un message qui nomme la variable.

- **MariaDB n'est plus joignable depuis le réseau, et n'a plus de mots de passe publiés ([#551](https://github.com/guycorbaz/kesh/issues/551)).** `docker-compose.yml` publiait le port 3306 de MariaDB sur **toutes** les interfaces de l'hôte, avec des mots de passe par défaut écrits dans le dépôt (`kesh_dev_root` pour root, `kesh_dev` pour Kesh) — et un port publié par Docker **contourne le pare-feu de l'hôte** (UFW) : le pare-feu recommandé par le manuel ne le fermait pas. Le port n'est plus publié (Kesh passe par le réseau interne de Docker) ; `MARIADB_ROOT_PASSWORD` et `MARIADB_PASSWORD` n'ont plus de défaut : sans eux, Compose refuse de démarrer en nommant la variable ; `.env.example` ne les pose plus (lignes commentées). Kesh **avertit au démarrage** quand le mot de passe de `DATABASE_URL` est un mot de passe publié (`kesh_dev`, `kesh_dev_root`) ou un gabarit `<…>` recopié du manuel, et un refus d'identifiant de MariaDB (erreur 1045) est désormais suivi d'un **indice**. Le manuel fait générer le mot de passe MariaDB de l'installation Synology en hexadécimal (`openssl rand -hex 32`) : la sortie base64 qu'il prescrivait contenait une fois sur deux un `/`, qui rendait la `DATABASE_URL` inexploitable.
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ juge pas au nombre : il se juge fichier par fichier contre cette liste.
KESH_TEST_MODE=true KESH_HOST=127.0.0.1 KESH_COOKIE_SECURE=false \
DATABASE_URL='mysql://kesh:kesh_dev@127.0.0.1:3306/kesh_e2e' \
KESH_JWT_SECRET='<32+ octets>' KESH_ADMIN_PASSWORD='<12+ caractères>' \
KESH_PORT=3000 KESH_STATIC_DIR=frontend/build \
KESH_PORT=3000 KESH_STATIC_DIR=frontend/build KESH_ADMIN_BACKUP_DIR=target/kesh-backup \
cargo run -p kesh-api

# terminal 2
Expand Down
Loading
Loading