Ajout des fonctions de création auto d'un bateau avec timer toutes les 5min
This commit is contained in:
+13
-1
@@ -124,6 +124,18 @@ define("ETAT_CARTE_EN_PANNE", 2);
|
||||
define("ETAT_CARTE_REBUT", 3);
|
||||
|
||||
|
||||
|
||||
//=====================================================================
|
||||
// Types et Sous_types d'historique :
|
||||
define ("SYSTEME", 1000);
|
||||
define ("SYSTEME_REINIT", 1001); // Réinitialisation de l'application
|
||||
define ("SYSTEME_ERREUR_MYSQL", 1002);
|
||||
define ("SYSTEME_CONNEXION_USER", 1003);
|
||||
define ("SYSTEME_REINIT_MDP_USER", 1004);
|
||||
define ("SYSTEME_REBOOT", 1005); // Reboot du système
|
||||
define ("SYSTEME_ARRET", 1006); // Arrêt du système (Shutdown -h)
|
||||
define ("SYSTEME_REINIT_BOX", 1007);
|
||||
define ("SYSTEME_TIME_OUT_CONNEXION", 1008);
|
||||
define ("SYSTEME_REINIT_TEIOT", 1009);
|
||||
define ("SYSTEME_DECONNEXION_USER", 1010);
|
||||
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ function chargement_langue()
|
||||
|
||||
Ouverture_bdd(); // Connexion à MySql
|
||||
|
||||
$requete = "SELECT Fichier, Page, Bloc, Nom, (SELECT Texte FROM TB_Textes_traduction WHERE ID_LANGUAGES=(SELECT ID FROM TB_Langues WHERE Prefix=?) AND ID_TYPE = ".TYPE_UTILISATEUR_PROFESSIONNEL." AND TB_Textes_traduction.ID_TEXTES=TB_Textes_acces.ID_TEXTES) AS Libelle FROM TB_Textes_acces WHERE Validite='1'";
|
||||
$requete = "SELECT Fichier, Page, Bloc, Nom, (SELECT Texte FROM TB_Textes_traduction WHERE ID_LANGUAGES=(SELECT ID FROM TB_Langues WHERE Prefix=?) AND ID_TYPE = 2 AND TB_Textes_traduction.ID_TEXTES=TB_Textes_acces.ID_TEXTES) AS Libelle FROM TB_Textes_acces WHERE Validite='1'";
|
||||
$reponse = Requete_preparee_bdd($requete,
|
||||
array(
|
||||
array('s', $_SESSION['langue'])),
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
// ============================================================================
|
||||
// generer_relais_live.php - Génération des relais Traefik du Live distant, un par bateau (synthèse §49, étape 2f).
|
||||
// Exécuté NATIVEMENT sur le VPS, de façon planifiée (ship2net-relais-live.timer) ou à la main :
|
||||
// /usr/bin/php /var/www/generer_relais_live.php
|
||||
//
|
||||
// Pour chaque bateau valide dont le module affecté (affectation active) a une adresse NetBird connue
|
||||
// (TB_Cartes.Adresse_netbird), écrit /opt/netbird/dynamic/bateau-<N>.yml : https://b<N>.ship2net.fr relayé vers ce
|
||||
// module, avec ouverture de session (/_auth) et vérification de la session à chaque requête (ForwardAuth) - cf
|
||||
// live-commun.yml. Supprime les relais des bateaux qui ne remplissent plus ces conditions (bateau dévalidé,
|
||||
// affectation clôturée, adresse retirée).
|
||||
//
|
||||
// Ajouter un bateau au Live ne demande donc rien d'autre que son affectation (lot 4) : ni DNS, ni certificat
|
||||
// (certificat générique *.ship2net.fr), ni fichier à écrire à la main. Une réaffectation de module change la
|
||||
// destination du relais, pas l'adresse du bateau.
|
||||
//
|
||||
// Principes :
|
||||
// - seuls les fichiers "bateau-<N>.yml" sont gérés : aucun autre fichier du dossier n'est jamais touché ;
|
||||
// - un fichier n'est réécrit que si son contenu change (Traefik recharge sa configuration à chaque écriture) ;
|
||||
// - écriture atomique (fichier temporaire puis renommage) : Traefik ne lit jamais un fichier à moitié écrit ;
|
||||
// - l'adresse NetBird est validée (adresse IPv4 stricte) avant d'être écrite dans la configuration de Traefik.
|
||||
// ============================================================================
|
||||
|
||||
chdir("/var/www/html");
|
||||
require_once __DIR__ . "/config/config.php";
|
||||
require_once __DIR__ . "/config/fonctions_bdd.php";
|
||||
|
||||
const REPERTOIRE_RELAIS = "/opt/netbird/dynamic";
|
||||
const MOTIF_FICHIER_RELAIS = '/^bateau-([1-9][0-9]*)\.yml$/';
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Contenu du fichier de relais d'un bateau (modèle validé sur le bateau 1, étape 2d).
|
||||
// ----------------------------------------------------------------------------
|
||||
function Contenu_relais($id_bateau, $adresse_netbird)
|
||||
{
|
||||
$hote = "b".$id_bateau.".".URL_SITE;
|
||||
return <<<YAML
|
||||
# ============================================================================
|
||||
# Live distant du bateau {$id_bateau} - https://{$hote} (synthèse §49)
|
||||
# FICHIER GÉNÉRÉ AUTOMATIQUEMENT par /var/www/generer_relais_live.php - ne pas modifier à la main
|
||||
# (il serait réécrit au passage suivant). Module affecté : {$adresse_netbird}
|
||||
# ============================================================================
|
||||
http:
|
||||
routers:
|
||||
bateau-{$id_bateau}-auth:
|
||||
rule: "Host(`{$hote}`) && Path(`/_auth`)"
|
||||
priority: 1000
|
||||
entrypoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: ovh
|
||||
domains:
|
||||
- main: "*.ship2net.fr"
|
||||
middlewares:
|
||||
- live-auth-chemin@file
|
||||
service: live-auth@file
|
||||
bateau-{$id_bateau}:
|
||||
rule: "Host(`{$hote}`)"
|
||||
entrypoints:
|
||||
- websecure
|
||||
tls:
|
||||
certResolver: ovh
|
||||
domains:
|
||||
- main: "*.ship2net.fr"
|
||||
middlewares:
|
||||
- live-verification@file
|
||||
service: bateau-{$id_bateau}
|
||||
services:
|
||||
bateau-{$id_bateau}:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://{$adresse_netbird}:80"
|
||||
|
||||
YAML;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Écriture atomique : fichier temporaire (extension non lue par Traefik) puis renommage.
|
||||
// ----------------------------------------------------------------------------
|
||||
function Ecrire_atomique($chemin, $contenu)
|
||||
{
|
||||
$temporaire = $chemin.".tmp";
|
||||
if (file_put_contents($temporaire, $contenu) === false) return false;
|
||||
chmod($temporaire, 0644);
|
||||
return rename($temporaire, $chemin);
|
||||
}
|
||||
|
||||
|
||||
function Generer_relais_live()
|
||||
{
|
||||
if (!is_dir(REPERTOIRE_RELAIS) || !is_writable(REPERTOIRE_RELAIS))
|
||||
{
|
||||
echo "ERREUR : répertoire ".REPERTOIRE_RELAIS." absent ou non accessible en écriture.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
Ouverture_bdd("127.0.0.1:3307", BDD_USER, BDD_PASSWORD, BDD_DATABASE);
|
||||
$db = $_SESSION['db'];
|
||||
|
||||
// Bateaux éligibles : valides, avec une affectation active vers un module dont l'adresse NetBird est connue.
|
||||
$requete = "SELECT B.Cle_primaire, C.Adresse_netbird
|
||||
FROM TB_Bateaux B
|
||||
JOIN TB_Affectations_bateaux A ON A.ID_Bateau = B.Cle_primaire AND A.Date_fin IS NULL
|
||||
JOIN TB_Cartes C ON C.Adresse_mac = A.Adresse_mac
|
||||
WHERE B.Validite = 1 AND C.Adresse_netbird IS NOT NULL AND C.Adresse_netbird <> ''";
|
||||
$resultat = $db->query($requete);
|
||||
$attendus = array();
|
||||
while ($ligne = $resultat->fetch_assoc())
|
||||
{
|
||||
$adresse = trim($ligne['Adresse_netbird']);
|
||||
if (filter_var($adresse, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false)
|
||||
{
|
||||
echo " ! bateau {$ligne['Cle_primaire']} : adresse NetBird '{$adresse}' invalide - relais non généré.\n";
|
||||
continue;
|
||||
}
|
||||
$attendus["bateau-".(int)$ligne['Cle_primaire'].".yml"] = Contenu_relais((int)$ligne['Cle_primaire'], $adresse);
|
||||
}
|
||||
$resultat->free();
|
||||
Fermeture_bdd();
|
||||
|
||||
$nb_crees = $nb_modifies = $nb_supprimes = $nb_inchanges = 0;
|
||||
|
||||
// Création / mise à jour :
|
||||
foreach ($attendus as $nom => $contenu)
|
||||
{
|
||||
$chemin = REPERTOIRE_RELAIS."/".$nom;
|
||||
$existant = is_file($chemin) ? file_get_contents($chemin) : null;
|
||||
if ($existant === $contenu) { $nb_inchanges++; continue; }
|
||||
if (!Ecrire_atomique($chemin, $contenu)) { echo " ! écriture impossible : {$chemin}\n"; continue; }
|
||||
if ($existant === null) { $nb_crees++; echo " + {$nom} créé\n"; }
|
||||
else { $nb_modifies++; echo " ~ {$nom} mis à jour\n"; }
|
||||
}
|
||||
|
||||
// Suppression des relais qui ne correspondent plus à un bateau éligible (fichiers "bateau-<N>.yml" seulement) :
|
||||
foreach (scandir(REPERTOIRE_RELAIS) as $nom)
|
||||
{
|
||||
if (preg_match(MOTIF_FICHIER_RELAIS, $nom) !== 1 || isset($attendus[$nom])) continue;
|
||||
if (unlink(REPERTOIRE_RELAIS."/".$nom)) { $nb_supprimes++; echo " - {$nom} supprimé\n"; }
|
||||
else echo " ! suppression impossible : {$nom}\n";
|
||||
}
|
||||
|
||||
echo count($attendus)." bateau(x) éligible(s) : {$nb_crees} créé(s), {$nb_modifies} mis à jour, {$nb_supprimes} supprimé(s), {$nb_inchanges} inchangé(s).\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
exit(Generer_relais_live());
|
||||
+41
-1
@@ -388,7 +388,9 @@ $requete .= ' TB_Bateaux.Date_derniere_maj AS Date_derniere_maj, ';
|
||||
|
||||
$requete = 'SELECT *, ';
|
||||
$requete .= ' (SELECT Adresse_mac FROM TB_Affectations_bateaux WHERE ID_Bateau=TB_Bateaux.Cle_primaire AND Date_fin IS NULL) AS Adresse_mac, ';
|
||||
$requete .= ' (SELECT Version_soft FROM TB_Generations_bateaux WHERE ID_Bateau=TB_Bateaux.Cle_primaire AND Date_fin IS NULL) AS version ';
|
||||
$requete .= ' (SELECT Version_soft FROM TB_Generations_bateaux WHERE ID_Bateau=TB_Bateaux.Cle_primaire AND Date_fin IS NULL) AS version, ';
|
||||
// Adresse NetBird du module affecté (Live distant possible seulement si elle est connue - synthèse §49) :
|
||||
$requete .= ' (SELECT C.Adresse_netbird FROM TB_Affectations_bateaux A JOIN TB_Cartes C ON C.Adresse_mac=A.Adresse_mac WHERE A.ID_Bateau=TB_Bateaux.Cle_primaire AND A.Date_fin IS NULL) AS Adresse_netbird ';
|
||||
$requete .= ' FROM TB_Bateaux WHERE Cle_primaire=? ';
|
||||
//echo "Requête = [ ".$requete."; ]<br />";
|
||||
$reponse = Requete_preparee_bdd($requete,
|
||||
@@ -491,6 +493,31 @@ $requete .= ' TB_Bateaux.Date_derniere_maj AS Date_derniere_maj, ';
|
||||
$est_administrateur = ($_SESSION['shipmember']['Niveau_acces'] >= NIVEAU_ACCES_MINI_ADMINISTRATEUR);
|
||||
$peut_inviter = ($est_administrateur || $acces_profil['Creation_profils'] == 1);
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------
|
||||
// Live distant (synthèse §49) : création d'un ticket (aléatoire, 60 s, usage unique, lié à l'utilisateur et au
|
||||
// bateau) puis redirection vers https://b<N>.ship2net.fr/_auth?ticket=... - le vérificateur du VPS l'échange
|
||||
// contre une session (cookie propre au sous-domaine du bateau). Seule l'empreinte du ticket est enregistrée.
|
||||
// Droit : arriver jusqu'ici suffit (administrateur, ou accès actif à CE bateau - contrôlé plus haut) ; le
|
||||
// vérificateur revérifie de toute façon à l'ouverture de la session puis à chaque requête.
|
||||
$live_possible = ($bateau['Adresse_netbird'] ?? '') != '';
|
||||
if (isset($_POST['ouvrir_live']))
|
||||
{
|
||||
if (!$live_possible)
|
||||
{
|
||||
$_SESSION['Bateaux']['flash_erreur'] = t("Aucun module ShipBase joignable n'est affecté à ce bateau : le Live est indisponible.");
|
||||
Fermeture_bdd();
|
||||
header('Location: bateaux.php');
|
||||
exit();
|
||||
}
|
||||
$ticket = bin2hex(random_bytes(32));
|
||||
Requete_preparee_bdd("INSERT INTO TB_Tickets_live (Empreinte, ID_Shipmember, ID_Bateau, Expiration) VALUES (?, ?, ?, NOW() + INTERVAL 60 SECOND)",
|
||||
array(array('s', hash('sha256', $ticket)), array('i', $_SESSION['shipmember']['Cle_primaire']), array('i', $bateau['Cle_primaire'])),
|
||||
__FILE__, __LINE__);
|
||||
Fermeture_bdd();
|
||||
header('Location: https://b'.(int)$bateau['Cle_primaire'].'.'.URL_SITE.'/_auth?ticket='.$ticket, true, 303);
|
||||
exit();
|
||||
}
|
||||
|
||||
if (isset($_POST['creer_invitation']))
|
||||
{
|
||||
if (!$peut_inviter) $_SESSION['Bateaux']['flash_erreur'] = t("Vous n'avez pas le droit d'inviter sur ce bateau.");
|
||||
@@ -768,6 +795,19 @@ $requete .= ' TB_Bateaux.Date_derniere_maj AS Date_derniere_maj, ';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
//--------------------------------
|
||||
// Bouton Live (synthèse §49) - formulaire POST avec jeton (un navigateur qui pré-charge les liens ne crée pas
|
||||
// de tickets), ouvert dans un nouvel onglet :
|
||||
echo '<div class="bouton_live">';
|
||||
if ($live_possible)
|
||||
{
|
||||
echo '<form action="bateaux.php" method="post" target="_blank">'.Champ_jeton_csrf();
|
||||
echo '<button type="submit" name="ouvrir_live" title="'.t("Ouvrir le bateau en temps réel (nouvel onglet)").'">'.t("Live").'</button>';
|
||||
echo '</form>';
|
||||
}
|
||||
else echo '<span class="live_indisponible" title="'.t("Aucun module ShipBase joignable n'est affecté à ce bateau").'">'.t("Live indisponible").'</span>';
|
||||
echo '</div>';
|
||||
|
||||
echo '<div class="derniere_maj">'.t("Dernières données reçues").' : <span class="gras">'.$bateau['Date_derniere_maj'].' [UTC]</span></div>';
|
||||
echo '</div>';
|
||||
//--------------------------------
|
||||
|
||||
@@ -32,6 +32,26 @@ Fichier bateaux.php
|
||||
background-color: var(--bleu_koriolan);
|
||||
color: var(--bleu_koriolan_fonce);
|
||||
}
|
||||
/* Bouton Live (synthèse §49) : à gauche du bouton "Gestion des accès" */
|
||||
#Bateaux .entete_bateau .bouton_live {
|
||||
float: right;
|
||||
margin-right: 15px;
|
||||
}
|
||||
#Bateaux .entete_bateau .bouton_live button {
|
||||
padding: 5px 25px;
|
||||
line-height: 1.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
#Bateaux .entete_bateau .bouton_live button::before {
|
||||
content: "\25CF\00A0"; /* point "en direct" */
|
||||
color: #e53935;
|
||||
}
|
||||
#Bateaux .entete_bateau .bouton_live .live_indisponible {
|
||||
display: inline-block;
|
||||
padding: 5px 10px;
|
||||
color: var(--gris_pale);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
#Bateaux .boutons_haut_de_page {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=Ship2net - génération des relais Traefik du Live distant (un par bateau)
|
||||
After=network-online.target docker.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=koriolan
|
||||
Group=koriolan
|
||||
ExecStart=/usr/bin/php /var/www/generer_relais_live.php
|
||||
Nice=10
|
||||
TimeoutStartSec=5min
|
||||
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Ship2net - relais du Live distant toutes les 5 minutes
|
||||
|
||||
[Timer]
|
||||
OnBootSec=1min
|
||||
OnCalendar=*:0/5
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
Reference in New Issue
Block a user