Différences
Ci-dessous, les différences entre deux révisions de la page.
| Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
| apt-cacher-ng-cron-apt [2020/02/09 18:43] – [Serveur de cache APT / cron-apt] cbiot | apt-cacher-ng-cron-apt [2022/10/04 17:49] (Version actuelle) – modification externe 127.0.0.1 | ||
|---|---|---|---|
| Ligne 1: | Ligne 1: | ||
| ====== Serveur de cache APT / cron-apt ====== | ====== Serveur de cache APT / cron-apt ====== | ||
| - | <note important> | ||
| - | ===== Installation et lancement | + | ===== Téléchargement |
| Télécharger la dernière version depuis le git (version stable et devel - de préférence prenez la stable) : | Télécharger la dernière version depuis le git (version stable et devel - de préférence prenez la stable) : | ||
| - | * [[https:// | + | * [[https:// |
| - | * [[https:// | + | |
| - | Ce script nécessite d' | + | |
| - | __**Récupérer | + | puis installer |
| - | <code bash> | + | |
| - | Password: | + | |
| - | root@asgard-freeBSD: | + | |
| - | root@asgard-freeBSD: | + | |
| - | --2020-02-09 10: | + | |
| - | Résolution de raw.githubusercontent.com (raw.githubusercontent.com)… 151.101.120.133 | + | |
| - | Connexion à raw.githubusercontent.com (raw.githubusercontent.com)|151.101.120.133|: | + | |
| - | requête HTTP transmise, en attente de la réponse… 200 OK | + | |
| - | Taille : 13592 (13K) [text/ | + | |
| - | Sauvegarde en : « acn.py » | + | |
| - | acn.py | + | <code bash>$ sudo dpkg -i acn-py_VERSION.deb</code> |
| - | 2020-02-09 10:13:19 (344 KB/s) — « acn.py » sauvegardé [13592/ | + | ou |
| - | Lancer le script. | + | <code bash># dpkg -i acn-py_VERSION.deb</ |
| - | <code bash>root@asgard-freeBSD:~ # python3.6 acn.py</ | + | |
| + | |||
| + | ===== Construire son paquet ===== | ||
| + | |||
| + | Ceux qui préfèrent peuvent construire eux même leur paquet .deb , les sources debian étant disponibles | ||
| + | Pour construire le paquet | ||
| + | <code bash>$ git clone https:// | ||
| + | Clonage dans ' | ||
| + | remote: Enumerating objects: 58, done. | ||
| + | remote: Counting objects: 100% (58/58), done. | ||
| + | remote: Compressing objects: 100% (43/43), done. | ||
| + | remote: Total 58 (delta 12), reused 50 (delta | ||
| + | Dépaquetage des objets: 100% (58/58), 47.63 Kio | 541.00 Kio/s, fait. | ||
| + | $ cd acn-py | ||
| + | $ debuild -us -uc</ | ||
| + | |||
| + | Le paquet est alors disponible | ||
| + | <code bash>$ ls | ||
| + | acn-py_2.0.2+nmu5_all.deb | ||
| + | acn-py_2.0.2+nmu5_amd64.build | ||
| + | acn-py_2.0.2+nmu5_amd64.buildinfo | ||
| + | acn-py_2.0.2+nmu5_amd64.changes | ||
| ===== Utilités de ce script ===== | ===== Utilités de ce script ===== | ||
| + | {{ : | ||
| Script d'auto configuration d'un **serveur de cache pour APT, côté serveur et client**. | Script d'auto configuration d'un **serveur de cache pour APT, côté serveur et client**. | ||
| Installation d'un **cron-apt récupérant les mises à jour la nuit** et **installant automatiquement les mises à jour de sécurité** des repo. présentes sur votre système. | Installation d'un **cron-apt récupérant les mises à jour la nuit** et **installant automatiquement les mises à jour de sécurité** des repo. présentes sur votre système. | ||
| Ligne 47: | Ligne 55: | ||
| Le principe est qu'une machine centrale héberge le mandataire pour un réseau local. Les clients règlent leur configuration d'APT pour télécharger sur cette machine. Apt-Cacher NG conserve une copie de toutes les données utiles transitant à travers lui et, quand une requête similaire est faite, la copie en cache des données est délivrée sans être téléchargée à nouveau.// | Le principe est qu'une machine centrale héberge le mandataire pour un réseau local. Les clients règlent leur configuration d'APT pour télécharger sur cette machine. Apt-Cacher NG conserve une copie de toutes les données utiles transitant à travers lui et, quand une requête similaire est faite, la copie en cache des données est délivrée sans être téléchargée à nouveau.// | ||
| - | {{ : | + | |
| Ligne 57: | Ligne 65: | ||
| * apt-cacher-ng permet d' | * apt-cacher-ng permet d' | ||
| - | ===== Spéficités du script ===== | + | ===== Les avantages |
| - | + | Ce script installe également | |
| - | ==== Configuration, | + | |
| - | + | ||
| - | Installation des paquets : | + | |
| - | + | ||
| - | * apt-cacher-ng | + | |
| - | * cron-apt | + | |
| - | + | ||
| - | + | ||
| - | Lisez les messages du terminal, ils contiennent des données importantes : | + | |
| - | + | ||
| - | <code bash> | + | |
| - | Traitement des actions différées (« triggers ») pour man-db (2.9.0-2) ... | + | |
| - | Traitement des actions différées (« triggers ») pour libc-bin (2.29-9) ... | + | |
| - | ============================================== | + | |
| - | Le serveur de cache est dès lors opérationnel | + | |
| - | Le port d' | + | |
| - | Page d' | + | |
| - | Notez bien l'ip de votre serveur, elle vous sera indispensable pour la configuration des clients. | + | |
| - | L'IP du serveur est : 192.168.0.20 | + | |
| - | Indispensable : cette IP doit être FIXE (réglage sur votre BOX ou serveur DHCP). | + | |
| - | Cette machine est un serveur, mettre de ne l' | + | |
| - | ....................... | + | |
| - | + | ||
| - | Dès lors, le système installera les mises à jour de sécurité, toutes les nuits à 4 heures. | + | |
| - | .......................</ | + | |
| - | + | ||
| - | Voir config** | + | |
| - | + | ||
| - | + | ||
| - | ==== Configuration, | + | |
| - | __**Paquets installés**__ | + | |
| - | + | ||
| - | | + | |
| - | * python-nmap | + | |
| - | + | ||
| - | Le script va scanner, depuis la reconnaissance de l'IP du PC, le réseau local à la recherche d'une machine dont le port spécifique à **Acn-cacher-ng** est ouvert (**port 3142** par défaut mais modifiable). | + | |
| - | + | ||
| - | __**Fichier crée**__ | + | |
| - | Un fichier de proxy apt pointant sur l'IP du serveur est créé à cet endroit avec ce contenu | + | |
| - | + | ||
| - | <code bash> | + | |
| - | Acquire:: | + | |
| - | </ | + | |
| - | + | ||
| - | __Le scan du réseau peut avoir à 3 solutions :__ | + | |
| - | * Aucune machine n'est détecter. il faut revoir la configuration du serveur. | + | |
| - | *Une seule machine est détectée : elle est proposée par défaut. | + | |
| - | *Plusieurs machines sont détectée. Une Liste de choix est proposée. | + | |
| - | + | ||
| - | A chaque niveau, des choix sont possibles (**modification des IP ou du port**) afin de mettre la jour la configuration en cas de modification des paramètres serveur. | + | |
| - | + | ||
| - | <code bash> | + | |
| - | Vous utilisez un système Debian (su pour administration). | + | |
| - | Type d' | + | |
| - | Installation de type client | + | |
| - | Utiliser le port par défaut 3142 (recommandé) ?. [Oui / Non] Oui | + | |
| - | Type d' | + | |
| - | Installation de type client | + | |
| - | ======================================== | + | |
| - | Votre machine possède l'ip 192.168.0.20. | + | |
| - | Le motif de scan sera donc : 192.168.0.0 | + | |
| - | ---------------------------------------------------- | + | |
| - | Host : 192.168.0.15 () | + | |
| - | State : up | + | |
| - | ---------------------------------------------------- | + | |
| - | Host : 192.168.0.20 () | + | |
| - | State : up | + | |
| - | ---------------------------------------------------- | + | |
| - | Host : 192.168.0.254 () | + | |
| - | State : up | + | |
| - | ======================================== | + | |
| - | Résultats du scan réseau : (True si port Apt-cache-server trouvé. | + | |
| - | 192.168.0.15 : Le port est fermé. Code d' | + | |
| - | 192.168.0.15 | + | |
| - | 192.168.0.20 : Le port 3142 est ouvert. Possibilié de serveur ACN. | + | |
| - | 192.168.0.20 | + | |
| - | 192.168.0.254 : Le port est fermé. Code d' | + | |
| - | 192.168.0.254 | + | |
| - | Serveur ACN possible : 192.168.0.20 | + | |
| - | Valider ce choix ? (Oui / Non)</ | + | |
| - | + | ||
| - | ==== cron-apt, configuration ==== | + | |
| - | + | ||
| - | Le script automatise sa configuration, | + | |
| - | + | ||
| - | **__Paquet installé__** | + | |
| - | * **cron-apt** | + | |
| - | + | ||
| - | **__Fichiers spécifiques créées__** | + | |
| - | * **/ | + | |
| - | + | ||
| - | Scanne des entrées du dossier contenant | + | |
| - | + | ||
| - | Ce fichier commence par cette entête : | + | |
| - | <code bash> | + | |
| - | # Security Update. For Primtux Apt-cacher-ng. | + | |
| - | deb http:// | + | |
| - | * __**/ | + | |
| - | + | ||
| - | Ce fichier contient les information de configuration pour utiliser avec cron-apt le fichier précédant. | + | |
| - | + | ||
| - | <code bash># cat / | + | |
| - | upgrade -y -o APT:: | + | |
| - | OPTIONS=" | + | |
| - | MAILTO=" | + | |
| - | MAILON=" | + | |
| - | + | ||
| - | Contrairement aux autres | + | |
| - | + | ||
| - | ==== Import de .deb dans acn-cacher-ng ==== | + | |
| - | Faire pointer un navigateur sur le serveur avec le port d' | + | |
| - | Ce sera un url de ce type : http:// | + | |
| - | + | ||
| - | L'url correspondant à votre configuration est donné | + | |
| - | + | ||
| - | {{ : | + | |
| - | + | ||
| - | + | ||
| - | + | ||
| - | ===== Le script ===== | + | |
| - | Ci dessus la version 2.0.0 | + | |
| - | + | ||
| - | Mais préfèrable de suivre la version du GIT : [[https:// | + | |
| - | <code bash># | + | |
| - | # -*- coding: utf-8 -*- | + | |
| - | + | ||
| - | """ | + | |
| - | apt-cacher-ng soit en tant que serveur (ajout du paquet sur le système | + | |
| - | soit en tant que client (cration d'un fichier de proxy apt) | + | |
| - | Possibilité pour les clients de choisir le port d' | + | |
| - | Installation de cron-apt avec configuration spécifique | + | |
| - | pour installation automatique des mises à jour de sécurité des repo | + | |
| - | présents sur le système | + | |
| - | """ | + | |
| - | + | ||
| - | __author__ = " | + | |
| - | __copyright__ = " | + | |
| - | __credits__ = " | + | |
| - | __license__ = " | + | |
| - | __version__ = " | + | |
| - | __date__ = " | + | |
| - | __maintainer__ = " | + | |
| - | __email__ = " | + | |
| - | __status__ = " | + | |
| - | + | ||
| - | import os, re, sys, platform | + | |
| - | import nmap, subprocess, socket | + | |
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | def baseDebian(): | + | |
| - | """ | + | |
| - | Fonction permettant de connaitre le Systeme d' | + | |
| - | Ou DEBIAN ou UBUNTU pour savoir si on utilise su ou sudo | + | |
| - | Retourne une variable de type string (admin) | + | |
| - | :return: admin soit ' | + | |
| - | """ | + | |
| - | # Ubuntu ou DEBIAN | + | |
| - | if ' | + | |
| - | # Si DEBIAN, verif si root lance le script | + | |
| - | print(' | + | |
| - | if not os.geteuid() == 0: | + | |
| - | sys.exit(" | + | |
| - | distrib = ' | + | |
| - | else: | + | |
| - | if not os.geteuid() == 0: | + | |
| - | print(" | + | |
| - | sys.exit(" | + | |
| - | print(' | + | |
| - | distrib = ' | + | |
| - | return distrib | + | |
| - | + | ||
| - | def installPackage(package, | + | |
| - | """ | + | |
| - | Fonction installant un package debian ou ubuntu | + | |
| - | :param package: le nom du paquet | + | |
| - | :param debianUbuntu: | + | |
| - | :return: None | + | |
| - | """ | + | |
| - | retval = subprocess.call([' | + | |
| - | if retval != 0: | + | |
| - | print(" | + | |
| - | + | ||
| - | # Paramètres de l' | + | |
| - | cmdInstall = [' | + | |
| - | cmdUpdate = [' | + | |
| - | + | ||
| - | # Adaptation système Ubuntu | + | |
| - | if debianUbuntu == ' | + | |
| - | cmdInstall.insert(0, | + | |
| - | cmdUpdate.insert(0, | + | |
| - | + | ||
| - | # On installe le paquet | + | |
| - | subprocess.run(cmdInstall) | + | |
| - | # | + | |
| - | else: | + | |
| - | print(' | + | |
| - | + | ||
| - | return None | + | |
| - | + | ||
| - | def installServeur(ip, | + | |
| - | """ | + | |
| - | Fonction installant le serveur de cache apt-cacher-ng | + | |
| - | :param ip: IP du Serveur | + | |
| - | :param port: interger port ACN | + | |
| - | :param distrib: Ubuntu ou Debian | + | |
| - | :return: None | + | |
| - | """ | + | |
| - | + | ||
| - | # Installation du serveur | + | |
| - | installPackage(' | + | |
| - | + | ||
| - | # Affichage Informations | + | |
| - | print(" | + | |
| - | print(" | + | |
| - | print(" | + | |
| - | print(" | + | |
| - | print(" | + | |
| - | print(" | + | |
| - | print(" | + | |
| - | print(" | + | |
| - | + | ||
| - | return None | + | |
| - | + | ||
| - | + | ||
| - | def installClient(ipServeur, | + | |
| - | """ | + | |
| - | Fonction installant un fichier de configuration apt pour les postes clients | + | |
| - | Créer un fichier dans / | + | |
| - | :param ipServeur: | + | |
| - | :param portACN: port d' | + | |
| - | :return: None | + | |
| - | """ | + | |
| - | + | ||
| - | # COnfig IP serveur dans un fichier de proxy APT | + | |
| - | msgApt = ' | + | |
| - | print(msgApt) | + | |
| - | dirInstall = '/ | + | |
| - | fileName = ' | + | |
| - | fileLocInstall = dirInstall + fileName | + | |
| - | fichier = open(fileLocInstall, | + | |
| - | fichier.write(msgApt) | + | |
| - | fichier.close() | + | |
| - | return None | + | |
| - | + | ||
| - | + | ||
| - | + | ||
| - | def portSelection(portACN): | + | |
| - | while True: | + | |
| - | try: | + | |
| - | portDefault = input(" | + | |
| - | if portDefault.lower() == ' | + | |
| - | print(' | + | |
| - | break | + | |
| - | + | ||
| - | elif portDefault.lower() == ' | + | |
| - | try: | + | |
| - | portSelect = int(input(" | + | |
| - | if -1 < portSelect < 65536: | + | |
| - | print(" | + | |
| - | portACN = portSelect | + | |
| - | break | + | |
| - | except ValueError: | + | |
| - | print(" | + | |
| - | except ValueError: | + | |
| - | print(" | + | |
| - | + | ||
| - | print(" | + | |
| - | + | ||
| - | def ipRecuperation(): | + | |
| - | """ | + | |
| - | Fonction récupérant | + | |
| - | :return: l'ip de la machine lançant ce script | + | |
| - | """ | + | |
| - | s = socket.socket(socket.AF_INET, | + | |
| - | s.setsockopt(socket.SOL_SOCKET, | + | |
| - | s.connect(('< | + | |
| - | return s.getsockname()[0] | + | |
| - | + | ||
| - | + | ||
| - | def ipTest(ip): | + | |
| - | """ | + | |
| - | Fonction testant la validité d'une adresse IPv4 | + | |
| - | :param ip: ip à tester | + | |
| - | :return: True si IP valide, False sinon | + | |
| - | """ | + | |
| - | reg = r" | + | |
| - | if re.match(reg, | + | |
| - | return True | + | |
| - | else: | + | |
| - | return False | + | |
| - | + | ||
| - | + | ||
| - | def clientServeur(): | + | |
| - | """ | + | |
| - | Fonction déterminant s'il s'agit d'une installation de type Serveur ou Client | + | |
| - | :return: Retourne une variable string soit client soit serveur | + | |
| - | """ | + | |
| - | while True: | + | |
| - | try: | + | |
| - | choixInstall = input(" | + | |
| - | if choixInstall.lower() in [' | + | |
| - | print(' | + | |
| - | break | + | |
| - | else: | + | |
| - | print(' | + | |
| - | print(' | + | |
| - | except ValueError: | + | |
| - | print(" | + | |
| - | return choixInstall | + | |
| - | + | ||
| - | def portStatus(ip, | + | |
| - | """ | + | |
| - | Fonction de scanne d'un port d'une machine en fonction de son IP | + | |
| - | :param ip: IP de la machine à scanner | + | |
| - | :param port: port à scanner | + | |
| - | :return: Retourne True si port ouvert ou False si port fermé | + | |
| - | """ | + | |
| - | + | ||
| - | sock = socket.socket(socket.AF_INET, | + | |
| - | sock.settimeout(1) | + | |
| - | result = sock.connect_ex((ip, | + | |
| - | if result == 0: | + | |
| - | message = str(ip) + ' : Le port ' + str(port) + ' est ouvert. Possibilié de serveur ACN.' | + | |
| - | print(message) | + | |
| - | return True | + | |
| - | else: | + | |
| - | message = str(ip) + ' : Le port est fermé. Code d\' | + | |
| - | message += '. Pas de serveur ACN' | + | |
| - | print(message) | + | |
| - | return False | + | |
| - | + | ||
| - | def chercherServeurACN(ip, | + | |
| - | """ | + | |
| - | Fonction recherchant | + | |
| - | :param ip: IP du client lançant le scan, permet de trouver un motif réseau | + | |
| - | :param port: port à scanner (port ACN) | + | |
| - | :return: retourne une liste contenant les IP possibles des machines ayant port spécifié ouvert | + | |
| - | """ | + | |
| - | ipModele = '' | + | |
| - | listeHosts = [] | + | |
| - | ipServeurACN = [] | + | |
| - | + | ||
| - | + | ||
| - | # Création d'un motif pour le scan reseau | + | |
| - | ipSplit = ip.split(' | + | |
| - | for i in range (0,3): | + | |
| - | ipModele += ipSplit[i] + ' | + | |
| - | ipModele += ' | + | |
| - | + | ||
| - | # debug | + | |
| - | print(' | + | |
| - | print(' | + | |
| - | + | ||
| - | # Scan reseau à la recherche de clients | + | |
| - | nm = nmap.PortScanner() # instantiate nmap.PortScanner object | + | |
| - | nm.scan(hosts=ipModele+'/ | + | |
| - | for host in nm.all_hosts(): | + | |
| - | print(' | + | |
| - | print(' | + | |
| - | print(' | + | |
| - | # Creation d'un mappage reseau | + | |
| - | listeHosts.append(host) | + | |
| - | + | ||
| - | # Sca, port ACN des clients | + | |
| - | print(' | + | |
| - | print(' | + | |
| - | # Pour chacune des machines du réseau, on teste le port d'ACN (par defaut 3142 | + | |
| - | for i in listeHosts: | + | |
| - | testPort = portStatus(i, | + | |
| - | # Si réponse True, c'est le serveur | + | |
| - | if testPort == True: | + | |
| - | ipServeurACN.append(i) | + | |
| - | message = ' | + | |
| - | else: | + | |
| - | message = 'Pas de port ACN ouvert' | + | |
| - | print(i, ' : ', testPort, '. ', message) | + | |
| - | + | ||
| - | return ipServeurACN | + | |
| - | + | ||
| - | def validerIpServeurACN(listIp): | + | |
| - | """ | + | |
| - | Fonction recupérant la liste des machines susceptibles d' | + | |
| - | Teste de cette liste pour valider ces IP ou les infirmer | + | |
| - | :param listIp: liste contenant les IP des machines écoutant le port ACN | + | |
| - | :return: IP de la machine sélectionnée comme serveur ACN | + | |
| - | """ | + | |
| - | if len(listIp) == 0: | + | |
| - | sys.exit(' | + | |
| - | ' | + | |
| - | ' | + | |
| - | elif len(listIp) == 1: | + | |
| - | print(' | + | |
| - | while True: | + | |
| - | try: | + | |
| - | ouiNon = input(" | + | |
| - | if ouiNon.lower() == ' | + | |
| - | print(' | + | |
| - | ipServeur = listIp[0] | + | |
| - | break | + | |
| - | elif ouiNon.lower() == ' | + | |
| - | sys.exit(' | + | |
| - | ' | + | |
| - | ' | + | |
| - | ' | + | |
| - | except ValueError: | + | |
| - | print(" | + | |
| - | # Valider l'ip unique | + | |
| - | else: | + | |
| - | print(' | + | |
| - | print(' | + | |
| - | for i in enumerate(listIp): | + | |
| - | print(' | + | |
| - | # Installation client | + | |
| - | while True: | + | |
| - | try: | + | |
| - | ipServeur = input(" | + | |
| - | if ipTest(ipServeur) is True and ipServeur in listIp: | + | |
| - | break | + | |
| - | except ValueError: | + | |
| - | print(" | + | |
| - | return ipServeur | + | |
| - | + | ||
| - | + | ||
| - | def installCronApt(distrib): | + | |
| - | """ | + | |
| - | Fonction Recuperation des entrées des mises à jour de sécurité dans | + | |
| - | dans les divers sources.list possibles | + | |
| - | Et création d'un sources.list basé que sur ces entrées (security) | + | |
| - | Le fichier est propre à primtux. Donc si existe, on le régénère sinon on le crée | + | |
| - | Et envoi mail sur root | + | |
| - | :param distrib: Ubuntu ou Debian | + | |
| - | :return: None | + | |
| - | """ | + | |
| - | + | ||
| - | mailRoot = ' | + | |
| - | aptSecurity = "find /etc/apt -type f -name ' | + | |
| - | "| xargs cat " \ | + | |
| - | " | + | |
| - | + | ||
| - | # Installation de cron-apt | + | |
| - | installPackage(' | + | |
| - | + | ||
| - | # Création sources.list spécial sécurité | + | |
| - | log = open('/ | + | |
| - | log.write('# | + | |
| - | log.flush() | + | |
| - | c = subprocess.call(aptSecurity, | + | |
| - | + | ||
| - | # Configuration d'une action dans la conf de cron-apt | + | |
| - | # / | + | |
| - | fichier = open('/ | + | |
| - | fichier.write(" | + | |
| - | fichier.write(" | + | |
| - | " | + | |
| - | "-o Dir:: | + | |
| - | fichier.write(" | + | |
| - | fichier.write(" | + | |
| - | fichier.close() | + | |
| - | + | ||
| - | print(" | + | |
| - | + | ||
| - | return None | + | |
| - | + | ||
| - | + | ||
| - | + | ||
| - | def main(): | + | |
| - | """ | + | |
| - | Lancement du script | + | |
| - | :return: None | + | |
| - | """ | + | |
| - | # Défnition du port par defaut d' | + | |
| - | portACN = 3142 | + | |
| - | # Recupere le type de distribution faisant tourner le script | + | |
| - | distrib | + | |
| - | + | ||
| - | choixInstall = clientServeur() | + | |
| - | if choixInstall.lower() == ' | + | |
| - | ipServeur = ipRecuperation() | + | |
| - | installServeur(ipServeur, | + | |
| - | else: | + | |
| - | # Installation client | + | |
| - | portSelection(portACN) | + | |
| - | ip = ipRecuperation() | + | |
| - | ipServeur = chercherServeurACN(ip, | + | |
| - | ipServeur = validerIpServeurACN(ipServeur) | + | |
| - | installClient(ipServeur, | + | |
| - | + | ||
| - | # Que ce sont pour l'un ou l' | + | |
| - | installCronApt(distrib) | + | |
| - | return None | + | |
| - | + | ||
| - | """ | + | |
| - | | + | |
| - | """ | + | |
| - | if __name__ == " | + | |
| - | # execute only if run as a script | + | |
| - | main() | + | |
| - | </ | + | |
| - | ===== Liens ===== | + | Par contre, les mises à jour de sécurité sont, elles, automatiquement installées. |
| - | *[[https:// | + | |
| - | *[[https:// | + | |
| + | --- // | ||