OCS Inventory NG¶
Rôle¶
OCS Inventory NG est l'outil d'inventaire de parc (agents → serveur de communication → base de données) de l'infrastructure Indio, complémentaire à GLPI (qui embarque désormais son propre inventaire natif et sert de CMDB/ITSM de référence — voir GLPI). Redéployé le 2026-07-16 à la demande explicite du user malgré la note interne "GLPI remplace OCS" : le script Deploy-WorkstationBaseline.ps1 du projet ad-hardening déploie justement un agent OCS classique sur les postes, ce qui justifie un vrai serveur OCS indépendamment de GLPI.
Architecture¶
- VM unique INDSERV026 (
10.100.2.156), zone service (seg-service), 2 vCPU / 4 Go, clone du template Rocky 9.6 durci — IP allouée dynamiquement via NetBox. La VM d'origine (ocs/INDSERV001, provisionnée le 2026-07-04) a été détruite le 2026-07-07 dans le round de décommissionnement seg-service ; ce numéro001n'a volontairement pas été réutilisé (convention : ne jamais reprendre un numéro libéré tant que le segment reste partiellement occupé — voir Convention de nommage). - OCS Inventory NG 2.12.4, installé depuis le tarball officiel (
setup.sh, dépôt Nexusocs-tarball) — aucun paquet EL9 n'existe pour cette version. - Composants :
- Serveur de communication (agents) + API REST : modules Apache httpd + mod_perl (
Apache::Ocsinventory,Plack::Handler::Apache2) — aucun équivalent PHP-FPM/nginx-natif n'existe en branche 2.x, contrairement à GLPI. En écoute loopback uniquement (127.0.0.1:8080, patché depuis le défautListen 80). - Console
ocsreports: PHP classique multi-fichiers (pas un front-controller unique comme GLPI), servie par PHP-FPM (pool dédié) derrière nginx. - nginx termine le TLS en public (443) et fait office de reverse proxy unique vers httpd (comm server/API,
proxy_pass 127.0.0.1:8080) et vers PHP-FPM (console, socket unix).
- Serveur de communication (agents) + API REST : modules Apache httpd + mod_perl (
- MariaDB 10.11 locale, base
ocsweb, utilisateurocs(nom figé par les gabarits Apache livrés par OCS, jamais renommé) accessible à la fois en TCP127.0.0.1(mod_perl/DBD::mysql, pas de support socket unix côté Perl) et en socket unix (console PHP) — mêmebind-addresspar défaut couvre les deux, pas deskip-networking. - FQDN
inventaire.infra.indio, certificat TLS Vault PKI (même pattern que GLPI, voir Procédure manuelle).
Structure du dépôt¶
| Fichier / dossier | Contenu |
|---|---|
main.tf |
VM unique vsphere_virtual_machine.ocs, IP résolue via NetBox |
ansible/site.yml |
Rôles mariadb puis ocs |
ansible/roles/mariadb |
Base ocsweb (utf8mb3) + utilisateur ocs (localhost + 127.0.0.1) |
ansible/roles/ocs/tasks/main.yml |
httpd/mod_perl, PHP-FPM, nginx, pilotage de setup.sh |
ansible/roles/ocs/templates/setup.answers.j2 |
Réponses pré-remplies pour setup.sh |
ansible/roles/ocs/templates/dbconfig.inc.php.j2 |
Remplace le fichier legacy généré par setup.sh |
ansible/roles/ocs/templates/ocs-nginx.conf.j2 |
vhost nginx (double reverse proxy) |
ansible/roles/ocs/files/tls/ |
Certificat/clé Vault PKI (statiques, non repris ici) |
Chemin de requête (double pile applicative)¶
flowchart LR
Agents["Agents OCS<br/>(postes)"] -->|"443 TLS"| Nginx["nginx public<br/>(443, Vault PKI)"]
Admin(("Navigateur admin")) -->|"443 TLS<br/>/ocsreports"| Nginx
Nginx -->|"/ocsreports/*.php<br/>fastcgi unix:/run/php-fpm/ocs.sock"| PHPFPM["php-fpm<br/>(pool dédié ocs)"]
Nginx -->|"/ocsinventory /ocsapi /ocsinterface<br/>proxy_pass 127.0.0.1:8080"| Httpd["httpd + mod_perl<br/>(loopback uniquement)"]
PHPFPM -->|"socket unix"| MariaDB[("MariaDB 10.11<br/>base ocsweb (utf8mb3)")]
Httpd -->|"TCP 127.0.0.1<br/>DBD::mysql"| MariaDB
Provisioning Terraform¶
main.tf: VM uniquevsphere_virtual_machine.ocs, IP résolue via NetBox.- Variables clés (
variables.tf) :vm_name= INDSERV026,vsphere_network=seg-service,vm_cpu= 2,vm_ram= 4096. - Providers (
versions.tf) :hashicorp/vsphere+e-breuninger/netbox.
Configuration Ansible¶
Playbook ansible/site.yml, deux rôles en séquence sur l'hôte ocs :
- mariadb — même schéma que GLPI (mot de passe root, nettoyage des comptes anonymes/test), mais base créée en
utf8mb3/utf8mb3_general_ci(format du schéma OCS livré) et utilisateurocscréé pourlocalhostet127.0.0.1(boucle sur les 2 hôtes). - ocs — installe httpd/mod_perl et les dépendances Perl requises (
perl-XML-Simple,perl-IO-Compress,perl-Apache-DBI,perl-Net-IP,perl-Mojolicious,perl-Plack,perl-Archive-Zip,perl-Switch,perl-YAML,perl-DBI,perl-DBD-MySQL— toutes disponibles en RPM EL9 viaindio-epel/indio-crb, zéro module CPAN à compiler) ainsi que PHP-FPM/nginx ; télécharge le tarball OCS depuis Nexus (dépôtocs-tarball) ; déploiesetup.answerspuis exécutesetup.shen mode piloté (yes "" | timeout 180 bash ./setup.sh) ; repatche le mot de passe DB dans les 2 fichiers de conf Apache générés ; désactive le vhost Apache de la console (remplacée par nginx/PHP-FPM) ; fait basculer httpd en écoute loopback 8080 ; importe le schéma SQL (ocsbase_new.sql) ; redéploiedbconfig.inc.phpau bon format ; change le mot de passe admin (hash SHA-256) ; déploie le pool PHP-FPM, les certificats TLS et le vhost nginx ; corrige les permissions Unix (groupeapache→nginx) ; active les contextes et booléens SELinux nécessaires.
Secrets (group_vars/ocs.yml, chiffrés ansible-vault) : mariadb_root_password, ocs_db_password, ocs_admin_new_password.
Procédure manuelle¶
Comme pour GLPI, le certificat TLS n'est pas émis par le rôle Ansible : préparé manuellement une fois via Vault PKI et copié dans roles/ocs/files/tls/ avant tout déploiement (vault write pki_int/issue/infra-indio common_name=inventaire.infra.indio ip_sans=10.100.2.156, ca_chain complet Root+Intermediate). Voir la procédure détaillée sur la page GLPI (identique).
Procédure de déploiement¶
terraform apply→ provisionne la VM.- Préparer les certificats TLS sous
roles/ocs/files/tls/si pas déjà présents. ansible-playbook site.yml(tagsmariadb,ocs).
Contrôle de santé / Vérification¶
Vérifications intégrées : la console ocsreports répond en HTTPS (200/302, retry jusqu'à 30 fois/5 s) ; l'endpoint /ocsinventory (serveur de communication, via proxy nginx → httpd loopback) répond.
systemctl status nginx php-fpm httpd mariadb
curl -sk -o /dev/null -w '%{http_code}\n' https://inventaire.infra.indio/ocsreports/
Points d'attention¶
setup.sh n'a aucun mode non interactif natif (~1600 lignes, 28 prompts bloquants). Onze pièges concrets, tous rencontrés et corrigés lors du déploiement du 2026-07-16, documentés en commentaires dans le rôle Ansible — à relire intégralement avant toute réinstallation ou montée de version :
setup.answers— piège fondamental. Le fichier livré est commenté à 100 %. Sept variables (APACHE_BIN,APACHE_CONFIG_FILE,APACHE_USER,APACHE_GROUP,APACHE_CONFIG_DIRECTORY,PERL_BIN,MAKE) sont protégées côté script parif [ -z "$VAR" ]; then <auto-détection> fi. Les pré-remplir désactive l'auto-détection sans les renseigner :[ -d $VAR_VIDE ]dégénère en[ -d ](test à un seul argument, toujours vrai côté POSIX) → boucle infinie sur le prompt correspondant. Ne jamais renseigner ces 7 variables ; seules celles sans logique d'auto-détection (DB_SERVER_*, cheminsOCS_COM_SRV_*/ADM_SERVER_*, noms de fichiers conf) sont sûres à pré-remplir.- Shebang
#!/bin/sh. Sur Rocky 9,source fichier(sans/) sous/bin/shéchoue (lePATHne contient pas.), alors que bash retombe silencieusement sur le répertoire courant. Toujours invoquerbash ./setup.sh, jamais./setup.shseul. - Pas de
pipefailsur la tâcheyes "" | timeout 180 bash ./setup.sh. Une foissetup.shterminé (exit 0), il ferme son stdin ;yesreçoit SIGPIPE (mort normale de ce pattern) et sort en 141.pipefailremonterait ce 141 comme un échec de toute la tâche malgré une installation réussie. - Toutes les dépendances doivent être pré-installées avant de lancer
setup.sh— sinon les prompts à défautn(abandon) se déclenchent et bloquent, contrairement aux promptsynormalement traversés paryes "". dbconfig.inc.phpécrit parsetup.shest un format legacy inutilisable ($_SESSION["SERVEUR_SQL"]=...) — le code runtime (backend/require/connexion.php) attend desdefine("DB_NAME", ...)(format normalement écrit parinstall.phpvia navigateur, qui code aussi en dur le mot de passe applicatifocs/ocs). Le rôle écrit ce fichier lui-même via un template Ansible dédié plutôt que de passer parinstall.php.- Le schéma DB n'est pas créé par
setup.sh: import direct deocsreports/files/ocsbase_new.sql(base neuve) — pasocsbase.sql, qui sert uniquement aux migrations d'un schémaGUI_VERSION < 7006. - Mot de passe DB applicatif
ocs/ocscodé en dur dans les gabarits livrésocsinventory-server.conf(PerlSetVar OCS_DB_PWD ocs) etocsinventory-restapi.conf($ENV{OCS_DB_PWD} = 'ocs';) — non substitué parsetup.sh(contrairement àDATABASE_SERVER/DATABASE_PORT, qui eux le sont). Repatché après coup par 2 tâchesansible.builtin.replaceciblées (regex, garde le nom d'utilisateurocs, ne change que le mot de passe). - Mot de passe admin GUI
admin/admin: tableoperators, colonnePASSWD. Le seed livré est enPASSWORD_VERSION=2, ce qui fait passerbackend/AUTH/methode/local.phpparhash(PASSWORD_CRYPT, $mdp) === $rowOp->PASSWD, oùPASSWORD_CRYPTest défini en dur (ocsreports/var.php) comme'sha256'— pas bcrypt malgré le nom de la constante.UPDATE operators SET PASSWD = SHA2(<nouveau mdp>, 256) WHERE ID='admin'suffit, pas besoin d'invoquer PHP. - Permissions Unix (pas SELinux) — le piège le plus long à diagnostiquer.
setup.shpositionne tout enapache:apache(auto-détecté), mais la console est servie par nginx+PHP-FPM (utilisateur dédiénginx, pas membre du groupeapache) →Permission deniedsurstat()côté nginx (ausearch -m avcne montre rien, ce n'est pas SELinux) → dégénère en boucle de réécriture interne nginx (try_filesrajoute/index.phpindéfiniment car il n'arrive jamais à confirmer l'existence du fichier). Fix :chgrp -R nginxsur le dossier statique (/usr/share/ocsinventory-reports) et le dossier var (/var/lib/ocsinventory-reports, écrit par le code PHP lui-même) — propriétaire/mode inchangés, ils accordent déjàrx/rwxau groupe. - SELinux (une fois les permissions Unix réglées, ce sont ensuite de vraies denials).
/usr/share/ocsinventory-reportsn'est pas labellisé par défaut (usr_t) →sefcontext+restoreconenhttpd_sys_content_t(lecture) et/var/lib/ocsinventory-reportsenhttpd_sys_rw_content_t(écriture). Booléenhttpd_can_network_connectrequis pour que nginx proxifie vers127.0.0.1:8080— nginx tourne sous le même domaine SELinuxhttpd_tque httpd sur cette distribution ; sans ce booléen,name_connectrefusé vers le port 8080 (labelliséhttp_cache_port_t) → 502 Bad Gateway côté client malgré httpd qui écoute correctement.httpd_can_network_connect_db+httpd_can_sendmailactivés par précaution (comme GLPI). - Certificat TLS Vault PKI : IP SAN nécessaire en plus du DNS SAN si la vérification se fait par IP plutôt que par nom — sans SAN IP,
CERTIFICATE_VERIFY_FAILED: IP address mismatch. Émettre avecip_sans=<ip>en plus decommon_name. Utiliserca_chain(tableau Root+Intermediate) de la réponse Vault, pasissuing_caseul — un seul certificat intermédiaire ne suffit pas à bâtir la chaîne de confiance jusqu'au Root (unable to get issuer certificatesinon).
Autres points, hors du strict périmètre setup.sh :
- Base créée en
utf8mb3(et nonutf8mb4) car c'est le format du schéma livré (ocsbase_new.sql) — ne pas « moderniser » sans revalider la compatibilité du schéma OCS. - Utilisateur
ocsaccessible en TCP et socket unix (contrairement à GLPI, en socket unix seul) : nécessaire carDBD::mysqlcôté mod_perl n'a pas de support socket unix ici. - Certificat et clé TLS stockés sous
roles/ocs/files/tls/(contenu non repris dans cette documentation pour des raisons de sécurité).