partiduo-admin

Partiduo — administration du parc de dossiers (partiduo-admin)

= partiduo-admin :toc: left :toc-title: Table des matières :icons: font

Administration du parc de dossiers Partiduo (ADR-008 de partiduo-docs) : une application Marten séparée, avec sa propre base, qui gère le cycle de vie des dossiers sans jamais entrer dans leurs données comptables (ADR-008 D3), et partiduo-agent, l'exécutant installé sur chaque serveur d'hébergement (ADR-008 D4).

Licence : AGPL-3.0-or-later (voir LICENSE).

== Contenu

[cols="1,3",options="header"] |=== |Binaire |Rôle

|partiduo-admin (src/server.cr) |Interface HTMX + Bulma (charte et theme.css de la maquette), fr/en/nl, servie sur admin.<domaine> ; API de l'exécutant /api/agent/v1/….

|partiduo-admin-manage (manage.cr) |Ligne de commande Marten : migrate, bootstrap (premier super-admin par invitation), schedule (planification, toutes les 15 minutes).

|partiduo-agent (src/agent.cr) |Exécutant : tire ses tâches en HTTPS avec le jeton du serveur, liste fermée de types traduits en appels à partiduo-provision et à partiduo-manage instance (contrat doc/api/instance-cli.adoc de partiduo-app), tâches idempotentes et reprenables, journal renvoyé. |===

== Modèle

  • Structures : cabinet (plusieurs collaborateurs), gestionnaire indépendant (une seule personne, admin de sa structure, qui gère tous ses dossiers) ou parc sans cabinet (un seul, périmètre propre du super-admin). Utilisateurs d'administration et trois rôles (ADR-008 D2) : super-admin (tout le parc, passkey exigée), admin de cabinet (sa structure), gestionnaire de dossiers (dossiers confiés) ; affectations.
  • Donneurs d'ordre : qui commande et paie un dossier (cabinet, la société elle-même, autre payeur) — identité, SIREN, adresse de facturation, contact. La facturation (tarifs, périodes, factures) est hors périmètre ; la vue « dossiers par donneur d'ordre » s'exporte en CSV.
  • Dossiers (inventaire) : sous-domaine, régime, modules, extensions, serveur, paquet, version relevée sur l'instance, état, cabinet gestionnaire, donneur d'ordre obligatoire.
  • Serveurs (jeton d'exécutant en empreinte), tâches, sauvegardes (mode de chiffrement, empreinte de la clé), demandes d'opérations sensibles (mode à une ou deux personnes), alertes, certificats émis (quota Let's Encrypt) et journal d'audit en ajout seul (déclencheur PostgreSQL).

Les droits par rôle sont tous dans src/admin/services/access.cr.

== Actions (ADR-008 D5)

Créer ; modifier modules et extensions ; suspendre ; réactiver ; archiver (sauvegarde figée vérifiée, conservation dix ans) ; restaurer l'archive ; supprimer définitivement après la durée légale (à une ou deux personnes, voir ci-dessous) ; sauvegardes planifiées, rétention, restauration à une date (instance neuve ou remplacement), restauration test ; supervision (service, base, certificat et échéance, quota Let's Encrypt, disque, dernière sauvegarde, exécutant silencieux) avec alertes à l'écran et par courriel ; recours d'accès par réémission de l'invitation d'administrateur (à une ou deux personnes).

== Paquet d'un dossier

Les serveurs d'hébergement sont sous FreeBSD ; les instances Partiduo y sont installées par l'un de deux paquets exclusifs, aux mêmes emplacements : partiduo-app (production) ou partiduo-app-devel (développement, sur le serveur de test) ; une seule version par serveur. Chaque dossier porte le paquet attendu sur son serveur, son paquet (app ou devel), choisi à la création (app par défaut) et affiché sur la fiche et la liste des dossiers ; une restauration dans une instance neuve garde le paquet du dossier restauré. Chaque tâche d'un dossier transmet son paquet à l'exécutant (paramètre package), qui le compare au paquet installé pour une instance pas encore déclarée sur le serveur (création, instance neuve) et refuse la tâche s'il diffère : ensuite, c'est le paquet du serveur qui la sert.

La mise à jour des paquets n'est pas l'affaire de l'administration : beryl fait le pkg upgrade sur tous les serveurs, et le script rc.d de chaque instance applique ses migrations à son démarrage. L'administration ne publie donc aucune version et ne monte aucun dossier ; la version affichée d'un dossier est celle que rend l'instance (création, restauration, supervision), et chaque sauvegarde retient celle de l'instance au moment de la prise.

API de l'exécutant 2.0.0 : type instance.upgrade et état de tâche waiting retirés, paramètre version des tâches remplacé par package.

== Validation à deux au choix

Les opérations sensibles — suppression définitive et recours d'accès — passent par une demande. Chaque structure règle si elles exigent deux personnes (DECISIONS D-VAL2-001 à D-VAL2-008 de partiduo-app) : écran « Validation à deux » (menu de l'admin de la structure ; colonne de la liste des structures et menu du parc sans cabinet pour le super-admin).

[cols="1,3",options="header"] |=== |Mode |Effet

|Une personne (défaut) |Le demandeur, s'il est habilité à valider (admin de la structure ou super-admin), confirme seul sur la page « Confirmer seul » : ré-authentification forte de moins de cinq minutes (passkey, ou code TOTP pour les rôles au niveau 2 ; la connexion elle-même compte), puis confirmation explicite — sous-domaine du dossier retapé pour une suppression définitive, case cochée pour un recours d'accès. La demande d'un gestionnaire de dossiers attend toujours un admin.

|Deux personnes |Une autre personne habilitée (admin de la structure ou super-admin) valide, comme avant. Le demandeur ne peut pas confirmer seul. |===

  • Qui règle : l'admin de la structure (cabinet, gestionnaire indépendant) ; le super-admin règle le parc sans cabinet et consulte les autres sans les changer.
  • Défaut : une personne pour toute nouvelle structure ; proposée (bandeau) à un cabinet d'au moins deux personnes habilitées, jamais imposée. Reprise des données : un cabinet qui comptait déjà deux admins actifs reste à deux personnes.
  • Personnes habilitées : admins actifs de la structure ; pour le parc sans cabinet, les super-admins actifs. Le mode « deux personnes » est refusé sous deux ; si l'équipe y retombe (compte désactivé, départ), il se désactive de lui-même (dual_approval.auto_off au journal d'audit, à la désactivation et à chaque planification).
  • Bascule (dual_approval.change) : une demande en attente reste en attente si l'on passe à deux personnes ; si l'on revient à une personne, son demandeur peut la confirmer seul après ré-authentification.
  • Ce qui ne change pas : durée légale et état « archivé » revérifiés par l'exécutant (D-CRA-007), tâche annulable tant qu'elle attend, jamais rejouée après un échec (D-AFN-010), journal d'audit (approval.request, approval.confirm_alone, approval.approve, auth.reauth).
  • Exécutant (API 1.2.0) : chaque tâche porte approval_mode (single ou dual) et approvers ; l'exécutant exige un nom en mode une personne et deux noms distincts en mode deux personnes (absent : deux), l'écrit au journal de la tâche, et pour un recours d'accès passe à l'instance --approval-mode single|dual avec le ou les noms (contrat d'instance 1.1.0, B-VAL2-001 levé). Instance encore au contrat 1.0.0 : la validation à deux lui est transmise sans l'option (repli consigné au journal de la tâche), celle à une personne est refusée avant tout geste — mettre l'instance à jour.

== Sauvegardes et chiffrement

Chaque sauvegarde d'un dossier est un pg_dump -Fc de sa base et une archive tar.gz de ses pièces jointes. Elle est préparée sur le serveur qui l'héberge, relue en entier, décrite par un manifeste signé par ce serveur, puis envoyée au stockage, hors du serveur de production (quantas, lui-même copié sur des NAS) : rien d'elle ne reste sur le serveur. Le chiffrement est un choix réglé dans l'administration (DECISIONS D-CHF-001 à D-CHF-024 de partiduo-app) :

[cols="1,3",options="header"] |=== |Mode |Effet

|Aucun |Fichiers en clair (.dump, .media.tar.gz) dans le stockage.

|Clé d'exploitation (défaut) |Fichiers age post-quantiques. Le serveur n'a que les clés publiques d'exploitation : il ne relit pas les sauvegardes passées, pas même les siennes, et aucune copie n'est lisible sans une clé privée d'exploitation, gardée hors ligne. Restaurer, sur n'importe quel serveur, exige d'ouvrir la capsule de la sauvegarde sur le poste de l'exploitant.

|Clé du cabinet |Fichiers age post-quantiques dont la clé est chiffrée pour la clé publique RSA du cabinet : ni le serveur, ni l'exploitant ne peuvent les lire. Restaurer exige la clé privée de l'admin du cabinet, utilisée dans son navigateur. |===

  • Clé de sauvegarde et capsule (D-CHF-014) : chaque sauvegarde est chiffrée pour une clé age tirée pour elle seule (age-keygen -pq, AGE-SECRET-KEY-PQ-1…), jamais écrite en clair, effacée à la fin de la tâche. Elle est gardée dans une capsule : ….key.age, fichier age pour les clés d'exploitation du serveur ; ….key.rsa, RSA-OAEP (SHA-256) pour la clé du cabinet. La capsule est déposée au stockage à côté des fichiers et gardée par l'administration.
  • Clés d'exploitation (D-CHF-015) : paires age post-quantiques produites hors ligne, la clé privée chiffrée par une phrase de passe, en deux exemplaires au moins tenus par deux personnes. Seules les clés publiques (age1pq1…) vont sur les serveurs, par beryl (/usr/local/etc/partiduo-agent/exploitation.recipients, une par ligne). Les clés non post-quantiques sont refusées.

[source,sh]

age-keygen -pq | age -p > exploitation-2026.age age -d exploitation-2026.age | age-keygen -y

La première commande demande la phrase de passe ; la seconde affiche la clé publique, à confier à beryl.

  • Restauration (D-CHF-016) : la page « Clé de la sauvegarde requise » donne la capsule et la commande à lancer sur le poste de l'exploitant (age -d -i exploitation-2026.age backup-….key.age). L'exploitant colle la clé de sauvegarde affichée. L'administration la contrôle (son destinataire est celui de la sauvegarde), la remet une seule fois à l'exécutant, qui la garde en mémoire. Pour la clé du cabinet, la page ouvre la capsule dans le navigateur, la clé privée ne quittant pas la page. Une restauration interrompue est à redemander avec la clé.
  • Signature (D-CHF-018) : chaque serveur signe le manifeste de ses sauvegardes avec sa clé Ed25519 (ssh-keygen -Y sign -n partiduo-backup, clé /usr/local/etc/partiduo-agent/backup-signing.key posée par beryl). Le super-admin déclare la clé publique de chaque serveur (écran « Serveurs ») ; une sauvegarde n'est relue que si la signature de son manifeste est vérifiée par une clé de son serveur d'origine, valable à sa date de prise, et si ses fichiers ont les tailles et empreintes du manifeste et de l'administration (D-CHF-011). Une clé révoquée ne vaut plus pour les sauvegardes prises après sa révocation.
  • Restauration test (D-CHF-017) : toute sauvegarde est relue à sa prise (pg_restore --list, tar -t, après déchiffrement). Ensuite, sans la clé de la sauvegarde : signature, empreintes et structure des fichiers age (« enveloppe seule ») ; avec elle (« Tester avec la clé ») : restauration complète dans une base temporaire.
  • Rotation (D-CHF-019) : les gros fichiers ne changent jamais ; seules les capsules sont re-chiffrées, avec age (B-CHF-003).

[source,sh]

age -d -i exploitation-2025.age backup-….key.age | age -R nouvelles.recipients > backup-….key.age.nouvelle

Un attaquant qui a déjà copié une sauvegarde et sa capsule, et qui obtient la clé privée qui ouvrait cette capsule, lit ce qu'il a copié : aucune rotation n'y peut rien.

  • Réglage : par cabinet, surchargeable par dossier. Déposer la clé du cabinet et quitter le mode « clé du cabinet » sont réservés à l'admin du cabinet (D-CHF-008). Clé du cabinet : paire RSA-OAEP de 3072 bits produite dans le navigateur de l'admin du cabinet, ou par une commande locale :

[source,sh]

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -aes-256-cbc -out cabinet-prive.pem openssl pkey -in cabinet-prive.pem -pubout -out cabinet-public.pem openssl pkey -pubin -in cabinet-public.pem -outform DER | openssl dgst -sha256

Seule la clé publique (cabinet-public.pem) est déposée ; la dernière commande affiche son empreinte.

  • Clair jamais sur le disque : pg_dump, tar et pg_restore écrivent et lisent par un tube relié à age (- des gestes dump, restore, media-archive, media-restore de l'enveloppe de sudo). Une restauration authentifie d'abord tout le fichier : rien n'est remplacé par une sauvegarde altérée ou avec une mauvaise clé.
  • Journal d'audit : backup_encryption.firm, backup_encryption.dossier, backup_key.deposit, backup.restore et backup.test_restore (mode, clé fournie ou non), backup.capsule (capsule téléchargée), signing_key.add et signing_key.revoke ; jamais une clé de sauvegarde.

=== Stockage hors du serveur

Fichiers d'une sauvegarde, sous <serveur>/<dossier>/ (serveur qui l'a prise) :

[cols="2,3",options="header"] |=== |Fichier |Contenu

|backup-<horodatage>.dump.age |base (pg_dump -Fc), chiffrée |backup-<horodatage>.media.tar.gz.age |pièces jointes, chiffrées |backup-<horodatage>.key.age ou .key.rsa |capsule |backup-<horodatage>.manifest |manifeste (texte) |backup-<horodatage>.manifest.sig |signature du manifeste (sshsig) |===

Préfixes : backup-, archive- (archive de fin de vie), pre-restore- (sauvegarde de sûreté d'une restauration) ; sans chiffrement, ni .age ni capsule.

L'exécutant joint le stockage par --store-command, une commande sans shell qui parle le protocole de partiduo-agent store-serve. En production, c'est ssh vers le serveur de sauvegarde, dont authorized_keys force la commande :

[source,sh]

command="/usr/local/bin/partiduo-agent store-serve --root /backups/partiduo --client loulou",restrict ssh-ed25519 AAAA… partiduo-agent@loulou

Le serveur loulou n'écrit que sous loulou/, sans jamais écraser un fichier, et lit tout (restauration d'une sauvegarde d'un autre serveur). Il n'efface que sous loulou/ ; une archive dans sa durée légale n'est jamais effacée (D-CRA-007). L'élagage, par un autre serveur, des sauvegardes d'un dossier déplacé les garde (kept).

=== Format des fichiers (format 2)

  • Base et pièces jointes : fichiers age (C2SP age-encryption.org/v1), un seul destinataire mlkem768x25519 (la clé de sauvegarde), segments de 64 Kio ChaCha20-Poly1305 authentifiés. age-inspect --json en donne la structure.
  • Capsule : age (destinataires mlkem768x25519 des clés d'exploitation) ou RSA-OAEP (SHA-256, MGF1-SHA-256, sans étiquette) de la clé de sauvegarde, en texte.
  • Manifeste :

partiduo-backup/2 server loulou dossier backup backup- taken_at 2026-10-09T10:15:00Z mode exploitation recipient age1pq1… capsule-key <SHA-256 de la clé d'exploitation> file backup-.dump.age file backup-.media.tar.gz.age

Le cabinet est nommé par cabinet-key <empreinte> ; sans chiffrement, ni recipient ni capsule.

=== Restauration hors de Partiduo

Avec les fichiers d'une sauvegarde (copiés du stockage ou d'un NAS), sans administration ni réseau, avec les seuls outils age, openssl et ssh-keygen :

[source,sh]

age -d -i exploitation-2026.age backup-….key.age > backup.key age -d -i backup.key -o backup.dump backup-….dump.age age -d -i backup.key -o media.tar.gz backup-….media.tar.gz.age

Ces trois commandes valent pour la clé d'exploitation. Pour la clé du cabinet, la première commande devient :

[source,sh]

openssl pkeyutl -decrypt -inkey cabinet-prive.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256 -in backup-….key.rsa -out backup.key

Authenticité, avec le fichier allowed_signers des clés de signature (<serveur> namespaces="partiduo-backup" ssh-ed25519 AAAA…) :

[source,sh]

ssh-keygen -Y verify -f allowed_signers -I loulou -n partiduo-backup -s backup-….manifest.sig < backup-….manifest sha256 backup-….dump.age backup-….media.tar.gz.age

La seconde commande donne les empreintes, à comparer à celles du manifeste. On restaure ensuite comme une sauvegarde en clair :

[source,sh]

createdb --encoding=UTF8 dossier_restaure && pg_restore --no-owner -d dossier_restaure backup.dump tar -xzf media.tar.gz -C <stockage des pièces jointes>

partiduo-agent decrypt (--key, ou --capsule avec --identity ou --private-key) et partiduo-agent inspect (fichier age, ou manifeste avec --signers) font de même.

== Modes de l'exécutant

dry-run (à blanc):: Rien n'est exécuté : état simulé, chaque geste écrit au journal. Specs et répétitions. --fail-on TEXTE (à blanc seulement) fait échouer la première opération qui contient TEXTE : répétition d'un échec et de sa reprise, par exemple --fail-on "tar" pour une sauvegarde.

local:: Machine de développement : vraies bases partiduo_adm_* (toute autre base est refusée), vrais partiduo-provision, pg_dump, pg_restore, tar ; ni vhost, ni service rc.d, ni Let's Encrypt — les fichiers de service sont produits dans --work-dir, l'arrêt d'un service est un fichier témoin. --manage et --provision désignent les outils du paquet app (défaut : /usr/local/bin/partiduo-manage et partiduo-provision, liens du paquet partiduo-app ou partiduo-app-devel, installés aux mêmes emplacements), --manage-devel et --provision-devel ceux du paquet devel (défaut : vides, ceux de app ; à fournir seulement pour essayer deux versions côte à côte hors paquet) ; le paquet d'une instance est retenu dans le fichier PACKAGE de son répertoire.

production:: Serveur d'hébergement FreeBSD : /usr/local/bin/sudo -n limité par deploy/partiduo-agent.sudoers à deux scripts enveloppes possédés par root (deploy/libexec/), qui valident leurs arguments et lisent /usr/local/etc/partiduo-agent/helpers.conf (deploy/helpers.conf.example) : partiduo-agent-root (installation et retrait d'une instance, service par service(8), certificat) et partiduo-agent-instance (interface d'instance, partiduo-provision, bases, sauvegardes, sous le compte des instances, propriétaire des bases). Une seule version par serveur : le paquet qui sert les instances est celui qui y est installé (partiduo-app ou partiduo-app-devel, exclusifs, aux mêmes emplacements), et les instances sont déclarées dans partiduo_instances (rc.conf) ; les enveloppes refusent un paquet absent du serveur, ou une instance non déclarée quand la tâche ne précise pas de paquet. --domain est obligatoire : l'hôte et la base de chaque tâche sont recalculés depuis le sous-domaine (D-AFN-002, D-AFN-004, D-AFN-005).

Invitations remises par le serveur (tous modes, recommandé en production) : --mail-command "/usr/sbin/sendmail -oi" --mail-from noreply@partiduo.app. L'exécutant envoie lui-même le lien d'invitation de l'administrateur d'un dossier (création, recours d'accès) ; l'administration n'en reçoit que la trace invitation_delivered. Sans cette option, le lien lui est rendu et elle l'envoie (D-ADM-009) : une faille de l'administration le révélerait (D-CRA-003).

Ce que l'exécutant refuse d'une administration compromise : une sauvegarde hors du répertoire du dossier (<backup-dir>/<sous-domaine>/, D-CRA-001), une valeur prise pour une option de manage instance (D-CRA-002), une pièce jointe hors du stockage de l'instance (D-CRA-004) ; les lignes de PostgreSQL qui citent des valeurs de la base ne remontent pas dans le journal des tâches (D-CRA-005). Une sauvegarde chiffrée par la clé du cabinet ne peut pas être lue sur le serveur (voir « Sauvegardes et chiffrement »).

== Développement

[source,sh]

SKIP_MARTEN_CLI_PRECOMPILATION=1 shards install createdb --encoding=UTF8 partiduo_admin crystal run manage.cr -- migrate crystal run manage.cr -- bootstrap --email=vous@exemple.fr # affiche le lien d'invitation MARTEN_ENV=development crystal run src/server.cr # http://127.0.0.1:8200

Exécutant local (jeton affiché à la création du serveur dans l'interface)

crystal build src/agent.cr -o bin/partiduo-agent bin/partiduo-agent --admin-url http://127.0.0.1:8200 --token-file jeton.txt --mode local
--manage ../partiduo-app/bin/partiduo-manage --provision ../partiduo-app/bin/partiduo-provision
--pg-socket /tmp --backup-dir /tmp/partiduo-backups --state-dir /tmp/partiduo-agent
--server-name local --store-dir /tmp/partiduo-store

Hors production, le stockage est un répertoire (--store-dir, défaut <state-dir>/store), la clé de signature est créée au premier usage dans l'état de l'exécutant, et les clés d'exploitation sont lues dans <state-dir>/exploitation.recipients. age 1.3 ou suivant (age, age-keygen, age-inspect) et ssh-keygen sont requis, par l'exécutant comme par l'administration (contrôle d'une clé de sauvegarde remise).

Specs (base propre, dont le nom contient test) ; celles du chiffrement et du stockage tournent seules, sans base ni réseau (crystal spec spec/agent/backup_crypto_spec.cr) :

[source,sh]

createdb --encoding=UTF8 partiduo_admin_test DATABASE_URL='postgres:///partiduo_admin_test?host=/tmp' crystal spec --order random crystal tool format --check src spec config scripts manage.cr crystal build lib/ameba/bin/ameba.cr -o bin/ameba && bin/ameba

== Développement local

shard.yml et shard.lock référencent les shards maison sur GitHub (aloli-crystal/* et aloli-partiduo/*), suivis par branche. Le verrou versionné ne contient aucune dépendance path: : les ports FreeBSD et la CI l'exigent.

Pour travailler sur les copies de travail voisines (../../prod-crystal/<nom> et ../partiduo-<dépôt>), lancer :

[source,sh]

scripts/override-local.sh

Le script génère un shard.override.yml local et non versionné (ignoré par git), installe les shards, puis remet shard.lock à son état versionné. Une copie de travail absente reste prise sur GitHub.

Pour régénérer le verrou versionné, supprimer d'abord l'override local :

[source,sh]

rm -f shard.override.yml SKIP_MARTEN_CLI_PRECOMPILATION=1 shards update scripts/override-local.sh # retour au mode local

== Exploitation (FreeBSD)

Les serveurs tournent sous FreeBSD 15. Tout s'installe par paquets, depuis l'arbre de ports aloli-ports :

  • finance/partiduo-admin : l'application (partiduo-admin, partiduo-admin-manage, fichiers statiques, gabarits et traductions rangés dans share/partiduo-admin/ à côté des programmes, voir config/settings/production.cr), son script rc.d partiduo_admin et la planification /usr/local/etc/cron.d/partiduo-admin (partiduo-admin-manage schedule toutes les quinze minutes) ;
  • sysutils/partiduo-agent : l'exécutant (partiduo-agent), les deux scripts enveloppes (deploy/libexec/) dans /usr/local/libexec/partiduo-agent/, les règles sudoers (deploy/partiduo-agent.sudoers) dans /usr/local/etc/sudoers.d/partiduo-agent et son script rc.d partiduo_agent ;
  • finance/partiduo-app et finance/partiduo-app-devel : les instances (l'un ou l'autre, exclusifs : /usr/local/lib/partiduo, script rc.d partiduo), que l'exécutant installe avec leur partiduo-provision, leurs gabarits (deploy/templates/) et leur partiduo-manage. Les paquets sont mis à jour par beryl, pas par l'exécutant : le script rc.d d'une instance passe ses migrations avant chaque démarrage.

deploy/ ne contient plus que ce que les ports reprennent : les règles sudoers, les scripts enveloppes et l'exemple de leur configuration. Les paquets créent les comptes système (partiduo par partiduo-app, partiduo-agent et partiduo-admin par les leurs) ; sans eux : pw useradd partiduo-agent -c "Partiduo agent" -d /nonexistent -s /usr/sbin/nologin (de même pour partiduo-admin).

Serveur d'hébergement, après pkg install partiduo-agent partiduo-app (partiduo-agent-devel partiduo-app-devel sur le serveur de test), en root :

[source,sh]

Configuration des enveloppes (root 0644 : elles refusent de démarrer sinon)

install -d -o root -g wheel -m 755 /usr/local/etc/partiduo-agent install -o root -g wheel -m 644 deploy/helpers.conf.example /usr/local/etc/partiduo-agent/helpers.conf # à adapter visudo -c # règles de /usr/local/etc/sudoers.d/partiduo-agent (paquet)

Répertoires : sauvegardes (les deux comptes y écrivent ; les répertoires des

dossiers héritent du groupe, règle BSD), fichiers de partiduo-provision,

état de l'exécutant

install -d -o partiduo -g partiduo-agent -m 770 /var/backups/partiduo install -d -o partiduo -g partiduo -m 750 /var/db/partiduo-stage install -d -o partiduo-agent -g partiduo-agent -m 700 /var/db/partiduo-agent

Rôle PostgreSQL des instances (socket /tmp, authentification peer)

su -m postgres -c 'createuser --createdb --no-superuser partiduo'

nginx lit les vhosts des instances : dans le bloc http de

/usr/local/etc/nginx/nginx.conf, « include partiduo/*.conf; »

Jeton du serveur (affiché à sa création dans l'administration)

install -o partiduo-agent -g partiduo-agent -m 600 jeton.txt /usr/local/etc/partiduo-agent/agent.token sysrc partiduo_agent_enable=YES sysrc partiduo_agent_flags="--mode production --admin-url https://admin.partiduo.app
--token-file /usr/local/etc/partiduo-agent/agent.token --domain partiduo.app --server-name loulou
--store-command '/usr/bin/ssh -T -i /usr/local/etc/partiduo-agent/store.key -o BatchMode=yes partiduo-store@quantas'
--mail-command '/usr/sbin/sendmail -oi' --mail-from noreply@partiduo.app" service partiduo_agent start

Posés par la recette beryl partiduo-agent (D-CHF-022) : le paquet age, les clés publiques d'exploitation (/usr/local/etc/partiduo-agent/exploitation.recipients, 0644), la clé de signature du serveur (backup-signing.key, 0600, partiduo-agent, et sa .pub), la clé ssh du stockage (store.key, 0600). Ces clés privées sont propres à chaque serveur et viennent du coffre de beryl. La clé publique de signature est déclarée dans l'administration (écran « Serveurs »). La clé privée d'exploitation n'est sur aucun serveur.

Serveur de sauvegarde (quantas), après pkg install partiduo-agent : un compte partiduo-store, propriétaire de la racine du stockage, et une ligne authorized_keys par serveur de production (section « Stockage hors du serveur »). Ce serveur n'a aucune clé de chiffrement ; ses copies sur les NAS se font telles quelles.

--domain : même valeur que DOMAIN de helpers.conf. --server-name : nom du serveur dans l'administration. --store-command est obligatoire en production. Défauts de l'exécutant : --state-dir /var/db/partiduo-agent, --backup-dir /var/backups/partiduo, --pg-socket /tmp, --helpers-dir /usr/local/libexec/partiduo-agent. --mail-command exige un MTA local (D-CRA-003).

Pour chaque instance, l'enveloppe racine fait les gestes de l'INSTALL.txt de partiduo-provision : environnement /usr/local/etc/partiduo/<dossier>.env (root:partiduo 0640, relu et validé clé par clé), données /var/db/partiduo/<dossier> (pièces jointes dans media/), journal /var/log/partiduo/<dossier>.log, déclaration dans rc.conf (partiduo_instances, partiduo_enable=YES), service partiduo start <dossier>, tâche quotidienne /usr/local/etc/cron.d/partiduo-<dossier>, vhost /usr/local/etc/nginx/partiduo/<dossier>.conf et certificat Let's Encrypt par acme.sh, comme sur le parc beryl et selon les conventions du port FreeBSD security/acme.sh : acme.sh sous l'utilisateur acme (su -m), jamais en root, home /var/db/acme/.acme.sh, certificat laissé dans /var/db/acme/certs/<hôte>_ecc/ (fullchain.cer, <hôte>.key), défi HTTP-01 sur /usr/local/www/letsencrypt (possédé par acme), --reloadcmd '/usr/local/bin/sudo /usr/sbin/service nginx reload' rejoué par le cron de renouvellement /usr/local/etc/cron.d/acme ; un certificat encore valide n'est pas réémis. Prérequis : acme.sh (/usr/local/sbin/acme.sh), l'utilisateur acme, son cron et la règle /usr/local/etc/sudoers.d/acme, posés par la recette beryl acme ; l'enveloppe refuse l'installation, avant toute écriture, s'il en manque un.

Serveur de l'administration, après pkg install partiduo-admin age (age-keygen contrôle une clé de sauvegarde remise) :

[source,sh]

su -m postgres -c 'createuser partiduo-admin && createdb --owner=partiduo-admin --encoding=UTF8 partiduo_admin'

Avec l'environnement de l'application (ci-dessous), sous partiduo-admin :

partiduo-admin-manage migrate

partiduo-admin-manage bootstrap --email=vous@exemple.fr # lien d'invitation

sysrc partiduo_admin_enable=YES service partiduo_admin start

Variables de l'application (fichier d'environnement lu par son script rc.d, voir le port) : DATABASE_URL, MARTEN_SECRET_KEY, PARTIDUO_DOMAIN (partiduo.app), PARTIDUO_ADMIN_HOST, PARTIDUO_ADMIN_URL, PARTIDUO_ADMIN_MAIL_FROM, PARTIDUO_ADMIN_ALERTS_TO ; au besoin PARTIDUO_ADMIN_ASSETS_ROOT et PARTIDUO_ADMIN_ARTIFACTS_ROOT, prioritaires sur les fichiers du paquet.

Décisions et blocages : ../partiduo-app/DECISIONS.adoc (D-ADM-…) et ../partiduo-app/BLOCAGES.adoc (B-ADM-…).

Repository

partiduo-admin

Owner
Statistic
  • 0
  • 0
  • 0
  • 0
  • 8
  • about 1 hour ago
  • September 28, 2026
License

GNU Affero General Public License v3.0

Links
Synced at

Mon, 12 Oct 2026 16:29:00 GMT

Languages