[FIX] nixos : pare-feu ouvert, et ce qu'on appelle par son nom déclaré
NixOS active un pare-feu par défaut ; aucune des quatre autres images cloud n'en active. Le service écoutait bien et répondait en local, mais l'hôte ne recevait RIEN — pas un refus, un silence jusqu'au délai — et le suivi déclarait Odoo absent sur une machine où il tournait. Mesuré : 000 après 12 s, puis 303 en 9 ms. Un système déclaratif n'a que ce qui est écrit : xmlsec1, sans lequel Odoo refuse d'installer auth_saml ; parallel et shfmt, appelés par leur nom nu ; growpart, dont l'absence faisait rendre 0 à un agrandissement qui n'agrandissait rien ; et l'agent invité, qui venait de l'image. --- EN --- NixOS enables a firewall by default; none of the four other cloud images does. The service did listen and answered locally, but the host received NOTHING — not a refusal, silence until the timeout — and the monitor declared Odoo absent on a machine where it was running. Measured: 000 after 12 s, then 303 in 9 ms. A declarative system has only what is written: xmlsec1, without which Odoo refuses to install auth_saml; parallel and shfmt, called by bare name; growpart, whose absence made a disk grow return 0 while growing nothing; and the guest agent, which came from the image. Assisted-by: Claude Opus 5
This commit is contained in:
parent
e0624d9aaa
commit
c1f2f8db7f
2 changed files with 168 additions and 0 deletions
|
|
@ -146,6 +146,24 @@
|
|||
wget
|
||||
unzip
|
||||
wkhtmltopdf
|
||||
# « xmlsec » pose bin/xmlsec1, que les CINQ autres plateformes posent
|
||||
# aussi. Le manifeste d'auth_saml (OCA server-auth, présent dans
|
||||
# l'addons_path) déclare « bin: ["xmlsec1"] », et Odoo REFUSE d'installer
|
||||
# ou de mettre à niveau le module tant que le binaire n'est pas dans le
|
||||
# PATH : « Unable to find 'xmlsec1' in path », en boîte de dialogue.
|
||||
xmlsec
|
||||
# « parallel » et « shfmt » sont appelés par leur NOM NU, l'un par
|
||||
# script/database/db_drop_all.py, l'autre par script/maintenance/
|
||||
# format_bash.sh. Sans parallel, « make db_drop_all » annonce des bases
|
||||
# détruites qui ne l'ont pas été — une opération destructrice qui rend un
|
||||
# succès qu'elle n'a pas obtenu.
|
||||
parallel
|
||||
shfmt
|
||||
# « cloud-utils » porte growpart, qu'aucune autre voie ne fournit ici.
|
||||
# L'agrandissement du disque s'écrit « sudo growpart … || true » : sans le
|
||||
# binaire, il rend 0 sans rien agrandir, et la VM garde la taille de son
|
||||
# image pendant que le déploiement annonce la taille demandée.
|
||||
cloud-utils
|
||||
];
|
||||
|
||||
# Le profil du système ne porte PAS « /include » : la liste par défaut de
|
||||
|
|
@ -178,6 +196,51 @@
|
|||
PKG_CONFIG_PATH = "/run/current-system/sw/lib/pkgconfig";
|
||||
};
|
||||
|
||||
# L'agent invité, DÉCLARÉ ici plutôt que reçu de l'image.
|
||||
#
|
||||
# L'image épinglée l'active déjà (son configuration.nix porte la ligne), et
|
||||
# c'est précisément le problème : la garantie appartient alors au tiers qui
|
||||
# rebâtit l'image, pas au dépôt. Les quatre autres distributions reçoivent
|
||||
# l'agent par le runcmd du déploiement, qui appelle leur gestionnaire de
|
||||
# paquets — geste impossible sur un système déclaratif. Ici, c'est cette
|
||||
# ligne ou rien.
|
||||
#
|
||||
# Sans agent, la voie Proxmox perd sa source PRIMAIRE d'adresse et retombe
|
||||
# sur « ip neigh ». Déclarer l'option deux fois est sans effet : une option
|
||||
# booléenne ne rompt l'évaluation que sur des valeurs DIFFÉRENTES. Hors
|
||||
# QEMU, l'unité ne démarre pas — elle n'a pas de section [Install] et
|
||||
# attend une règle udev sur le port virtio.
|
||||
services.qemuGuest.enable = true;
|
||||
|
||||
# Ce que l'agent doit trouver dans son PATH.
|
||||
#
|
||||
# Une unité systemd n'hérite pas du profil du système : celle-ci ne porte
|
||||
# que coreutils, findutils, grep, sed et systemd. Or « guest-exec » exécute
|
||||
# les commandes du produit DANS ce PATH, et l'agrandissement du disque
|
||||
# commence par « findmnt -no SOURCE / » — absent, donc code 127 dès la
|
||||
# première ligne.
|
||||
systemd.services.qemu-guest-agent.path = with pkgs; [
|
||||
util-linux
|
||||
e2fsprogs
|
||||
cloud-utils
|
||||
];
|
||||
|
||||
# Le port d'Odoo, OUVERT.
|
||||
#
|
||||
# NixOS active un pare-feu par défaut ; aucune des images cloud des quatre
|
||||
# autres distributions n'en active un. Le service écoute bien sur
|
||||
# 0.0.0.0:8069 et répond en local, mais l'extérieur ne reçoit RIEN — pas un
|
||||
# refus, un silence, donc une attente jusqu'au délai. Ce qui sonde depuis
|
||||
# l'hôte conclut « Odoo absent » sur une machine où il tourne, et le journal
|
||||
# de l'installation ne porte aucune trace de la cause : elle est dans le
|
||||
# pare-feu, pas dans l'application.
|
||||
#
|
||||
# 8069 SEUL. Le port websocket est configuré à 8072, mais Odoo ne le lie
|
||||
# qu'en mode multi-processus, que cette configuration n'emploie pas : rien
|
||||
# n'y écoute, et l'ouvrir donnerait un port béant sans service derrière.
|
||||
# PostgreSQL n'écoute déjà que sur la boucle locale et n'a rien à ouvrir.
|
||||
networking.firewall.allowedTCPPorts = [ 8069 ];
|
||||
|
||||
# Le service ERPLibre, DÉCLARÉ et non écrit.
|
||||
#
|
||||
# Sur toute autre distribution l'installation dépose l'unité par
|
||||
|
|
|
|||
|
|
@ -337,6 +337,111 @@ class LeServiceEstDeclare(unittest.TestCase):
|
|||
self.assertIn(marqueur, self.script)
|
||||
|
||||
|
||||
class CeQueLeDepotAPPELLE_ParSonNomNu(unittest.TestCase):
|
||||
"""Un système déclaratif n'a que ce qui est écrit.
|
||||
|
||||
Les quatre autres distributions posent ces outils par leur gestionnaire
|
||||
de paquets ; ici, l'absence ne se voit qu'à l'usage, et tard.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.src = MODULE.read_text(encoding="utf-8")
|
||||
|
||||
def test_xmlsec_is_declared(self):
|
||||
"""Le manifeste d'auth_saml (OCA server-auth, dans l'addons_path)
|
||||
déclare « bin: ["xmlsec1"] » : Odoo REFUSE d'installer et de mettre à
|
||||
niveau le module tant que le binaire n'est pas dans le PATH."""
|
||||
self.assertIn("\n xmlsec\n", self.src)
|
||||
|
||||
def test_the_tools_called_by_bare_name_are_declared(self):
|
||||
"""« parallel » et « shfmt » sont invoqués sans chemin. Sans
|
||||
parallel, « make db_drop_all » annonce des bases détruites qui ne
|
||||
l'ont pas été."""
|
||||
for outil in ("parallel", "shfmt"):
|
||||
with self.subTest(outil=outil):
|
||||
self.assertIn(f"\n {outil}\n", self.src)
|
||||
|
||||
def test_growpart_has_a_provider(self):
|
||||
"""L'agrandissement s'écrit « sudo growpart … || true » : sans le
|
||||
binaire il rend 0 sans rien agrandir, et la VM garde la taille de son
|
||||
image pendant que le déploiement annonce celle demandée."""
|
||||
self.assertIn("\n cloud-utils\n", self.src)
|
||||
|
||||
|
||||
class LAgentInviteVientDuDepot(unittest.TestCase):
|
||||
"""L'image épinglée active déjà l'agent — et c'est le problème : la
|
||||
garantie appartient alors au tiers qui la rebâtit.
|
||||
|
||||
Les quatre autres distributions le reçoivent du runcmd, qui appelle leur
|
||||
gestionnaire de paquets. Sur un système déclaratif, c'est cette ligne ou
|
||||
rien.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.src = MODULE.read_text(encoding="utf-8")
|
||||
|
||||
def test_the_agent_is_declared(self):
|
||||
self.assertIn("services.qemuGuest.enable = true;", self.src)
|
||||
|
||||
def test_its_path_carries_what_guest_exec_runs(self):
|
||||
"""Une unité systemd n'hérite pas du profil du système. « guest-exec »
|
||||
exécute les commandes du produit DANS ce PATH, et l'agrandissement
|
||||
commence par « findmnt -no SOURCE / » : absent, code 127 dès la
|
||||
première ligne."""
|
||||
i = self.src.index("systemd.services.qemu-guest-agent.path")
|
||||
bloc = self.src[i : self.src.index("];", i)]
|
||||
for paquet in ("util-linux", "e2fsprogs", "cloud-utils"):
|
||||
with self.subTest(paquet=paquet):
|
||||
self.assertIn(paquet, bloc)
|
||||
|
||||
|
||||
class LePortDOdooTraverseLePareFeu(unittest.TestCase):
|
||||
"""NixOS active un pare-feu par défaut ; aucune des images cloud des
|
||||
quatre autres distributions n'en active un.
|
||||
|
||||
Le service écoute bien sur 0.0.0.0:8069 et répond en local, mais
|
||||
l'extérieur ne reçoit RIEN — pas un refus, un silence, donc une attente
|
||||
jusqu'au délai. Ce qui sonde depuis l'hôte conclut « Odoo absent » sur une
|
||||
machine où il tourne, et le journal de l'installation ne porte aucune
|
||||
trace de la cause.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.src = MODULE.read_text(encoding="utf-8")
|
||||
|
||||
def _port_sonde(self):
|
||||
"""Le port que l'hôte sonde VRAIMENT, lu de sa signature : une
|
||||
constante recopiée ici vieillirait sans que rien ne le dise."""
|
||||
import inspect
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, str(RACINE))
|
||||
from script.todo.qemu_install_monitor import _port_open
|
||||
|
||||
return inspect.signature(_port_open).parameters["port"].default
|
||||
|
||||
def test_the_port_probed_from_the_host_is_the_one_opened(self):
|
||||
port = self._port_sonde()
|
||||
self.assertIn(
|
||||
f"networking.firewall.allowedTCPPorts = [ {port} ];", self.src
|
||||
)
|
||||
|
||||
def test_the_firewall_is_not_simply_turned_off(self):
|
||||
"""Le couper ouvrirait TOUT. PostgreSQL n'écoute aujourd'hui que sur
|
||||
la boucle locale, mais un module qui désactive le pare-feu ne le
|
||||
protégerait plus le jour où cela changerait."""
|
||||
self.assertNotIn("networking.firewall.enable = false", self.src)
|
||||
|
||||
def test_only_what_serves_is_opened(self):
|
||||
"""Odoo ne lie le port websocket qu'en mode multi-processus, que cette
|
||||
configuration n'emploie pas : rien n'y écoute, et l'ouvrir donnerait
|
||||
un port béant sans service derrière."""
|
||||
i = self.src.index("allowedTCPPorts")
|
||||
ligne = self.src[i : self.src.index("\n", i)]
|
||||
self.assertNotIn("8072", ligne)
|
||||
self.assertNotIn("5432", ligne)
|
||||
|
||||
|
||||
class LeMenuNEcritPasDansEtcSurNixos(unittest.TestCase):
|
||||
def _cmd(self, prod=False):
|
||||
import sys
|
||||
|
|
|
|||
Loading…
Reference in a new issue