<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
	<id>https://tala-informatique.fr/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Jc.forton</id>
	<title>The Linux Craftsman - Contributions [fr]</title>
	<link rel="self" type="application/atom+xml" href="https://tala-informatique.fr/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Jc.forton"/>
	<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Sp%C3%A9cial:Contributions/Jc.forton"/>
	<updated>2026-04-29T11:48:16Z</updated>
	<subtitle>Contributions</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Ifcfg-ethX&amp;diff=4193</id>
		<title>Ifcfg-ethX</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Ifcfg-ethX&amp;diff=4193"/>
		<updated>2026-01-25T09:11:42Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Exemple */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Principe =&lt;br /&gt;
&lt;br /&gt;
Tout ce passe dans le dossier &#039;&#039;/etc/sysconfig/network-scripts&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Dans ce répertoire il y a plusieurs fichiers et notamment les fichiers de configuration des interfaces réseaux qui commencent tous par &#039;&#039;ifcfg-X&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Remplacez &#039;&#039;X&#039;&#039; par le nom de l&#039;interface réseau et, si le fichier n&#039;existe pas, il suffit de le créer !&lt;br /&gt;
&lt;br /&gt;
= Exemple =&lt;br /&gt;
&lt;br /&gt;
Prenons comme exemple &#039;&#039;eth0&#039;&#039;. Son fichier de configuration sera &#039;&#039;ifcfg-eth0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
DEVICE=eth0&lt;br /&gt;
HWADDR=DE:B4:49:A5:3A:07&lt;br /&gt;
TYPE=Ethernet&lt;br /&gt;
ONBOOT=yes&lt;br /&gt;
NM_CONTROLLED=yes&lt;br /&gt;
BOOTPROTO=dhcp&lt;br /&gt;
PEERDNS=no&lt;br /&gt;
DOMAIN=&amp;quot;tala-informatique.fr labo.tala-informatique.fr&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* DEVICE &amp;amp;rarr; indique le nom de l&#039;interface&lt;br /&gt;
* TYPE &amp;amp;rarr; précise le type de périphérique, peut prendre les valeurs [Ethernet|Bridge|Vlan]&lt;br /&gt;
* ONBOOT &amp;amp;rarr; indique si l&#039;interface doit démarrer au démarrage de la machine&lt;br /&gt;
* NM_CONTROLLED &amp;amp;rarr; précise si le Network Manager doit contrôler cette interface (en général oui)&lt;br /&gt;
* BOOTPROTO &amp;amp;rarr; indique le type de démarrage de l&#039;interface, peut prendre les valeurs [dhcp|static|none]&lt;br /&gt;
** Si la valeur &#039;&#039;static&#039;&#039; est utilisée, il faudra également les paramètres suivants:&lt;br /&gt;
*** IPADDR &amp;amp;rarr; pour l&#039;adresse IP&lt;br /&gt;
*** NETMASK &amp;amp;rarr; pour le masque de sous-réseau&lt;br /&gt;
*** GATEWAY &amp;amp;rarr; pour spécifier, le cas échéant, une passerelle&lt;br /&gt;
* DNS1 &amp;amp;rarr; permet de spécifier un serveur DNS primaire&lt;br /&gt;
* DNS2 &amp;amp;rarr; permet de spécifier un serveur DNS secondaire&lt;br /&gt;
*PEERDNS &amp;amp;rarr; précise si les &#039;&#039;DNS&#039;&#039; fournis en DHCP sur cette interface doivent être ajoutés au fichier &#039;&#039;/etc/resolv.conf&#039;&#039;&lt;br /&gt;
*MACADDR &amp;amp;rarr; permet de spécifier une adresse &#039;&#039;MAC&#039;&#039; à utiliser, particulièrement pratique pour les déplacement de VMs, ou pour les baux &#039;&#039;DHCP&#039;&#039; statiques.&lt;br /&gt;
*DOMAIN &amp;amp;rarr; permet de spécifier autant de domaine de recherche qui seront ajouté au fichier &#039;&#039;/etc/resolv.conf&#039;&#039;&lt;br /&gt;
*MTU &amp;amp;rarr; permet de modifier la taille du payload (Maximum Transmission Unit) avant fragmentation. Permet d&#039;activer les &#039;&#039;Jumbo Frame&#039;&#039; pour baisser l&#039;utilisation du CPU dans les réseaux supérieurs au &#039;&#039;gigabit&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Que faire si ma carte n&#039;a pas le bon numéro ?? =&lt;br /&gt;
&lt;br /&gt;
Lorsque l&#039;on ajoute / enlève une carte réseau, le noyau incrémente le numéro de celle-ci. Par exemple, on avait eth0 et puis on se retrouve avec eth1...&lt;br /&gt;
&lt;br /&gt;
C&#039;est &#039;&#039;UDEV&#039;&#039; qui, à chaque fois que l&#039;on insère une carte réseau avec une adresse MAC différente, change de numéro.&lt;br /&gt;
&lt;br /&gt;
On peut modifier cela dans le fichier &#039;&#039;/etc/udev/rules.d/70-persistent-net.rules&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# This file was automatically generated by the /lib/udev/write_net_rules&lt;br /&gt;
# program, run by the persistent-net-generator.rules rules file.&lt;br /&gt;
#&lt;br /&gt;
# You can modify it, as long as you keep each rule on a single&lt;br /&gt;
# line, and change only the value of the NAME= key.&lt;br /&gt;
&lt;br /&gt;
# PCI device 0x1022:0x2000 (pcnet32)&lt;br /&gt;
SUBSYSTEM==&amp;quot;net&amp;quot;, ACTION==&amp;quot;add&amp;quot;, DRIVERS==&amp;quot;?*&amp;quot;, ATTR{address}==&amp;quot;00:0c:29:f7:cf:de&amp;quot;, ATTR{type}==&amp;quot;1&amp;quot;, KERNEL==&amp;quot;eth*&amp;quot;, NAME=&amp;quot;eth0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# PCI device 0x1022:0x2000 (pcnet32)&lt;br /&gt;
SUBSYSTEM==&amp;quot;net&amp;quot;, ACTION==&amp;quot;add&amp;quot;, DRIVERS==&amp;quot;?*&amp;quot;, ATTR{address}==&amp;quot;00:0c:29:e6:c5:aa&amp;quot;, ATTR{type}==&amp;quot;1&amp;quot;, KERNEL==&amp;quot;eth*&amp;quot;, NAME=&amp;quot;eth1&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il suffit de supprimer la première ligne qui fait référence à l&#039;ancienne carte et, de décrémenter le numéro de la deuxième ligne pour que &#039;&#039;eth1&#039;&#039; devienne &#039;&#039;eth0&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# This file was automatically generated by the /lib/udev/write_net_rules&lt;br /&gt;
# program, run by the persistent-net-generator.rules rules file.&lt;br /&gt;
#&lt;br /&gt;
# You can modify it, as long as you keep each rule on a single&lt;br /&gt;
# line, and change only the value of the NAME= key.&lt;br /&gt;
&lt;br /&gt;
# PCI device 0x1022:0x2000 (pcnet32)&lt;br /&gt;
SUBSYSTEM==&amp;quot;net&amp;quot;, ACTION==&amp;quot;add&amp;quot;, DRIVERS==&amp;quot;?*&amp;quot;, ATTR{address}==&amp;quot;00:0c:29:e6:c5:aa&amp;quot;, ATTR{type}==&amp;quot;1&amp;quot;, KERNEL==&amp;quot;eth*&amp;quot;, NAME=&amp;quot;eth0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que les modifications soient effectives, il faut redémarrer le système.&lt;br /&gt;
&lt;br /&gt;
{|style=&amp;quot;width:90%&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning-icon.png|centré|100px]]&lt;br /&gt;
|style=&amp;quot;width:5%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Sur les distributions à base de Debian, vous êtes obligé de mettre à jour &#039;&#039;initramfs&#039;&#039; pour que les modifications soient prises en compte avant de redémarrer le système :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# update-initramfs -u&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Ifcfg-ethX&amp;diff=4192</id>
		<title>Ifcfg-ethX</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Ifcfg-ethX&amp;diff=4192"/>
		<updated>2026-01-25T09:09:22Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Que faire si ma carte n&amp;#039;a pas le bon numéro ?? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Principe =&lt;br /&gt;
&lt;br /&gt;
Tout ce passe dans le dossier &#039;&#039;/etc/sysconfig/network-scripts&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Dans ce répertoire il y a plusieurs fichiers et notamment les fichiers de configuration des interfaces réseaux qui commencent tous par &#039;&#039;ifcfg-X&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Remplacez &#039;&#039;X&#039;&#039; par le nom de l&#039;interface réseau et, si le fichier n&#039;existe pas, il suffit de le créer !&lt;br /&gt;
&lt;br /&gt;
= Exemple =&lt;br /&gt;
&lt;br /&gt;
Prenons comme exemple &#039;&#039;eth0&#039;&#039;. Son fichier de configuration sera &#039;&#039;ifcfg-eth0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
DEVICE=eth0&lt;br /&gt;
HWADDR=DE:B4:49:A5:3A:07&lt;br /&gt;
TYPE=Ethernet&lt;br /&gt;
ONBOOT=yes&lt;br /&gt;
NM_CONTROLLED=yes&lt;br /&gt;
BOOTPROTO=dhcp&lt;br /&gt;
PEERDNS=no&lt;br /&gt;
DOMAIN=&amp;quot;tala-informatique.fr labo.tala-informatique.fr&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* DEVICE &amp;amp;rarr; indique le nom de l&#039;interface&lt;br /&gt;
* TYPE &amp;amp;rarr; précise le type de périphérique, peut prendre les valeurs [Ethernet|Bridge|Vlan]&lt;br /&gt;
* ONBOOT &amp;amp;rarr; indique si l&#039;interface doit démarrer au démarrage de la machine&lt;br /&gt;
* NM_CONTROLLED &amp;amp;rarr; précise si le Network Manager doit contrôler cette interface (en général oui)&lt;br /&gt;
* BOOTPROTO &amp;amp;rarr; indique le type de démarrage de l&#039;interface, peut prendre les valeurs [dhcp|static|none]&lt;br /&gt;
** Si la valeur &#039;&#039;static&#039;&#039; est utilisée, il faudra également les paramètres suivants:&lt;br /&gt;
*** IPADDR &amp;amp;rarr; pour l&#039;adresse IP&lt;br /&gt;
*** NETMASK &amp;amp;rarr; pour le masque de sous-réseau&lt;br /&gt;
*** GATEWAY &amp;amp;rarr; pour spécifier, le cas échéant, une passerelle&lt;br /&gt;
* DNS1 &amp;amp;rarr; permet de spécifier un serveur DNS primaire&lt;br /&gt;
* DNS2 &amp;amp;rarr; permet de spécifier un serveur DNS secondaire&lt;br /&gt;
*PEERDNS &amp;amp;rarr; précise si les &#039;&#039;DNS&#039;&#039; fournis en DHCP sur cette interface doivent être ajoutés au fichier &#039;&#039;/etc/resolv.conf&#039;&#039;&lt;br /&gt;
*MACADDR &amp;amp;rarr; permet de spécifier une adresse &#039;&#039;MAC&#039;&#039; à utiliser, particulièrement pratique pour les déplacement de VMs, ou pour les baux &#039;&#039;DHCP&#039;&#039; statiques.&lt;br /&gt;
*DOMAIN &amp;amp;rarr; permet de spécifier autant de domaine de recherche qui seront ajouté au fichier &#039;&#039;/etc/resolv.conf&#039;&#039;&lt;br /&gt;
*MTU &amp;amp;rarr; permet de modifier la taille du payload (Maximum Transmission Unit) avant fragmentation. Permet d&#039;activer les &#039;&#039;Jumbo Frame&#039;&#039; pour baisser l&#039;utilisation du CPU dans les réseaux &#039;&#039;Gigabit&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Que faire si ma carte n&#039;a pas le bon numéro ?? =&lt;br /&gt;
&lt;br /&gt;
Lorsque l&#039;on ajoute / enlève une carte réseau, le noyau incrémente le numéro de celle-ci. Par exemple, on avait eth0 et puis on se retrouve avec eth1...&lt;br /&gt;
&lt;br /&gt;
C&#039;est &#039;&#039;UDEV&#039;&#039; qui, à chaque fois que l&#039;on insère une carte réseau avec une adresse MAC différente, change de numéro.&lt;br /&gt;
&lt;br /&gt;
On peut modifier cela dans le fichier &#039;&#039;/etc/udev/rules.d/70-persistent-net.rules&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# This file was automatically generated by the /lib/udev/write_net_rules&lt;br /&gt;
# program, run by the persistent-net-generator.rules rules file.&lt;br /&gt;
#&lt;br /&gt;
# You can modify it, as long as you keep each rule on a single&lt;br /&gt;
# line, and change only the value of the NAME= key.&lt;br /&gt;
&lt;br /&gt;
# PCI device 0x1022:0x2000 (pcnet32)&lt;br /&gt;
SUBSYSTEM==&amp;quot;net&amp;quot;, ACTION==&amp;quot;add&amp;quot;, DRIVERS==&amp;quot;?*&amp;quot;, ATTR{address}==&amp;quot;00:0c:29:f7:cf:de&amp;quot;, ATTR{type}==&amp;quot;1&amp;quot;, KERNEL==&amp;quot;eth*&amp;quot;, NAME=&amp;quot;eth0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# PCI device 0x1022:0x2000 (pcnet32)&lt;br /&gt;
SUBSYSTEM==&amp;quot;net&amp;quot;, ACTION==&amp;quot;add&amp;quot;, DRIVERS==&amp;quot;?*&amp;quot;, ATTR{address}==&amp;quot;00:0c:29:e6:c5:aa&amp;quot;, ATTR{type}==&amp;quot;1&amp;quot;, KERNEL==&amp;quot;eth*&amp;quot;, NAME=&amp;quot;eth1&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il suffit de supprimer la première ligne qui fait référence à l&#039;ancienne carte et, de décrémenter le numéro de la deuxième ligne pour que &#039;&#039;eth1&#039;&#039; devienne &#039;&#039;eth0&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# This file was automatically generated by the /lib/udev/write_net_rules&lt;br /&gt;
# program, run by the persistent-net-generator.rules rules file.&lt;br /&gt;
#&lt;br /&gt;
# You can modify it, as long as you keep each rule on a single&lt;br /&gt;
# line, and change only the value of the NAME= key.&lt;br /&gt;
&lt;br /&gt;
# PCI device 0x1022:0x2000 (pcnet32)&lt;br /&gt;
SUBSYSTEM==&amp;quot;net&amp;quot;, ACTION==&amp;quot;add&amp;quot;, DRIVERS==&amp;quot;?*&amp;quot;, ATTR{address}==&amp;quot;00:0c:29:e6:c5:aa&amp;quot;, ATTR{type}==&amp;quot;1&amp;quot;, KERNEL==&amp;quot;eth*&amp;quot;, NAME=&amp;quot;eth0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que les modifications soient effectives, il faut redémarrer le système.&lt;br /&gt;
&lt;br /&gt;
{|style=&amp;quot;width:90%&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning-icon.png|centré|100px]]&lt;br /&gt;
|style=&amp;quot;width:5%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Sur les distributions à base de Debian, vous êtes obligé de mettre à jour &#039;&#039;initramfs&#039;&#039; pour que les modifications soient prises en compte avant de redémarrer le système :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# update-initramfs -u&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Ifcfg-ethX&amp;diff=4191</id>
		<title>Ifcfg-ethX</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Ifcfg-ethX&amp;diff=4191"/>
		<updated>2026-01-25T09:08:37Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Exemple */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Principe =&lt;br /&gt;
&lt;br /&gt;
Tout ce passe dans le dossier &#039;&#039;/etc/sysconfig/network-scripts&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Dans ce répertoire il y a plusieurs fichiers et notamment les fichiers de configuration des interfaces réseaux qui commencent tous par &#039;&#039;ifcfg-X&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Remplacez &#039;&#039;X&#039;&#039; par le nom de l&#039;interface réseau et, si le fichier n&#039;existe pas, il suffit de le créer !&lt;br /&gt;
&lt;br /&gt;
= Exemple =&lt;br /&gt;
&lt;br /&gt;
Prenons comme exemple &#039;&#039;eth0&#039;&#039;. Son fichier de configuration sera &#039;&#039;ifcfg-eth0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
DEVICE=eth0&lt;br /&gt;
HWADDR=DE:B4:49:A5:3A:07&lt;br /&gt;
TYPE=Ethernet&lt;br /&gt;
ONBOOT=yes&lt;br /&gt;
NM_CONTROLLED=yes&lt;br /&gt;
BOOTPROTO=dhcp&lt;br /&gt;
PEERDNS=no&lt;br /&gt;
DOMAIN=&amp;quot;tala-informatique.fr labo.tala-informatique.fr&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* DEVICE &amp;amp;rarr; indique le nom de l&#039;interface&lt;br /&gt;
* TYPE &amp;amp;rarr; précise le type de périphérique, peut prendre les valeurs [Ethernet|Bridge|Vlan]&lt;br /&gt;
* ONBOOT &amp;amp;rarr; indique si l&#039;interface doit démarrer au démarrage de la machine&lt;br /&gt;
* NM_CONTROLLED &amp;amp;rarr; précise si le Network Manager doit contrôler cette interface (en général oui)&lt;br /&gt;
* BOOTPROTO &amp;amp;rarr; indique le type de démarrage de l&#039;interface, peut prendre les valeurs [dhcp|static|none]&lt;br /&gt;
** Si la valeur &#039;&#039;static&#039;&#039; est utilisée, il faudra également les paramètres suivants:&lt;br /&gt;
*** IPADDR &amp;amp;rarr; pour l&#039;adresse IP&lt;br /&gt;
*** NETMASK &amp;amp;rarr; pour le masque de sous-réseau&lt;br /&gt;
*** GATEWAY &amp;amp;rarr; pour spécifier, le cas échéant, une passerelle&lt;br /&gt;
* DNS1 &amp;amp;rarr; permet de spécifier un serveur DNS primaire&lt;br /&gt;
* DNS2 &amp;amp;rarr; permet de spécifier un serveur DNS secondaire&lt;br /&gt;
*PEERDNS &amp;amp;rarr; précise si les &#039;&#039;DNS&#039;&#039; fournis en DHCP sur cette interface doivent être ajoutés au fichier &#039;&#039;/etc/resolv.conf&#039;&#039;&lt;br /&gt;
*MACADDR &amp;amp;rarr; permet de spécifier une adresse &#039;&#039;MAC&#039;&#039; à utiliser, particulièrement pratique pour les déplacement de VMs, ou pour les baux &#039;&#039;DHCP&#039;&#039; statiques.&lt;br /&gt;
*DOMAIN &amp;amp;rarr; permet de spécifier autant de domaine de recherche qui seront ajouté au fichier &#039;&#039;/etc/resolv.conf&#039;&#039;&lt;br /&gt;
*MTU &amp;amp;rarr; permet de modifier la taille du payload (Maximum Transmission Unit) avant fragmentation. Permet d&#039;activer les &#039;&#039;Jumbo Frame&#039;&#039; pour baisser l&#039;utilisation du CPU dans les réseaux &#039;&#039;Gigabit&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Que faire si ma carte n&#039;a pas le bon numéro ?? =&lt;br /&gt;
&lt;br /&gt;
Lorsque l&#039;on ajoute / enlève une carte réseau, le noyau incrémente le numéro de celle-ci. Par exemple, on avait eth0 et puis on se retrouve avec eth1...&lt;br /&gt;
&lt;br /&gt;
C&#039;est &#039;&#039;UDEV&#039;&#039; qui, à chaque fois que l&#039;on insère une carte réseau avec une adresse MAC différente, change de numéro.&lt;br /&gt;
&lt;br /&gt;
On peut modifier cela dans le fichier &#039;&#039;/etc/udev/rules.d/70-persistent-net.rules&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# This file was automatically generated by the /lib/udev/write_net_rules&lt;br /&gt;
# program, run by the persistent-net-generator.rules rules file.&lt;br /&gt;
#&lt;br /&gt;
# You can modify it, as long as you keep each rule on a single&lt;br /&gt;
# line, and change only the value of the NAME= key.&lt;br /&gt;
&lt;br /&gt;
# PCI device 0x1022:0x2000 (pcnet32)&lt;br /&gt;
SUBSYSTEM==&amp;quot;net&amp;quot;, ACTION==&amp;quot;add&amp;quot;, DRIVERS==&amp;quot;?*&amp;quot;, ATTR{address}==&amp;quot;00:0c:29:f7:cf:de&amp;quot;, ATTR{type}==&amp;quot;1&amp;quot;, KERNEL==&amp;quot;eth*&amp;quot;, NAME=&amp;quot;eth0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# PCI device 0x1022:0x2000 (pcnet32)&lt;br /&gt;
SUBSYSTEM==&amp;quot;net&amp;quot;, ACTION==&amp;quot;add&amp;quot;, DRIVERS==&amp;quot;?*&amp;quot;, ATTR{address}==&amp;quot;00:0c:29:e6:c5:aa&amp;quot;, ATTR{type}==&amp;quot;1&amp;quot;, KERNEL==&amp;quot;eth*&amp;quot;, NAME=&amp;quot;eth1&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il suffit de supprimer la première ligne qui fait référence à l&#039;ancienne carte et, de décrémenter le numéro de la deuxième ligne pour que &#039;&#039;eth1&#039;&#039; devienne &#039;&#039;eth0&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# This file was automatically generated by the /lib/udev/write_net_rules&lt;br /&gt;
# program, run by the persistent-net-generator.rules rules file.&lt;br /&gt;
#&lt;br /&gt;
# You can modify it, as long as you keep each rule on a single&lt;br /&gt;
# line, and change only the value of the NAME= key.&lt;br /&gt;
&lt;br /&gt;
# PCI device 0x1022:0x2000 (pcnet32)&lt;br /&gt;
SUBSYSTEM==&amp;quot;net&amp;quot;, ACTION==&amp;quot;add&amp;quot;, DRIVERS==&amp;quot;?*&amp;quot;, ATTR{address}==&amp;quot;00:0c:29:e6:c5:aa&amp;quot;, ATTR{type}==&amp;quot;1&amp;quot;, KERNEL==&amp;quot;eth*&amp;quot;, NAME=&amp;quot;eth0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que les modifications soient effectives, il faut redémarrer le système.&lt;br /&gt;
&lt;br /&gt;
{|style=&amp;quot;width:90%&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning-icon.png|centré|100px]]&lt;br /&gt;
|style=&amp;quot;width:5%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Sur les distributions à base de Debian, vous êtes obligé de mettre à jour &#039;&#039;initramfs&#039;&#039; pour que les modifications soient prises en compte :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# update-initramfs -u&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Ifcfg-ethX&amp;diff=4190</id>
		<title>Ifcfg-ethX</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Ifcfg-ethX&amp;diff=4190"/>
		<updated>2026-01-25T09:08:18Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Que faire si ma carte n&amp;#039;a pas le bon numéro ?? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Principe =&lt;br /&gt;
&lt;br /&gt;
Tout ce passe dans le dossier &#039;&#039;/etc/sysconfig/network-scripts&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Dans ce répertoire il y a plusieurs fichiers et notamment les fichiers de configuration des interfaces réseaux qui commencent tous par &#039;&#039;ifcfg-X&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Remplacez &#039;&#039;X&#039;&#039; par le nom de l&#039;interface réseau et, si le fichier n&#039;existe pas, il suffit de le créer !&lt;br /&gt;
&lt;br /&gt;
= Exemple =&lt;br /&gt;
&lt;br /&gt;
Prenons comme exemple &#039;&#039;eth0&#039;&#039;. Son fichier de configuration sera &#039;&#039;ifcfg-eth0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DEVICE=eth0&lt;br /&gt;
HWADDR=DE:B4:49:A5:3A:07&lt;br /&gt;
TYPE=Ethernet&lt;br /&gt;
ONBOOT=yes&lt;br /&gt;
NM_CONTROLLED=yes&lt;br /&gt;
BOOTPROTO=dhcp&lt;br /&gt;
PEERDNS=no&lt;br /&gt;
DOMAIN=&amp;quot;tala-informatique.fr labo.tala-informatique.fr&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* DEVICE &amp;amp;rarr; indique le nom de l&#039;interface&lt;br /&gt;
* TYPE &amp;amp;rarr; précise le type de périphérique, peut prendre les valeurs [Ethernet|Bridge|Vlan]&lt;br /&gt;
* ONBOOT &amp;amp;rarr; indique si l&#039;interface doit démarrer au démarrage de la machine&lt;br /&gt;
* NM_CONTROLLED &amp;amp;rarr; précise si le Network Manager doit contrôler cette interface (en général oui)&lt;br /&gt;
* BOOTPROTO &amp;amp;rarr; indique le type de démarrage de l&#039;interface, peut prendre les valeurs [dhcp|static|none]&lt;br /&gt;
** Si la valeur &#039;&#039;static&#039;&#039; est utilisée, il faudra également les paramètres suivants:&lt;br /&gt;
*** IPADDR &amp;amp;rarr; pour l&#039;adresse IP&lt;br /&gt;
*** NETMASK &amp;amp;rarr; pour le masque de sous-réseau&lt;br /&gt;
*** GATEWAY &amp;amp;rarr; pour spécifier, le cas échéant, une passerelle&lt;br /&gt;
* DNS1 &amp;amp;rarr; permet de spécifier un serveur DNS primaire&lt;br /&gt;
* DNS2 &amp;amp;rarr; permet de spécifier un serveur DNS secondaire&lt;br /&gt;
*PEERDNS &amp;amp;rarr; précise si les &#039;&#039;DNS&#039;&#039; fournis en DHCP sur cette interface doivent être ajoutés au fichier &#039;&#039;/etc/resolv.conf&#039;&#039;&lt;br /&gt;
*MACADDR &amp;amp;rarr; permet de spécifier une adresse &#039;&#039;MAC&#039;&#039; à utiliser, particulièrement pratique pour les déplacement de VMs, ou pour les baux &#039;&#039;DHCP&#039;&#039; statiques.&lt;br /&gt;
*DOMAIN &amp;amp;rarr; permet de spécifier autant de domaine de recherche qui seront ajouté au fichier &#039;&#039;/etc/resolv.conf&#039;&#039;&lt;br /&gt;
*MTU &amp;amp;rarr; permet de modifier la taille du payload (Maximum Transmission Unit) avant fragmentation. Permet d&#039;activer les &#039;&#039;Jumbo Frame&#039;&#039; pour baisser l&#039;utilisation du CPU dans les réseaux &#039;&#039;Gigabit&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Que faire si ma carte n&#039;a pas le bon numéro ?? =&lt;br /&gt;
&lt;br /&gt;
Lorsque l&#039;on ajoute / enlève une carte réseau, le noyau incrémente le numéro de celle-ci. Par exemple, on avait eth0 et puis on se retrouve avec eth1...&lt;br /&gt;
&lt;br /&gt;
C&#039;est &#039;&#039;UDEV&#039;&#039; qui, à chaque fois que l&#039;on insère une carte réseau avec une adresse MAC différente, change de numéro.&lt;br /&gt;
&lt;br /&gt;
On peut modifier cela dans le fichier &#039;&#039;/etc/udev/rules.d/70-persistent-net.rules&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# This file was automatically generated by the /lib/udev/write_net_rules&lt;br /&gt;
# program, run by the persistent-net-generator.rules rules file.&lt;br /&gt;
#&lt;br /&gt;
# You can modify it, as long as you keep each rule on a single&lt;br /&gt;
# line, and change only the value of the NAME= key.&lt;br /&gt;
&lt;br /&gt;
# PCI device 0x1022:0x2000 (pcnet32)&lt;br /&gt;
SUBSYSTEM==&amp;quot;net&amp;quot;, ACTION==&amp;quot;add&amp;quot;, DRIVERS==&amp;quot;?*&amp;quot;, ATTR{address}==&amp;quot;00:0c:29:f7:cf:de&amp;quot;, ATTR{type}==&amp;quot;1&amp;quot;, KERNEL==&amp;quot;eth*&amp;quot;, NAME=&amp;quot;eth0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# PCI device 0x1022:0x2000 (pcnet32)&lt;br /&gt;
SUBSYSTEM==&amp;quot;net&amp;quot;, ACTION==&amp;quot;add&amp;quot;, DRIVERS==&amp;quot;?*&amp;quot;, ATTR{address}==&amp;quot;00:0c:29:e6:c5:aa&amp;quot;, ATTR{type}==&amp;quot;1&amp;quot;, KERNEL==&amp;quot;eth*&amp;quot;, NAME=&amp;quot;eth1&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il suffit de supprimer la première ligne qui fait référence à l&#039;ancienne carte et, de décrémenter le numéro de la deuxième ligne pour que &#039;&#039;eth1&#039;&#039; devienne &#039;&#039;eth0&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# This file was automatically generated by the /lib/udev/write_net_rules&lt;br /&gt;
# program, run by the persistent-net-generator.rules rules file.&lt;br /&gt;
#&lt;br /&gt;
# You can modify it, as long as you keep each rule on a single&lt;br /&gt;
# line, and change only the value of the NAME= key.&lt;br /&gt;
&lt;br /&gt;
# PCI device 0x1022:0x2000 (pcnet32)&lt;br /&gt;
SUBSYSTEM==&amp;quot;net&amp;quot;, ACTION==&amp;quot;add&amp;quot;, DRIVERS==&amp;quot;?*&amp;quot;, ATTR{address}==&amp;quot;00:0c:29:e6:c5:aa&amp;quot;, ATTR{type}==&amp;quot;1&amp;quot;, KERNEL==&amp;quot;eth*&amp;quot;, NAME=&amp;quot;eth0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour que les modifications soient effectives, il faut redémarrer le système.&lt;br /&gt;
&lt;br /&gt;
{|style=&amp;quot;width:90%&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning-icon.png|centré|100px]]&lt;br /&gt;
|style=&amp;quot;width:5%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Sur les distributions à base de Debian, vous êtes obligé de mettre à jour &#039;&#039;initramfs&#039;&#039; pour que les modifications soient prises en compte :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# update-initramfs -u&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=The_Linux_Craftsman&amp;diff=4189</id>
		<title>The Linux Craftsman</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=The_Linux_Craftsman&amp;diff=4189"/>
		<updated>2026-01-25T07:11:21Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* La pratique */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Ce wiki =&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;100%&amp;quot;&lt;br /&gt;
! Le contenu !! Public visé !! L&#039;auteur&lt;br /&gt;
|-valign=top&lt;br /&gt;
|width=&amp;quot;33%&amp;quot;|&lt;br /&gt;
Ce wiki propose des articles, cours, TPs et TDs sur des sujets gravitant autour des technologies du système d&#039;information, notamment sur le système Linux, sur la distribution CentOS et maintenant Rocky Linux.&lt;br /&gt;
&lt;br /&gt;
Vous y trouverez des articles traitant de la mise en place de services réseaux tels que DHCP et DNS mais également des articles sur la mise en place de pare-feu, site Web, etc... Il y a un peu de tout et je vous encourage à utiliser le champ de recherche pour trouver ce dont vous avez besoin.&lt;br /&gt;
|width=&amp;quot;33%&amp;quot;|&lt;br /&gt;
&lt;br /&gt;
Ce wiki s&#039;adresse principalement à mes élèves mais il peut également servir à des enseignants qui désirent monter leurs cours sans se &amp;quot;prendre la tête&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Tout est disponible sous [http://www.gnu.org/copyleft/fdl.html licence GNU Free Documentation License 1.3] ou ultérieure et vous pouvez récupérer les contenus et en faire ce que vous voulez !&lt;br /&gt;
|width=&amp;quot;33%&amp;quot;|&lt;br /&gt;
&lt;br /&gt;
Je m&#039;appelle Jean-Christophe FORTON et je suis professeur d&#039;informatique depuis 2011 mais cela n&#039;a pas toujours été mon métier, plus d&#039;info [[jcf|ici]]...&lt;br /&gt;
&lt;br /&gt;
N&#039;hésitez pas à faire un tour sur :&lt;br /&gt;
{|&lt;br /&gt;
|-valign=middle&lt;br /&gt;
|align=center|&lt;br /&gt;
[[Image:Logo-YouTube-rouge.png|30px|link=https://www.youtube.com/@tala-informatique]]&lt;br /&gt;
|| ma chaîne Youtube pour découvrir mes aventures ou (re)visionner certains cours&lt;br /&gt;
|-valign=middle&lt;br /&gt;
|align=center|&lt;br /&gt;
[[Fichier:Logo-Tipeee.png|27px|link=https://fr.tipeee.com/tala-informatique]]&lt;br /&gt;
||mon Tipeee pour me remercier&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Les cours =&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;100%&amp;quot;&lt;br /&gt;
! Système  !! Sécurité !! Réseaux !! Développement !! Général&lt;br /&gt;
|-valign=top&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Linux&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:intro_linux.pdf|Introduction au système Linux]]&lt;br /&gt;
* [[:Media:tp_linux_commandes_de_bases.pdf|TP Linux : commandes de bases]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Windows&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Architecture des ordinateurs en environnement Windows&lt;br /&gt;
** [[:Media:materiel.pdf|Le matériel]]&lt;br /&gt;
** [[:Media:system_exploitation.pdf|Le système d&#039;exploitation]]&lt;br /&gt;
** [[:Media:gestion_disques.pdf|La gestion des disques]]&lt;br /&gt;
** [[:Media:sauvegarde.pdf|La sauvegarde]]&lt;br /&gt;
* Active Directory&lt;br /&gt;
** [[:Media:ad_intro.pdf|Les principes fondamentaux]]&lt;br /&gt;
** [[:Media:ad_gpo.pdf|Gestion des stratégies]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Réseaux&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:Les pare-feux.pdf|Les pare-feux]]&lt;br /&gt;
* [[:Media:Les pare-feux_tech.pdf|Les pare-feux (fiche technique)]]&lt;br /&gt;
* [[:Media:Les VPN.pdf|Les VPN]]&lt;br /&gt;
* [[:Media:Haute_dispo.pdf|Haute disponibilité]]&lt;br /&gt;
* [[:Media:sauvegarde_pca.pdf|La sauvegarde]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Sécurité des Systèmes d’Information&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:principes_SSI.pdf|Les principes fondamentaux]]&lt;br /&gt;
* [[:Media:implémentation_concrete_SSI.pdf|Implémentation concrète de la sécurité]]&lt;br /&gt;
* [[:Media:cryptographie.pdf|Cryptographie]]&lt;br /&gt;
* [[:Media:TD1_SSI.pdf|TD1: Principes Fondamentaux]]&lt;br /&gt;
* [[:Media:TP1_SSI.pdf|TP1: Écoute d&#039;une connexion]]&lt;br /&gt;
* [[:Media:TP2_SSI.pdf|TP2: Chiffrement asymétrique avec PGP]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Web&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:apache server.pdf|Le serveur Apache]]&lt;br /&gt;
* [[:Media:pilotage web bdd.pdf|Pilotage d&#039;une page web]]&lt;br /&gt;
* [[:Media:web app security.pdf|Sécurité des applications Web]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Modèle OSI&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
*[[:Media:OSI.pdf|Le modèle OSI en version cours]]&lt;br /&gt;
*[[:Media:OSI_Slides.pdf|Le modèle OSI en version slides]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 1 &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:Normes_cablages.pdf|Les normes de câblage]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 2&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:Ethernet-802.3.pdf|La norme Ethernet (802.3)]]&lt;br /&gt;
* [[:Media:WiFi-802.11.pdf|La norme Wi-Fi (802.11)]]&lt;br /&gt;
* [[:Media:Bridge.pdf|Les bridges]]&lt;br /&gt;
* [[:Media:STP.pdf|Spanning Tree Protocol]]&lt;br /&gt;
* [[:Media:VLAN.pdf|Les Vlans]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 3+&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Routage:&lt;br /&gt;
** [[:Media:CIDR.pdf|Classless Inter-Domain Routing]]&lt;br /&gt;
** [[:Media:resume_protocoles_vecteur_distance.pdf|Résumé sur les protocoles à vecteur de distance]]&lt;br /&gt;
** [[:Media:EIGRP.pdf|Enhanced Interior Gateway Routing Protocol]]&lt;br /&gt;
** [[:Media:OSPF.pdf|Open Shortest Path First]]&lt;br /&gt;
** [[:Media:BGP.pdf|Border Gateway Protocol]]&lt;br /&gt;
*Services:&lt;br /&gt;
** [[:Media:DHCP.pdf|DHCP]]&lt;br /&gt;
** [[:Media:DNS.pdf|DNS]]&lt;br /&gt;
** [[:Media:SAMBA.pdf|Samba]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Qualité de service&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:QoS.pdf|La QoS]]&lt;br /&gt;
* [[:Media:Iproute2_QoS.pdf|Iproute2 et la QoS]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Protocole&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:HTTP.pdf|HTTP]]&lt;br /&gt;
* [[:Media:SNMP.pdf|SNMP]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;L&#039;algorithmique&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:algo_intro.pdf|Introduction]]&lt;br /&gt;
* [[:Media:algo_langage.pdf|Le Langage]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;SQL&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:conception_bdd.pdf|Conception de base de données]]&lt;br /&gt;
* [[:Media:conception_bdd_simple.pdf|Conception de base de données (simplifié)]]&lt;br /&gt;
* [[:Media:langage_de_requête.pdf|Langage de requête]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Langages du Web&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:HTML xHTML intro.pdf|Le langage HTML]]&lt;br /&gt;
* [[:Media:css.pdf|Le langage CSS]]&lt;br /&gt;
* [[:Media:javascript.pdf|Le langage JavaScript]]&lt;br /&gt;
* [[:Media:php.pdf|Le langage PHP]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Langage bas niveau&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:C lang.pdf|Le langage C]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Architecture SOA&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:webservices.pdf|Les Web Services RESTful]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Informatique embarquée&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:arduino_avr.pdf|Arduino et AVR]]&lt;br /&gt;
* [[:Media:IOT.pdf|L&#039;Internet des objets]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Gestion&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:gestion_projet.pdf|Gestion de projet]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Bureautique&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:powerpoint.pdf|PowerPoint]]&lt;br /&gt;
* [[:Media:excel.pdf|Excel]]&lt;br /&gt;
* [[:Media:bdd_com.pdf|Les bases de données]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Histoire / évolutions&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:expertise_codage.pdf|Expertise codage]]&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= La pratique =&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;100%&amp;quot;&lt;br /&gt;
! Système !! Sécurité !! Réseaux !! Développement !! Embarquée !! Virtualisation&lt;br /&gt;
|-valign=&amp;quot;top&amp;quot;&lt;br /&gt;
|align=left|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Général&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[Installation de CentOS]]&lt;br /&gt;
* [[Installation de Rocky]]&lt;br /&gt;
* [[migration_centos8torocky8| Migration CentOS 8 &amp;amp;rarr; Rocky 8]]&lt;br /&gt;
* [[upgrade_rocky8to9| Upgrade Rocky 8 &amp;amp;rarr; 9]]&lt;br /&gt;
* [[WSL | WSL : Installation de Linux sur Windows 10]]&lt;br /&gt;
* [[Vi / Vim]]&lt;br /&gt;
* [[linux_repository|Les dépots (EPEL, EL, ...)]]&lt;br /&gt;
* [[cron| Gestionnaire des tâches: cron]]&lt;br /&gt;
* [[rpm_yum| Gestionnaires de paquetages: RPM &amp;amp; YUM]]&lt;br /&gt;
* [[Les logs]]&lt;br /&gt;
* [[users_groups|Utilisateurs et groupes]]&lt;br /&gt;
* [[Gestion des disques]]&lt;br /&gt;
* CentOS &amp;amp;#8805; 7 (&amp;amp;asymp; Rocky) [[Fichier:Warning-icon.png|20px]]&lt;br /&gt;
** [[systemctl|Systemctl sur SystemD]]&lt;br /&gt;
** [[systemctl service|Créer un service avec Systemctl (démon)]]&lt;br /&gt;
** [[iptables_on_systemd | Firewalld ?!? Rendez moi Iptables ! ]]&lt;br /&gt;
** [[chrony|&#039;&#039;NTP&#039;&#039; est mort, vive &#039;&#039;Chrony&#039;&#039;]]&lt;br /&gt;
* CentOS 6&lt;br /&gt;
** [[Gestionnaire de démarrage|Gestionnaire de démarrage SysVInit]]&lt;br /&gt;
** [[start_stop_daemon|Création d&#039;un service avec start-stop-daemon (SysVInit)]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Réseaux&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[ifcfg-ethX|Les interfaces réseaux]]&lt;br /&gt;
* [[sysconfig-network|Les paramètres réseaux]]&lt;br /&gt;
* [[resolv.conf|Configuration du client DNS]]&lt;br /&gt;
* [[ntpd|Configuration du client NTP]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Haute disponibilité&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[drbd|Réplication à chaud avec DRBD]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Multimédia&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[DLNA|Partage de contenu cross-platform avec DLNA]]&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Réseaux&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[Iptables]]&lt;br /&gt;
* [[Squid]]&lt;br /&gt;
* [[:Media:Chillispot.pdf|Hotspot avec Chillispot]]&lt;br /&gt;
* [[Sécuriser un service avec Fail2ban]]&lt;br /&gt;
* [[Openvpn]]&lt;br /&gt;
* [[Wireguard]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Système&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[SSH]]&lt;br /&gt;
* [[SELinux]]&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Les outils&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[tcpdump | Le scanner &#039;&#039;tcpdump&#039;&#039;]]&lt;br /&gt;
* [[iproute2 | Contrôle réseau avec la commande &#039;&#039;ip&#039;&#039;]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 2&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[source routing|Le routage source]]&lt;br /&gt;
* [[bridge|Les bridges]]&lt;br /&gt;
* [[alias|Les alias]]&lt;br /&gt;
* [[vlan|Les vlans]]&lt;br /&gt;
* [[one-arm_router|La passerelle &#039;&#039;one-arm&#039;&#039;]]&lt;br /&gt;
* [[transparent_firewall|Le proxy &#039;&#039;transparent&#039;&#039;]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 3+&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Services:&lt;br /&gt;
** [[DHCP]]&lt;br /&gt;
** [[DNS]]&lt;br /&gt;
*Haute-disponibilité&lt;br /&gt;
** [[Ucarp]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 7&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Services:&lt;br /&gt;
** [[SAMBA]]&lt;br /&gt;
** [[HTTPD]]&lt;br /&gt;
** [[xmpp | Serveur XMPP avec Ejabberd]]&lt;br /&gt;
** [[vnc|Installer un serveur VNC]]&lt;br /&gt;
* Industrialisation:&lt;br /&gt;
** [[:Media:pxe.pdf|Monter un serveur PXE (pdf)]]&lt;br /&gt;
** [[PXE|Monter un serveur PXE]]&lt;br /&gt;
* Supervision&lt;br /&gt;
** [[Nagios| Nagios]]&lt;br /&gt;
** [[Cacti| Installer un serveur Cacti]]&lt;br /&gt;
** [[SNMP | Utiliser SNMP]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Cisco ISR (routeur)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[ISR-basics|Les bases]]&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Général&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[eclipse_install|Mise en place de l&#039;environnement de développement]]&lt;br /&gt;
* [[svn|Serveur de version SVN]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;PHP&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
*Les basiques :&lt;br /&gt;
** [[php_httpd_install | Installation sous Linux]]&lt;br /&gt;
** [[php_devel| Premier projet en &#039;&#039;PHP&#039;&#039;]]&lt;br /&gt;
** [[php_algo|Un peu d&#039;algorithmique]]&lt;br /&gt;
** [[php_object| Les objets]]&lt;br /&gt;
** [[php_xdebug| Debugger avec &#039;&#039;Xdebug&#039;&#039;]]&lt;br /&gt;
* La partie CLI :&lt;br /&gt;
** [[php_memcached| Memcached un serveur de cache]]&lt;br /&gt;
** [[php_daemon | Écriture d&#039;un démon]]&lt;br /&gt;
** [[php_socket | Utilisation des sockets]]&lt;br /&gt;
* La partie Web :&lt;br /&gt;
** [[php_$get_$post_$session| Passer des informations entres pages]]&lt;br /&gt;
** [[php_pdo| Utiliser une base MySQL avec PDO]]&lt;br /&gt;
** [[php_slim | Le framework SLIM]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;JavaScript&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
*Les basiques :&lt;br /&gt;
** [[js_devel| Premier projet en &#039;&#039;JavaScript&#039;&#039;]]&lt;br /&gt;
** [[js_algo| Un peu d&#039;algorithmique]]&lt;br /&gt;
** [[js_object| Les objets]]&lt;br /&gt;
*Les RIA (&#039;&#039;R&#039;&#039;ich &#039;&#039;I&#039;&#039;nternet &#039;&#039;A&#039;&#039;pplication) :&lt;br /&gt;
** [[js_AJAX| Client AJAX pour utiliser des Web Services]]&lt;br /&gt;
** [[js_AJAX_auth | Authentification avec un client AJAX]]&lt;br /&gt;
** [[js_AJAX_fetch_api | Découverte de l&#039;API &#039;&#039;fetch&#039;&#039;]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Java&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
*Les basiques :&lt;br /&gt;
** [[java_devel| Premier projet en &#039;&#039;Java&#039;&#039;]]&lt;br /&gt;
** [[java_jar|Mon premier objet]]&lt;br /&gt;
** [[java_algo| Un peu d&#039;algorithmique]]&lt;br /&gt;
* La partie Web:&lt;br /&gt;
** [[java_servlet|Les Servlets]]&lt;br /&gt;
** [[java_ws_restful| Web Service Restful]]&lt;br /&gt;
* Stocker des informations:&lt;br /&gt;
** [[java_mysql| Utiliser une base MySQL avec Java]]&lt;br /&gt;
** [[java_memcached| Memcached un serveur de cache]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;C&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
*Les basiques :&lt;br /&gt;
** [[c_devel| Premier projet en &#039;&#039;C&#039;&#039;]]&lt;br /&gt;
** [[c_devel_cross| Compilation croisée (Cross Compilation)]]&lt;br /&gt;
** [[c_algo| Un peu d&#039;algorithmique]]&lt;br /&gt;
*Execution parallèle :&lt;br /&gt;
** [[c_pthread| Les threads]]&lt;br /&gt;
** [[c_fork| Les forks]]&lt;br /&gt;
* IPC:&lt;br /&gt;
** [[c_pipe| Les tubes]]&lt;br /&gt;
** [[c_semaphore| Les sémaphores]]&lt;br /&gt;
** [[c_signals| Les signaux POSIX]]&lt;br /&gt;
** [[c_socket| Les sockets]]&lt;br /&gt;
* GP-GPU avec CUDA:&lt;br /&gt;
** [[cuda_install| Installation de CUDA]]&lt;br /&gt;
** [[cuda_hello_world| Les concepts et bases]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Python&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[python_devel| Premier projet en &#039;&#039;Python&#039;&#039;]]&lt;br /&gt;
* [[python_algo|Un peu d&#039;algorithmique]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;SQL&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[sql_install| Installation d&#039;un SGBDR]]&lt;br /&gt;
* [[SQL_import| Importation d&#039;une base de données]]&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Général&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[Arduino_sketch_writing | Écriture d&#039;un sketch]]&lt;br /&gt;
* [[Arduino_CH340_driver_install | Installation du pilote CH340 (Serial TTL)]]&lt;br /&gt;
* [[Arduino_FTDI_driver_install | Installation du pilote FTDI(Serial TTL)]]&lt;br /&gt;
* [[Arduino_CP210X_driver_install | Installation du pilote CP210X(Serial TTL)]]&lt;br /&gt;
* [[Arduino_Eclipse_sketch | Création d&#039;un projet sous Eclipse]]&lt;br /&gt;
* [[arduino_diagram | Schémas des cartes Arduino]]&lt;br /&gt;
* [[esp8266_diagram | Schémas des cartes ESP8266]]&lt;br /&gt;
* [[esp32_diagram | Schémas des cartes ESP32]]&lt;br /&gt;
* [[sbc_diagram | Schémas des SBC]]&lt;br /&gt;
* [[µc_datasheet | Fiches techniques des microcontrôleurs]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Capteurs&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[Arduino_LDR | Luminosité avec une photorésistance]]&lt;br /&gt;
* [[Arduino_BH1750 | Luminosité avec le BH1750]]&lt;br /&gt;
* [[Arduino_DS18B20 | Température avec le DS18B20]]&lt;br /&gt;
* [[Arduino_LM35DZ | Température avec le LM35]]&lt;br /&gt;
* [[Arduino_DHT11 | Température et humidité avec le DHT11]]&lt;br /&gt;
* [[Arduino_BMP280 | Température, pression et altitude avec le BMP280]]&lt;br /&gt;
* [[Arduino_A3144 | Effet de Hall (magnétisme) avec le A3144 ]]&lt;br /&gt;
* [[Arduino_SR501 | Détection de mouvement avec le SR501 (PIR) ]]&lt;br /&gt;
* [[Arduino_soil_moisture | Capteur d&#039;humidité du sol ]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Communication&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Sans-fil:&lt;br /&gt;
** [[Arduino_HC12 | Communication RF433 avec un HC12]]&lt;br /&gt;
** [[Arduino_NRF24L01 | Communication 2.4Ghz avec un NRF24L01]]&lt;br /&gt;
** [[Arduino_ESP05 | Communication Wi-Fi avec un ESP-05]]&lt;br /&gt;
* Shield Ethernet (W5100):&lt;br /&gt;
** [[Arduino_W5100_intro | Présentation du shield]]&lt;br /&gt;
** [[Arduino_W5100_OSI3 | Configuration OSI 3]]&lt;br /&gt;
** [[Arduino_W5100_web_server | Utilisation du Shield Ethernet pour faire un serveur Web]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Composants et montages divers&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[ potentiometre | Potentiomètre ]]&lt;br /&gt;
* [[ shift_register | Registre à décalage ]]&lt;br /&gt;
* [[ Arduino_2axis_joystick_button | Joystick 2 axes avec bouton ]]&lt;br /&gt;
* [[ Arduino_SD_CARD | Module pour cartes SD ]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;ESP8266 / ESP32&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[esp_ide_arduino | Cartes ESP et IDE Arduino]]&lt;br /&gt;
* [[esp8266_wifi | Utilisation du WiFi ]]&lt;br /&gt;
* [[esp8266_webserver | Utilisation du serveur web ]]&lt;br /&gt;
* [[esp8266_udp_server | Serveur UDP ]]&lt;br /&gt;
* [[esp8266_ntp_client | Client NTP ]]&lt;br /&gt;
* [[:Media:esp8266_tp_meteo_dht11.pdf | TP station météo avec le DHT11 ]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Raspberry / Banana / Orange Pi&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[iso_install_sdcard | Installation d&#039;une image ]]&lt;br /&gt;
* [[pi_java_install | Mise en place de Java ]]&lt;br /&gt;
* [[sbc_qemu_emulation | Émulation avec Qemu ]]&lt;br /&gt;
* [[Linux sunxi armbian gpio | Manipulation des GPIO sous Linux]]&lt;br /&gt;
* [[Linux sunxi armbian w1 | Utilisation du protocole OneWire sous Linux]]&lt;br /&gt;
* [[Linux uart sunxi armbian | Utilisation du protocole UART sous Linux]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Notions avancées&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Arduino (ATmega328)&lt;br /&gt;
** [[atmega328_registers | Manipulation de registres ]]&lt;br /&gt;
** [[atmega328_timers | Les timers ]]&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;VmWare&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[vmware_install | Installation ]]&lt;br /&gt;
* [[vmware_network | La partie réseau ]]&lt;br /&gt;
* [[vmware_create_vm | Création d&#039;une machine virtuelle ]]&lt;br /&gt;
* [[vmware_debug_vm | Débogage réseau d&#039;une machine virtuelle]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Proxmox&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[proxmox_install | Installation ]]&lt;br /&gt;
* [[proxmox_nic_name | Les interfaces réseaux ]]&lt;br /&gt;
* [[proxmox_iptables | Utilisation d&#039;iptables ]]&lt;br /&gt;
* [[proxmox_fail2ban | Fail2ban ]]&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;visibility:hidden&amp;quot;&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
__NOTOC__&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Vi_/_Vim&amp;diff=4188</id>
		<title>Vi / Vim</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Vi_/_Vim&amp;diff=4188"/>
		<updated>2026-01-23T05:14:48Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Dans tout le fichier */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
== Création d&#039;un fichier texte ==&lt;br /&gt;
&lt;br /&gt;
vi ou vim (alias sous CentOS) est un éditeur en mode console peu convivial certes, mais ultra puissant et qui s&#039;utilise de la manière suivante:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
vi &amp;lt;nom_fichier&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Après l’appui sur la touche entrer, le prompt disparaît pour laisser place à une série de tilde (~) qui indiquent que les lignes sont vides.&lt;br /&gt;
&lt;br /&gt;
== Les modes ==&lt;br /&gt;
&lt;br /&gt;
=== Permutation ===&lt;br /&gt;
&lt;br /&gt;
Il y a deux modes dans vi:&lt;br /&gt;
* le mode saisie;&lt;br /&gt;
* le mode commande.&lt;br /&gt;
&lt;br /&gt;
Par défaut il est en mode commande et :&lt;br /&gt;
* la touche  &#039;&#039;insert&#039;&#039; ou &#039;&#039;i&#039;&#039; permet de passer en mode insertion ;&lt;br /&gt;
* la touche &#039;&#039;Echap&#039;&#039; permet de revenir au mode commande.&lt;br /&gt;
&lt;br /&gt;
Lorsque l&#039;on est en mode insertion un nouvel appui sur la touche &#039;&#039;insert&#039;&#039; permet de passer en mode &#039;&#039;replace&#039;&#039; (remplacement).&lt;br /&gt;
&lt;br /&gt;
=== Répétitions ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
n&amp;lt;commande&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Par exemple &#039;&#039;dd&#039;&#039; efface une ligne et &#039;&#039;5dd&#039;&#039; efface 5 lignes&lt;br /&gt;
&lt;br /&gt;
== Quitter... ==&lt;br /&gt;
=== ...en sauvant ===&lt;br /&gt;
&lt;br /&gt;
Lorsque l&#039;on a fini d&#039;éditer son fichier texte, on peut le sauver en repassant en mode commande (touche &#039;&#039;Echap&#039;&#039;) et en tapant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:wq&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
ou bien&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
ou encore en appuyant deux fois sur la touche &#039;&#039;Z&#039;&#039; (deux &#039;&#039;Z&#039;&#039; majuscules)&lt;br /&gt;
&lt;br /&gt;
=== ...en abandonnant les modifications ===&lt;br /&gt;
Si l&#039;on a fait une erreur et que l&#039;on veut quitter sans sauver, on peut le faire en repassant en mode commande (touche &#039;&#039;Echap&#039;&#039;) et en tapant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:q!&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Éditer =&lt;br /&gt;
&lt;br /&gt;
== Undo ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;u&#039;&#039; permet de défaire une modification&lt;br /&gt;
&lt;br /&gt;
== Repeat ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;.&#039;&#039; permet de répéter la  dernière commande d’édition&lt;br /&gt;
&lt;br /&gt;
== Effacer ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;x&#039;&#039; permet d’effacer le caractère courant&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;c&#039;&#039; permet d’effacer le texte et de passer en mode insertion:&lt;br /&gt;
**&#039;&#039;cw&#039;&#039; &amp;amp;rarr; change du curseur jusqu’à la fin du mot&lt;br /&gt;
**&#039;&#039;c$&#039;&#039; &amp;amp;rarr; change jusqu’à la fin de la ligne&lt;br /&gt;
**&#039;&#039;cc&#039;&#039; &amp;amp;rarr; change la ligne courante&lt;br /&gt;
&lt;br /&gt;
== Couper ==&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;d&#039;&#039; permet de couper grâce à l’appui de:&lt;br /&gt;
**&#039;&#039;dw&#039;&#039; &amp;amp;rarr; coupe du curseur jusqu’à la fin du mot&lt;br /&gt;
**&#039;&#039;dd&#039;&#039; &amp;amp;rarr; coupe la ligne complète&lt;br /&gt;
**&#039;&#039;d$&#039;&#039; &amp;amp;rarr; coupe du curseur à la fin de la ligne&lt;br /&gt;
**&#039;&#039;d^&#039;&#039; &amp;amp;rarr; coupe du curseur au premier caractère non vide (pas une tabulation ou un espace) &lt;br /&gt;
**&#039;&#039;d0&#039;&#039; &amp;amp;rarr; coupe du curseur jusqu’au début de la ligne&lt;br /&gt;
&lt;br /&gt;
== Copier ==&lt;br /&gt;
y permet de copier grâce à l’appui de: &lt;br /&gt;
*&#039;&#039;yw&#039;&#039; &amp;amp;rarr; copie du curseur à la fin du mot (&#039;&#039;nyw&#039;&#039; copie les n mots)&lt;br /&gt;
*&#039;&#039;yy&#039;&#039; ou &#039;&#039;Y&#039;&#039; &amp;amp;rarr; (yank) copie la ligne courante dans le buffer (&#039;&#039;nyy&#039;&#039; copie les n lignes)&lt;br /&gt;
*&#039;&#039;y0&#039;&#039; → copie du curseur jusqu’au début de la ligne&lt;br /&gt;
&lt;br /&gt;
== Coller ==&lt;br /&gt;
*&#039;&#039;p&#039;&#039; &amp;amp;rarr; colle les lignes copiées en dessous du curseur&lt;br /&gt;
*&#039;&#039;P&#039;&#039; &amp;amp;rarr; colle les lignes copiées au-dessus du curseur&lt;br /&gt;
&lt;br /&gt;
== Suppressions ==&lt;br /&gt;
=== Dans tout le fichier ===&lt;br /&gt;
*&#039;&#039;:g/^#/d&#039;&#039; &amp;amp;rarr; permet de supprimer les lignes  commençant par &#039;&#039;#&#039;&#039;&lt;br /&gt;
*&#039;&#039;:g/^$/d&#039;&#039; &amp;amp;rarr; permet de supprimer les lignes vides (sans espace ni tabulation)&lt;br /&gt;
&lt;br /&gt;
=== Sur la ligne ===&lt;br /&gt;
*&#039;&#039;dfX&#039;&#039; &amp;amp;rarr; permet de supprimer du curseur jusqu&#039;au caractère X&lt;br /&gt;
*&#039;&#039;dtX&#039;&#039; &amp;amp;rarr; permet de supprimer du curseur jusqu&#039;au caractère X non inclue&lt;br /&gt;
&lt;br /&gt;
== Insertion ==&lt;br /&gt;
* &#039;&#039;o&#039;&#039; &amp;amp;rarr; permet d&#039;insérer une ligne en-dessous du curseur&lt;br /&gt;
* &#039;&#039;O&#039;&#039; &amp;amp;rarr; permet d&#039;insérer une ligne au-dessus du curseur&lt;br /&gt;
&lt;br /&gt;
== Remplacements ==&lt;br /&gt;
=== Au niveau fichier ===&lt;br /&gt;
*&#039;&#039;:s/toto/titi/g&#039;&#039; &amp;amp;rarr; permet de remplacer toutes les occurrences (grâce au &#039;&#039;/g&#039;&#039;) de &#039;&#039;toto&#039;&#039; par &#039;&#039;titi&#039;&#039; sur la ligne du curseur &lt;br /&gt;
*&#039;&#039;:%s/toto/titi/g&#039;&#039; &amp;amp;rarr; permet de remplacer toutes les occurrences (grâce au &#039;&#039;/g&#039;&#039;) de &#039;&#039;toto&#039;&#039; par &#039;&#039;titi&#039;&#039; dans tout le document (grâce au &#039;&#039;%&#039;&#039;) &lt;br /&gt;
*&#039;&#039;:1,5/toto/titi/g&#039;&#039; &amp;amp;rarr; permet de remplacer toutes les occurrences (grâce au &#039;&#039;/g&#039;&#039;) de &#039;&#039;toto&#039;&#039; par &#039;&#039;titi&#039;&#039; de la ligne 1 à la ligne 5 (grâce au &#039;&#039;1,5&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
=== Au niveau curseur ===&lt;br /&gt;
*&#039;&#039;x&#039;&#039; &amp;amp;rarr; permet de supprimer le caractère sur le curseur&lt;br /&gt;
*&#039;&#039;rX&#039;&#039; &amp;amp;rarr; permet de remplacer le caractère sur le curseur par X&lt;br /&gt;
*&#039;&#039;R&#039;&#039; &amp;amp;rarr; permet de passer en mode remplacement&lt;br /&gt;
&lt;br /&gt;
=Placements =&lt;br /&gt;
=== Dans la globalité du fichier ===&lt;br /&gt;
*&#039;&#039;A&#039;&#039; &amp;amp;rarr; permet de placer le curseur en mode &#039;&#039;insert&#039;&#039; à la fin de la ligne courante&lt;br /&gt;
*&#039;&#039;:X&#039;&#039; ou &#039;&#039;XG&#039;&#039; &amp;amp;rarr; permet de se déplacer à la ligne X du fichier&lt;br /&gt;
*&#039;&#039;G&#039;&#039; &amp;amp;rarr; permet de se déplacer à la fin du fichier&lt;br /&gt;
*&#039;&#039;``&#039;&#039; &amp;amp;rarr; permet de revenir à la position précédente&lt;br /&gt;
&lt;br /&gt;
=== Sur la partie présente à l&#039;écran ===&lt;br /&gt;
*&#039;&#039;H&#039;&#039; &amp;amp;rarr; déplace le curseur à la première ligne visible à l&#039;écran (&#039;&#039;HOME&#039;&#039;)&lt;br /&gt;
*&#039;&#039;M&#039;&#039; &amp;amp;rarr; déplace le curseur au milieu de l&#039;écran (&#039;&#039;MIDDLE&#039;&#039;)&lt;br /&gt;
*&#039;&#039;L&#039;&#039; &amp;amp;rarr; déplace le curseur à la dernière ligne visible à l&#039;écran (&#039;&#039;LAST&#039;&#039;)&lt;br /&gt;
*&#039;&#039;zz&#039;&#039; &amp;amp;rarr; centre l&#039;écran sur le curseur&lt;br /&gt;
&lt;br /&gt;
=== Sur la ligne ===&lt;br /&gt;
*&#039;&#039;W&#039;&#039; &amp;amp;rarr; déplace le curseur sur le mot suivant sans tenir compte de la ponctuation&lt;br /&gt;
*&#039;&#039;w&#039;&#039; &amp;amp;rarr; déplace le curseur sur le mot suivant&lt;br /&gt;
*&#039;&#039;B&#039;&#039; &amp;amp;rarr; déplace le curseur sur le mot précédent sans tenir compte de la ponctuation&lt;br /&gt;
*&#039;&#039;b&#039;&#039; &amp;amp;rarr; déplace le curseur sur le mot précédent&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;^&#039;&#039; &amp;amp;rarr; déplace le curseur sur le premier caractère non vide (pas une tabulation ou un espace)&lt;br /&gt;
*&#039;&#039;0&#039;&#039; &amp;amp;rarr; déplace le curseur au début de la ligne&lt;br /&gt;
*&#039;&#039;$&#039;&#039; &amp;amp;rarr; déplace le curseur à la fin de la ligne&lt;br /&gt;
&lt;br /&gt;
== Recherche ==&lt;br /&gt;
*&#039;&#039;/coucou&#039;&#039; &amp;amp;rarr; permet de placer le curseur sur la prochaine occurrence de &#039;&#039;coucou&#039;&#039;&lt;br /&gt;
* &#039;&#039;n&#039;&#039; &amp;amp;rarr; permet de passer à l&#039;occurence suivante (&#039;&#039;next&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;fX&#039;&#039; permet de recherche le caractère X en avant en appuyant sur &#039;&#039;;&#039;&#039;&lt;br /&gt;
*&#039;&#039;FX&#039;&#039; permet de recherche le caractère X en arrière en appuyant sur &#039;&#039;;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=Astuces=&lt;br /&gt;
== Exécution ==&lt;br /&gt;
*&#039;&#039;!&#039;&#039; &amp;amp;rarr; permet d’exécuter une  commande depuis vi, cela démarrera un Shell interactif.&lt;br /&gt;
*&#039;&#039;:sh&#039;&#039; &amp;amp;rarr; permet de passer l’éditeur de texte en arrière plan pour avoir un shell et &#039;&#039;ctrl+d&#039;&#039; permet de le reprendre&lt;br /&gt;
&lt;br /&gt;
== Récupération ==&lt;br /&gt;
Si le système crash, il est possible de récupérer les changements grâce à l&#039;option &#039;&#039;-r&#039;&#039; (&#039;&#039;recovery&#039;&#039;) de &#039;&#039;vi&#039;&#039; : &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# vi -r &amp;lt;mon_fichier&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Systemctl&amp;diff=4187</id>
		<title>Systemctl</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Systemctl&amp;diff=4187"/>
		<updated>2025-12-29T04:29:37Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Maintenant, que faire ? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
== Déni, Colère, Expression, Dépression, Acceptation ==&lt;br /&gt;
Nous étions habitués aux commandes &#039;&#039;service&#039;&#039;, &#039;&#039;chkconfig&#039;&#039; ou encore &#039;&#039;init&#039;&#039; mais maintenant c&#039;est de l&#039;histoire ancienne puisque SysVInit à été remplacé par SystemD, le nouveau gestionnaire de démarrage écrit par RedHat. &lt;br /&gt;
&lt;br /&gt;
On peut se demander pourquoi on change un système qui fonctionne ? &lt;br /&gt;
&lt;br /&gt;
La raison est simple : SysVInit est lent, mal architecturé, possède des faiblesses. C&#039;en était trop pour certain et c&#039;est pourquoi, d&#039;une architecture modulaire (chacun sa tâche), nous sommes passé à une architecture centralisée (une seule commande). Ce nouveau &#039;&#039;moteur&#039;&#039; est écrit en &#039;&#039;C&#039;&#039; et permet un démarrage parallélisé des processus plutôt qu&#039;en série. &lt;br /&gt;
Les développeur d&#039;Ubuntu avaient commencé le travail en écrivant &#039;&#039;UStart&#039;&#039; mais n&#039;étaient pas allé jusqu&#039;au bout des choses ! &lt;br /&gt;
&lt;br /&gt;
Ci-dessous un dessin expliquant comment se déroule le démarrage du système :&lt;br /&gt;
[[Fichier:Comparing services start.png|centré|500px]]&lt;br /&gt;
&lt;br /&gt;
== Explication de texte ==&lt;br /&gt;
On voit clairement le gain de temps au démarrage du système ! Une question vient à l&#039;esprit, comment est-ce possible ?&amp;lt;br&amp;gt;&lt;br /&gt;
La réponse se trouve dans le type de socket utilisées pour la communication entres les services : on est passé de socket &#039;&#039;AF_INET&#039;&#039;, orienté réseaux, à des socket &#039;&#039;AF_UNIX&#039;&#039;, orientée système.&lt;br /&gt;
&lt;br /&gt;
Le premier type de socket, &#039;&#039;AF_INET&#039;&#039;, permet des connexions réseau entre les services, surtout utilisée dans les systèmes distribués, plus vraiment d&#039;actualité sur un système centralisé.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour qu&#039;une communication réseau fonctionne, il faut attendre que le service que l&#039;on veut contacté soit démarré et en écoute sur le réseau... ce qui prend du temps !&lt;br /&gt;
&lt;br /&gt;
Les sockets &#039;&#039;AF_UNIX&#039;&#039; sont des fichiers sur le disque !&amp;lt;br&amp;gt;&lt;br /&gt;
Elles ne nécessitent pas la présence du service cible pour démarrer et sont donc créées par le système d&#039;exploitation au démarrage. Les services démarrent en parallèle et se &#039;&#039;branchent&#039;&#039; à la socket, c&#039;est à dire ouvre le fichier pour en lire le contenu, quand ils sont prêt.&lt;br /&gt;
&lt;br /&gt;
Ainsi, le démarrage des services est accéléré au détriment de la possibilité de les faire tourner sur une machine distante... on va s&#039;en remettre :)&lt;br /&gt;
&lt;br /&gt;
= Maintenant, que faire ? =&lt;br /&gt;
Ci-dessous un tableau qui fait le lien entre les anciennes et les nouvelles commandes :&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
! SysVInit!! SystemD !! Description&lt;br /&gt;
|-&lt;br /&gt;
||service $daemon start&lt;br /&gt;
||systemctl start $daemon.service&lt;br /&gt;
||Permet de démarrer $daemon&lt;br /&gt;
|-&lt;br /&gt;
||service $daemon  stop&lt;br /&gt;
||systemctl stop daemon.service&lt;br /&gt;
||Permet de stopper $daemon&lt;br /&gt;
|-&lt;br /&gt;
||service $daemon restart&lt;br /&gt;
||systemctl restart $daemon.service&lt;br /&gt;
||Permet de redémarrer $daemon&lt;br /&gt;
|-&lt;br /&gt;
||service $daemon reload&lt;br /&gt;
||systemctl reload $daemon.service&lt;br /&gt;
||Permet de recharger la configuration de $daemon&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|X&lt;br /&gt;
||systemctl reload-or-restart $daemon&lt;br /&gt;
||Permet de recharger ou redémarrer $daemon&lt;br /&gt;
|-&lt;br /&gt;
||service $daemon status&lt;br /&gt;
||systemctl status $daemon&lt;br /&gt;
||Affiche des informations sur le service $daemon&lt;br /&gt;
|-&lt;br /&gt;
||chkconfig $daemon on&lt;br /&gt;
||systemctl enable $daemon.service&lt;br /&gt;
||Permet d&#039;enregistrer $daemon au démarrage du système&lt;br /&gt;
|-&lt;br /&gt;
||chkconfig $daemon off&lt;br /&gt;
||systemctl disable $daemon.service&lt;br /&gt;
||Permet d&#039;effacer $daemon du démarrage du système&lt;br /&gt;
|-&lt;br /&gt;
||chkconfig $daemon&lt;br /&gt;
||systemctl is-enabled $daemon.service&lt;br /&gt;
||Permet de voir si $daemon est actif au démarrage du système&lt;br /&gt;
|-&lt;br /&gt;
||chkconfig $daemon --list&lt;br /&gt;
||systemctl list-units --type=service --all&lt;br /&gt;
||Permet de voir la liste des services et s&#039;ils sont actifs ou non au démarrage du système&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|X&lt;br /&gt;
||systemctl is-failed $daemon.service&lt;br /&gt;
||Retourne la valeur &#039;&#039;active&#039;&#039; si le service fonctionne sinon &#039;&#039;failed&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|X&lt;br /&gt;
||systemctl list-units [--all] [--state=inactive] [--type=service]&lt;br /&gt;
||Liste toutes les &#039;&#039;unités&#039;&#039; gérées par &#039;&#039;systemctl&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|X&lt;br /&gt;
||systemctl show --property MainPID --value $daemon.service&lt;br /&gt;
||Affiche le PID de $daemon&lt;br /&gt;
|}&lt;br /&gt;
Il est possible de ne pas ajouter le &#039;&#039;.service&#039;&#039;, &#039;&#039;systemctl&#039;&#039; comprendra très bien qu&#039;il s&#039;agit d&#039;un service !&lt;br /&gt;
&lt;br /&gt;
Nous allons maintenant passer en revue les unités que nous pouvons manipuler avec systemctl !&lt;br /&gt;
&lt;br /&gt;
= Gestion des unités =&lt;br /&gt;
== État des unités ==&lt;br /&gt;
&#039;&#039;systemctl&#039;&#039; peut afficher des informations sur les unités :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl list-units&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Et on peut appliquer des filtre sur cette commande :&lt;br /&gt;
* --type : pour spécifier le type d&#039;unité&lt;br /&gt;
** mount : les points de montage&lt;br /&gt;
** service : les démons&lt;br /&gt;
** target : les runlevel ou événements&lt;br /&gt;
** scope : les sessions actives&lt;br /&gt;
** device : les périphériques&lt;br /&gt;
* --state&lt;br /&gt;
** loaded : fichier de configuration lu par systemD&lt;br /&gt;
** active : l&#039;unité est active&lt;br /&gt;
** inactive : l&#039;unité des inactive&lt;br /&gt;
* --all : toutes les unités&lt;br /&gt;
Par exemple, pour lister les points de montage actif :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl list-units --type=mount --state=active&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== A distance ==&lt;br /&gt;
Toutes les commandes qui figure ici peuvent être exécutées à distance en ajoutant le paramètre &#039;&#039;-H @ip_machine&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Par exemple, pour vérifier le status du démon &#039;&#039;httpd&#039;&#039; sur l&#039;hôte 192.168.1.1 :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl -H 127.0.0.1 status httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Configuration des unités ==&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||On peut afficher les fichiers de configuration des unités&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl list-unit-files&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Cette commande affiche tous les fichiers, même ceux des unités qui ne sont pas actuellement chargées !&lt;br /&gt;
|-&lt;br /&gt;
||Il est possible de voir la configuration d&#039;une unité&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl cat sshd.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||Il est possible d&#039;éditer la configuration d&#039;une unité en utilisant &#039;&#039;edit&#039;&#039;. Cela va créer un répertoire portant le nom de l&#039;unité et se terminant par un &#039;&#039;.d&#039;&#039;. Par exemple pour Apache, cela créer un répertoire &#039;&#039;/etc/systemd/system/httpd.service.d&#039;&#039;. Les informations présentes dans ce fichier vont venir surcharger les informations déjà présentes dans le fichier original. Pour modifier directement la configuration original, il faut utiliser l&#039;option &#039;&#039;--full&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Pour effacer une configuration additionnelle (surcharge), il suffit de supprimer le répertoire en &#039;&#039;.d&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Une fois les modifications faite, il ne faut pas oublier de recharger systemD !&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl edit sshd.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl edit sshd.service --full&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl daemon-reload&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||On peut également voir les dépendances d&#039;une unité&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl list-dependencies sshd.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||On peut également voir l&#039;inverse, c&#039;est à dire quand l&#039;unité est requise. On voit que le service sshd démarre en mode console (multi-user.target) et en mode graphique (graphical.target). Nous aurions, au temps de SysVInit, parlé des [[Gestionnaire_de_démarrage#runlevel.2C_kesako_.3F |runlevel]] 3 et 5.&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl list-dependencies --reverse sshd.service&lt;br /&gt;
sshd.service&lt;br /&gt;
● └─multi-user.target&lt;br /&gt;
●   └─graphical.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|| On peut demander les propriétés d&#039;une unité&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il est même possible de demander une propriété en particulier&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl show sshd.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl show sshd.service -p WantedBy&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||Il est possible de masquer des unités, c&#039;est à dire d&#039;empêcher le démarrage manuel et automatique de l&#039;unité. Il suffit alors de la démasquer pour pouvoir l&#039;utiliser à nouveau&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl mask httpd.service&lt;br /&gt;
Created symlink from /etc/systemd/system/httpd.service to /dev/null.&lt;br /&gt;
# systemctl start httpd.service&lt;br /&gt;
Failed to start httpd.service: Unit is masked.&lt;br /&gt;
# systemctl unmask httpd.service&lt;br /&gt;
Removed symlink /etc/systemd/system/httpd.service.&lt;br /&gt;
# systemctl start httpd.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Les cibles ==&lt;br /&gt;
===Aperçu===&lt;br /&gt;
Les cibles reprennent le concept des [[Gestionnaire_de_démarrage#runlevel.2C_kesako_.3F|&#039;&#039;runlevel&#039;&#039;]] de SysVInit :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# find / -name &amp;quot;runlevel*.target&amp;quot;&lt;br /&gt;
/usr/lib/systemd/system/runlevel5.target&lt;br /&gt;
/usr/lib/systemd/system/runlevel0.target&lt;br /&gt;
/usr/lib/systemd/system/runlevel6.target&lt;br /&gt;
/usr/lib/systemd/system/runlevel1.target&lt;br /&gt;
/usr/lib/systemd/system/runlevel2.target&lt;br /&gt;
/usr/lib/systemd/system/runlevel3.target&lt;br /&gt;
/usr/lib/systemd/system/runlevel4.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On voit qu&#039;il y en a 6 et voici leur utilité :&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
!Run Level!! Target (unité SystemD) !! Description&lt;br /&gt;
|-&lt;br /&gt;
||0&lt;br /&gt;
||runlevel0.target, poweroff.target&lt;br /&gt;
||Arrêt du système&lt;br /&gt;
|-&lt;br /&gt;
||1&lt;br /&gt;
||runlevel1.target, rescue.target&lt;br /&gt;
||Démarrage du système avec un shell de secours&lt;br /&gt;
|-&lt;br /&gt;
||2,3,4&lt;br /&gt;
||runlevel[2,3,4].target, multi-user.target&lt;br /&gt;
||Démarrage du système en mode console&lt;br /&gt;
|-&lt;br /&gt;
||5&lt;br /&gt;
||runlevel5.target, graphical.target&lt;br /&gt;
||Démarrage du système en mode graphique&lt;br /&gt;
|-&lt;br /&gt;
||6&lt;br /&gt;
||runlevel6.target, reboot.target&lt;br /&gt;
||Redémarrage du système&lt;br /&gt;
|}&lt;br /&gt;
On peut voir les liens symboliques qui sont fait sur les &#039;&#039;targets&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ls -l /usr/lib/systemd/system/*.target | grep runlevel&lt;br /&gt;
lrwxrwxrwx. 1 root root  15 26 déc.  20:23 /usr/lib/systemd/system/runlevel0.target -&amp;gt; poweroff.target&lt;br /&gt;
lrwxrwxrwx. 1 root root  13 26 déc.  20:23 /usr/lib/systemd/system/runlevel1.target -&amp;gt; rescue.target&lt;br /&gt;
lrwxrwxrwx. 1 root root  17 26 déc.  20:23 /usr/lib/systemd/system/runlevel2.target -&amp;gt; multi-user.target&lt;br /&gt;
lrwxrwxrwx. 1 root root  17 26 déc.  20:23 /usr/lib/systemd/system/runlevel3.target -&amp;gt; multi-user.target&lt;br /&gt;
lrwxrwxrwx. 1 root root  17 26 déc.  20:23 /usr/lib/systemd/system/runlevel4.target -&amp;gt; multi-user.target&lt;br /&gt;
lrwxrwxrwx. 1 root root  16 26 déc.  20:23 /usr/lib/systemd/system/runlevel5.target -&amp;gt; graphical.target&lt;br /&gt;
lrwxrwxrwx. 1 root root  13 26 déc.  20:23 /usr/lib/systemd/system/runlevel6.target -&amp;gt; reboot.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Cible courante===&lt;br /&gt;
Pour voir la &#039;&#039;target&#039;&#039; courante ou anciennement la commande &#039;&#039;runlevel&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl get-default&lt;br /&gt;
multi-user.target&lt;br /&gt;
# runlevel&lt;br /&gt;
N 3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Changement de cible===&lt;br /&gt;
Enfin, on peut changer la &#039;&#039;target&#039;&#039; courante grâce à l&#039;option &#039;&#039;isolate&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl isolate graphical.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Quand l&#039;option &#039;&#039;isolate&#039;&#039; est utilisée, le suffixe &#039;&#039;.target&#039;&#039; n&#039;est pas obligatoire.&lt;br /&gt;
&lt;br /&gt;
Certaine target on des racourcis :&lt;br /&gt;
* &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl isolate poweroff.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
équivaut à&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# poweroff&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl isolate reboot.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
équivaut à&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# reboot&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Changer la cible par défaut===&lt;br /&gt;
La cible par défaut se trouve dans le répertoire &#039;&#039;/etc/systemd/system&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ll /etc/systemd/system/default.target&lt;br /&gt;
lrwxrwxrwx. 1 root root 37 26 déc.  20:29 /etc/systemd/system/default.target -&amp;gt; /lib/systemd/system/multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il suffit de remplacer le lien symbolique pour modifier la cible par défaut !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ln -fs /lib/systemd/system/graphical.target /etc/systemd/system/default.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ou, plus simple, on peut utiliser l&#039;option &#039;&#039;set-default&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl set-default graphical.target&lt;br /&gt;
Removed symlink /etc/systemd/system/default.target.&lt;br /&gt;
Created symlink from /etc/systemd/system/default.target to /usr/lib/systemd/system/graphical.target.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Comme avec &#039;&#039;init&#039;&#039; on évitera de mettre &#039;&#039;poweroff.target&#039;&#039; et &#039;&#039;reboot.target&#039;&#039; comme cible par défaut...&lt;br /&gt;
&lt;br /&gt;
== Gestion des points de montage ==&lt;br /&gt;
&#039;&#039;systemctl&#039;&#039; est capable de gérer les points de montage et de monter automatiquement un emplacement physique dans un répertoire.&lt;br /&gt;
Dans cet exemple, le disque &#039;&#039;sdb&#039;&#039; à déjà été [[Gestion_des_disques#Cr.C3.A9ation_d.27une_partition |partitionné]] et [[Gestion_des_disques#Formatage|formaté]], il ne reste plus qu&#039;à le monter. Traditionnellement nous aurions utilisé la commande [[Gestion_des_disques#mount | &#039;&#039;mount&#039;&#039;]] et modifié le fichier [[Gestion_des_disques#fstab |&#039;&#039;/etc/fstab&#039;&#039;]] pour rendre le point de montage persistant. &lt;br /&gt;
&lt;br /&gt;
Maintenant nous allons créer un fichier &#039;&#039;opt-sdb.mount&#039;&#039; dans le répertoire &#039;&#039;/etc/systemd/system/&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=Deuxième disque&lt;br /&gt;
&lt;br /&gt;
[Mount]&lt;br /&gt;
What=/dev/sdb1&lt;br /&gt;
Where=/opt/sdb&lt;br /&gt;
Type=ext4&lt;br /&gt;
Options=defaults&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=local-fs.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Maintenant que le fichier de montage est crée, on peut en informer &#039;&#039;systemctl&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl daemon-reload&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
et démarrer le point de montage :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start opt-sdb.mount&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rien ne nous empêche, comme pour les services, de l&#039;enregistrer au démarrage :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl enable opt-sdb.mount&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut même contrôler l&#039;état du point de montage :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl list-units --type=mount | grep opt-sdb&lt;br /&gt;
opt-sdb.mount           loaded active mounted Deuxième disque&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
{|style=&amp;quot;width:800px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning manual.jpg|centré|300px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Le nom du fichier doit &#039;&#039;&#039;absolument&#039;&#039;&#039; correspondre à la destination du point de montage sous peine d&#039;avoir l&#039;erreur suivante : &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;systemd[1]: sdb.mount&#039;s Where= setting doesn&#039;t match&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Par exemple, si votre point de montage est &#039;&#039;/opt/sdb&#039;&#039; le fichier &#039;&#039;&#039;doit&#039;&#039;&#039; s&#039;appeler &#039;&#039;opt-sdb.mount&#039;&#039; !&lt;br /&gt;
|}&lt;br /&gt;
= Journalisation =&lt;br /&gt;
Il est possible d&#039;afficher les logs grâce à la commande &#039;&#039;journalctl&#039;&#039;.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
! Description !! Commande &#039;&#039;journalctl&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||Affichage de tous les logs&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||Affichage des logs d&#039;un démon&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -u $daemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||Suivi des logs&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -f &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||Affichage des logs du kernel uniquement&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -k&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Systemctl&amp;diff=4186</id>
		<title>Systemctl</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Systemctl&amp;diff=4186"/>
		<updated>2025-12-29T04:27:12Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Explication de texte */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
== Déni, Colère, Expression, Dépression, Acceptation ==&lt;br /&gt;
Nous étions habitués aux commandes &#039;&#039;service&#039;&#039;, &#039;&#039;chkconfig&#039;&#039; ou encore &#039;&#039;init&#039;&#039; mais maintenant c&#039;est de l&#039;histoire ancienne puisque SysVInit à été remplacé par SystemD, le nouveau gestionnaire de démarrage écrit par RedHat. &lt;br /&gt;
&lt;br /&gt;
On peut se demander pourquoi on change un système qui fonctionne ? &lt;br /&gt;
&lt;br /&gt;
La raison est simple : SysVInit est lent, mal architecturé, possède des faiblesses. C&#039;en était trop pour certain et c&#039;est pourquoi, d&#039;une architecture modulaire (chacun sa tâche), nous sommes passé à une architecture centralisée (une seule commande). Ce nouveau &#039;&#039;moteur&#039;&#039; est écrit en &#039;&#039;C&#039;&#039; et permet un démarrage parallélisé des processus plutôt qu&#039;en série. &lt;br /&gt;
Les développeur d&#039;Ubuntu avaient commencé le travail en écrivant &#039;&#039;UStart&#039;&#039; mais n&#039;étaient pas allé jusqu&#039;au bout des choses ! &lt;br /&gt;
&lt;br /&gt;
Ci-dessous un dessin expliquant comment se déroule le démarrage du système :&lt;br /&gt;
[[Fichier:Comparing services start.png|centré|500px]]&lt;br /&gt;
&lt;br /&gt;
== Explication de texte ==&lt;br /&gt;
On voit clairement le gain de temps au démarrage du système ! Une question vient à l&#039;esprit, comment est-ce possible ?&amp;lt;br&amp;gt;&lt;br /&gt;
La réponse se trouve dans le type de socket utilisées pour la communication entres les services : on est passé de socket &#039;&#039;AF_INET&#039;&#039;, orienté réseaux, à des socket &#039;&#039;AF_UNIX&#039;&#039;, orientée système.&lt;br /&gt;
&lt;br /&gt;
Le premier type de socket, &#039;&#039;AF_INET&#039;&#039;, permet des connexions réseau entre les services, surtout utilisée dans les systèmes distribués, plus vraiment d&#039;actualité sur un système centralisé.&amp;lt;br&amp;gt;&lt;br /&gt;
Pour qu&#039;une communication réseau fonctionne, il faut attendre que le service que l&#039;on veut contacté soit démarré et en écoute sur le réseau... ce qui prend du temps !&lt;br /&gt;
&lt;br /&gt;
Les sockets &#039;&#039;AF_UNIX&#039;&#039; sont des fichiers sur le disque !&amp;lt;br&amp;gt;&lt;br /&gt;
Elles ne nécessitent pas la présence du service cible pour démarrer et sont donc créées par le système d&#039;exploitation au démarrage. Les services démarrent en parallèle et se &#039;&#039;branchent&#039;&#039; à la socket, c&#039;est à dire ouvre le fichier pour en lire le contenu, quand ils sont prêt.&lt;br /&gt;
&lt;br /&gt;
Ainsi, le démarrage des services est accéléré au détriment de la possibilité de les faire tourner sur une machine distante... on va s&#039;en remettre :)&lt;br /&gt;
&lt;br /&gt;
= Maintenant, que faire ? =&lt;br /&gt;
Ci-dessous un tableau qui fait le lien entre les anciennes et les nouvelles commandes :&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
! SysVInit!! SystemD !! Description&lt;br /&gt;
|-&lt;br /&gt;
||service $daemon start&lt;br /&gt;
||systemctl start $daemon.service&lt;br /&gt;
||Permet de démarrer $daemon&lt;br /&gt;
|-&lt;br /&gt;
||service $daemon  stop&lt;br /&gt;
||systemctl stop daemon.service&lt;br /&gt;
||Permet de stopper $daemon&lt;br /&gt;
|-&lt;br /&gt;
||service $daemon restart&lt;br /&gt;
||systemctl restart $daemon.service&lt;br /&gt;
||Permet de redémarrer $daemon&lt;br /&gt;
|-&lt;br /&gt;
||service $daemon reload&lt;br /&gt;
||systemctl reload $daemon.service&lt;br /&gt;
||Permet de recharger la configuration de $daemon&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|X&lt;br /&gt;
||systemctl reload-or-restart $daemon&lt;br /&gt;
||Permet de recharger ou redémarrer $daemon&lt;br /&gt;
|-&lt;br /&gt;
||service $daemon status&lt;br /&gt;
||systemctl status $daemon&lt;br /&gt;
||Affiche des informations sur le service $daemon&lt;br /&gt;
|-&lt;br /&gt;
||chkconfig $daemon on&lt;br /&gt;
||systemctl enable $daemon.service&lt;br /&gt;
||Permet d&#039;enregistrer $daemon au démarrage du système&lt;br /&gt;
|-&lt;br /&gt;
||chkconfig $daemon off&lt;br /&gt;
||systemctl disable $daemon.service&lt;br /&gt;
||Permet d&#039;effacer $daemon du démarrage du système&lt;br /&gt;
|-&lt;br /&gt;
||chkconfig $daemon&lt;br /&gt;
||systemctl is-enabled $daemon.service&lt;br /&gt;
||Permet de voir si $daemon est actif au démarrage du système&lt;br /&gt;
|-&lt;br /&gt;
||chkconfig $daemon --list&lt;br /&gt;
||systemctl list-units --type=service --all&lt;br /&gt;
||Permet de voir la liste des services et s&#039;ils sont actifs ou non au démarrage du système&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|X&lt;br /&gt;
||systemctl is-failed $daemon.service&lt;br /&gt;
||Retourne la valeur &#039;&#039;active&#039;&#039; si le service fonctionne sinon &#039;&#039;failed&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|X&lt;br /&gt;
||systemctl list-units [--all] [--state=inactive] [--type=service]&lt;br /&gt;
||Liste toutes les &#039;&#039;unités&#039;&#039; gérées par &#039;&#039;systemctl&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
Il est possible de ne pas ajouter le &#039;&#039;.service&#039;&#039;, &#039;&#039;systemctl&#039;&#039; comprendra très bien qu&#039;il s&#039;agit d&#039;un service !&lt;br /&gt;
&lt;br /&gt;
Nous allons maintenant passer en revue les unités que nous pouvons manipuler avec systemctl !&lt;br /&gt;
&lt;br /&gt;
= Gestion des unités =&lt;br /&gt;
== État des unités ==&lt;br /&gt;
&#039;&#039;systemctl&#039;&#039; peut afficher des informations sur les unités :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl list-units&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Et on peut appliquer des filtre sur cette commande :&lt;br /&gt;
* --type : pour spécifier le type d&#039;unité&lt;br /&gt;
** mount : les points de montage&lt;br /&gt;
** service : les démons&lt;br /&gt;
** target : les runlevel ou événements&lt;br /&gt;
** scope : les sessions actives&lt;br /&gt;
** device : les périphériques&lt;br /&gt;
* --state&lt;br /&gt;
** loaded : fichier de configuration lu par systemD&lt;br /&gt;
** active : l&#039;unité est active&lt;br /&gt;
** inactive : l&#039;unité des inactive&lt;br /&gt;
* --all : toutes les unités&lt;br /&gt;
Par exemple, pour lister les points de montage actif :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl list-units --type=mount --state=active&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== A distance ==&lt;br /&gt;
Toutes les commandes qui figure ici peuvent être exécutées à distance en ajoutant le paramètre &#039;&#039;-H @ip_machine&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Par exemple, pour vérifier le status du démon &#039;&#039;httpd&#039;&#039; sur l&#039;hôte 192.168.1.1 :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl -H 127.0.0.1 status httpd&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Configuration des unités ==&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||On peut afficher les fichiers de configuration des unités&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl list-unit-files&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Cette commande affiche tous les fichiers, même ceux des unités qui ne sont pas actuellement chargées !&lt;br /&gt;
|-&lt;br /&gt;
||Il est possible de voir la configuration d&#039;une unité&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl cat sshd.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||Il est possible d&#039;éditer la configuration d&#039;une unité en utilisant &#039;&#039;edit&#039;&#039;. Cela va créer un répertoire portant le nom de l&#039;unité et se terminant par un &#039;&#039;.d&#039;&#039;. Par exemple pour Apache, cela créer un répertoire &#039;&#039;/etc/systemd/system/httpd.service.d&#039;&#039;. Les informations présentes dans ce fichier vont venir surcharger les informations déjà présentes dans le fichier original. Pour modifier directement la configuration original, il faut utiliser l&#039;option &#039;&#039;--full&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Pour effacer une configuration additionnelle (surcharge), il suffit de supprimer le répertoire en &#039;&#039;.d&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Une fois les modifications faite, il ne faut pas oublier de recharger systemD !&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl edit sshd.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl edit sshd.service --full&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl daemon-reload&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||On peut également voir les dépendances d&#039;une unité&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl list-dependencies sshd.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||On peut également voir l&#039;inverse, c&#039;est à dire quand l&#039;unité est requise. On voit que le service sshd démarre en mode console (multi-user.target) et en mode graphique (graphical.target). Nous aurions, au temps de SysVInit, parlé des [[Gestionnaire_de_démarrage#runlevel.2C_kesako_.3F |runlevel]] 3 et 5.&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl list-dependencies --reverse sshd.service&lt;br /&gt;
sshd.service&lt;br /&gt;
● └─multi-user.target&lt;br /&gt;
●   └─graphical.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|| On peut demander les propriétés d&#039;une unité&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il est même possible de demander une propriété en particulier&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl show sshd.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl show sshd.service -p WantedBy&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||Il est possible de masquer des unités, c&#039;est à dire d&#039;empêcher le démarrage manuel et automatique de l&#039;unité. Il suffit alors de la démasquer pour pouvoir l&#039;utiliser à nouveau&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl mask httpd.service&lt;br /&gt;
Created symlink from /etc/systemd/system/httpd.service to /dev/null.&lt;br /&gt;
# systemctl start httpd.service&lt;br /&gt;
Failed to start httpd.service: Unit is masked.&lt;br /&gt;
# systemctl unmask httpd.service&lt;br /&gt;
Removed symlink /etc/systemd/system/httpd.service.&lt;br /&gt;
# systemctl start httpd.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Les cibles ==&lt;br /&gt;
===Aperçu===&lt;br /&gt;
Les cibles reprennent le concept des [[Gestionnaire_de_démarrage#runlevel.2C_kesako_.3F|&#039;&#039;runlevel&#039;&#039;]] de SysVInit :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# find / -name &amp;quot;runlevel*.target&amp;quot;&lt;br /&gt;
/usr/lib/systemd/system/runlevel5.target&lt;br /&gt;
/usr/lib/systemd/system/runlevel0.target&lt;br /&gt;
/usr/lib/systemd/system/runlevel6.target&lt;br /&gt;
/usr/lib/systemd/system/runlevel1.target&lt;br /&gt;
/usr/lib/systemd/system/runlevel2.target&lt;br /&gt;
/usr/lib/systemd/system/runlevel3.target&lt;br /&gt;
/usr/lib/systemd/system/runlevel4.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On voit qu&#039;il y en a 6 et voici leur utilité :&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
!Run Level!! Target (unité SystemD) !! Description&lt;br /&gt;
|-&lt;br /&gt;
||0&lt;br /&gt;
||runlevel0.target, poweroff.target&lt;br /&gt;
||Arrêt du système&lt;br /&gt;
|-&lt;br /&gt;
||1&lt;br /&gt;
||runlevel1.target, rescue.target&lt;br /&gt;
||Démarrage du système avec un shell de secours&lt;br /&gt;
|-&lt;br /&gt;
||2,3,4&lt;br /&gt;
||runlevel[2,3,4].target, multi-user.target&lt;br /&gt;
||Démarrage du système en mode console&lt;br /&gt;
|-&lt;br /&gt;
||5&lt;br /&gt;
||runlevel5.target, graphical.target&lt;br /&gt;
||Démarrage du système en mode graphique&lt;br /&gt;
|-&lt;br /&gt;
||6&lt;br /&gt;
||runlevel6.target, reboot.target&lt;br /&gt;
||Redémarrage du système&lt;br /&gt;
|}&lt;br /&gt;
On peut voir les liens symboliques qui sont fait sur les &#039;&#039;targets&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ls -l /usr/lib/systemd/system/*.target | grep runlevel&lt;br /&gt;
lrwxrwxrwx. 1 root root  15 26 déc.  20:23 /usr/lib/systemd/system/runlevel0.target -&amp;gt; poweroff.target&lt;br /&gt;
lrwxrwxrwx. 1 root root  13 26 déc.  20:23 /usr/lib/systemd/system/runlevel1.target -&amp;gt; rescue.target&lt;br /&gt;
lrwxrwxrwx. 1 root root  17 26 déc.  20:23 /usr/lib/systemd/system/runlevel2.target -&amp;gt; multi-user.target&lt;br /&gt;
lrwxrwxrwx. 1 root root  17 26 déc.  20:23 /usr/lib/systemd/system/runlevel3.target -&amp;gt; multi-user.target&lt;br /&gt;
lrwxrwxrwx. 1 root root  17 26 déc.  20:23 /usr/lib/systemd/system/runlevel4.target -&amp;gt; multi-user.target&lt;br /&gt;
lrwxrwxrwx. 1 root root  16 26 déc.  20:23 /usr/lib/systemd/system/runlevel5.target -&amp;gt; graphical.target&lt;br /&gt;
lrwxrwxrwx. 1 root root  13 26 déc.  20:23 /usr/lib/systemd/system/runlevel6.target -&amp;gt; reboot.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Cible courante===&lt;br /&gt;
Pour voir la &#039;&#039;target&#039;&#039; courante ou anciennement la commande &#039;&#039;runlevel&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl get-default&lt;br /&gt;
multi-user.target&lt;br /&gt;
# runlevel&lt;br /&gt;
N 3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Changement de cible===&lt;br /&gt;
Enfin, on peut changer la &#039;&#039;target&#039;&#039; courante grâce à l&#039;option &#039;&#039;isolate&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl isolate graphical.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Quand l&#039;option &#039;&#039;isolate&#039;&#039; est utilisée, le suffixe &#039;&#039;.target&#039;&#039; n&#039;est pas obligatoire.&lt;br /&gt;
&lt;br /&gt;
Certaine target on des racourcis :&lt;br /&gt;
* &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl isolate poweroff.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
équivaut à&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# poweroff&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl isolate reboot.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
équivaut à&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# reboot&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Changer la cible par défaut===&lt;br /&gt;
La cible par défaut se trouve dans le répertoire &#039;&#039;/etc/systemd/system&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ll /etc/systemd/system/default.target&lt;br /&gt;
lrwxrwxrwx. 1 root root 37 26 déc.  20:29 /etc/systemd/system/default.target -&amp;gt; /lib/systemd/system/multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il suffit de remplacer le lien symbolique pour modifier la cible par défaut !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ln -fs /lib/systemd/system/graphical.target /etc/systemd/system/default.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ou, plus simple, on peut utiliser l&#039;option &#039;&#039;set-default&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl set-default graphical.target&lt;br /&gt;
Removed symlink /etc/systemd/system/default.target.&lt;br /&gt;
Created symlink from /etc/systemd/system/default.target to /usr/lib/systemd/system/graphical.target.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Comme avec &#039;&#039;init&#039;&#039; on évitera de mettre &#039;&#039;poweroff.target&#039;&#039; et &#039;&#039;reboot.target&#039;&#039; comme cible par défaut...&lt;br /&gt;
&lt;br /&gt;
== Gestion des points de montage ==&lt;br /&gt;
&#039;&#039;systemctl&#039;&#039; est capable de gérer les points de montage et de monter automatiquement un emplacement physique dans un répertoire.&lt;br /&gt;
Dans cet exemple, le disque &#039;&#039;sdb&#039;&#039; à déjà été [[Gestion_des_disques#Cr.C3.A9ation_d.27une_partition |partitionné]] et [[Gestion_des_disques#Formatage|formaté]], il ne reste plus qu&#039;à le monter. Traditionnellement nous aurions utilisé la commande [[Gestion_des_disques#mount | &#039;&#039;mount&#039;&#039;]] et modifié le fichier [[Gestion_des_disques#fstab |&#039;&#039;/etc/fstab&#039;&#039;]] pour rendre le point de montage persistant. &lt;br /&gt;
&lt;br /&gt;
Maintenant nous allons créer un fichier &#039;&#039;opt-sdb.mount&#039;&#039; dans le répertoire &#039;&#039;/etc/systemd/system/&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=Deuxième disque&lt;br /&gt;
&lt;br /&gt;
[Mount]&lt;br /&gt;
What=/dev/sdb1&lt;br /&gt;
Where=/opt/sdb&lt;br /&gt;
Type=ext4&lt;br /&gt;
Options=defaults&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=local-fs.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Maintenant que le fichier de montage est crée, on peut en informer &#039;&#039;systemctl&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl daemon-reload&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
et démarrer le point de montage :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start opt-sdb.mount&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rien ne nous empêche, comme pour les services, de l&#039;enregistrer au démarrage :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl enable opt-sdb.mount&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut même contrôler l&#039;état du point de montage :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl list-units --type=mount | grep opt-sdb&lt;br /&gt;
opt-sdb.mount           loaded active mounted Deuxième disque&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
{|style=&amp;quot;width:800px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning manual.jpg|centré|300px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Le nom du fichier doit &#039;&#039;&#039;absolument&#039;&#039;&#039; correspondre à la destination du point de montage sous peine d&#039;avoir l&#039;erreur suivante : &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;systemd[1]: sdb.mount&#039;s Where= setting doesn&#039;t match&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Par exemple, si votre point de montage est &#039;&#039;/opt/sdb&#039;&#039; le fichier &#039;&#039;&#039;doit&#039;&#039;&#039; s&#039;appeler &#039;&#039;opt-sdb.mount&#039;&#039; !&lt;br /&gt;
|}&lt;br /&gt;
= Journalisation =&lt;br /&gt;
Il est possible d&#039;afficher les logs grâce à la commande &#039;&#039;journalctl&#039;&#039;.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
! Description !! Commande &#039;&#039;journalctl&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||Affichage de tous les logs&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||Affichage des logs d&#039;un démon&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -u $daemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||Suivi des logs&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -f &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||Affichage des logs du kernel uniquement&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -k&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4185</id>
		<title>Systemctl service</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4185"/>
		<updated>2025-12-24T04:18:15Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Utilisation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Dans cet article nous allons créer un service, très simplement, avec &#039;&#039;&#039;Systemctl&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Pour cela, nous allons d&#039;abord utiliser PHP pour &#039;&#039;fabriquer&#039;&#039; notre service, puis nous allons créer un fichier service pour automatiser son démarrage !&lt;br /&gt;
&lt;br /&gt;
= Le programme =&lt;br /&gt;
== Création ==&lt;br /&gt;
Le service que nous allons faire va simplement écrire le [https://fr.wikipedia.org/wiki/Heure_Unix temps unix] dans un fichier de journalisation pour que, une fois démarré et passé en tâche de fond, nous puissions nous assurer qu&#039;il tourne toujours !&lt;br /&gt;
&lt;br /&gt;
Dans le dossier &#039;&#039;/opt/php-service&#039;&#039; nous allons créer le fichier clock.php :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
define(&amp;quot;LOG_FILE&amp;quot;, &amp;quot;/tmp/clock.log&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
while(1){&lt;br /&gt;
    file_put_contents(LOG_FILE, time().&amp;quot;\n&amp;quot;, FILE_APPEND);&lt;br /&gt;
    sleep(2);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Le but de ce programme est d&#039;écrire, toutes les 2 secondes, la date dans le fichier &#039;&#039;/tmp/clock.log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Pas un démon ==&lt;br /&gt;
On peut remarquer que ce n&#039;est même pas un démon et qu&#039;il ne rend pas la main lorsque nous allons l’exécuter. &lt;br /&gt;
D’ailleurs il n&#039;utilise aucune des fonctions décrites dans [[Php daemon|cet article]] car nous allons demander à &#039;&#039;SystemD&#039;&#039; de s&#039;occuper de tout.&lt;br /&gt;
&lt;br /&gt;
== Test sans SystemD ==&lt;br /&gt;
Nous allons tester ce programme :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# php clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Puis dans un autre terminal, nous pouvons faire :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# tail -f /tmp/clock.php&lt;br /&gt;
1715971610&lt;br /&gt;
1715971612&lt;br /&gt;
1715971614&lt;br /&gt;
1715971616&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Préparation pour devenir un service ==&lt;br /&gt;
=== shebang===&lt;br /&gt;
Nous pourrions utiliser PHP à chaque fois que l&#039;on veut invoquer notre service mais on va plutôt ajouter le [https://fr.wikipedia.org/wiki/Shebang shebang] à la première ligne de notre fichier &#039;&#039;clock.php&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/usr/bin/php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le dossier de l&#039;exécutable de PHP peut changer en fonction des distributions Linux et, si vous avez un doute, n&#039;hésitez pas à utiliser la commande &#039;&#039;whereis&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# whereis php&lt;br /&gt;
php: /usr/bin/php /usr/lib64/php /usr/share/php /usr/share/man/man1/php.1.gz&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Droit d&#039;exécution ===&lt;br /&gt;
Maintenant que nous avons ajouté le shebang, &#039;&#039;bash&#039;&#039; va savoir comment exécuter notre programme et il ne nous reste plus qu&#039;a modifier ses droits pour le rendre exécutable :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# chmod +x clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut maintenant exécuter notre programme ainsi :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# ./clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ajout dans le path ===&lt;br /&gt;
Pour reproduire le même comportement que les autres programmes, il est possible d&#039;ajouter notre programme au path pour pouvoir l&#039;appeler grâce à l’autocomplétion. Pour cela, nous allons ajouter un lien symbolique dans le répertoire &#039;&#039;/usr/bin/&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# ln -fs /opt/php-service/clock.php /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le programme clock existant déjà, on rajoute &#039;&#039;-php&#039;&#039; à la fin pour ne pas écraser le programme original :). Si votre programme possède un nom unique sur le système, vous n&#039;avez pas besoin de faire ça !&lt;br /&gt;
&lt;br /&gt;
Maintenant de n&#039;importe où on peut utiliser l&#039;autocomplétion pour appeler notre programme :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# clock-php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Et même demander à whereis où il se trouve :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# whereis clock-php&lt;br /&gt;
clock-php: /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Nice !&lt;br /&gt;
&lt;br /&gt;
=== Création d&#039;un utilisateur et un groupe ===&lt;br /&gt;
Pour des raisons de sécurité, il est préférable que le programme ne tourne pas en tant que &#039;&#039;root&#039;&#039; mais en tant que simple utilisateur.&lt;br /&gt;
&lt;br /&gt;
Puis un utilisateur un peu spécial, qui ne peut pas se connecter et qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; : &lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# useradd -r -s /sbin/nologin clock-php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Notez que l&#039;option &#039;&#039;-r&#039;&#039; créer un utilisateur système, c&#039;est à dire qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; mais, cela s&#039;occupe également de la création du groupe associé (clock-php) et ajoute l&#039;utilisateur dans ce groupe !&lt;br /&gt;
&lt;br /&gt;
= Fichier de service systemd =&lt;br /&gt;
== Création ==&lt;br /&gt;
Maintenant que notre programme ressemble à un &#039;&#039;vrai&#039;&#039; programme, nous pouvons créer un fichier &#039;&#039;.service&#039;&#039; pour demander à systemd de s&#039;en occuper.&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;clock-php.service&#039;&#039; dans le même répertoire que le programme pour des raison de simplicité :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=clock-php daemon service&lt;br /&gt;
After=network-online.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=simple&lt;br /&gt;
&lt;br /&gt;
User=clock-php&lt;br /&gt;
Group=clock-php&lt;br /&gt;
UMask=007&lt;br /&gt;
&lt;br /&gt;
ExecStart=/usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
Restart=on-failure&lt;br /&gt;
&lt;br /&gt;
TimeoutStopSec=300&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explications ==&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|| Section || Directive || Description&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || Description || la description qui apparait avec la commande &#039;&#039;systemctl status clock-php&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || After || Nom de la cible après laquelle le service doit être démarré (et avant laquelle il doit être stoppé). Si vous voulez la liste exhaustive de toutes les cibles : &#039;&#039;systemctl list-units --type target&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Type || Type de notre service, peut notamment prendre les valeurs suivante :&lt;br /&gt;
* simple &amp;amp;rarr; systemd considère que le service démarre de suite (notre cas de figure)&lt;br /&gt;
* forking &amp;amp;rarr; systemd attend que le processus soit détaché du contexte d&#039;exécution courant et que le processus père ait rendu la main (soit terminé). Utile si on avait fait un fork comme mentionné dans [[Php daemon | cet article]]. Dans ce cas de figure il est intéressant de préciser le fichier de pid avec la directive &#039;&#039;&#039;PIDFile=&#039;&#039;&#039;&lt;br /&gt;
* oneshot &amp;amp;rarr; utile pour des script qui font un travail et rendent la main. Vous pouvez maintenir l&#039;état du service avec la directive &#039;&#039;&#039;RemainAfterExit=yes&#039;&#039;&#039; pour que systemd considère le service comme actif après l&#039;exécution du script.&lt;br /&gt;
* ...&lt;br /&gt;
|-&lt;br /&gt;
|| Service || User || l&#039;utilisateur utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Group || le groupe utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Umask || le masque utilisé pour la création de fichiers&lt;br /&gt;
|-&lt;br /&gt;
|| Service || ExecStart || Le chemin de l&#039;exécutable&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Restart || précise quand redémarrer le service :&lt;br /&gt;
* on-failure &amp;amp;rarr; quand le service échoue (code retour différent de zéro)&lt;br /&gt;
* always &amp;amp;rarr; dès que le service se termine indépendamment du code retour. C&#039;est utile si vous n&#039;avez pas de boucle qui retient le programme (while ou autre)&lt;br /&gt;
|-&lt;br /&gt;
|| Service || TimeoutStopSec || temps laissé au programme pour s’arrêter avant de le tuer (SIGKILL)&lt;br /&gt;
|-&lt;br /&gt;
|| Install || WantedBy || le niveau de démarrage du système dans lequel appelé le programme. Je vous laisse voir cette article à propos des [[Systemctl#Aper.C3.A7u | niveau de démarrage]] mais pour faire simple :&lt;br /&gt;
* multi-user.target &amp;amp;rarr; ligne de commande&lt;br /&gt;
* graphical.target &amp;amp;rarr; interface graphique&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Lien symbolique et activation ==&lt;br /&gt;
Maintenant que le fichier existe, nous allons créer un lien symbolique dans le répertoire &#039;&#039;/etc/systemd/system&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# systemctl link /opt/php-service/clock-php.service&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le service devrait être visible:&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# systemctl status clock-php.service&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
On peut maintenant utiliser le service avec les commandes traditionnelle :&lt;br /&gt;
* start&lt;br /&gt;
* stop&lt;br /&gt;
* status&lt;br /&gt;
* ...&lt;br /&gt;
Vous avez tous les détails sur systemd dans [[Systemctl | cet article]] mais pour résumer :&lt;br /&gt;
*On démarre donc le service avec :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# systemctl start clock-php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*On vérifie le status du service avec :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# systemctl status clock-php&lt;br /&gt;
● clock-php.service - clock-php daemon service&lt;br /&gt;
     Loaded: loaded (/etc/systemd/system/clock-php.service; alias)&lt;br /&gt;
     Active: active (running) since Sat 2024-05-18 01:07:46 CEST; 1s ago&lt;br /&gt;
   Main PID: 1487 (clock-php)&lt;br /&gt;
      Tasks: 1 (limit: 5904)&lt;br /&gt;
     Memory: 8.6M&lt;br /&gt;
        CPU: 27ms&lt;br /&gt;
     CGroup: /system.slice/clock-php.service&lt;br /&gt;
             └─1487 /usr/bin/php /usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
May 18 01:07:46 prog systemd[1]: Started clock-php daemon service.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*On peut même enregistrer le service au démarrage :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# systemctl enable clock-php&lt;br /&gt;
Created symlink /etc/systemd/system/multi-user.target.wants/clock-php.service → /opt/php-service/clock-php.service.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4184</id>
		<title>Systemctl service</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4184"/>
		<updated>2025-12-24T04:17:30Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Lien symbolique et activation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Dans cet article nous allons créer un service, très simplement, avec &#039;&#039;&#039;Systemctl&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Pour cela, nous allons d&#039;abord utiliser PHP pour &#039;&#039;fabriquer&#039;&#039; notre service, puis nous allons créer un fichier service pour automatiser son démarrage !&lt;br /&gt;
&lt;br /&gt;
= Le programme =&lt;br /&gt;
== Création ==&lt;br /&gt;
Le service que nous allons faire va simplement écrire le [https://fr.wikipedia.org/wiki/Heure_Unix temps unix] dans un fichier de journalisation pour que, une fois démarré et passé en tâche de fond, nous puissions nous assurer qu&#039;il tourne toujours !&lt;br /&gt;
&lt;br /&gt;
Dans le dossier &#039;&#039;/opt/php-service&#039;&#039; nous allons créer le fichier clock.php :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
define(&amp;quot;LOG_FILE&amp;quot;, &amp;quot;/tmp/clock.log&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
while(1){&lt;br /&gt;
    file_put_contents(LOG_FILE, time().&amp;quot;\n&amp;quot;, FILE_APPEND);&lt;br /&gt;
    sleep(2);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Le but de ce programme est d&#039;écrire, toutes les 2 secondes, la date dans le fichier &#039;&#039;/tmp/clock.log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Pas un démon ==&lt;br /&gt;
On peut remarquer que ce n&#039;est même pas un démon et qu&#039;il ne rend pas la main lorsque nous allons l’exécuter. &lt;br /&gt;
D’ailleurs il n&#039;utilise aucune des fonctions décrites dans [[Php daemon|cet article]] car nous allons demander à &#039;&#039;SystemD&#039;&#039; de s&#039;occuper de tout.&lt;br /&gt;
&lt;br /&gt;
== Test sans SystemD ==&lt;br /&gt;
Nous allons tester ce programme :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# php clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Puis dans un autre terminal, nous pouvons faire :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# tail -f /tmp/clock.php&lt;br /&gt;
1715971610&lt;br /&gt;
1715971612&lt;br /&gt;
1715971614&lt;br /&gt;
1715971616&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Préparation pour devenir un service ==&lt;br /&gt;
=== shebang===&lt;br /&gt;
Nous pourrions utiliser PHP à chaque fois que l&#039;on veut invoquer notre service mais on va plutôt ajouter le [https://fr.wikipedia.org/wiki/Shebang shebang] à la première ligne de notre fichier &#039;&#039;clock.php&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/usr/bin/php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le dossier de l&#039;exécutable de PHP peut changer en fonction des distributions Linux et, si vous avez un doute, n&#039;hésitez pas à utiliser la commande &#039;&#039;whereis&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# whereis php&lt;br /&gt;
php: /usr/bin/php /usr/lib64/php /usr/share/php /usr/share/man/man1/php.1.gz&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Droit d&#039;exécution ===&lt;br /&gt;
Maintenant que nous avons ajouté le shebang, &#039;&#039;bash&#039;&#039; va savoir comment exécuter notre programme et il ne nous reste plus qu&#039;a modifier ses droits pour le rendre exécutable :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# chmod +x clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut maintenant exécuter notre programme ainsi :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# ./clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ajout dans le path ===&lt;br /&gt;
Pour reproduire le même comportement que les autres programmes, il est possible d&#039;ajouter notre programme au path pour pouvoir l&#039;appeler grâce à l’autocomplétion. Pour cela, nous allons ajouter un lien symbolique dans le répertoire &#039;&#039;/usr/bin/&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# ln -fs /opt/php-service/clock.php /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le programme clock existant déjà, on rajoute &#039;&#039;-php&#039;&#039; à la fin pour ne pas écraser le programme original :). Si votre programme possède un nom unique sur le système, vous n&#039;avez pas besoin de faire ça !&lt;br /&gt;
&lt;br /&gt;
Maintenant de n&#039;importe où on peut utiliser l&#039;autocomplétion pour appeler notre programme :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# clock-php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Et même demander à whereis où il se trouve :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# whereis clock-php&lt;br /&gt;
clock-php: /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Nice !&lt;br /&gt;
&lt;br /&gt;
=== Création d&#039;un utilisateur et un groupe ===&lt;br /&gt;
Pour des raisons de sécurité, il est préférable que le programme ne tourne pas en tant que &#039;&#039;root&#039;&#039; mais en tant que simple utilisateur.&lt;br /&gt;
&lt;br /&gt;
Puis un utilisateur un peu spécial, qui ne peut pas se connecter et qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; : &lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# useradd -r -s /sbin/nologin clock-php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Notez que l&#039;option &#039;&#039;-r&#039;&#039; créer un utilisateur système, c&#039;est à dire qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; mais, cela s&#039;occupe également de la création du groupe associé (clock-php) et ajoute l&#039;utilisateur dans ce groupe !&lt;br /&gt;
&lt;br /&gt;
= Fichier de service systemd =&lt;br /&gt;
== Création ==&lt;br /&gt;
Maintenant que notre programme ressemble à un &#039;&#039;vrai&#039;&#039; programme, nous pouvons créer un fichier &#039;&#039;.service&#039;&#039; pour demander à systemd de s&#039;en occuper.&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;clock-php.service&#039;&#039; dans le même répertoire que le programme pour des raison de simplicité :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=clock-php daemon service&lt;br /&gt;
After=network-online.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=simple&lt;br /&gt;
&lt;br /&gt;
User=clock-php&lt;br /&gt;
Group=clock-php&lt;br /&gt;
UMask=007&lt;br /&gt;
&lt;br /&gt;
ExecStart=/usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
Restart=on-failure&lt;br /&gt;
&lt;br /&gt;
TimeoutStopSec=300&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explications ==&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|| Section || Directive || Description&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || Description || la description qui apparait avec la commande &#039;&#039;systemctl status clock-php&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || After || Nom de la cible après laquelle le service doit être démarré (et avant laquelle il doit être stoppé). Si vous voulez la liste exhaustive de toutes les cibles : &#039;&#039;systemctl list-units --type target&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Type || Type de notre service, peut notamment prendre les valeurs suivante :&lt;br /&gt;
* simple &amp;amp;rarr; systemd considère que le service démarre de suite (notre cas de figure)&lt;br /&gt;
* forking &amp;amp;rarr; systemd attend que le processus soit détaché du contexte d&#039;exécution courant et que le processus père ait rendu la main (soit terminé). Utile si on avait fait un fork comme mentionné dans [[Php daemon | cet article]]. Dans ce cas de figure il est intéressant de préciser le fichier de pid avec la directive &#039;&#039;&#039;PIDFile=&#039;&#039;&#039;&lt;br /&gt;
* oneshot &amp;amp;rarr; utile pour des script qui font un travail et rendent la main. Vous pouvez maintenir l&#039;état du service avec la directive &#039;&#039;&#039;RemainAfterExit=yes&#039;&#039;&#039; pour que systemd considère le service comme actif après l&#039;exécution du script.&lt;br /&gt;
* ...&lt;br /&gt;
|-&lt;br /&gt;
|| Service || User || l&#039;utilisateur utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Group || le groupe utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Umask || le masque utilisé pour la création de fichiers&lt;br /&gt;
|-&lt;br /&gt;
|| Service || ExecStart || Le chemin de l&#039;exécutable&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Restart || précise quand redémarrer le service :&lt;br /&gt;
* on-failure &amp;amp;rarr; quand le service échoue (code retour différent de zéro)&lt;br /&gt;
* always &amp;amp;rarr; dès que le service se termine indépendamment du code retour. C&#039;est utile si vous n&#039;avez pas de boucle qui retient le programme (while ou autre)&lt;br /&gt;
|-&lt;br /&gt;
|| Service || TimeoutStopSec || temps laissé au programme pour s’arrêter avant de le tuer (SIGKILL)&lt;br /&gt;
|-&lt;br /&gt;
|| Install || WantedBy || le niveau de démarrage du système dans lequel appelé le programme. Je vous laisse voir cette article à propos des [[Systemctl#Aper.C3.A7u | niveau de démarrage]] mais pour faire simple :&lt;br /&gt;
* multi-user.target &amp;amp;rarr; ligne de commande&lt;br /&gt;
* graphical.target &amp;amp;rarr; interface graphique&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Lien symbolique et activation ==&lt;br /&gt;
Maintenant que le fichier existe, nous allons créer un lien symbolique dans le répertoire &#039;&#039;/etc/systemd/system&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# systemctl link /opt/php-service/clock-php.service&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le service devrait être visible:&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# systemctl status clock-php.service&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
On peut maintenant utiliser le service avec les commandes traditionnelle :&lt;br /&gt;
* start&lt;br /&gt;
* stop&lt;br /&gt;
* status&lt;br /&gt;
* ...&lt;br /&gt;
Vous avez tous les détails sur systemd dans [[Systemctl | cet article]] mais pour résumer :&lt;br /&gt;
*On démarre donc le service avec :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*On vérifie le status du service avec :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl status clock-php&lt;br /&gt;
● clock-php.service - clock-php daemon service&lt;br /&gt;
     Loaded: loaded (/etc/systemd/system/clock-php.service; alias)&lt;br /&gt;
     Active: active (running) since Sat 2024-05-18 01:07:46 CEST; 1s ago&lt;br /&gt;
   Main PID: 1487 (clock-php)&lt;br /&gt;
      Tasks: 1 (limit: 5904)&lt;br /&gt;
     Memory: 8.6M&lt;br /&gt;
        CPU: 27ms&lt;br /&gt;
     CGroup: /system.slice/clock-php.service&lt;br /&gt;
             └─1487 /usr/bin/php /usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
May 18 01:07:46 prog systemd[1]: Started clock-php daemon service.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*On peut même enregistrer le service au démarrage :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl enable clock-php&lt;br /&gt;
Created symlink /etc/systemd/system/multi-user.target.wants/clock-php.service → /opt/php-service/clock-php.service.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4183</id>
		<title>Systemctl service</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4183"/>
		<updated>2025-12-24T04:17:04Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Création d&amp;#039;un utilisateur et un groupe */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Dans cet article nous allons créer un service, très simplement, avec &#039;&#039;&#039;Systemctl&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Pour cela, nous allons d&#039;abord utiliser PHP pour &#039;&#039;fabriquer&#039;&#039; notre service, puis nous allons créer un fichier service pour automatiser son démarrage !&lt;br /&gt;
&lt;br /&gt;
= Le programme =&lt;br /&gt;
== Création ==&lt;br /&gt;
Le service que nous allons faire va simplement écrire le [https://fr.wikipedia.org/wiki/Heure_Unix temps unix] dans un fichier de journalisation pour que, une fois démarré et passé en tâche de fond, nous puissions nous assurer qu&#039;il tourne toujours !&lt;br /&gt;
&lt;br /&gt;
Dans le dossier &#039;&#039;/opt/php-service&#039;&#039; nous allons créer le fichier clock.php :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
define(&amp;quot;LOG_FILE&amp;quot;, &amp;quot;/tmp/clock.log&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
while(1){&lt;br /&gt;
    file_put_contents(LOG_FILE, time().&amp;quot;\n&amp;quot;, FILE_APPEND);&lt;br /&gt;
    sleep(2);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Le but de ce programme est d&#039;écrire, toutes les 2 secondes, la date dans le fichier &#039;&#039;/tmp/clock.log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Pas un démon ==&lt;br /&gt;
On peut remarquer que ce n&#039;est même pas un démon et qu&#039;il ne rend pas la main lorsque nous allons l’exécuter. &lt;br /&gt;
D’ailleurs il n&#039;utilise aucune des fonctions décrites dans [[Php daemon|cet article]] car nous allons demander à &#039;&#039;SystemD&#039;&#039; de s&#039;occuper de tout.&lt;br /&gt;
&lt;br /&gt;
== Test sans SystemD ==&lt;br /&gt;
Nous allons tester ce programme :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# php clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Puis dans un autre terminal, nous pouvons faire :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# tail -f /tmp/clock.php&lt;br /&gt;
1715971610&lt;br /&gt;
1715971612&lt;br /&gt;
1715971614&lt;br /&gt;
1715971616&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Préparation pour devenir un service ==&lt;br /&gt;
=== shebang===&lt;br /&gt;
Nous pourrions utiliser PHP à chaque fois que l&#039;on veut invoquer notre service mais on va plutôt ajouter le [https://fr.wikipedia.org/wiki/Shebang shebang] à la première ligne de notre fichier &#039;&#039;clock.php&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/usr/bin/php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le dossier de l&#039;exécutable de PHP peut changer en fonction des distributions Linux et, si vous avez un doute, n&#039;hésitez pas à utiliser la commande &#039;&#039;whereis&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# whereis php&lt;br /&gt;
php: /usr/bin/php /usr/lib64/php /usr/share/php /usr/share/man/man1/php.1.gz&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Droit d&#039;exécution ===&lt;br /&gt;
Maintenant que nous avons ajouté le shebang, &#039;&#039;bash&#039;&#039; va savoir comment exécuter notre programme et il ne nous reste plus qu&#039;a modifier ses droits pour le rendre exécutable :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# chmod +x clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut maintenant exécuter notre programme ainsi :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# ./clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ajout dans le path ===&lt;br /&gt;
Pour reproduire le même comportement que les autres programmes, il est possible d&#039;ajouter notre programme au path pour pouvoir l&#039;appeler grâce à l’autocomplétion. Pour cela, nous allons ajouter un lien symbolique dans le répertoire &#039;&#039;/usr/bin/&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# ln -fs /opt/php-service/clock.php /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le programme clock existant déjà, on rajoute &#039;&#039;-php&#039;&#039; à la fin pour ne pas écraser le programme original :). Si votre programme possède un nom unique sur le système, vous n&#039;avez pas besoin de faire ça !&lt;br /&gt;
&lt;br /&gt;
Maintenant de n&#039;importe où on peut utiliser l&#039;autocomplétion pour appeler notre programme :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# clock-php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Et même demander à whereis où il se trouve :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# whereis clock-php&lt;br /&gt;
clock-php: /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Nice !&lt;br /&gt;
&lt;br /&gt;
=== Création d&#039;un utilisateur et un groupe ===&lt;br /&gt;
Pour des raisons de sécurité, il est préférable que le programme ne tourne pas en tant que &#039;&#039;root&#039;&#039; mais en tant que simple utilisateur.&lt;br /&gt;
&lt;br /&gt;
Puis un utilisateur un peu spécial, qui ne peut pas se connecter et qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; : &lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# useradd -r -s /sbin/nologin clock-php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Notez que l&#039;option &#039;&#039;-r&#039;&#039; créer un utilisateur système, c&#039;est à dire qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; mais, cela s&#039;occupe également de la création du groupe associé (clock-php) et ajoute l&#039;utilisateur dans ce groupe !&lt;br /&gt;
&lt;br /&gt;
= Fichier de service systemd =&lt;br /&gt;
== Création ==&lt;br /&gt;
Maintenant que notre programme ressemble à un &#039;&#039;vrai&#039;&#039; programme, nous pouvons créer un fichier &#039;&#039;.service&#039;&#039; pour demander à systemd de s&#039;en occuper.&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;clock-php.service&#039;&#039; dans le même répertoire que le programme pour des raison de simplicité :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=clock-php daemon service&lt;br /&gt;
After=network-online.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=simple&lt;br /&gt;
&lt;br /&gt;
User=clock-php&lt;br /&gt;
Group=clock-php&lt;br /&gt;
UMask=007&lt;br /&gt;
&lt;br /&gt;
ExecStart=/usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
Restart=on-failure&lt;br /&gt;
&lt;br /&gt;
TimeoutStopSec=300&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explications ==&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|| Section || Directive || Description&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || Description || la description qui apparait avec la commande &#039;&#039;systemctl status clock-php&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || After || Nom de la cible après laquelle le service doit être démarré (et avant laquelle il doit être stoppé). Si vous voulez la liste exhaustive de toutes les cibles : &#039;&#039;systemctl list-units --type target&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Type || Type de notre service, peut notamment prendre les valeurs suivante :&lt;br /&gt;
* simple &amp;amp;rarr; systemd considère que le service démarre de suite (notre cas de figure)&lt;br /&gt;
* forking &amp;amp;rarr; systemd attend que le processus soit détaché du contexte d&#039;exécution courant et que le processus père ait rendu la main (soit terminé). Utile si on avait fait un fork comme mentionné dans [[Php daemon | cet article]]. Dans ce cas de figure il est intéressant de préciser le fichier de pid avec la directive &#039;&#039;&#039;PIDFile=&#039;&#039;&#039;&lt;br /&gt;
* oneshot &amp;amp;rarr; utile pour des script qui font un travail et rendent la main. Vous pouvez maintenir l&#039;état du service avec la directive &#039;&#039;&#039;RemainAfterExit=yes&#039;&#039;&#039; pour que systemd considère le service comme actif après l&#039;exécution du script.&lt;br /&gt;
* ...&lt;br /&gt;
|-&lt;br /&gt;
|| Service || User || l&#039;utilisateur utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Group || le groupe utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Umask || le masque utilisé pour la création de fichiers&lt;br /&gt;
|-&lt;br /&gt;
|| Service || ExecStart || Le chemin de l&#039;exécutable&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Restart || précise quand redémarrer le service :&lt;br /&gt;
* on-failure &amp;amp;rarr; quand le service échoue (code retour différent de zéro)&lt;br /&gt;
* always &amp;amp;rarr; dès que le service se termine indépendamment du code retour. C&#039;est utile si vous n&#039;avez pas de boucle qui retient le programme (while ou autre)&lt;br /&gt;
|-&lt;br /&gt;
|| Service || TimeoutStopSec || temps laissé au programme pour s’arrêter avant de le tuer (SIGKILL)&lt;br /&gt;
|-&lt;br /&gt;
|| Install || WantedBy || le niveau de démarrage du système dans lequel appelé le programme. Je vous laisse voir cette article à propos des [[Systemctl#Aper.C3.A7u | niveau de démarrage]] mais pour faire simple :&lt;br /&gt;
* multi-user.target &amp;amp;rarr; ligne de commande&lt;br /&gt;
* graphical.target &amp;amp;rarr; interface graphique&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Lien symbolique et activation ==&lt;br /&gt;
Maintenant que le fichier existe, nous allons créer un lien symbolique dans le répertoire &#039;&#039;/etc/systemd/system&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl link /opt/php-service/clock-php.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le service devrait être visible:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl status clock-php.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
On peut maintenant utiliser le service avec les commandes traditionnelle :&lt;br /&gt;
* start&lt;br /&gt;
* stop&lt;br /&gt;
* status&lt;br /&gt;
* ...&lt;br /&gt;
Vous avez tous les détails sur systemd dans [[Systemctl | cet article]] mais pour résumer :&lt;br /&gt;
*On démarre donc le service avec :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*On vérifie le status du service avec :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl status clock-php&lt;br /&gt;
● clock-php.service - clock-php daemon service&lt;br /&gt;
     Loaded: loaded (/etc/systemd/system/clock-php.service; alias)&lt;br /&gt;
     Active: active (running) since Sat 2024-05-18 01:07:46 CEST; 1s ago&lt;br /&gt;
   Main PID: 1487 (clock-php)&lt;br /&gt;
      Tasks: 1 (limit: 5904)&lt;br /&gt;
     Memory: 8.6M&lt;br /&gt;
        CPU: 27ms&lt;br /&gt;
     CGroup: /system.slice/clock-php.service&lt;br /&gt;
             └─1487 /usr/bin/php /usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
May 18 01:07:46 prog systemd[1]: Started clock-php daemon service.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*On peut même enregistrer le service au démarrage :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl enable clock-php&lt;br /&gt;
Created symlink /etc/systemd/system/multi-user.target.wants/clock-php.service → /opt/php-service/clock-php.service.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4182</id>
		<title>Systemctl service</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4182"/>
		<updated>2025-12-24T04:16:42Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Ajout dans le path */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Dans cet article nous allons créer un service, très simplement, avec &#039;&#039;&#039;Systemctl&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Pour cela, nous allons d&#039;abord utiliser PHP pour &#039;&#039;fabriquer&#039;&#039; notre service, puis nous allons créer un fichier service pour automatiser son démarrage !&lt;br /&gt;
&lt;br /&gt;
= Le programme =&lt;br /&gt;
== Création ==&lt;br /&gt;
Le service que nous allons faire va simplement écrire le [https://fr.wikipedia.org/wiki/Heure_Unix temps unix] dans un fichier de journalisation pour que, une fois démarré et passé en tâche de fond, nous puissions nous assurer qu&#039;il tourne toujours !&lt;br /&gt;
&lt;br /&gt;
Dans le dossier &#039;&#039;/opt/php-service&#039;&#039; nous allons créer le fichier clock.php :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
define(&amp;quot;LOG_FILE&amp;quot;, &amp;quot;/tmp/clock.log&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
while(1){&lt;br /&gt;
    file_put_contents(LOG_FILE, time().&amp;quot;\n&amp;quot;, FILE_APPEND);&lt;br /&gt;
    sleep(2);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Le but de ce programme est d&#039;écrire, toutes les 2 secondes, la date dans le fichier &#039;&#039;/tmp/clock.log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Pas un démon ==&lt;br /&gt;
On peut remarquer que ce n&#039;est même pas un démon et qu&#039;il ne rend pas la main lorsque nous allons l’exécuter. &lt;br /&gt;
D’ailleurs il n&#039;utilise aucune des fonctions décrites dans [[Php daemon|cet article]] car nous allons demander à &#039;&#039;SystemD&#039;&#039; de s&#039;occuper de tout.&lt;br /&gt;
&lt;br /&gt;
== Test sans SystemD ==&lt;br /&gt;
Nous allons tester ce programme :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# php clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Puis dans un autre terminal, nous pouvons faire :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# tail -f /tmp/clock.php&lt;br /&gt;
1715971610&lt;br /&gt;
1715971612&lt;br /&gt;
1715971614&lt;br /&gt;
1715971616&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Préparation pour devenir un service ==&lt;br /&gt;
=== shebang===&lt;br /&gt;
Nous pourrions utiliser PHP à chaque fois que l&#039;on veut invoquer notre service mais on va plutôt ajouter le [https://fr.wikipedia.org/wiki/Shebang shebang] à la première ligne de notre fichier &#039;&#039;clock.php&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/usr/bin/php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le dossier de l&#039;exécutable de PHP peut changer en fonction des distributions Linux et, si vous avez un doute, n&#039;hésitez pas à utiliser la commande &#039;&#039;whereis&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# whereis php&lt;br /&gt;
php: /usr/bin/php /usr/lib64/php /usr/share/php /usr/share/man/man1/php.1.gz&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Droit d&#039;exécution ===&lt;br /&gt;
Maintenant que nous avons ajouté le shebang, &#039;&#039;bash&#039;&#039; va savoir comment exécuter notre programme et il ne nous reste plus qu&#039;a modifier ses droits pour le rendre exécutable :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# chmod +x clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut maintenant exécuter notre programme ainsi :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# ./clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ajout dans le path ===&lt;br /&gt;
Pour reproduire le même comportement que les autres programmes, il est possible d&#039;ajouter notre programme au path pour pouvoir l&#039;appeler grâce à l’autocomplétion. Pour cela, nous allons ajouter un lien symbolique dans le répertoire &#039;&#039;/usr/bin/&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# ln -fs /opt/php-service/clock.php /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le programme clock existant déjà, on rajoute &#039;&#039;-php&#039;&#039; à la fin pour ne pas écraser le programme original :). Si votre programme possède un nom unique sur le système, vous n&#039;avez pas besoin de faire ça !&lt;br /&gt;
&lt;br /&gt;
Maintenant de n&#039;importe où on peut utiliser l&#039;autocomplétion pour appeler notre programme :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# clock-php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Et même demander à whereis où il se trouve :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# whereis clock-php&lt;br /&gt;
clock-php: /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Nice !&lt;br /&gt;
&lt;br /&gt;
=== Création d&#039;un utilisateur et un groupe ===&lt;br /&gt;
Pour des raisons de sécurité, il est préférable que le programme ne tourne pas en tant que &#039;&#039;root&#039;&#039; mais en tant que simple utilisateur.&lt;br /&gt;
&lt;br /&gt;
Puis un utilisateur un peu spécial, qui ne peut pas se connecter et qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; : &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# useradd -r -s /sbin/nologin clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Notez que l&#039;option &#039;&#039;-r&#039;&#039; créer un utilisateur système, c&#039;est à dire qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; mais, cela s&#039;occupe également de la création du groupe associé (clock-php) et ajoute l&#039;utilisateur dans ce groupe !&lt;br /&gt;
&lt;br /&gt;
= Fichier de service systemd =&lt;br /&gt;
== Création ==&lt;br /&gt;
Maintenant que notre programme ressemble à un &#039;&#039;vrai&#039;&#039; programme, nous pouvons créer un fichier &#039;&#039;.service&#039;&#039; pour demander à systemd de s&#039;en occuper.&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;clock-php.service&#039;&#039; dans le même répertoire que le programme pour des raison de simplicité :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=clock-php daemon service&lt;br /&gt;
After=network-online.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=simple&lt;br /&gt;
&lt;br /&gt;
User=clock-php&lt;br /&gt;
Group=clock-php&lt;br /&gt;
UMask=007&lt;br /&gt;
&lt;br /&gt;
ExecStart=/usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
Restart=on-failure&lt;br /&gt;
&lt;br /&gt;
TimeoutStopSec=300&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explications ==&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|| Section || Directive || Description&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || Description || la description qui apparait avec la commande &#039;&#039;systemctl status clock-php&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || After || Nom de la cible après laquelle le service doit être démarré (et avant laquelle il doit être stoppé). Si vous voulez la liste exhaustive de toutes les cibles : &#039;&#039;systemctl list-units --type target&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Type || Type de notre service, peut notamment prendre les valeurs suivante :&lt;br /&gt;
* simple &amp;amp;rarr; systemd considère que le service démarre de suite (notre cas de figure)&lt;br /&gt;
* forking &amp;amp;rarr; systemd attend que le processus soit détaché du contexte d&#039;exécution courant et que le processus père ait rendu la main (soit terminé). Utile si on avait fait un fork comme mentionné dans [[Php daemon | cet article]]. Dans ce cas de figure il est intéressant de préciser le fichier de pid avec la directive &#039;&#039;&#039;PIDFile=&#039;&#039;&#039;&lt;br /&gt;
* oneshot &amp;amp;rarr; utile pour des script qui font un travail et rendent la main. Vous pouvez maintenir l&#039;état du service avec la directive &#039;&#039;&#039;RemainAfterExit=yes&#039;&#039;&#039; pour que systemd considère le service comme actif après l&#039;exécution du script.&lt;br /&gt;
* ...&lt;br /&gt;
|-&lt;br /&gt;
|| Service || User || l&#039;utilisateur utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Group || le groupe utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Umask || le masque utilisé pour la création de fichiers&lt;br /&gt;
|-&lt;br /&gt;
|| Service || ExecStart || Le chemin de l&#039;exécutable&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Restart || précise quand redémarrer le service :&lt;br /&gt;
* on-failure &amp;amp;rarr; quand le service échoue (code retour différent de zéro)&lt;br /&gt;
* always &amp;amp;rarr; dès que le service se termine indépendamment du code retour. C&#039;est utile si vous n&#039;avez pas de boucle qui retient le programme (while ou autre)&lt;br /&gt;
|-&lt;br /&gt;
|| Service || TimeoutStopSec || temps laissé au programme pour s’arrêter avant de le tuer (SIGKILL)&lt;br /&gt;
|-&lt;br /&gt;
|| Install || WantedBy || le niveau de démarrage du système dans lequel appelé le programme. Je vous laisse voir cette article à propos des [[Systemctl#Aper.C3.A7u | niveau de démarrage]] mais pour faire simple :&lt;br /&gt;
* multi-user.target &amp;amp;rarr; ligne de commande&lt;br /&gt;
* graphical.target &amp;amp;rarr; interface graphique&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Lien symbolique et activation ==&lt;br /&gt;
Maintenant que le fichier existe, nous allons créer un lien symbolique dans le répertoire &#039;&#039;/etc/systemd/system&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl link /opt/php-service/clock-php.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le service devrait être visible:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl status clock-php.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
On peut maintenant utiliser le service avec les commandes traditionnelle :&lt;br /&gt;
* start&lt;br /&gt;
* stop&lt;br /&gt;
* status&lt;br /&gt;
* ...&lt;br /&gt;
Vous avez tous les détails sur systemd dans [[Systemctl | cet article]] mais pour résumer :&lt;br /&gt;
*On démarre donc le service avec :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*On vérifie le status du service avec :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl status clock-php&lt;br /&gt;
● clock-php.service - clock-php daemon service&lt;br /&gt;
     Loaded: loaded (/etc/systemd/system/clock-php.service; alias)&lt;br /&gt;
     Active: active (running) since Sat 2024-05-18 01:07:46 CEST; 1s ago&lt;br /&gt;
   Main PID: 1487 (clock-php)&lt;br /&gt;
      Tasks: 1 (limit: 5904)&lt;br /&gt;
     Memory: 8.6M&lt;br /&gt;
        CPU: 27ms&lt;br /&gt;
     CGroup: /system.slice/clock-php.service&lt;br /&gt;
             └─1487 /usr/bin/php /usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
May 18 01:07:46 prog systemd[1]: Started clock-php daemon service.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*On peut même enregistrer le service au démarrage :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl enable clock-php&lt;br /&gt;
Created symlink /etc/systemd/system/multi-user.target.wants/clock-php.service → /opt/php-service/clock-php.service.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4181</id>
		<title>Systemctl service</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4181"/>
		<updated>2025-12-24T04:16:13Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Test sans SystemD */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Dans cet article nous allons créer un service, très simplement, avec &#039;&#039;&#039;Systemctl&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Pour cela, nous allons d&#039;abord utiliser PHP pour &#039;&#039;fabriquer&#039;&#039; notre service, puis nous allons créer un fichier service pour automatiser son démarrage !&lt;br /&gt;
&lt;br /&gt;
= Le programme =&lt;br /&gt;
== Création ==&lt;br /&gt;
Le service que nous allons faire va simplement écrire le [https://fr.wikipedia.org/wiki/Heure_Unix temps unix] dans un fichier de journalisation pour que, une fois démarré et passé en tâche de fond, nous puissions nous assurer qu&#039;il tourne toujours !&lt;br /&gt;
&lt;br /&gt;
Dans le dossier &#039;&#039;/opt/php-service&#039;&#039; nous allons créer le fichier clock.php :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
define(&amp;quot;LOG_FILE&amp;quot;, &amp;quot;/tmp/clock.log&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
while(1){&lt;br /&gt;
    file_put_contents(LOG_FILE, time().&amp;quot;\n&amp;quot;, FILE_APPEND);&lt;br /&gt;
    sleep(2);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Le but de ce programme est d&#039;écrire, toutes les 2 secondes, la date dans le fichier &#039;&#039;/tmp/clock.log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Pas un démon ==&lt;br /&gt;
On peut remarquer que ce n&#039;est même pas un démon et qu&#039;il ne rend pas la main lorsque nous allons l’exécuter. &lt;br /&gt;
D’ailleurs il n&#039;utilise aucune des fonctions décrites dans [[Php daemon|cet article]] car nous allons demander à &#039;&#039;SystemD&#039;&#039; de s&#039;occuper de tout.&lt;br /&gt;
&lt;br /&gt;
== Test sans SystemD ==&lt;br /&gt;
Nous allons tester ce programme :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# php clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Puis dans un autre terminal, nous pouvons faire :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# tail -f /tmp/clock.php&lt;br /&gt;
1715971610&lt;br /&gt;
1715971612&lt;br /&gt;
1715971614&lt;br /&gt;
1715971616&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Préparation pour devenir un service ==&lt;br /&gt;
=== shebang===&lt;br /&gt;
Nous pourrions utiliser PHP à chaque fois que l&#039;on veut invoquer notre service mais on va plutôt ajouter le [https://fr.wikipedia.org/wiki/Shebang shebang] à la première ligne de notre fichier &#039;&#039;clock.php&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/usr/bin/php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le dossier de l&#039;exécutable de PHP peut changer en fonction des distributions Linux et, si vous avez un doute, n&#039;hésitez pas à utiliser la commande &#039;&#039;whereis&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# whereis php&lt;br /&gt;
php: /usr/bin/php /usr/lib64/php /usr/share/php /usr/share/man/man1/php.1.gz&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Droit d&#039;exécution ===&lt;br /&gt;
Maintenant que nous avons ajouté le shebang, &#039;&#039;bash&#039;&#039; va savoir comment exécuter notre programme et il ne nous reste plus qu&#039;a modifier ses droits pour le rendre exécutable :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# chmod +x clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut maintenant exécuter notre programme ainsi :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# ./clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ajout dans le path ===&lt;br /&gt;
Pour reproduire le même comportement que les autres programmes, il est possible d&#039;ajouter notre programme au path pour pouvoir l&#039;appeler grâce à l’autocomplétion. Pour cela, nous allons ajouter un lien symbolique dans le répertoire &#039;&#039;/usr/bin/&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ln -fs /opt/php-service/clock.php /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le programme clock existant déjà, on rajoute &#039;&#039;-php&#039;&#039; à la fin pour ne pas écraser le programme original :). Si votre programme possède un nom unique sur le système, vous n&#039;avez pas besoin de faire ça !&lt;br /&gt;
&lt;br /&gt;
Maintenant de n&#039;importe où on peut utiliser l&#039;autocomplétion pour appeler notre programme :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Et même demander à whereis où il se trouve :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# whereis clock-php&lt;br /&gt;
clock-php: /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nice !&lt;br /&gt;
=== Création d&#039;un utilisateur et un groupe ===&lt;br /&gt;
Pour des raisons de sécurité, il est préférable que le programme ne tourne pas en tant que &#039;&#039;root&#039;&#039; mais en tant que simple utilisateur.&lt;br /&gt;
&lt;br /&gt;
Puis un utilisateur un peu spécial, qui ne peut pas se connecter et qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; : &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# useradd -r -s /sbin/nologin clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Notez que l&#039;option &#039;&#039;-r&#039;&#039; créer un utilisateur système, c&#039;est à dire qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; mais, cela s&#039;occupe également de la création du groupe associé (clock-php) et ajoute l&#039;utilisateur dans ce groupe !&lt;br /&gt;
&lt;br /&gt;
= Fichier de service systemd =&lt;br /&gt;
== Création ==&lt;br /&gt;
Maintenant que notre programme ressemble à un &#039;&#039;vrai&#039;&#039; programme, nous pouvons créer un fichier &#039;&#039;.service&#039;&#039; pour demander à systemd de s&#039;en occuper.&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;clock-php.service&#039;&#039; dans le même répertoire que le programme pour des raison de simplicité :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=clock-php daemon service&lt;br /&gt;
After=network-online.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=simple&lt;br /&gt;
&lt;br /&gt;
User=clock-php&lt;br /&gt;
Group=clock-php&lt;br /&gt;
UMask=007&lt;br /&gt;
&lt;br /&gt;
ExecStart=/usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
Restart=on-failure&lt;br /&gt;
&lt;br /&gt;
TimeoutStopSec=300&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explications ==&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|| Section || Directive || Description&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || Description || la description qui apparait avec la commande &#039;&#039;systemctl status clock-php&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || After || Nom de la cible après laquelle le service doit être démarré (et avant laquelle il doit être stoppé). Si vous voulez la liste exhaustive de toutes les cibles : &#039;&#039;systemctl list-units --type target&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Type || Type de notre service, peut notamment prendre les valeurs suivante :&lt;br /&gt;
* simple &amp;amp;rarr; systemd considère que le service démarre de suite (notre cas de figure)&lt;br /&gt;
* forking &amp;amp;rarr; systemd attend que le processus soit détaché du contexte d&#039;exécution courant et que le processus père ait rendu la main (soit terminé). Utile si on avait fait un fork comme mentionné dans [[Php daemon | cet article]]. Dans ce cas de figure il est intéressant de préciser le fichier de pid avec la directive &#039;&#039;&#039;PIDFile=&#039;&#039;&#039;&lt;br /&gt;
* oneshot &amp;amp;rarr; utile pour des script qui font un travail et rendent la main. Vous pouvez maintenir l&#039;état du service avec la directive &#039;&#039;&#039;RemainAfterExit=yes&#039;&#039;&#039; pour que systemd considère le service comme actif après l&#039;exécution du script.&lt;br /&gt;
* ...&lt;br /&gt;
|-&lt;br /&gt;
|| Service || User || l&#039;utilisateur utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Group || le groupe utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Umask || le masque utilisé pour la création de fichiers&lt;br /&gt;
|-&lt;br /&gt;
|| Service || ExecStart || Le chemin de l&#039;exécutable&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Restart || précise quand redémarrer le service :&lt;br /&gt;
* on-failure &amp;amp;rarr; quand le service échoue (code retour différent de zéro)&lt;br /&gt;
* always &amp;amp;rarr; dès que le service se termine indépendamment du code retour. C&#039;est utile si vous n&#039;avez pas de boucle qui retient le programme (while ou autre)&lt;br /&gt;
|-&lt;br /&gt;
|| Service || TimeoutStopSec || temps laissé au programme pour s’arrêter avant de le tuer (SIGKILL)&lt;br /&gt;
|-&lt;br /&gt;
|| Install || WantedBy || le niveau de démarrage du système dans lequel appelé le programme. Je vous laisse voir cette article à propos des [[Systemctl#Aper.C3.A7u | niveau de démarrage]] mais pour faire simple :&lt;br /&gt;
* multi-user.target &amp;amp;rarr; ligne de commande&lt;br /&gt;
* graphical.target &amp;amp;rarr; interface graphique&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Lien symbolique et activation ==&lt;br /&gt;
Maintenant que le fichier existe, nous allons créer un lien symbolique dans le répertoire &#039;&#039;/etc/systemd/system&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl link /opt/php-service/clock-php.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le service devrait être visible:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl status clock-php.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
On peut maintenant utiliser le service avec les commandes traditionnelle :&lt;br /&gt;
* start&lt;br /&gt;
* stop&lt;br /&gt;
* status&lt;br /&gt;
* ...&lt;br /&gt;
Vous avez tous les détails sur systemd dans [[Systemctl | cet article]] mais pour résumer :&lt;br /&gt;
*On démarre donc le service avec :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*On vérifie le status du service avec :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl status clock-php&lt;br /&gt;
● clock-php.service - clock-php daemon service&lt;br /&gt;
     Loaded: loaded (/etc/systemd/system/clock-php.service; alias)&lt;br /&gt;
     Active: active (running) since Sat 2024-05-18 01:07:46 CEST; 1s ago&lt;br /&gt;
   Main PID: 1487 (clock-php)&lt;br /&gt;
      Tasks: 1 (limit: 5904)&lt;br /&gt;
     Memory: 8.6M&lt;br /&gt;
        CPU: 27ms&lt;br /&gt;
     CGroup: /system.slice/clock-php.service&lt;br /&gt;
             └─1487 /usr/bin/php /usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
May 18 01:07:46 prog systemd[1]: Started clock-php daemon service.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*On peut même enregistrer le service au démarrage :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl enable clock-php&lt;br /&gt;
Created symlink /etc/systemd/system/multi-user.target.wants/clock-php.service → /opt/php-service/clock-php.service.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4180</id>
		<title>Systemctl service</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4180"/>
		<updated>2025-12-24T04:15:48Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Droit d&amp;#039;exécution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Dans cet article nous allons créer un service, très simplement, avec &#039;&#039;&#039;Systemctl&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Pour cela, nous allons d&#039;abord utiliser PHP pour &#039;&#039;fabriquer&#039;&#039; notre service, puis nous allons créer un fichier service pour automatiser son démarrage !&lt;br /&gt;
&lt;br /&gt;
= Le programme =&lt;br /&gt;
== Création ==&lt;br /&gt;
Le service que nous allons faire va simplement écrire le [https://fr.wikipedia.org/wiki/Heure_Unix temps unix] dans un fichier de journalisation pour que, une fois démarré et passé en tâche de fond, nous puissions nous assurer qu&#039;il tourne toujours !&lt;br /&gt;
&lt;br /&gt;
Dans le dossier &#039;&#039;/opt/php-service&#039;&#039; nous allons créer le fichier clock.php :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
define(&amp;quot;LOG_FILE&amp;quot;, &amp;quot;/tmp/clock.log&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
while(1){&lt;br /&gt;
    file_put_contents(LOG_FILE, time().&amp;quot;\n&amp;quot;, FILE_APPEND);&lt;br /&gt;
    sleep(2);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Le but de ce programme est d&#039;écrire, toutes les 2 secondes, la date dans le fichier &#039;&#039;/tmp/clock.log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Pas un démon ==&lt;br /&gt;
On peut remarquer que ce n&#039;est même pas un démon et qu&#039;il ne rend pas la main lorsque nous allons l’exécuter. &lt;br /&gt;
D’ailleurs il n&#039;utilise aucune des fonctions décrites dans [[Php daemon|cet article]] car nous allons demander à &#039;&#039;SystemD&#039;&#039; de s&#039;occuper de tout.&lt;br /&gt;
&lt;br /&gt;
== Test sans SystemD ==&lt;br /&gt;
Nous allons tester ce programme :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# php clock.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Puis dans un autre terminal, nous pouvons faire :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /tmp/clock.php&lt;br /&gt;
1715971610&lt;br /&gt;
1715971612&lt;br /&gt;
1715971614&lt;br /&gt;
1715971616&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Préparation pour devenir un service ==&lt;br /&gt;
=== shebang===&lt;br /&gt;
Nous pourrions utiliser PHP à chaque fois que l&#039;on veut invoquer notre service mais on va plutôt ajouter le [https://fr.wikipedia.org/wiki/Shebang shebang] à la première ligne de notre fichier &#039;&#039;clock.php&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/usr/bin/php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le dossier de l&#039;exécutable de PHP peut changer en fonction des distributions Linux et, si vous avez un doute, n&#039;hésitez pas à utiliser la commande &#039;&#039;whereis&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# whereis php&lt;br /&gt;
php: /usr/bin/php /usr/lib64/php /usr/share/php /usr/share/man/man1/php.1.gz&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Droit d&#039;exécution ===&lt;br /&gt;
Maintenant que nous avons ajouté le shebang, &#039;&#039;bash&#039;&#039; va savoir comment exécuter notre programme et il ne nous reste plus qu&#039;a modifier ses droits pour le rendre exécutable :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# chmod +x clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut maintenant exécuter notre programme ainsi :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# ./clock.php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ajout dans le path ===&lt;br /&gt;
Pour reproduire le même comportement que les autres programmes, il est possible d&#039;ajouter notre programme au path pour pouvoir l&#039;appeler grâce à l’autocomplétion. Pour cela, nous allons ajouter un lien symbolique dans le répertoire &#039;&#039;/usr/bin/&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ln -fs /opt/php-service/clock.php /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le programme clock existant déjà, on rajoute &#039;&#039;-php&#039;&#039; à la fin pour ne pas écraser le programme original :). Si votre programme possède un nom unique sur le système, vous n&#039;avez pas besoin de faire ça !&lt;br /&gt;
&lt;br /&gt;
Maintenant de n&#039;importe où on peut utiliser l&#039;autocomplétion pour appeler notre programme :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Et même demander à whereis où il se trouve :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# whereis clock-php&lt;br /&gt;
clock-php: /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nice !&lt;br /&gt;
=== Création d&#039;un utilisateur et un groupe ===&lt;br /&gt;
Pour des raisons de sécurité, il est préférable que le programme ne tourne pas en tant que &#039;&#039;root&#039;&#039; mais en tant que simple utilisateur.&lt;br /&gt;
&lt;br /&gt;
Puis un utilisateur un peu spécial, qui ne peut pas se connecter et qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; : &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# useradd -r -s /sbin/nologin clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Notez que l&#039;option &#039;&#039;-r&#039;&#039; créer un utilisateur système, c&#039;est à dire qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; mais, cela s&#039;occupe également de la création du groupe associé (clock-php) et ajoute l&#039;utilisateur dans ce groupe !&lt;br /&gt;
&lt;br /&gt;
= Fichier de service systemd =&lt;br /&gt;
== Création ==&lt;br /&gt;
Maintenant que notre programme ressemble à un &#039;&#039;vrai&#039;&#039; programme, nous pouvons créer un fichier &#039;&#039;.service&#039;&#039; pour demander à systemd de s&#039;en occuper.&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;clock-php.service&#039;&#039; dans le même répertoire que le programme pour des raison de simplicité :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=clock-php daemon service&lt;br /&gt;
After=network-online.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=simple&lt;br /&gt;
&lt;br /&gt;
User=clock-php&lt;br /&gt;
Group=clock-php&lt;br /&gt;
UMask=007&lt;br /&gt;
&lt;br /&gt;
ExecStart=/usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
Restart=on-failure&lt;br /&gt;
&lt;br /&gt;
TimeoutStopSec=300&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explications ==&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|| Section || Directive || Description&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || Description || la description qui apparait avec la commande &#039;&#039;systemctl status clock-php&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || After || Nom de la cible après laquelle le service doit être démarré (et avant laquelle il doit être stoppé). Si vous voulez la liste exhaustive de toutes les cibles : &#039;&#039;systemctl list-units --type target&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Type || Type de notre service, peut notamment prendre les valeurs suivante :&lt;br /&gt;
* simple &amp;amp;rarr; systemd considère que le service démarre de suite (notre cas de figure)&lt;br /&gt;
* forking &amp;amp;rarr; systemd attend que le processus soit détaché du contexte d&#039;exécution courant et que le processus père ait rendu la main (soit terminé). Utile si on avait fait un fork comme mentionné dans [[Php daemon | cet article]]. Dans ce cas de figure il est intéressant de préciser le fichier de pid avec la directive &#039;&#039;&#039;PIDFile=&#039;&#039;&#039;&lt;br /&gt;
* oneshot &amp;amp;rarr; utile pour des script qui font un travail et rendent la main. Vous pouvez maintenir l&#039;état du service avec la directive &#039;&#039;&#039;RemainAfterExit=yes&#039;&#039;&#039; pour que systemd considère le service comme actif après l&#039;exécution du script.&lt;br /&gt;
* ...&lt;br /&gt;
|-&lt;br /&gt;
|| Service || User || l&#039;utilisateur utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Group || le groupe utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Umask || le masque utilisé pour la création de fichiers&lt;br /&gt;
|-&lt;br /&gt;
|| Service || ExecStart || Le chemin de l&#039;exécutable&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Restart || précise quand redémarrer le service :&lt;br /&gt;
* on-failure &amp;amp;rarr; quand le service échoue (code retour différent de zéro)&lt;br /&gt;
* always &amp;amp;rarr; dès que le service se termine indépendamment du code retour. C&#039;est utile si vous n&#039;avez pas de boucle qui retient le programme (while ou autre)&lt;br /&gt;
|-&lt;br /&gt;
|| Service || TimeoutStopSec || temps laissé au programme pour s’arrêter avant de le tuer (SIGKILL)&lt;br /&gt;
|-&lt;br /&gt;
|| Install || WantedBy || le niveau de démarrage du système dans lequel appelé le programme. Je vous laisse voir cette article à propos des [[Systemctl#Aper.C3.A7u | niveau de démarrage]] mais pour faire simple :&lt;br /&gt;
* multi-user.target &amp;amp;rarr; ligne de commande&lt;br /&gt;
* graphical.target &amp;amp;rarr; interface graphique&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Lien symbolique et activation ==&lt;br /&gt;
Maintenant que le fichier existe, nous allons créer un lien symbolique dans le répertoire &#039;&#039;/etc/systemd/system&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl link /opt/php-service/clock-php.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le service devrait être visible:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl status clock-php.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
On peut maintenant utiliser le service avec les commandes traditionnelle :&lt;br /&gt;
* start&lt;br /&gt;
* stop&lt;br /&gt;
* status&lt;br /&gt;
* ...&lt;br /&gt;
Vous avez tous les détails sur systemd dans [[Systemctl | cet article]] mais pour résumer :&lt;br /&gt;
*On démarre donc le service avec :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*On vérifie le status du service avec :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl status clock-php&lt;br /&gt;
● clock-php.service - clock-php daemon service&lt;br /&gt;
     Loaded: loaded (/etc/systemd/system/clock-php.service; alias)&lt;br /&gt;
     Active: active (running) since Sat 2024-05-18 01:07:46 CEST; 1s ago&lt;br /&gt;
   Main PID: 1487 (clock-php)&lt;br /&gt;
      Tasks: 1 (limit: 5904)&lt;br /&gt;
     Memory: 8.6M&lt;br /&gt;
        CPU: 27ms&lt;br /&gt;
     CGroup: /system.slice/clock-php.service&lt;br /&gt;
             └─1487 /usr/bin/php /usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
May 18 01:07:46 prog systemd[1]: Started clock-php daemon service.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*On peut même enregistrer le service au démarrage :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl enable clock-php&lt;br /&gt;
Created symlink /etc/systemd/system/multi-user.target.wants/clock-php.service → /opt/php-service/clock-php.service.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4179</id>
		<title>Systemctl service</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4179"/>
		<updated>2025-12-24T04:15:21Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* shebang */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Dans cet article nous allons créer un service, très simplement, avec &#039;&#039;&#039;Systemctl&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Pour cela, nous allons d&#039;abord utiliser PHP pour &#039;&#039;fabriquer&#039;&#039; notre service, puis nous allons créer un fichier service pour automatiser son démarrage !&lt;br /&gt;
&lt;br /&gt;
= Le programme =&lt;br /&gt;
== Création ==&lt;br /&gt;
Le service que nous allons faire va simplement écrire le [https://fr.wikipedia.org/wiki/Heure_Unix temps unix] dans un fichier de journalisation pour que, une fois démarré et passé en tâche de fond, nous puissions nous assurer qu&#039;il tourne toujours !&lt;br /&gt;
&lt;br /&gt;
Dans le dossier &#039;&#039;/opt/php-service&#039;&#039; nous allons créer le fichier clock.php :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
define(&amp;quot;LOG_FILE&amp;quot;, &amp;quot;/tmp/clock.log&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
while(1){&lt;br /&gt;
    file_put_contents(LOG_FILE, time().&amp;quot;\n&amp;quot;, FILE_APPEND);&lt;br /&gt;
    sleep(2);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Le but de ce programme est d&#039;écrire, toutes les 2 secondes, la date dans le fichier &#039;&#039;/tmp/clock.log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Pas un démon ==&lt;br /&gt;
On peut remarquer que ce n&#039;est même pas un démon et qu&#039;il ne rend pas la main lorsque nous allons l’exécuter. &lt;br /&gt;
D’ailleurs il n&#039;utilise aucune des fonctions décrites dans [[Php daemon|cet article]] car nous allons demander à &#039;&#039;SystemD&#039;&#039; de s&#039;occuper de tout.&lt;br /&gt;
&lt;br /&gt;
== Test sans SystemD ==&lt;br /&gt;
Nous allons tester ce programme :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# php clock.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Puis dans un autre terminal, nous pouvons faire :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /tmp/clock.php&lt;br /&gt;
1715971610&lt;br /&gt;
1715971612&lt;br /&gt;
1715971614&lt;br /&gt;
1715971616&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Préparation pour devenir un service ==&lt;br /&gt;
=== shebang===&lt;br /&gt;
Nous pourrions utiliser PHP à chaque fois que l&#039;on veut invoquer notre service mais on va plutôt ajouter le [https://fr.wikipedia.org/wiki/Shebang shebang] à la première ligne de notre fichier &#039;&#039;clock.php&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/usr/bin/php&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le dossier de l&#039;exécutable de PHP peut changer en fonction des distributions Linux et, si vous avez un doute, n&#039;hésitez pas à utiliser la commande &#039;&#039;whereis&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
# whereis php&lt;br /&gt;
php: /usr/bin/php /usr/lib64/php /usr/share/php /usr/share/man/man1/php.1.gz&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Droit d&#039;exécution ===&lt;br /&gt;
Maintenant que nous avons ajouté le shebang, &#039;&#039;bash&#039;&#039; va savoir comment exécuter notre programme et il ne nous reste plus qu&#039;a modifier ses droits pour le rendre exécutable :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x clock.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut maintenant exécuter notre programme ainsi :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ./clock.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Ajout dans le path ===&lt;br /&gt;
Pour reproduire le même comportement que les autres programmes, il est possible d&#039;ajouter notre programme au path pour pouvoir l&#039;appeler grâce à l’autocomplétion. Pour cela, nous allons ajouter un lien symbolique dans le répertoire &#039;&#039;/usr/bin/&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ln -fs /opt/php-service/clock.php /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le programme clock existant déjà, on rajoute &#039;&#039;-php&#039;&#039; à la fin pour ne pas écraser le programme original :). Si votre programme possède un nom unique sur le système, vous n&#039;avez pas besoin de faire ça !&lt;br /&gt;
&lt;br /&gt;
Maintenant de n&#039;importe où on peut utiliser l&#039;autocomplétion pour appeler notre programme :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Et même demander à whereis où il se trouve :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# whereis clock-php&lt;br /&gt;
clock-php: /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nice !&lt;br /&gt;
=== Création d&#039;un utilisateur et un groupe ===&lt;br /&gt;
Pour des raisons de sécurité, il est préférable que le programme ne tourne pas en tant que &#039;&#039;root&#039;&#039; mais en tant que simple utilisateur.&lt;br /&gt;
&lt;br /&gt;
Puis un utilisateur un peu spécial, qui ne peut pas se connecter et qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; : &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# useradd -r -s /sbin/nologin clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Notez que l&#039;option &#039;&#039;-r&#039;&#039; créer un utilisateur système, c&#039;est à dire qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; mais, cela s&#039;occupe également de la création du groupe associé (clock-php) et ajoute l&#039;utilisateur dans ce groupe !&lt;br /&gt;
&lt;br /&gt;
= Fichier de service systemd =&lt;br /&gt;
== Création ==&lt;br /&gt;
Maintenant que notre programme ressemble à un &#039;&#039;vrai&#039;&#039; programme, nous pouvons créer un fichier &#039;&#039;.service&#039;&#039; pour demander à systemd de s&#039;en occuper.&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;clock-php.service&#039;&#039; dans le même répertoire que le programme pour des raison de simplicité :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=clock-php daemon service&lt;br /&gt;
After=network-online.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=simple&lt;br /&gt;
&lt;br /&gt;
User=clock-php&lt;br /&gt;
Group=clock-php&lt;br /&gt;
UMask=007&lt;br /&gt;
&lt;br /&gt;
ExecStart=/usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
Restart=on-failure&lt;br /&gt;
&lt;br /&gt;
TimeoutStopSec=300&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explications ==&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|| Section || Directive || Description&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || Description || la description qui apparait avec la commande &#039;&#039;systemctl status clock-php&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || After || Nom de la cible après laquelle le service doit être démarré (et avant laquelle il doit être stoppé). Si vous voulez la liste exhaustive de toutes les cibles : &#039;&#039;systemctl list-units --type target&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Type || Type de notre service, peut notamment prendre les valeurs suivante :&lt;br /&gt;
* simple &amp;amp;rarr; systemd considère que le service démarre de suite (notre cas de figure)&lt;br /&gt;
* forking &amp;amp;rarr; systemd attend que le processus soit détaché du contexte d&#039;exécution courant et que le processus père ait rendu la main (soit terminé). Utile si on avait fait un fork comme mentionné dans [[Php daemon | cet article]]. Dans ce cas de figure il est intéressant de préciser le fichier de pid avec la directive &#039;&#039;&#039;PIDFile=&#039;&#039;&#039;&lt;br /&gt;
* oneshot &amp;amp;rarr; utile pour des script qui font un travail et rendent la main. Vous pouvez maintenir l&#039;état du service avec la directive &#039;&#039;&#039;RemainAfterExit=yes&#039;&#039;&#039; pour que systemd considère le service comme actif après l&#039;exécution du script.&lt;br /&gt;
* ...&lt;br /&gt;
|-&lt;br /&gt;
|| Service || User || l&#039;utilisateur utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Group || le groupe utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Umask || le masque utilisé pour la création de fichiers&lt;br /&gt;
|-&lt;br /&gt;
|| Service || ExecStart || Le chemin de l&#039;exécutable&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Restart || précise quand redémarrer le service :&lt;br /&gt;
* on-failure &amp;amp;rarr; quand le service échoue (code retour différent de zéro)&lt;br /&gt;
* always &amp;amp;rarr; dès que le service se termine indépendamment du code retour. C&#039;est utile si vous n&#039;avez pas de boucle qui retient le programme (while ou autre)&lt;br /&gt;
|-&lt;br /&gt;
|| Service || TimeoutStopSec || temps laissé au programme pour s’arrêter avant de le tuer (SIGKILL)&lt;br /&gt;
|-&lt;br /&gt;
|| Install || WantedBy || le niveau de démarrage du système dans lequel appelé le programme. Je vous laisse voir cette article à propos des [[Systemctl#Aper.C3.A7u | niveau de démarrage]] mais pour faire simple :&lt;br /&gt;
* multi-user.target &amp;amp;rarr; ligne de commande&lt;br /&gt;
* graphical.target &amp;amp;rarr; interface graphique&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Lien symbolique et activation ==&lt;br /&gt;
Maintenant que le fichier existe, nous allons créer un lien symbolique dans le répertoire &#039;&#039;/etc/systemd/system&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl link /opt/php-service/clock-php.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le service devrait être visible:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl status clock-php.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
On peut maintenant utiliser le service avec les commandes traditionnelle :&lt;br /&gt;
* start&lt;br /&gt;
* stop&lt;br /&gt;
* status&lt;br /&gt;
* ...&lt;br /&gt;
Vous avez tous les détails sur systemd dans [[Systemctl | cet article]] mais pour résumer :&lt;br /&gt;
*On démarre donc le service avec :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*On vérifie le status du service avec :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl status clock-php&lt;br /&gt;
● clock-php.service - clock-php daemon service&lt;br /&gt;
     Loaded: loaded (/etc/systemd/system/clock-php.service; alias)&lt;br /&gt;
     Active: active (running) since Sat 2024-05-18 01:07:46 CEST; 1s ago&lt;br /&gt;
   Main PID: 1487 (clock-php)&lt;br /&gt;
      Tasks: 1 (limit: 5904)&lt;br /&gt;
     Memory: 8.6M&lt;br /&gt;
        CPU: 27ms&lt;br /&gt;
     CGroup: /system.slice/clock-php.service&lt;br /&gt;
             └─1487 /usr/bin/php /usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
May 18 01:07:46 prog systemd[1]: Started clock-php daemon service.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*On peut même enregistrer le service au démarrage :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl enable clock-php&lt;br /&gt;
Created symlink /etc/systemd/system/multi-user.target.wants/clock-php.service → /opt/php-service/clock-php.service.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4178</id>
		<title>Systemctl service</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4178"/>
		<updated>2025-12-24T03:33:29Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Lien symbolique et activation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Dans cet article nous allons créer un service, très simplement, avec &#039;&#039;&#039;Systemctl&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Pour cela, nous allons d&#039;abord utiliser PHP pour &#039;&#039;fabriquer&#039;&#039; notre service, puis nous allons créer un fichier service pour automatiser son démarrage !&lt;br /&gt;
&lt;br /&gt;
= Le programme =&lt;br /&gt;
== Création ==&lt;br /&gt;
Le service que nous allons faire va simplement écrire le [https://fr.wikipedia.org/wiki/Heure_Unix temps unix] dans un fichier de journalisation pour que, une fois démarré et passé en tâche de fond, nous puissions nous assurer qu&#039;il tourne toujours !&lt;br /&gt;
&lt;br /&gt;
Dans le dossier &#039;&#039;/opt/php-service&#039;&#039; nous allons créer le fichier clock.php :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
define(&amp;quot;LOG_FILE&amp;quot;, &amp;quot;/tmp/clock.log&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
while(1){&lt;br /&gt;
    file_put_contents(LOG_FILE, time().&amp;quot;\n&amp;quot;, FILE_APPEND);&lt;br /&gt;
    sleep(2);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Le but de ce programme est d&#039;écrire, toutes les 2 secondes, la date dans le fichier &#039;&#039;/tmp/clock.log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Pas un démon ==&lt;br /&gt;
On peut remarquer que ce n&#039;est même pas un démon et qu&#039;il ne rend pas la main lorsque nous allons l’exécuter. &lt;br /&gt;
D’ailleurs il n&#039;utilise aucune des fonctions décrites dans [[Php daemon|cet article]] car nous allons demander à &#039;&#039;SystemD&#039;&#039; de s&#039;occuper de tout.&lt;br /&gt;
&lt;br /&gt;
== Test sans SystemD ==&lt;br /&gt;
Nous allons tester ce programme :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# php clock.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Puis dans un autre terminal, nous pouvons faire :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /tmp/clock.php&lt;br /&gt;
1715971610&lt;br /&gt;
1715971612&lt;br /&gt;
1715971614&lt;br /&gt;
1715971616&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Préparation pour devenir un service ==&lt;br /&gt;
=== shebang===&lt;br /&gt;
Nous pourrions utiliser PHP à chaque fois que l&#039;on veut invoquer notre service mais on va plutôt ajouter le [https://fr.wikipedia.org/wiki/Shebang shebang] à la première ligne de notre fichier &#039;&#039;clock.php&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/usr/bin/php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le dossier de l&#039;exécutable de PHP peut changer en fonction des distributions Linux et, si vous avez un doute, n&#039;hésitez pas à utiliser la commande &#039;&#039;whereis&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# whereis php&lt;br /&gt;
php: /usr/bin/php /usr/lib64/php /usr/share/php /usr/share/man/man1/php.1.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Droit d&#039;exécution ===&lt;br /&gt;
Maintenant que nous avons ajouté le shebang, &#039;&#039;bash&#039;&#039; va savoir comment exécuter notre programme et il ne nous reste plus qu&#039;a modifier ses droits pour le rendre exécutable :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x clock.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut maintenant exécuter notre programme ainsi :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ./clock.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Ajout dans le path ===&lt;br /&gt;
Pour reproduire le même comportement que les autres programmes, il est possible d&#039;ajouter notre programme au path pour pouvoir l&#039;appeler grâce à l’autocomplétion. Pour cela, nous allons ajouter un lien symbolique dans le répertoire &#039;&#039;/usr/bin/&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ln -fs /opt/php-service/clock.php /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le programme clock existant déjà, on rajoute &#039;&#039;-php&#039;&#039; à la fin pour ne pas écraser le programme original :). Si votre programme possède un nom unique sur le système, vous n&#039;avez pas besoin de faire ça !&lt;br /&gt;
&lt;br /&gt;
Maintenant de n&#039;importe où on peut utiliser l&#039;autocomplétion pour appeler notre programme :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Et même demander à whereis où il se trouve :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# whereis clock-php&lt;br /&gt;
clock-php: /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nice !&lt;br /&gt;
=== Création d&#039;un utilisateur et un groupe ===&lt;br /&gt;
Pour des raisons de sécurité, il est préférable que le programme ne tourne pas en tant que &#039;&#039;root&#039;&#039; mais en tant que simple utilisateur.&lt;br /&gt;
&lt;br /&gt;
Puis un utilisateur un peu spécial, qui ne peut pas se connecter et qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; : &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# useradd -r -s /sbin/nologin clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Notez que l&#039;option &#039;&#039;-r&#039;&#039; créer un utilisateur système, c&#039;est à dire qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; mais, cela s&#039;occupe également de la création du groupe associé (clock-php) et ajoute l&#039;utilisateur dans ce groupe !&lt;br /&gt;
&lt;br /&gt;
= Fichier de service systemd =&lt;br /&gt;
== Création ==&lt;br /&gt;
Maintenant que notre programme ressemble à un &#039;&#039;vrai&#039;&#039; programme, nous pouvons créer un fichier &#039;&#039;.service&#039;&#039; pour demander à systemd de s&#039;en occuper.&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;clock-php.service&#039;&#039; dans le même répertoire que le programme pour des raison de simplicité :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=clock-php daemon service&lt;br /&gt;
After=network-online.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=simple&lt;br /&gt;
&lt;br /&gt;
User=clock-php&lt;br /&gt;
Group=clock-php&lt;br /&gt;
UMask=007&lt;br /&gt;
&lt;br /&gt;
ExecStart=/usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
Restart=on-failure&lt;br /&gt;
&lt;br /&gt;
TimeoutStopSec=300&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explications ==&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|| Section || Directive || Description&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || Description || la description qui apparait avec la commande &#039;&#039;systemctl status clock-php&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || After || Nom de la cible après laquelle le service doit être démarré (et avant laquelle il doit être stoppé). Si vous voulez la liste exhaustive de toutes les cibles : &#039;&#039;systemctl list-units --type target&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Type || Type de notre service, peut notamment prendre les valeurs suivante :&lt;br /&gt;
* simple &amp;amp;rarr; systemd considère que le service démarre de suite (notre cas de figure)&lt;br /&gt;
* forking &amp;amp;rarr; systemd attend que le processus soit détaché du contexte d&#039;exécution courant et que le processus père ait rendu la main (soit terminé). Utile si on avait fait un fork comme mentionné dans [[Php daemon | cet article]]. Dans ce cas de figure il est intéressant de préciser le fichier de pid avec la directive &#039;&#039;&#039;PIDFile=&#039;&#039;&#039;&lt;br /&gt;
* oneshot &amp;amp;rarr; utile pour des script qui font un travail et rendent la main. Vous pouvez maintenir l&#039;état du service avec la directive &#039;&#039;&#039;RemainAfterExit=yes&#039;&#039;&#039; pour que systemd considère le service comme actif après l&#039;exécution du script.&lt;br /&gt;
* ...&lt;br /&gt;
|-&lt;br /&gt;
|| Service || User || l&#039;utilisateur utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Group || le groupe utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Umask || le masque utilisé pour la création de fichiers&lt;br /&gt;
|-&lt;br /&gt;
|| Service || ExecStart || Le chemin de l&#039;exécutable&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Restart || précise quand redémarrer le service :&lt;br /&gt;
* on-failure &amp;amp;rarr; quand le service échoue (code retour différent de zéro)&lt;br /&gt;
* always &amp;amp;rarr; dès que le service se termine indépendamment du code retour. C&#039;est utile si vous n&#039;avez pas de boucle qui retient le programme (while ou autre)&lt;br /&gt;
|-&lt;br /&gt;
|| Service || TimeoutStopSec || temps laissé au programme pour s’arrêter avant de le tuer (SIGKILL)&lt;br /&gt;
|-&lt;br /&gt;
|| Install || WantedBy || le niveau de démarrage du système dans lequel appelé le programme. Je vous laisse voir cette article à propos des [[Systemctl#Aper.C3.A7u | niveau de démarrage]] mais pour faire simple :&lt;br /&gt;
* multi-user.target &amp;amp;rarr; ligne de commande&lt;br /&gt;
* graphical.target &amp;amp;rarr; interface graphique&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Lien symbolique et activation ==&lt;br /&gt;
Maintenant que le fichier existe, nous allons créer un lien symbolique dans le répertoire &#039;&#039;/etc/systemd/system&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl link /opt/php-service/clock-php.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le service devrait être visible:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl status clock-php.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
On peut maintenant utiliser le service avec les commandes traditionnelle :&lt;br /&gt;
* start&lt;br /&gt;
* stop&lt;br /&gt;
* status&lt;br /&gt;
* ...&lt;br /&gt;
Vous avez tous les détails sur systemd dans [[Systemctl | cet article]] mais pour résumer :&lt;br /&gt;
*On démarre donc le service avec :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*On vérifie le status du service avec :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl status clock-php&lt;br /&gt;
● clock-php.service - clock-php daemon service&lt;br /&gt;
     Loaded: loaded (/etc/systemd/system/clock-php.service; alias)&lt;br /&gt;
     Active: active (running) since Sat 2024-05-18 01:07:46 CEST; 1s ago&lt;br /&gt;
   Main PID: 1487 (clock-php)&lt;br /&gt;
      Tasks: 1 (limit: 5904)&lt;br /&gt;
     Memory: 8.6M&lt;br /&gt;
        CPU: 27ms&lt;br /&gt;
     CGroup: /system.slice/clock-php.service&lt;br /&gt;
             └─1487 /usr/bin/php /usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
May 18 01:07:46 prog systemd[1]: Started clock-php daemon service.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*On peut même enregistrer le service au démarrage :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl enable clock-php&lt;br /&gt;
Created symlink /etc/systemd/system/multi-user.target.wants/clock-php.service → /opt/php-service/clock-php.service.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4177</id>
		<title>Systemctl service</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Systemctl_service&amp;diff=4177"/>
		<updated>2025-12-24T03:33:17Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Lien symbolique et activation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Dans cet article nous allons créer un service, très simplement, avec &#039;&#039;&#039;Systemctl&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Pour cela, nous allons d&#039;abord utiliser PHP pour &#039;&#039;fabriquer&#039;&#039; notre service, puis nous allons créer un fichier service pour automatiser son démarrage !&lt;br /&gt;
&lt;br /&gt;
= Le programme =&lt;br /&gt;
== Création ==&lt;br /&gt;
Le service que nous allons faire va simplement écrire le [https://fr.wikipedia.org/wiki/Heure_Unix temps unix] dans un fichier de journalisation pour que, une fois démarré et passé en tâche de fond, nous puissions nous assurer qu&#039;il tourne toujours !&lt;br /&gt;
&lt;br /&gt;
Dans le dossier &#039;&#039;/opt/php-service&#039;&#039; nous allons créer le fichier clock.php :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
define(&amp;quot;LOG_FILE&amp;quot;, &amp;quot;/tmp/clock.log&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
while(1){&lt;br /&gt;
    file_put_contents(LOG_FILE, time().&amp;quot;\n&amp;quot;, FILE_APPEND);&lt;br /&gt;
    sleep(2);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Le but de ce programme est d&#039;écrire, toutes les 2 secondes, la date dans le fichier &#039;&#039;/tmp/clock.log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Pas un démon ==&lt;br /&gt;
On peut remarquer que ce n&#039;est même pas un démon et qu&#039;il ne rend pas la main lorsque nous allons l’exécuter. &lt;br /&gt;
D’ailleurs il n&#039;utilise aucune des fonctions décrites dans [[Php daemon|cet article]] car nous allons demander à &#039;&#039;SystemD&#039;&#039; de s&#039;occuper de tout.&lt;br /&gt;
&lt;br /&gt;
== Test sans SystemD ==&lt;br /&gt;
Nous allons tester ce programme :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# php clock.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Puis dans un autre terminal, nous pouvons faire :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /tmp/clock.php&lt;br /&gt;
1715971610&lt;br /&gt;
1715971612&lt;br /&gt;
1715971614&lt;br /&gt;
1715971616&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Préparation pour devenir un service ==&lt;br /&gt;
=== shebang===&lt;br /&gt;
Nous pourrions utiliser PHP à chaque fois que l&#039;on veut invoquer notre service mais on va plutôt ajouter le [https://fr.wikipedia.org/wiki/Shebang shebang] à la première ligne de notre fichier &#039;&#039;clock.php&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/usr/bin/php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le dossier de l&#039;exécutable de PHP peut changer en fonction des distributions Linux et, si vous avez un doute, n&#039;hésitez pas à utiliser la commande &#039;&#039;whereis&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# whereis php&lt;br /&gt;
php: /usr/bin/php /usr/lib64/php /usr/share/php /usr/share/man/man1/php.1.gz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Droit d&#039;exécution ===&lt;br /&gt;
Maintenant que nous avons ajouté le shebang, &#039;&#039;bash&#039;&#039; va savoir comment exécuter notre programme et il ne nous reste plus qu&#039;a modifier ses droits pour le rendre exécutable :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x clock.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut maintenant exécuter notre programme ainsi :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ./clock.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Ajout dans le path ===&lt;br /&gt;
Pour reproduire le même comportement que les autres programmes, il est possible d&#039;ajouter notre programme au path pour pouvoir l&#039;appeler grâce à l’autocomplétion. Pour cela, nous allons ajouter un lien symbolique dans le répertoire &#039;&#039;/usr/bin/&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ln -fs /opt/php-service/clock.php /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le programme clock existant déjà, on rajoute &#039;&#039;-php&#039;&#039; à la fin pour ne pas écraser le programme original :). Si votre programme possède un nom unique sur le système, vous n&#039;avez pas besoin de faire ça !&lt;br /&gt;
&lt;br /&gt;
Maintenant de n&#039;importe où on peut utiliser l&#039;autocomplétion pour appeler notre programme :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Et même demander à whereis où il se trouve :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# whereis clock-php&lt;br /&gt;
clock-php: /usr/bin/clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nice !&lt;br /&gt;
=== Création d&#039;un utilisateur et un groupe ===&lt;br /&gt;
Pour des raisons de sécurité, il est préférable que le programme ne tourne pas en tant que &#039;&#039;root&#039;&#039; mais en tant que simple utilisateur.&lt;br /&gt;
&lt;br /&gt;
Puis un utilisateur un peu spécial, qui ne peut pas se connecter et qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; : &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# useradd -r -s /sbin/nologin clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Notez que l&#039;option &#039;&#039;-r&#039;&#039; créer un utilisateur système, c&#039;est à dire qui n&#039;a pas de répertoire &#039;&#039;home&#039;&#039; mais, cela s&#039;occupe également de la création du groupe associé (clock-php) et ajoute l&#039;utilisateur dans ce groupe !&lt;br /&gt;
&lt;br /&gt;
= Fichier de service systemd =&lt;br /&gt;
== Création ==&lt;br /&gt;
Maintenant que notre programme ressemble à un &#039;&#039;vrai&#039;&#039; programme, nous pouvons créer un fichier &#039;&#039;.service&#039;&#039; pour demander à systemd de s&#039;en occuper.&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;clock-php.service&#039;&#039; dans le même répertoire que le programme pour des raison de simplicité :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=clock-php daemon service&lt;br /&gt;
After=network-online.target&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=simple&lt;br /&gt;
&lt;br /&gt;
User=clock-php&lt;br /&gt;
Group=clock-php&lt;br /&gt;
UMask=007&lt;br /&gt;
&lt;br /&gt;
ExecStart=/usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
Restart=on-failure&lt;br /&gt;
&lt;br /&gt;
TimeoutStopSec=300&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explications ==&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;85%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|| Section || Directive || Description&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || Description || la description qui apparait avec la commande &#039;&#039;systemctl status clock-php&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Unit || After || Nom de la cible après laquelle le service doit être démarré (et avant laquelle il doit être stoppé). Si vous voulez la liste exhaustive de toutes les cibles : &#039;&#039;systemctl list-units --type target&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Type || Type de notre service, peut notamment prendre les valeurs suivante :&lt;br /&gt;
* simple &amp;amp;rarr; systemd considère que le service démarre de suite (notre cas de figure)&lt;br /&gt;
* forking &amp;amp;rarr; systemd attend que le processus soit détaché du contexte d&#039;exécution courant et que le processus père ait rendu la main (soit terminé). Utile si on avait fait un fork comme mentionné dans [[Php daemon | cet article]]. Dans ce cas de figure il est intéressant de préciser le fichier de pid avec la directive &#039;&#039;&#039;PIDFile=&#039;&#039;&#039;&lt;br /&gt;
* oneshot &amp;amp;rarr; utile pour des script qui font un travail et rendent la main. Vous pouvez maintenir l&#039;état du service avec la directive &#039;&#039;&#039;RemainAfterExit=yes&#039;&#039;&#039; pour que systemd considère le service comme actif après l&#039;exécution du script.&lt;br /&gt;
* ...&lt;br /&gt;
|-&lt;br /&gt;
|| Service || User || l&#039;utilisateur utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Group || le groupe utilisé pour l&#039;abaissement de privilège&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Umask || le masque utilisé pour la création de fichiers&lt;br /&gt;
|-&lt;br /&gt;
|| Service || ExecStart || Le chemin de l&#039;exécutable&lt;br /&gt;
|-&lt;br /&gt;
|| Service || Restart || précise quand redémarrer le service :&lt;br /&gt;
* on-failure &amp;amp;rarr; quand le service échoue (code retour différent de zéro)&lt;br /&gt;
* always &amp;amp;rarr; dès que le service se termine indépendamment du code retour. C&#039;est utile si vous n&#039;avez pas de boucle qui retient le programme (while ou autre)&lt;br /&gt;
|-&lt;br /&gt;
|| Service || TimeoutStopSec || temps laissé au programme pour s’arrêter avant de le tuer (SIGKILL)&lt;br /&gt;
|-&lt;br /&gt;
|| Install || WantedBy || le niveau de démarrage du système dans lequel appelé le programme. Je vous laisse voir cette article à propos des [[Systemctl#Aper.C3.A7u | niveau de démarrage]] mais pour faire simple :&lt;br /&gt;
* multi-user.target &amp;amp;rarr; ligne de commande&lt;br /&gt;
* graphical.target &amp;amp;rarr; interface graphique&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Lien symbolique et activation ==&lt;br /&gt;
Maintenant que le fichier existe, nous allons créer un lien symbolique dans le répertoire &#039;&#039;/etc/systemd/system&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl link /opt/php-service/clock-php.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le service devrait être visible:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl status clock-php.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
On peut maintenant utiliser le service avec les commandes traditionnelle :&lt;br /&gt;
* start&lt;br /&gt;
* stop&lt;br /&gt;
* status&lt;br /&gt;
* ...&lt;br /&gt;
Vous avez tous les détails sur systemd dans [[Systemctl | cet article]] mais pour résumer :&lt;br /&gt;
*On démarre donc le service avec :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start clock-php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*On vérifie le status du service avec :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl status clock-php&lt;br /&gt;
● clock-php.service - clock-php daemon service&lt;br /&gt;
     Loaded: loaded (/etc/systemd/system/clock-php.service; alias)&lt;br /&gt;
     Active: active (running) since Sat 2024-05-18 01:07:46 CEST; 1s ago&lt;br /&gt;
   Main PID: 1487 (clock-php)&lt;br /&gt;
      Tasks: 1 (limit: 5904)&lt;br /&gt;
     Memory: 8.6M&lt;br /&gt;
        CPU: 27ms&lt;br /&gt;
     CGroup: /system.slice/clock-php.service&lt;br /&gt;
             └─1487 /usr/bin/php /usr/bin/clock-php&lt;br /&gt;
&lt;br /&gt;
May 18 01:07:46 prog systemd[1]: Started clock-php daemon service.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*On peut même enregistrer le service au démarrage :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl enable clock-php&lt;br /&gt;
Created symlink /etc/systemd/system/multi-user.target.wants/clock-php.service → /opt/php-service/clock-php.service.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=The_Linux_Craftsman&amp;diff=4176</id>
		<title>The Linux Craftsman</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=The_Linux_Craftsman&amp;diff=4176"/>
		<updated>2025-12-21T16:36:31Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* La pratique */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Ce wiki =&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;100%&amp;quot;&lt;br /&gt;
! Le contenu !! Public visé !! L&#039;auteur&lt;br /&gt;
|-valign=top&lt;br /&gt;
|width=&amp;quot;33%&amp;quot;|&lt;br /&gt;
Ce wiki propose des articles, cours, TPs et TDs sur des sujets gravitant autour des technologies du système d&#039;information, notamment sur le système Linux, sur la distribution CentOS et maintenant Rocky Linux.&lt;br /&gt;
&lt;br /&gt;
Vous y trouverez des articles traitant de la mise en place de services réseaux tels que DHCP et DNS mais également des articles sur la mise en place de pare-feu, site Web, etc... Il y a un peu de tout et je vous encourage à utiliser le champ de recherche pour trouver ce dont vous avez besoin.&lt;br /&gt;
|width=&amp;quot;33%&amp;quot;|&lt;br /&gt;
&lt;br /&gt;
Ce wiki s&#039;adresse principalement à mes élèves mais il peut également servir à des enseignants qui désirent monter leurs cours sans se &amp;quot;prendre la tête&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Tout est disponible sous [http://www.gnu.org/copyleft/fdl.html licence GNU Free Documentation License 1.3] ou ultérieure et vous pouvez récupérer les contenus et en faire ce que vous voulez !&lt;br /&gt;
|width=&amp;quot;33%&amp;quot;|&lt;br /&gt;
&lt;br /&gt;
Je m&#039;appelle Jean-Christophe FORTON et je suis professeur d&#039;informatique depuis 2011 mais cela n&#039;a pas toujours été mon métier, plus d&#039;info [[jcf|ici]]...&lt;br /&gt;
&lt;br /&gt;
N&#039;hésitez pas à faire un tour sur :&lt;br /&gt;
{|&lt;br /&gt;
|-valign=middle&lt;br /&gt;
|align=center|&lt;br /&gt;
[[Image:Logo-YouTube-rouge.png|30px|link=https://www.youtube.com/@tala-informatique]]&lt;br /&gt;
|| ma chaîne Youtube pour découvrir mes aventures ou (re)visionner certains cours&lt;br /&gt;
|-valign=middle&lt;br /&gt;
|align=center|&lt;br /&gt;
[[Fichier:Logo-Tipeee.png|27px|link=https://fr.tipeee.com/tala-informatique]]&lt;br /&gt;
||mon Tipeee pour me remercier&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Les cours =&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;100%&amp;quot;&lt;br /&gt;
! Système  !! Sécurité !! Réseaux !! Développement !! Général&lt;br /&gt;
|-valign=top&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Linux&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:intro_linux.pdf|Introduction au système Linux]]&lt;br /&gt;
* [[:Media:tp_linux_commandes_de_bases.pdf|TP Linux : commandes de bases]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Windows&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Architecture des ordinateurs en environnement Windows&lt;br /&gt;
** [[:Media:materiel.pdf|Le matériel]]&lt;br /&gt;
** [[:Media:system_exploitation.pdf|Le système d&#039;exploitation]]&lt;br /&gt;
** [[:Media:gestion_disques.pdf|La gestion des disques]]&lt;br /&gt;
** [[:Media:sauvegarde.pdf|La sauvegarde]]&lt;br /&gt;
* Active Directory&lt;br /&gt;
** [[:Media:ad_intro.pdf|Les principes fondamentaux]]&lt;br /&gt;
** [[:Media:ad_gpo.pdf|Gestion des stratégies]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Réseaux&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:Les pare-feux.pdf|Les pare-feux]]&lt;br /&gt;
* [[:Media:Les pare-feux_tech.pdf|Les pare-feux (fiche technique)]]&lt;br /&gt;
* [[:Media:Les VPN.pdf|Les VPN]]&lt;br /&gt;
* [[:Media:Haute_dispo.pdf|Haute disponibilité]]&lt;br /&gt;
* [[:Media:sauvegarde_pca.pdf|La sauvegarde]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Sécurité des Systèmes d’Information&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:principes_SSI.pdf|Les principes fondamentaux]]&lt;br /&gt;
* [[:Media:implémentation_concrete_SSI.pdf|Implémentation concrète de la sécurité]]&lt;br /&gt;
* [[:Media:cryptographie.pdf|Cryptographie]]&lt;br /&gt;
* [[:Media:TD1_SSI.pdf|TD1: Principes Fondamentaux]]&lt;br /&gt;
* [[:Media:TP1_SSI.pdf|TP1: Écoute d&#039;une connexion]]&lt;br /&gt;
* [[:Media:TP2_SSI.pdf|TP2: Chiffrement asymétrique avec PGP]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Web&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:apache server.pdf|Le serveur Apache]]&lt;br /&gt;
* [[:Media:pilotage web bdd.pdf|Pilotage d&#039;une page web]]&lt;br /&gt;
* [[:Media:web app security.pdf|Sécurité des applications Web]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Modèle OSI&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
*[[:Media:OSI.pdf|Le modèle OSI en version cours]]&lt;br /&gt;
*[[:Media:OSI_Slides.pdf|Le modèle OSI en version slides]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 1 &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:Normes_cablages.pdf|Les normes de câblage]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 2&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:Ethernet-802.3.pdf|La norme Ethernet (802.3)]]&lt;br /&gt;
* [[:Media:WiFi-802.11.pdf|La norme Wi-Fi (802.11)]]&lt;br /&gt;
* [[:Media:Bridge.pdf|Les bridges]]&lt;br /&gt;
* [[:Media:STP.pdf|Spanning Tree Protocol]]&lt;br /&gt;
* [[:Media:VLAN.pdf|Les Vlans]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 3+&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Routage:&lt;br /&gt;
** [[:Media:CIDR.pdf|Classless Inter-Domain Routing]]&lt;br /&gt;
** [[:Media:resume_protocoles_vecteur_distance.pdf|Résumé sur les protocoles à vecteur de distance]]&lt;br /&gt;
** [[:Media:EIGRP.pdf|Enhanced Interior Gateway Routing Protocol]]&lt;br /&gt;
** [[:Media:OSPF.pdf|Open Shortest Path First]]&lt;br /&gt;
** [[:Media:BGP.pdf|Border Gateway Protocol]]&lt;br /&gt;
*Services:&lt;br /&gt;
** [[:Media:DHCP.pdf|DHCP]]&lt;br /&gt;
** [[:Media:DNS.pdf|DNS]]&lt;br /&gt;
** [[:Media:SAMBA.pdf|Samba]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Qualité de service&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:QoS.pdf|La QoS]]&lt;br /&gt;
* [[:Media:Iproute2_QoS.pdf|Iproute2 et la QoS]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Protocole&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:HTTP.pdf|HTTP]]&lt;br /&gt;
* [[:Media:SNMP.pdf|SNMP]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;L&#039;algorithmique&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:algo_intro.pdf|Introduction]]&lt;br /&gt;
* [[:Media:algo_langage.pdf|Le Langage]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;SQL&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:conception_bdd.pdf|Conception de base de données]]&lt;br /&gt;
* [[:Media:conception_bdd_simple.pdf|Conception de base de données (simplifié)]]&lt;br /&gt;
* [[:Media:langage_de_requête.pdf|Langage de requête]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Langages du Web&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:HTML xHTML intro.pdf|Le langage HTML]]&lt;br /&gt;
* [[:Media:css.pdf|Le langage CSS]]&lt;br /&gt;
* [[:Media:javascript.pdf|Le langage JavaScript]]&lt;br /&gt;
* [[:Media:php.pdf|Le langage PHP]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Langage bas niveau&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:C lang.pdf|Le langage C]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Architecture SOA&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:webservices.pdf|Les Web Services RESTful]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Informatique embarquée&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:arduino_avr.pdf|Arduino et AVR]]&lt;br /&gt;
* [[:Media:IOT.pdf|L&#039;Internet des objets]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Gestion&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:gestion_projet.pdf|Gestion de projet]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Bureautique&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:powerpoint.pdf|PowerPoint]]&lt;br /&gt;
* [[:Media:excel.pdf|Excel]]&lt;br /&gt;
* [[:Media:bdd_com.pdf|Les bases de données]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Histoire / évolutions&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:expertise_codage.pdf|Expertise codage]]&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= La pratique =&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;100%&amp;quot;&lt;br /&gt;
! Système !! Sécurité !! Réseaux !! Développement !! Embarquée !! Virtualisation&lt;br /&gt;
|-valign=&amp;quot;top&amp;quot;&lt;br /&gt;
|align=left|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Général&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[Installation de CentOS]]&lt;br /&gt;
* [[Installation de Rocky]]&lt;br /&gt;
* [[migration_centos8torocky8| Migration CentOS 8 &amp;amp;rarr; Rocky 8]]&lt;br /&gt;
* [[upgrade_rocky8to9| Upgrade Rocky 8 &amp;amp;rarr; 9]]&lt;br /&gt;
* [[WSL | WSL : Installation de Linux sur Windows 10]]&lt;br /&gt;
* [[Vi / Vim]]&lt;br /&gt;
* [[linux_repository|Les dépots (EPEL, EL, ...)]]&lt;br /&gt;
* [[cron| Gestionnaire des tâches: cron]]&lt;br /&gt;
* [[rpm_yum| Gestionnaires de paquetages: RPM &amp;amp; YUM]]&lt;br /&gt;
* [[Les logs]]&lt;br /&gt;
* [[users_groups|Utilisateurs et groupes]]&lt;br /&gt;
* [[Gestion des disques]]&lt;br /&gt;
* CentOS &amp;amp;#8805; 7 (&amp;amp;asymp; Rocky) [[Fichier:Warning-icon.png|20px]]&lt;br /&gt;
** [[systemctl|Systemctl sur SystemD]]&lt;br /&gt;
** [[systemctl service|Créer un service avec Systemctl (démon)]]&lt;br /&gt;
** [[iptables_on_systemd | Firewalld ?!? Rendez moi Iptables ! ]]&lt;br /&gt;
** [[chrony|&#039;&#039;NTP&#039;&#039; est mort, vive &#039;&#039;Chrony&#039;&#039;]]&lt;br /&gt;
* CentOS 6&lt;br /&gt;
** [[Gestionnaire de démarrage|Gestionnaire de démarrage SysVInit]]&lt;br /&gt;
** [[start_stop_daemon|Création d&#039;un service avec start-stop-daemon (SysVInit)]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Réseaux&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[ifcfg-ethX|Les interfaces réseaux]]&lt;br /&gt;
* [[sysconfig-network|Les paramètres réseaux]]&lt;br /&gt;
* [[resolv.conf|Configuration du client DNS]]&lt;br /&gt;
* [[ntpd|Configuration du client NTP]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Haute disponibilité&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[drbd|Réplication à chaud avec DRBD]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Multimédia&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[DLNA|Partage de contenu cross-platform avec DLNA]]&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Réseaux&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[Iptables]]&lt;br /&gt;
* [[Squid]]&lt;br /&gt;
* [[:Media:Chillispot.pdf|Hotspot avec Chillispot]]&lt;br /&gt;
* [[Sécuriser un service avec Fail2ban]]&lt;br /&gt;
* [[Openvpn]]&lt;br /&gt;
* [[Wireguard]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Système&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[SSH]]&lt;br /&gt;
* [[SELinux]]&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Les outils&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[tcpdump | Le scanner &#039;&#039;tcpdump&#039;&#039;]]&lt;br /&gt;
* [[iproute2 | Contrôle réseau avec la commande &#039;&#039;ip&#039;&#039;]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 2&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[source routing|Le routage source]]&lt;br /&gt;
* [[bridge|Les bridges]]&lt;br /&gt;
* [[alias|Les alias]]&lt;br /&gt;
* [[vlan|Les vlans]]&lt;br /&gt;
* [[one-arm_router|La passerelle &#039;&#039;one-arm&#039;&#039;]]&lt;br /&gt;
* [[transparent_firewall|Le proxy &#039;&#039;transparent&#039;&#039;]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 3+&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Services:&lt;br /&gt;
** [[DHCP]]&lt;br /&gt;
** [[DNS]]&lt;br /&gt;
*Haute-disponibilité&lt;br /&gt;
** [[Ucarp]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 7&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Services:&lt;br /&gt;
** [[SAMBA]]&lt;br /&gt;
** [[HTTPD]]&lt;br /&gt;
** [[xmpp | Serveur XMPP avec Ejabberd]]&lt;br /&gt;
** [[vnc|Installer un serveur VNC]]&lt;br /&gt;
* Industrialisation:&lt;br /&gt;
** [[:Media:pxe.pdf|Monter un serveur PXE (pdf)]]&lt;br /&gt;
** [[PXE|Monter un serveur PXE]]&lt;br /&gt;
* Supervision&lt;br /&gt;
** [[Nagios| Nagios]]&lt;br /&gt;
** [[Cacti| Installer un serveur Cacti]]&lt;br /&gt;
** [[SNMP | Utiliser SNMP]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Cisco ISR (routeur)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[ISR-basics|Les bases]]&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Général&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[eclipse_install|Mise en place de l&#039;environnement de développement]]&lt;br /&gt;
* [[svn|Serveur de version SVN]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;PHP&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
*Les basiques :&lt;br /&gt;
** [[php_httpd_install | Installation sous Linux]]&lt;br /&gt;
** [[php_devel| Premier projet en &#039;&#039;PHP&#039;&#039;]]&lt;br /&gt;
** [[php_algo|Un peu d&#039;algorithmique]]&lt;br /&gt;
** [[php_object| Les objets]]&lt;br /&gt;
** [[php_xdebug| Debugger avec &#039;&#039;Xdebug&#039;&#039;]]&lt;br /&gt;
* La partie CLI :&lt;br /&gt;
** [[php_memcached| Memcached un serveur de cache]]&lt;br /&gt;
** [[php_daemon | Écriture d&#039;un démon]]&lt;br /&gt;
** [[php_socket | Utilisation des sockets]]&lt;br /&gt;
* La partie Web :&lt;br /&gt;
** [[php_$get_$post_$session| Passer des informations entres pages]]&lt;br /&gt;
** [[php_pdo| Utiliser une base MySQL avec PDO]]&lt;br /&gt;
** [[php_slim | Le framework SLIM]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;JavaScript&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
*Les basiques :&lt;br /&gt;
** [[js_devel| Premier projet en &#039;&#039;JavaScript&#039;&#039;]]&lt;br /&gt;
** [[js_algo| Un peu d&#039;algorithmique]]&lt;br /&gt;
** [[js_object| Les objets]]&lt;br /&gt;
*Les RIA (&#039;&#039;R&#039;&#039;ich &#039;&#039;I&#039;&#039;nternet &#039;&#039;A&#039;&#039;pplication) :&lt;br /&gt;
** [[js_AJAX| Client AJAX pour utiliser des Web Services]]&lt;br /&gt;
** [[js_AJAX_auth | Authentification avec un client AJAX]]&lt;br /&gt;
** [[js_AJAX_fetch_api | Découverte de l&#039;API &#039;&#039;fetch&#039;&#039;]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Java&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
*Les basiques :&lt;br /&gt;
** [[java_devel| Premier projet en &#039;&#039;Java&#039;&#039;]]&lt;br /&gt;
** [[java_jar|Mon premier objet]]&lt;br /&gt;
** [[java_algo| Un peu d&#039;algorithmique]]&lt;br /&gt;
* La partie Web:&lt;br /&gt;
** [[java_servlet|Les Servlets]]&lt;br /&gt;
** [[java_ws_restful| Web Service Restful]]&lt;br /&gt;
* Stocker des informations:&lt;br /&gt;
** [[java_mysql| Utiliser une base MySQL avec Java]]&lt;br /&gt;
** [[java_memcached| Memcached un serveur de cache]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;C&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
*Les basiques :&lt;br /&gt;
** [[c_devel| Premier projet en &#039;&#039;C&#039;&#039;]]&lt;br /&gt;
** [[c_devel_cross| Compilation croisée (Cross Compilation)]]&lt;br /&gt;
** [[c_algo| Un peu d&#039;algorithmique]]&lt;br /&gt;
*Execution parallèle :&lt;br /&gt;
** [[c_pthread| Les threads]]&lt;br /&gt;
** [[c_fork| Les forks]]&lt;br /&gt;
* IPC:&lt;br /&gt;
** [[c_pipe| Les tubes]]&lt;br /&gt;
** [[c_semaphore| Les sémaphores]]&lt;br /&gt;
** [[c_signals| Les signaux POSIX]]&lt;br /&gt;
** [[c_socket| Les sockets]]&lt;br /&gt;
* GP-GPU avec CUDA:&lt;br /&gt;
** [[cuda_install| Installation de CUDA]]&lt;br /&gt;
** [[cuda_hello_world| Les concepts et bases]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Python&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[python_devel| Premier projet en &#039;&#039;Python&#039;&#039;]]&lt;br /&gt;
* [[python_algo|Un peu d&#039;algorithmique]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;SQL&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[sql_install| Installation d&#039;un SGBDR]]&lt;br /&gt;
* [[SQL_import| Importation d&#039;une base de données]]&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Général&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[Arduino_sketch_writing | Écriture d&#039;un sketch]]&lt;br /&gt;
* [[Arduino_CH340_driver_install | Installation du pilote CH340 (Serial TTL)]]&lt;br /&gt;
* [[Arduino_FTDI_driver_install | Installation du pilote FTDI(Serial TTL)]]&lt;br /&gt;
* [[Arduino_CP210X_driver_install | Installation du pilote CP210X(Serial TTL)]]&lt;br /&gt;
* [[Arduino_Eclipse_sketch | Création d&#039;un projet sous Eclipse]]&lt;br /&gt;
* [[arduino_diagram | Schémas des cartes Arduino]]&lt;br /&gt;
* [[esp8266_diagram | Schémas des cartes ESP8266]]&lt;br /&gt;
* [[esp32_diagram | Schémas des cartes ESP32]]&lt;br /&gt;
* [[sbc_diagram | Schémas des SBC]]&lt;br /&gt;
* [[µc_datasheet | Fiches techniques des microcontrôleurs]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Capteurs&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[Arduino_LDR | Luminosité avec une photorésistance]]&lt;br /&gt;
* [[Arduino_BH1750 | Luminosité avec le BH1750]]&lt;br /&gt;
* [[Arduino_DS18B20 | Température avec le DS18B20]]&lt;br /&gt;
* [[Arduino_LM35DZ | Température avec le LM35]]&lt;br /&gt;
* [[Arduino_DHT11 | Température et humidité avec le DHT11]]&lt;br /&gt;
* [[Arduino_BMP280 | Température, pression et altitude avec le BMP280]]&lt;br /&gt;
* [[Arduino_A3144 | Effet de Hall (magnétisme) avec le A3144 ]]&lt;br /&gt;
* [[Arduino_SR501 | Détection de mouvement avec le SR501 (PIR) ]]&lt;br /&gt;
* [[Arduino_soil_moisture | Capteur d&#039;humidité du sol ]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Communication&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Sans-fil:&lt;br /&gt;
** [[Arduino_HC12 | Communication RF433 avec un HC12]]&lt;br /&gt;
** [[Arduino_NRF24L01 | Communication 2.4Ghz avec un NRF24L01]]&lt;br /&gt;
** [[Arduino_ESP05 | Communication Wi-Fi avec un ESP-05]]&lt;br /&gt;
* Shield Ethernet (W5100):&lt;br /&gt;
** [[Arduino_W5100_intro | Présentation du shield]]&lt;br /&gt;
** [[Arduino_W5100_OSI3 | Configuration OSI 3]]&lt;br /&gt;
** [[Arduino_W5100_web_server | Utilisation du Shield Ethernet pour faire un serveur Web]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Composants et montages divers&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[ potentiometre | Potentiomètre ]]&lt;br /&gt;
* [[ shift_register | Registre à décalage ]]&lt;br /&gt;
* [[ Arduino_2axis_joystick_button | Joystick 2 axes avec bouton ]]&lt;br /&gt;
* [[ Arduino_SD_CARD | Module pour cartes SD ]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;ESP8266 / ESP32&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[esp_ide_arduino | Cartes ESP et IDE Arduino]]&lt;br /&gt;
* [[esp8266_wifi | Utilisation du WiFi ]]&lt;br /&gt;
* [[esp8266_webserver | Utilisation du serveur web ]]&lt;br /&gt;
* [[esp8266_udp_server | Serveur UDP ]]&lt;br /&gt;
* [[esp8266_ntp_client | Client NTP ]]&lt;br /&gt;
* [[:Media:esp8266_tp_meteo_dht11.pdf | TP station météo avec le DHT11 ]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Raspberry / Banana / Orange Pi&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[iso_install_sdcard | Installation d&#039;une image ]]&lt;br /&gt;
* [[pi_java_install | Mise en place de Java ]]&lt;br /&gt;
* [[sbc_qemu_emulation | Émulation avec Qemu ]]&lt;br /&gt;
* [[Linux sunxi armbian gpio | Manipulation des GPIO sous Linux]]&lt;br /&gt;
* [[Linux sunxi armbian w1 | Utilisation du protocole OneWire sous Linux]]&lt;br /&gt;
* [[Linux uart sunxi armbian | Utilisation du protocole UART sous Linux]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Notions avancées&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Arduino (ATmega328)&lt;br /&gt;
** [[atmega328_registers | Manipulation de registres ]]&lt;br /&gt;
** [[atmega328_timers | Les timers ]]&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;VmWare&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[vmware_install | Installation ]]&lt;br /&gt;
* [[vmware_network | La partie réseau ]]&lt;br /&gt;
* [[vmware_create_vm | Création d&#039;une machine virtuelle ]]&lt;br /&gt;
* [[vmware_debug_vm | Débogage réseau d&#039;une machine virtuelle]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Proxmox&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[proxmox_install | Installation ]]&lt;br /&gt;
* [[proxmox_iptables | Utilisation d&#039;iptables ]]&lt;br /&gt;
* [[proxmox_fail2ban | Fail2ban ]]&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;visibility:hidden&amp;quot;&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
__NOTOC__&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=DHCP&amp;diff=4175</id>
		<title>DHCP</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=DHCP&amp;diff=4175"/>
		<updated>2025-12-21T16:15:12Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{|border=1 class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! !! Client !! Serveur&lt;br /&gt;
|-align=&amp;quot;center&amp;quot;&lt;br /&gt;
| &#039;&#039;&#039;Protocole&#039;&#039;&#039; || udp || udp&lt;br /&gt;
|-align=&amp;quot;center&amp;quot;&lt;br /&gt;
| &#039;&#039;&#039;Port&#039;&#039;&#039; || 68 || 67&lt;br /&gt;
|-align=&amp;quot;center&amp;quot;&lt;br /&gt;
| &#039;&#039;&#039;Configuration Iptables&#039;&#039;&#039; || X || iptables -I INPUT 2 -p udp --dport 67 -j ACCEPT&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
*Dynamic Host Configuration Protocol (DHCP) est un protocole réseau ;&lt;br /&gt;
&lt;br /&gt;
*Défini dans la RFC1531 et présenté pour la première fois en 1993 ;&lt;br /&gt;
&lt;br /&gt;
*Son rôle est d&#039;assurer la configuration automatique des paramètres IP d&#039;une station ;&lt;br /&gt;
&lt;br /&gt;
*DHCP configure l&#039;adresse IP et le masque de la station cliente mais peut également configurer sa passerelle par défaut ainsi que ses serveurs DNS et NBNS (WINS).&lt;br /&gt;
&lt;br /&gt;
* DHCP est arrivé d&#039;un problème : la conception d&#039;IP suppose la configuration de chaque ordinateur connecté sur le réseau ;&lt;br /&gt;
&lt;br /&gt;
* Quasi impossible pour des réseaux de grande taille ;&lt;br /&gt;
&lt;br /&gt;
* Source d&#039;erreur pour les FAI qui possèdent plus de clients que d&#039;adresse IP publique.&lt;br /&gt;
&lt;br /&gt;
= Fonctionnement =&lt;br /&gt;
&lt;br /&gt;
L&#039;ordinateur équipé d&#039;une carte réseau mais dépourvu d&#039;IP :&lt;br /&gt;
&lt;br /&gt;
# Envoie par diffusion un datagramme &#039;&#039;&#039;DHCP DISCOVER&#039;&#039;&#039; sur le port 67 ;&lt;br /&gt;
# Tout serveur en mesure de répondre sur le réseau du client, envoie un datagramme &#039;&#039;&#039;DHCP OFFER&#039;&#039;&#039; en diffusion sur le port 68 et à destination du client (@MAC) ;&lt;br /&gt;
# Le client retient une des offres reçues, et diffuse un datagramme &#039;&#039;&#039;DHCP REQUEST&#039;&#039;&#039; comportant l&#039;IP du serveur et l&#039;IP retenue ;&lt;br /&gt;
# Le serveur DHCP envoie un datagramme &#039;&#039;&#039;DHCP ACK&#039;&#039;&#039; qui assigne au client l&#039;adresse IP, le masque ainsi que la durée du bail pour cette adresse. C&#039;est ce paquet qui contient les paramètres annexes comme la passerelle, les serveurs DNS, …&lt;br /&gt;
&lt;br /&gt;
= Préparation =&lt;br /&gt;
&lt;br /&gt;
Dans un premier temps, il faudra avoir une connexion à Internet, utiliser un serveur DNS et désactiver SELinux.&lt;br /&gt;
&lt;br /&gt;
Pour ceux qui auraient manqué des étapes, les voici:&lt;br /&gt;
* [[resolv.conf|Configuration du client DNS]]&lt;br /&gt;
* [[ifcfg-ethX|Paramétrer sa carte réseau]]&lt;br /&gt;
* [[SELinux#Changement_d.27.C3.A9tat|Désactiver SELinux]]&lt;br /&gt;
&lt;br /&gt;
Une fois ces étapes effectuées, entrons dans le vif du sujet !&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
* Sur CentOS 6 et 7&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# yum -y install dhcp&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Sur CentOS 8+ :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# dnf -y install dhcp-server&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration du service =&lt;br /&gt;
&lt;br /&gt;
Le fichier de configuration du démon DHCP est : /etc/dhcp/dhcpd.conf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Pour spécifier une interface d&#039;écoute&lt;br /&gt;
DHCPD_INTERFACE=&amp;quot;eth0&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
subnet 192.168.50.0 netmask 255.255.255.0 {&lt;br /&gt;
	# Ce serveur DHCP fait autorité pour ce sous-réseau&lt;br /&gt;
	authoritative;&lt;br /&gt;
	# Spécifie le routeur que le client prendra comme passerelle&lt;br /&gt;
	option routers 192.168.50.254;&lt;br /&gt;
	# Spécifie un serveur NTP&lt;br /&gt;
	option ntp-servers 192.168.50.254;&lt;br /&gt;
	# Le client prendra la chaîne comme suffixe DNS (eg. ping www au lieu de www.tala-informatique.fr)&lt;br /&gt;
	option domain-name &amp;quot;tala-informatique.fr&amp;quot;;&lt;br /&gt;
	# Spécifie le serveur DNS du réseau&lt;br /&gt;
	option domain-name-servers 192.168.50.253;&lt;br /&gt;
	# Utilise cette portion d&#039;IP pour l&#039;attribution d&#039;adresse&lt;br /&gt;
	# Cette plage doit exclure les adresses spécifiées plus bas pour les hôtes !&lt;br /&gt;
	range 192.168.50.10 192.168.50.50;&lt;br /&gt;
&lt;br /&gt;
	# Durée de conservation du bail	&lt;br /&gt;
	default-lease-time 7200;&lt;br /&gt;
	max-lease-time 10800;&lt;br /&gt;
&lt;br /&gt;
	# DHCP donnera le nom d&#039;hôte (eg. tc1) à la machine&lt;br /&gt;
	use-host-decl-names on;&lt;br /&gt;
&lt;br /&gt;
	host tc1 {&lt;br /&gt;
		hardware ethernet 00:80:64:1A:E9:14;&lt;br /&gt;
		fixed-address 192.168.50.1;&lt;br /&gt;
		# A mettre uniquement si l&#039;option &amp;quot;use-host-decl-names&amp;quot; n&#039;est pas explicitement spécifiée ou à &amp;quot;off&amp;quot;&lt;br /&gt;
		option host-name &amp;quot;tc1&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
* Pour SystemVInit:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# service dhcpd start&lt;br /&gt;
# chkconfig dhcpd on&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Pour SystemD :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start dhcpd.service&lt;br /&gt;
# systemctl enable dhcpd.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Parcours des logs =&lt;br /&gt;
{{#lst:Les_logs|log_dhcpd}}&lt;br /&gt;
= Mise à jour sécurisée du DNS =&lt;br /&gt;
&lt;br /&gt;
Il est possible de configurer &#039;&#039;dhcpd&#039;&#039; pour qu&#039;il envoie des mises à jour à &#039;&#039;named&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Configuration de &#039;&#039;dhcpd&#039;&#039; ==&lt;br /&gt;
&lt;br /&gt;
=== Section global ===&lt;br /&gt;
&lt;br /&gt;
Pour cela, il faut ajouter les lignes suivantes au début du fichier:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ddns-update-style interim;&lt;br /&gt;
ignore client-updates;&lt;br /&gt;
update-static-leases on;&lt;br /&gt;
&lt;br /&gt;
key DHCP_DAEMON {&lt;br /&gt;
        algorithm hmac-md5;&lt;br /&gt;
        secret &amp;quot;m6y00sQNwZQkQKq92ODj/5iSGIejHKGuURLVxHDE/iM=&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
zone tala-informatique.fr. {&lt;br /&gt;
  primary 192.168.200.253;&lt;br /&gt;
  key DHCP_DAEMON;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
zone 200.168.192.in-addr.arpa. {&lt;br /&gt;
  primary 192.168.200.253;&lt;br /&gt;
  key DHCP_DAEMON;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La méthode interim est le standard. Le client peut demander au serveur DHCP de mettre à jour le serveur DNS en lui passant ses propres paramètres et le serveur est configuré pour honorer (&#039;&#039;allow client-updates&#039;&#039;) ou pas la demande du client (&#039;&#039;ignore client-updates&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;ddns-domainname&#039;&#039; et &#039;&#039;ddns-rev-domainname&#039;&#039; permettent de spécifier au serveur DNS de quelle zone il s&#039;agit pour qu&#039;il ajoute les enregistrements de type &#039;&#039;A&#039;&#039; et &#039;&#039;PTR&#039;&#039; dans les bons fichiers de zones.&lt;br /&gt;
&lt;br /&gt;
=== Section &#039;&#039;subnet&#039;&#039; ===&lt;br /&gt;
&lt;br /&gt;
Dans la section subnet il faut ajouter une section &#039;&#039;group&#039;&#039; qui permettra de délimiter les hôtes mis à jour de manière automatique&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
group {&lt;br /&gt;
   option domain-name &amp;quot;tala-informatique.fr&amp;quot;;&lt;br /&gt;
   ddns-domainname &amp;quot;tala-informatique.fr&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
   host proxy {&lt;br /&gt;
       hardware ethernet 00:0C:29:A1:CA:BC;&lt;br /&gt;
       fixed-address 192.168.200.249;&lt;br /&gt;
       ddns-hostname &amp;quot;proxy&amp;quot;;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Vérification ==&lt;br /&gt;
&lt;br /&gt;
A chaque mise à jour on peut constater dans &#039;&#039;/var/log/messages&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/messages&lt;br /&gt;
Dec 27 22:50:47 fw dhcpd: Added new forward map from proxy.tala-informatique.fr to 192.168.200.249&lt;br /&gt;
Dec 27 22:50:47 fw dhcpd: added reverse map from 249.200.168.192.in-addr.arpa. to proxy.tala-informatique.fr&lt;br /&gt;
Dec 27 22:50:47 fw dhcpd: DHCPREQUEST for 192.168.200.249 from 00:0c:29:a1:ca:bc via eth1&lt;br /&gt;
Dec 27 22:50:47 fw dhcpd: DHCPACK on 192.168.200.249 to 00:0c:29:a1:ca:bc via eth1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Relais DHCP =&lt;br /&gt;
Quand les machines qui demandent une configuration de niveau 3 OSI ne sont pas sur le même domaine de broadcast que le serveur DHCP, il faut installer un relais DHCP.&lt;br /&gt;
[[Fichier:Dhcrelay.png|centré]]&lt;br /&gt;
Pour cela, il faut agir sur le routeur en lui ajoutant l&#039;agent de relais DHCP.&lt;br /&gt;
== Installation ==&lt;br /&gt;
Pour CentOS 6 / 7&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# yum -y install dhcp&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Pour CentOS 8+&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# dnf -y install dhcp-relay&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Configuration sur CentOS 6/7 ==&lt;br /&gt;
Partons du postulat suivant :&lt;br /&gt;
* le serveur DHCP est à l&#039;adresse 192.168.2.250&lt;br /&gt;
* le routeur possède les interfaces suivantes: eth0, eth1&lt;br /&gt;
Nous allons modifier le fichier &#039;&#039;/etc/sysconfig/dhcrelay&#039;&#039; comme cela :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Command line options here&lt;br /&gt;
DHCRELAYARGS=&amp;quot;&amp;quot;&lt;br /&gt;
# DHCPv4 only&lt;br /&gt;
INTERFACES=&amp;quot;eth0 eth1&amp;quot;&lt;br /&gt;
# DHCPv4 only&lt;br /&gt;
DHCPSERVERS=&amp;quot;192.168.3.250&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si vous avez plusieurs serveurs DHCP, ajoutez seulement leurs adresses IP séparées par un espace.&lt;br /&gt;
&lt;br /&gt;
== Configuration sur CentOS 8+ ==&lt;br /&gt;
Sur CentOS 8 ou ultérieur (eg. Rocky), il faut copier le fichier exemple dans le répertoire des services :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /lib/systemd/system/dhcrelay.service /etc/systemd/system/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Maintenant nous pouvons modifier la ligne suivante :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ExecStart=/usr/sbin/dhcrelay -d --no-pid&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour ajouter des options :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ExecStart=/usr/sbin/dhcrelay -d --no-pid -i eth0 -i eth1 192.168.3.250&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Cela mettra le service dhcrelay en écoute sur &#039;&#039;eth0&#039;&#039; et &#039;&#039;eth1&#039;&#039; pour relayer les requêtes au serveur dhcp à l&#039;adresse &#039;&#039;192.168.3.250&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Comme nous venons d&#039;ajouter un fichier dans le répertoire de systemD, il faut lui demander de recharger sa configuration :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl daemon-reload&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut maintenant enregistrer le service au démarrage et le démarrer :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl enable dhcrelay --now&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Démarrage ==&lt;br /&gt;
On n&#039;oublie pas de démarrer et d&#039;enregistrer le service dans le gestionnaire de démarrage :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# service dhcrelay start&lt;br /&gt;
# chkconfig dhcrelay on&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= DHCP Esclave =&lt;br /&gt;
Dans cette section, nous allons nous intéresser à la continuité de service en installant un deuxième serveur DHCP qui agira quand le serveur maître sera indisponible.&lt;br /&gt;
&lt;br /&gt;
Si vous avez déjà configuré votre serveur maître, c&#039;est très bien, nous allons utiliser cette configuration pour le serveur esclave, si vous n&#039;avez pas encore configuré votre serveur maître, retour à la case [[DHCP#Pr.C3.A9paration | départ]] !&lt;br /&gt;
&lt;br /&gt;
== Principe ==&lt;br /&gt;
Le serveur maître et le serveur esclave échange sur un port &#039;&#039;TCP&#039;&#039;, généralement le 520, leurs informations sur les baux délivré.&lt;br /&gt;
Il sont donc synchronisés et peuvent très bien marcher en tandem.&lt;br /&gt;
&lt;br /&gt;
On part du postulat suivant :&lt;br /&gt;
* DHCP maître  : 192.168.3.251&lt;br /&gt;
* DHCP esclave : 192.168.3.250&lt;br /&gt;
== Configuration du maître ==&lt;br /&gt;
Dans le fichier &#039;&#039;/etc/dhcp/dhcpd.conf&#039;&#039; vous devez mettre le bloc suivante au début, avant les déclarations de &#039;&#039;subnet&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
failover peer &amp;quot;dhcp&amp;quot; {&lt;br /&gt;
  primary;&lt;br /&gt;
  address 192.168.3.251;&lt;br /&gt;
  port 520;&lt;br /&gt;
  peer address 192.168.3.250;&lt;br /&gt;
  peer port 520;&lt;br /&gt;
  max-response-delay 60;&lt;br /&gt;
  max-unacked-updates 10;&lt;br /&gt;
  mclt 3600;&lt;br /&gt;
  split 128;&lt;br /&gt;
  load balance max seconds 3;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ensuite, il suffit de faire référence à l&#039;esclave dans chaque déclaration de &#039;&#039;subnet&#039;&#039; que l&#039;on veut redonder:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
subnet 192.168.3.0 netmask 255.255.255.0 {&lt;br /&gt;
   authoritative;&lt;br /&gt;
   pool{&lt;br /&gt;
      failover peer &amp;quot;dhcp&amp;quot;;&lt;br /&gt;
      option routers 192.168.1.254;&lt;br /&gt;
&lt;br /&gt;
      range 192.168.1.10 192.168.1.50;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Configuration de l&#039;esclave ==&lt;br /&gt;
Dans le fichier &#039;&#039;/etc/dhcp/dhcpd.conf&#039;&#039; c&#039;est la même histoire que pour le maître en inversant les IP, en remplaçant &#039;&#039;primary&#039;&#039; par &#039;&#039;secondary&#039;&#039; et en supprimant les informations de balance de charge:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
failover peer &amp;quot;dhcp&amp;quot; {&lt;br /&gt;
  secondary;&lt;br /&gt;
  address 192.168.3.250;&lt;br /&gt;
  port 520;&lt;br /&gt;
  peer address 192.168.3.251;&lt;br /&gt;
  peer port 520;&lt;br /&gt;
  max-response-delay 60;&lt;br /&gt;
  max-unacked-updates 10;&lt;br /&gt;
  mclt 3600;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ensuite, il suffit de faire référence à l&#039;esclave dans chaque déclaration de &#039;&#039;subnet&#039;&#039; que l&#039;on à redondé sur le maître ainsi que d&#039;enlever la directive &#039;&#039;authoritative&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
subnet 192.168.3.0 netmask 255.255.255.0 {&lt;br /&gt;
   pool{&lt;br /&gt;
      failover peer &amp;quot;dhcp&amp;quot;;&lt;br /&gt;
      option routers 192.168.1.254;&lt;br /&gt;
&lt;br /&gt;
      range 192.168.1.10 192.168.1.50;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Iptables ==&lt;br /&gt;
N&#039;oublions pas d&#039;ouvrir le port &#039;&#039;TCP&#039;&#039; 520 sur les deux machines:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# iptables -I INPUT 2 -p tcp --dport 520 -j ACCEPT&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relais ==&lt;br /&gt;
Si vous avez un &#039;&#039;dhcrelay&#039;&#039; sur votre ou vos pares-feu, n&#039;oubliez pas de modifier la directive &#039;&#039;DHCPSERVERS&#039;&#039; pour y ajouter l&#039;adresse de l&#039;esclave !&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=MediaWiki:Common.js&amp;diff=4174</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=MediaWiki:Common.js&amp;diff=4174"/>
		<updated>2025-12-20T18:26:05Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* Tout JavaScript ici sera chargé avec chaque page accédée par n’importe quel utilisateur. */&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=MediaWiki:Common.js&amp;diff=4173</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=MediaWiki:Common.js&amp;diff=4173"/>
		<updated>2025-12-20T18:24:54Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* Tout JavaScript ici sera chargé avec chaque page accédée par n’importe quel utilisateur. */&lt;br /&gt;
&amp;lt;script src=&amp;quot;//unpkg.com/@highlightjs/cdn-assets@11.5.1/languages/cpp.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Arduino_sketch_writing&amp;diff=4172</id>
		<title>Arduino sketch writing</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Arduino_sketch_writing&amp;diff=4172"/>
		<updated>2025-12-20T16:14:09Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Un sketch est le nom donnée par Arduino aux programmes qui sont téléversés sur un ATMega (cerveau de la carte Arduino).&lt;br /&gt;
&lt;br /&gt;
Comme un programme informatique, le sketch est découpé en plusieurs parties qui ont toutes leur importance.&lt;br /&gt;
=Les différentes sections d&#039;un sketch=&lt;br /&gt;
== Commentaires ==&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
La première partie d&#039;un sketch est très certainement composée de commentaires. Dans cette introduction, on décrit l&#039;objectif du sketch, l&#039;auteur, sa date de création, le montage électronique qu&#039;il implique, etc...&lt;br /&gt;
&lt;br /&gt;
Ci-contre un exemple de commentaire présent dans le sketch &#039;&#039;blink&#039;&#039; livré avec l&#039;IDE Arduino.&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
  Blink&lt;br /&gt;
  Turns on an LED on for one second, then off for one second, repeatedly.&lt;br /&gt;
&lt;br /&gt;
  Most Arduinos have an on-board LED you can control. On the UNO, MEGA and ZERO &lt;br /&gt;
  it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN takes care &lt;br /&gt;
  of use the correct LED pin whatever is the board used.&lt;br /&gt;
  If you want to know what pin the on-board LED is connected to on your Arduino model, check&lt;br /&gt;
  the Technical Specs of your board  at https://www.arduino.cc/en/Main/Products&lt;br /&gt;
  &lt;br /&gt;
  This example code is in the public domain.&lt;br /&gt;
&lt;br /&gt;
  modified 8 May 2014&lt;br /&gt;
  by Scott Fitzgerald&lt;br /&gt;
  &lt;br /&gt;
  modified 2 Sep 2016&lt;br /&gt;
  by Arturo Guadalupi&lt;br /&gt;
*/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Les imports ==&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Les imports, correspondent à des inclusions de bibliothèques ou librairies et ce font grâce au mot clé &#039;&#039;#include&#039;&#039;. En d&#039;autres termes, lorsque vous faites une inclusion, vous ajouter toutes les lignes de code qui composent la bibliothèque en question.&lt;br /&gt;
&lt;br /&gt;
Le problème avec les imports est que l&#039;on à du mal à évaluer la complexité du code qui est appelé. La plus part du temps, on utilise qu&#039;une infime partie des possibilités offertes par certaine bibliothèque très complète. Si c&#039;est le cas et que vous n&#039;avez plus de place pour votre sketch, il est peut-être intéressant d&#039;isoler le code dont on a besoin plutôt que d&#039;utiliser la bibliothèque en entier... la place est limitée sur un ATMega et chaque octet compte ! &lt;br /&gt;
&lt;br /&gt;
Ci-contre un exemple d&#039;inclusion de la bibliothèque &#039;&#039;Ethernet&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Cette inclusion fait 1482 lignes de code...&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
# include &amp;quot;Ethernet.h&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
== Variables globales ==&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Les variables globales servent, à l&#039;inverse de leurs homologues locales, à plusieurs endroit dans le sketch. Il peut aussi être intéressant, surtout pour les gros objets, de les faire instancier pendant la phase de démarrage de la puce plutôt que de le faire lors du premier appel. L’intérêt ici n&#039;est pas une utilisation à plusieurs endroit mais un gain de temps.&lt;br /&gt;
&lt;br /&gt;
Ci-contre un exemple de variable globale accessible dans tout le sketch :&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Variable statique (portée sketch) et constante (sa valeur ne change pas)&lt;br /&gt;
static const char METHOD[] = &amp;quot;GET&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fonction setup() ==&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
La fonction &#039;&#039;setup()&#039;&#039; est appelée une fois, au démarrage de la puce. C&#039;est généralement ici qu&#039;il faut positionner correctement les broches (en entrée ou en sortie) ainsi que leurs états (haut ou bas). &lt;br /&gt;
&lt;br /&gt;
Lorsque l&#039;on a des conditions qui doivent être satisfaites obligatoirement pour le bon fonctionnement du sketch, il ne faut pas hésiter à utiliser une LED pour montrer à l&#039;utilisateur que le démarrage s&#039;est effectué correctement, un peu comme avec un &#039;&#039;buzzer&#039;&#039; sur une carte mère.&lt;br /&gt;
&lt;br /&gt;
Ci-contre un exemple qui test le bon fonctionnement d&#039;un module HC12 et qui utilise une LED pour signaler à l&#039;utilisateur l&#039;état du système :&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void setup() {&lt;br /&gt;
  // Positionnement en sortie des pins&lt;br /&gt;
  pinMode(ledPin, OUTPUT);&lt;br /&gt;
  pinMode(setPin, OUTPUT);&lt;br /&gt;
  // On éteint la LED&lt;br /&gt;
  digitalWrite(ledPin, LOW);&lt;br /&gt;
  // passage en mode commande&lt;br /&gt;
  digitalWrite(setPin, LOW);&lt;br /&gt;
  // Démarrage de la communication avec le module&lt;br /&gt;
  hc12.begin(9600);&lt;br /&gt;
  // On demande au module un acquittement&lt;br /&gt;
  hc12.print(F(&amp;quot;AT+&amp;quot;));&lt;br /&gt;
  // Délais pour que le module traite la commande&lt;br /&gt;
  delay(100);&lt;br /&gt;
  // On attend la réponse du module&lt;br /&gt;
  while(!hc12.available());&lt;br /&gt;
  // Le module doit répondre &#039;&#039;OK&#039;&#039;&lt;br /&gt;
  if(strcmp(hc12.readString(), &amp;quot;OK&amp;quot;) != 0){&lt;br /&gt;
    // Quelque chose s&#039;est mal passé...&lt;br /&gt;
    while(true){&lt;br /&gt;
      // ... on fait clignoter la LED pour signaler l’erreur !&lt;br /&gt;
      digitalWrite(ledPin, HIGH);&lt;br /&gt;
      delay(500);&lt;br /&gt;
      digitalWrite(ledPin, LOW);&lt;br /&gt;
      delay(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  // passage en mode transparent&lt;br /&gt;
  digitalWrite(setPin, HIGH);&lt;br /&gt;
  // on allume la LED de manière fixe pour signaler la fin du setup()&lt;br /&gt;
  digitalWrite(ledPin, HIGH);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
== Fonction loop() ==&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
La fonction &#039;&#039;loop()&#039;&#039; est le cœur du sketch et peut s&#039;apparenter à un &#039;&#039;while(true)&#039;&#039; dans le sens ou le code à l&#039;intérieur de cette fonction va s’exécuter indéfiniment. &lt;br /&gt;
&lt;br /&gt;
Cette fonction permet de comprendre ce que le sketch est censé faire. Il ne faut pas la surcharger avec un code trop complexe. Il est considéré comme une bonne pratique de fragmenter le code en fonctions et de faire appel à ces fonctions dans &#039;&#039;loop()&#039;&#039;. &lt;br /&gt;
C&#039;est là tout l&#039;intérêt des fonction annexes !&lt;br /&gt;
&lt;br /&gt;
Ci-contre un exemple de la fonction &#039;&#039;loop()&#039;&#039; de l&#039;exemple &#039;&#039;blink&#039;&#039; :&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// the loop function runs over and over again forever&lt;br /&gt;
void loop() {&lt;br /&gt;
  digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)&lt;br /&gt;
  delay(1000);                       // wait for a second&lt;br /&gt;
  digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW&lt;br /&gt;
  delay(1000);                       // wait for a second&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
== Fonction annexes ==&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Les fonctions annexes sont la pour accueillir toute la complexité du code et chacune doit avoir un but précis. &lt;br /&gt;
&lt;br /&gt;
Il ne faut pas hésiter à &#039;&#039;décortiquer&#039;&#039; un code complexe en plusieurs fonctions :&lt;br /&gt;
* le code est plus simple ;&lt;br /&gt;
* le code gagne en lisibilité ;&lt;br /&gt;
* les bugs sont corrigés plus rapidement ;&lt;br /&gt;
&lt;br /&gt;
Ci-contre un exemple tronqué de fonctions annexes servant dans l&#039;élaboration d&#039;un serveur Web avec un module WizNet.&lt;br /&gt;
&lt;br /&gt;
Les fonctions annexes contiennent quelques 300 lignes qui auraient rendu illisible la fonction &#039;&#039;loop()&#039;&#039;...&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void loop() {&lt;br /&gt;
  // listen for incoming clients&lt;br /&gt;
  EthernetClient client = server.available();&lt;br /&gt;
  if (client) {&lt;br /&gt;
    while (client.connected()) {&lt;br /&gt;
      if (client.available()) {&lt;br /&gt;
        char c = client.read();&lt;br /&gt;
        if (readLine(c)) {&lt;br /&gt;
          if (readBytes == 0) {&lt;br /&gt;
            if (isUrl) {&lt;br /&gt;
              executeRequest(client);&lt;br /&gt;
            } else {&lt;br /&gt;
              // Error happened&lt;br /&gt;
              sendHeader(http_error, client);&lt;br /&gt;
              client.println();&lt;br /&gt;
            }&lt;br /&gt;
            // give the web browser time to receive the data&lt;br /&gt;
            delay(1);&lt;br /&gt;
            // close the connection:&lt;br /&gt;
            client.stop();&lt;br /&gt;
          } else {&lt;br /&gt;
            if (!isUrl &amp;amp;&amp;amp; readUrl()) {&lt;br /&gt;
              isUrl = true;&lt;br /&gt;
            }&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
   Read a line in the HTTP request and put it in a buffer&lt;br /&gt;
*/&lt;br /&gt;
bool readLine(char c) {&lt;br /&gt;
  // code de readLine&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
   Used to parse the URL and arguments&lt;br /&gt;
*/&lt;br /&gt;
bool readUrl() {&lt;br /&gt;
  // code de readUrl&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
   Process the HTTP request&lt;br /&gt;
*/&lt;br /&gt;
bool executeRequest(EthernetClient client) {&lt;br /&gt;
  // Code de executeRequest&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
   Used to send the HTTP header with the given code&lt;br /&gt;
*/&lt;br /&gt;
void sendHeader(int16_t code, EthernetClient client) {&lt;br /&gt;
 // code de sendHeader&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
== Sketch complet ==&lt;br /&gt;
Ci-dessous un sketch qui reprend toutes les section précédentes : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Sketch d&#039;exemple pour montrer &lt;br /&gt;
 * l&#039;utilité de chacune des sections&lt;br /&gt;
 * d&#039;un sketch&lt;br /&gt;
 * &lt;br /&gt;
 * @author jcf&lt;br /&gt;
 */&lt;br /&gt;
#include &amp;quot;Arduino.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Delais de clignotement en ms&lt;br /&gt;
static const uint8_t BLINK_DELAY=500;&lt;br /&gt;
// Pin sur laquelle est connectée l&#039;anode de la LED&lt;br /&gt;
static const uint8_t LED_PIN = 13;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  pinMode(LED_PIN, OUTPUT);&lt;br /&gt;
  digitalWrite(LED_PIN, LOW);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  blink();&lt;br /&gt;
}&lt;br /&gt;
/**&lt;br /&gt;
 * Fait clignoter une led&lt;br /&gt;
 */&lt;br /&gt;
void blink(){&lt;br /&gt;
  digitalWrite(LED_PIN, HIGH);&lt;br /&gt;
  delay(BLINK_DELAY);&lt;br /&gt;
  digitalWrite(LED_PIN, LOW);&lt;br /&gt;
  delay(BLINK_DELAY);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Le langage C/C++ =&lt;br /&gt;
==Les mots clés==&lt;br /&gt;
===static===&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Le mot clé [https://en.wikipedia.org/wiki/Static_(keyword) &#039;&#039;static&#039;&#039;] possède deux utilités :&lt;br /&gt;
* dans une fonction, il permet de garder la valeur de la variable entre deux invocations. Cette technique est intéressante car elle permet de ne pas polluer l&#039;espace global avec des variables utilisées uniquement dans une fonction mais, il faut garder à l&#039;esprit que le code est moins lisible ;&lt;br /&gt;
* pour une variable globale ou une fonction cela fixera la portée au niveau fichier (encapsulation).&lt;br /&gt;
&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;20%&amp;quot;|&lt;br /&gt;
Ci-dessous est illustré le premier cas de figure :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void setup(){&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop(){&lt;br /&gt;
  add();&lt;br /&gt;
  delay(100);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void add(){&lt;br /&gt;
  // instanciation et affectation&lt;br /&gt;
  static uint8_t i = 0;&lt;br /&gt;
  // incrémentation&lt;br /&gt;
  Serial.print(i++);&lt;br /&gt;
  if(i == 10){&lt;br /&gt;
    // Remise à zéro&lt;br /&gt;
    Serial.println(F(&amp;quot;.&amp;quot;));&lt;br /&gt;
    i = 0;&lt;br /&gt;
  }else{&lt;br /&gt;
    Serial.print(F(&amp;quot;, &amp;quot;));&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;10%&amp;quot;|&lt;br /&gt;
Le programme précédent affichera indéfiniment la ligne suivante :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0, 1, 2, 3, 4, 5, 6, 7, 8, 9.&lt;br /&gt;
0, 1, 2, 3, 4, 5, 6, 7, 8, 9.&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===const===&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;55%&amp;quot;|&lt;br /&gt;
Le mot clé [https://en.wikipedia.org/wiki/Const_(computer_programming) &#039;&#039;const&#039;&#039;] permet de préciser que la valeur d&#039;une variable ne changera pas, à l&#039;instar de son homologue, la variable. &lt;br /&gt;
&lt;br /&gt;
Attention cependant, en &#039;&#039;C&#039;&#039; &#039;&#039;const&#039;&#039; fait partie du type et non de la variable. Ce qui veut dire que le code ci-contre ne fonctionnera pas.&lt;br /&gt;
&lt;br /&gt;
Cette particularité permet au programmeur de spécifier un contrat avec l&#039;appelant. Si la fonction contient le mot clé &#039;&#039;const&#039;&#039;, la valeur des variables ou structures passées en paramètre n&#039;est pas modifiée. &lt;br /&gt;
&lt;br /&gt;
Cela fait partie d&#039;un sujet plus vaste qui s&#039;appelle la [https://en.wikipedia.org/wiki/Correctness_(computer_science) &#039;&#039;correction programmatique&#039;&#039;].&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;15%&amp;quot;|&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  void add(uint8_t i);&lt;br /&gt;
&lt;br /&gt;
  const uint8_t i = 0;&lt;br /&gt;
  /**&lt;br /&gt;
  * Impossible car i est de &lt;br /&gt;
  * type &amp;quot;const uint8_t&amp;quot; et non &amp;quot;uint8_t&amp;quot;&lt;br /&gt;
  */&lt;br /&gt;
  add(i);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===volatile===&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Le mot clé [https://en.wikipedia.org/wiki/Volatile_(computer_programming) &#039;&#039;volatile&#039;&#039;] permet de spécifier si la valeur d&#039;une variable est susceptible d&#039;être modifier entre deux accès. Cela permet de préciser à un compilateur, qui voudrait trop optimiser le code en supprimant des lectures ou écritures en mémoire, de ne pas réutiliser la valeur mais de la relire.&lt;br /&gt;
&lt;br /&gt;
La valeur peut être modifiée par un autre thread (processus) ou par une interruption. L&#039;exemple sur le site [https://www.arduino.cc/en/Reference/AttachInterrupt Arduino], met en exergue ce cas de figure :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
const byte ledPin = 13;&lt;br /&gt;
// Broche permettant une interruption (bouton, etc...)&lt;br /&gt;
const byte interruptPin = 2;&lt;br /&gt;
// État de la led à bas&lt;br /&gt;
volatile byte state = LOW;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  // On configure l&#039;anode en sortie&lt;br /&gt;
  pinMode(ledPin, OUTPUT);&lt;br /&gt;
  // On tire la broche du bouton vers le haut (pullup)&lt;br /&gt;
  pinMode(interruptPin, INPUT_PULLUP);&lt;br /&gt;
  /**&lt;br /&gt;
  * On attache une interruption sur la broche du bouton&lt;br /&gt;
  * Lorsque l&#039;on appuie sur le bouton, la fonction blink est appelée&lt;br /&gt;
  */&lt;br /&gt;
  attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  digitalWrite(ledPin, state);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void blink() {&lt;br /&gt;
  state = !state;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
On voit bien dans l&#039;exemple précédent que la valeur de la variable &#039;&#039;state&#039;&#039; ne change pas à l&#039;intérieur de la fonction &#039;&#039;loop()&#039;&#039; et cette modification d&#039;état est prise en compte uniquement grâce à &#039;&#039;volatile&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut garder à l&#039;esprit que le compilateur transforme le code !&lt;br /&gt;
*sans &#039;&#039;volatile&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void loop() {&lt;br /&gt;
  digitalWrite(13, LOW);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*avec &#039;&#039;volatile&#039;&#039; :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void loop() {&lt;br /&gt;
  digitalWrite(13, state);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Les types numériques==&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Quand on code en C, il est bon d&#039;utiliser la norme [https://en.wikipedia.org/wiki/C99 C99] qui spécifie plusieurs types numériques de taille fixe. Cette norme à été introduite pour palier les différences qu&#039;il y a entre les compilateurs et les plateformes pour lesquels ils sont écrits (processeur 32bit, microcontrôleur 8bits, etc...) :&lt;br /&gt;
*&#039;&#039;int8_t&#039;&#039; pour un nombre sur 8 bits (1 octet) ;&lt;br /&gt;
*&#039;&#039;int16_t&#039;&#039; pour un nombre sur 16 bits (2 octets) ;&lt;br /&gt;
*&#039;&#039;int32_t&#039;&#039; pour un nombre sur 32 bits (4 octets) ;&lt;br /&gt;
*&#039;&#039;int64_t&#039;&#039; pour un nombre sur 64 bits (8 octets).&lt;br /&gt;
&lt;br /&gt;
Les types de tailles variables restent disponible :&lt;br /&gt;
*&#039;&#039;byte&#039;&#039; sur 8 bits (1 octet);&lt;br /&gt;
*&#039;&#039;int&#039;&#039; sur 16 bits (2 octets) ;&lt;br /&gt;
*&#039;&#039;short&#039;&#039; sur 16 bits (2 octets) ;&lt;br /&gt;
*&#039;&#039;float&#039;&#039; sur 32 bits (4 octets) ;&lt;br /&gt;
*&#039;&#039;double&#039;&#039; sur 32 bits (4 octets) ;&lt;br /&gt;
*&#039;&#039;long&#039;&#039; sur 32 bits (4 octets) ;&lt;br /&gt;
&lt;br /&gt;
On en arrive à la conclusion suivante, sur &#039;&#039;Arduino&#039;&#039; :&lt;br /&gt;
*short = int ;&lt;br /&gt;
*float = double;&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Le sketch ci-dessous permet de s&#039;en rendre compte.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  Serial.println(F(&amp;quot;Types numeriques à taille variable : &amp;quot;));&lt;br /&gt;
  byte b = 0;&lt;br /&gt;
  Serial.print(F(&amp;quot;byte\t : &amp;quot;));&lt;br /&gt;
  Serial.println(sizeof(b));&lt;br /&gt;
  int i = 0;&lt;br /&gt;
  Serial.print(F(&amp;quot;int\t : &amp;quot;));&lt;br /&gt;
  Serial.println(sizeof(i));&lt;br /&gt;
  float f = 0;&lt;br /&gt;
  Serial.print(F(&amp;quot;float\t : &amp;quot;));&lt;br /&gt;
  Serial.println(sizeof(f));&lt;br /&gt;
  double d = 0;&lt;br /&gt;
  Serial.print(F(&amp;quot;double\t : &amp;quot;));&lt;br /&gt;
  Serial.println(sizeof(d));&lt;br /&gt;
  long l = 0;&lt;br /&gt;
  Serial.print(F(&amp;quot;long\t : &amp;quot;));&lt;br /&gt;
  Serial.println(sizeof(l));&lt;br /&gt;
  Serial.println(F(&amp;quot;Types numeriques à taille fixe (C99): &amp;quot;));&lt;br /&gt;
  int8_t i8 = 0;&lt;br /&gt;
  Serial.print(F(&amp;quot;uint8_t  : &amp;quot;));&lt;br /&gt;
  Serial.println(sizeof(i8));&lt;br /&gt;
  int16_t i16 = 0;&lt;br /&gt;
  Serial.print(F(&amp;quot;uint16_t : &amp;quot;));&lt;br /&gt;
  Serial.println(sizeof(i16));&lt;br /&gt;
  int32_t i32 = 0;&lt;br /&gt;
  Serial.print(F(&amp;quot;uint32_t : &amp;quot;));&lt;br /&gt;
  Serial.println(sizeof(i32));&lt;br /&gt;
  int64_t i64 = 0;&lt;br /&gt;
  Serial.print(F(&amp;quot;uint64_t : &amp;quot;));&lt;br /&gt;
  Serial.println(sizeof(i64));&lt;br /&gt;
}&lt;br /&gt;
void loop(){&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Il donne le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Types numeriques a taille variable : &lt;br /&gt;
int : 		2&lt;br /&gt;
float : 	4&lt;br /&gt;
double : 	4&lt;br /&gt;
long : 		4&lt;br /&gt;
long long : 	4&lt;br /&gt;
Types numeriques a taille fixe (C99): &lt;br /&gt;
uint8_t : 	1&lt;br /&gt;
uint16_t : 	2&lt;br /&gt;
uint32_t : 	4&lt;br /&gt;
uint64_t : 	8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Voici les valeurs aux limites des types fixes C99 :&lt;br /&gt;
{|border=1&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| int8_t &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| uint8_t &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| int16_t &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| uint16_t &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| int32_t &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| uint32_t &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| int64_t &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| uint64_t&lt;br /&gt;
|-&lt;br /&gt;
|min &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| -128&lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;| -32 678&lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;| -2 147 483 648&lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;| -922 337 203 685 477&lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 0&lt;br /&gt;
|-&lt;br /&gt;
|max &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 127 &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 255 &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 32 677 &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 65 535 &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 2 147 483 648 &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 4 294 967 295 &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 922 337 203 685 477 &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 18446744073709551616&lt;br /&gt;
|}&lt;br /&gt;
Voici les valeurs aux limites des types variables :&lt;br /&gt;
{|border=1&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| byte &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| short/int &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| unsigned short/int &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| float/double&lt;br /&gt;
|align=&amp;quot;center&amp;quot;| long &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| unsigned long &lt;br /&gt;
|-&lt;br /&gt;
|min &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;| -32,768&lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;| -3.4028235E+38&lt;br /&gt;
|align=&amp;quot;center&amp;quot;| -2 147 483 647&lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 0&lt;br /&gt;
|-&lt;br /&gt;
|max &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 255 &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 32 677 &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 65 535 &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 3.4028235E+38&lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 2 147 483 648 &lt;br /&gt;
|align=&amp;quot;center&amp;quot;| 4 294 967 295 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
En plus de rendre le programme portable sur plusieurs plateforme, l&#039;utilisation des types fixes permet de minimiser l&#039;espace occupé par les variables en mémoire vive. Cela force le programmeur à réfléchir à la taille des données qu&#039;il va manipuler et, par la même, augmente la robustesse du programme (débordement de tampon, etc...).&lt;br /&gt;
&lt;br /&gt;
==Compilation conditionnelle==&lt;br /&gt;
La compilation conditionnelle permet de choisir les lignes de codes qui vont faire partie du programme final en fonction d&#039;assertions.&lt;br /&gt;
Celle est particulièrement utile pour modifier le code, par exemple, en fonction du microcontrôleur utilisée (328p-pu, mega2560, etc...).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#if defined(__AVR_ATmega328P__)&lt;br /&gt;
 // Code du UNO&lt;br /&gt;
#elif defined(__AVR_ATmega1280__)&lt;br /&gt;
 // Code du Mega 1280&lt;br /&gt;
#elif defined(__AVR_ATmega2560__)&lt;br /&gt;
 // Code du Mega 2560&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Les fonctions spécifiques à Arduino=&lt;br /&gt;
==Numérique==&lt;br /&gt;
===pinMode()===&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Permet de configurer une broche en entrée ou en sortie.&lt;br /&gt;
*[https://www.arduino.cc/en/Reference/PinMode pinMode(pin, mode)]:&lt;br /&gt;
**&#039;&#039;pin&#039;&#039;: la broche à configurer ;&lt;br /&gt;
**&#039;&#039;mode&#039;&#039;: la configuration à appliquer sur la broche. Peut prendre les valeurs &#039;&#039;INPUT&#039;&#039; en entrée, &#039;&#039;OUTPUT&#039;&#039; en sortie ou &#039;&#039;INPUT_PULLUP&#039;&#039; pour tirer l&#039;entrée vers le haut.&lt;br /&gt;
&lt;br /&gt;
Ci-contre un exemple qui met la broche 13 en sortie :&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void setup(){&lt;br /&gt;
  pinMode(13, OUTPUT);&lt;br /&gt;
}&lt;br /&gt;
void loop(){&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===digitalWrite() / digitalRead()===&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Permet de modifier ou de lire l&#039;état d&#039;une broche.&lt;br /&gt;
*[https://www.arduino.cc/en/Reference/DigitalWrite digitalWrite(pin, value)]:&lt;br /&gt;
**&#039;&#039;pin&#039;&#039; correspond au numéro de la broche&lt;br /&gt;
**&#039;&#039;value&#039;&#039; correspond soit à la valeur HIGH (3.3v ou 5v) soit à la valeur LOW (0v).&lt;br /&gt;
*[https://www.arduino.cc/en/Reference/DigitalRead digitalRead(pin)]:&lt;br /&gt;
**&#039;&#039;pin&#039;&#039; correspond au numéro de la broche&lt;br /&gt;
&lt;br /&gt;
L&#039;exemple ci-contre fait clignoter la LED présente sur un &#039;&#039;Arduino UNO&#039;&#039;:&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void setup(){&lt;br /&gt;
  pinMode(13, OUTPUT);&lt;br /&gt;
  digitalWrite(13, LOW);&lt;br /&gt;
}&lt;br /&gt;
void loop(){&lt;br /&gt;
  digitalWrite(13, !digitalRead(13));&lt;br /&gt;
  delay(1000);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===pulseIn()===&lt;br /&gt;
Permet de mesurer la durée d&#039;une impulsion.&lt;br /&gt;
*[https://www.arduino.cc/en/Reference/PulseIn pulseIn(pin, value, timeout)]:&lt;br /&gt;
**&#039;&#039;pin&#039;&#039; correspond à la broche sur laquelle lire l&#039;impulsion ;&lt;br /&gt;
**&#039;&#039;value&#039;&#039; correspond au type d&#039;impulsion lue (HIGH ou LOW) ;&lt;br /&gt;
**&#039;&#039;timeout&#039;&#039; correspond au temps au bout duquel &#039;&#039;pulseIn()&#039;&#039; rend la main si l&#039;impulsion n&#039;est pas observée.&lt;br /&gt;
==Analogique==&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
===analogReference()===&lt;br /&gt;
Permet de configurer le voltage de référence utilisé pour les entrées analogiques&lt;br /&gt;
*[https://www.arduino.cc/en/Reference/AnalogReference analogReference(type)]: &#039;&#039;type&#039;&#039; correspond au type de référence et peut prendre les valeurs suivantes :&lt;br /&gt;
**DEFAULT: référence par défaut (3.3v ou 5v)&lt;br /&gt;
**INTERNAL: référence interne égale à 1.1v sur l&#039;ATmega168 et ATmega328 et égale à 2.56v sur l&#039;ATmega8&lt;br /&gt;
**INTERNAL1V1: référence interne de 1.1V sur le Mega&lt;br /&gt;
**INTERNAL2V56: référence interne de 2.56V sur le Mega&lt;br /&gt;
**EXTERNAL: référence externe présente sur la broche AREF (entre 0v et 5V uniquement).&lt;br /&gt;
===analogWrite() / analogRead()===&lt;br /&gt;
Permet de modifier ou de lire l&#039;état d&#039;une broche analogique. &lt;br /&gt;
*[https://www.arduino.cc/en/Reference/AnalogWrite analogWrite(pin, value)]:&lt;br /&gt;
**pin correspond au numéro de la broche ;&lt;br /&gt;
**value correspond à la largeur de l&#039;impulsion envoyée sur la broche ([https://fr.wikipedia.org/wiki/Modulation_de_largeur_d&#039;impulsion Modulation de Largeur d&#039;Impulsion]). La valeur varie entre 0 (toujours éteint) et 255 (toujours allumé) et permet de faire varier l&#039;intensité d&#039;une LED, la vitesse d&#039;un moteur, etc...&lt;br /&gt;
Seulement certaine broches permettent d&#039;effectuer cette modulation:&lt;br /&gt;
*ATMega168/328 : 3, 5, 6, 9, 10, et 11;&lt;br /&gt;
*ATMega1280/2560 : de 2 à 13 et de 44 à 46&lt;br /&gt;
&lt;br /&gt;
*[https://www.arduino.cc/en/Reference/AnalogRead analogRead(pin)]:&lt;br /&gt;
**pin correspond au numéro de la broche ;&lt;br /&gt;
&lt;br /&gt;
Ci-contre, un exemple qui fait briller une LED sur la broche [https://www.arduino.cc/en/Tutorial/PWM PWM] numéro 9:&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t ledPin = 9;&lt;br /&gt;
&lt;br /&gt;
void setup() {&lt;br /&gt;
  pinMode(ledPin, OUTPUT);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void loop() {&lt;br /&gt;
  // Entier correspondant à la largeur de l&#039;impulsion&lt;br /&gt;
  static uint8_t brightness = 0;&lt;br /&gt;
  // boolean pour savoir si on incrémente ou décrémente la valeur&lt;br /&gt;
  static bool isFading = false;&lt;br /&gt;
  if (isFading) {&lt;br /&gt;
    brightness--;&lt;br /&gt;
    // Si on arrive à 0, on ne décrémente plus&lt;br /&gt;
    if (brightness == 0) {&lt;br /&gt;
      isFading = false;&lt;br /&gt;
    }&lt;br /&gt;
  } else {&lt;br /&gt;
    brightness++;&lt;br /&gt;
    // Si on arrive à 255, on n&#039;incrémente plus&lt;br /&gt;
    if (brightness == 255) {&lt;br /&gt;
      isFading = true;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  // On modifie la largeur de l&#039;impulsion&lt;br /&gt;
  analogWrite(ledPin, brightness);&lt;br /&gt;
  // On attend un peu sinon c&#039;est trop rapide&lt;br /&gt;
  delay(5);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Temps==&lt;br /&gt;
===millis() / micros()===&lt;br /&gt;
Indique le temps qui s&#039;est écoulé depuis le démarrage du microcontrôleur.&lt;br /&gt;
* [https://www.arduino.cc/en/Reference/Millis millis()]: résolution de 50 jours, après la valeur repart à zéro (&#039;&#039;overflow&#039;&#039;)&lt;br /&gt;
* [https://www.arduino.cc/en/Reference/Micros micros()]: résolution de 70 minutes, après la valeur repart à zéro (&#039;&#039;overflow&#039;&#039;)&lt;br /&gt;
===delay() / delayMicroseconds()===&lt;br /&gt;
Permet de mettre l’exécution du programme en pause pendant le temps désiré.&lt;br /&gt;
* [https://www.arduino.cc/en/Reference/Delay delay(unsigned long ms)]: &#039;&#039;ms&#039;&#039; le temps en millisecondes pendant lequel le programme s&#039;arrête.&lt;br /&gt;
* [https://www.arduino.cc/en/Reference/DelayMicroseconds delayMicroseconds(unsigned long us)] : &#039;&#039;us&#039;&#039; le temps en micro secondes pendant lequel le programme s&#039;arrête. Cette fonction est précise pour un maximum de 16383 micro secondes, pour des délais plus grand il faut utiliser la fonction &#039;&#039;delay()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Attention, &#039;&#039;delayMicroseconds()&#039;&#039; arrête les interruptions avant son travail et repositionne le [https://en.wikipedia.org/wiki/Status_register SREG] (&#039;&#039;Status REGister&#039;&#039;) après.&lt;br /&gt;
&lt;br /&gt;
==Interruptions==&lt;br /&gt;
===attachInterrupt() / detachInterrupt()===&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;60%&amp;quot;|&lt;br /&gt;
Permet d&#039;attacher et détacher des interruptions externes. Les interruptions sont des éventements qui arrivent de manière non prédictive et dont on souhaite être prévenu.&lt;br /&gt;
&lt;br /&gt;
Cela peut-être intéressant pour déclencher du code à l&#039;appuie d&#039;un bouton ou encore pour &#039;&#039;réveiller&#039;&#039; l&#039;ATMega.&lt;br /&gt;
*[https://www.arduino.cc/en/Reference/AttachInterrupt attachInterrupt(interrupt, ISR, mode)]&lt;br /&gt;
**&#039;&#039;interrupt&#039;&#039;: correspond au numéro d&#039;interruption, en relation avec une broche, généralement déterminé par la fonction &#039;&#039;digitalPinToInterrupt(pin)&#039;&#039;. Cependant, il est possible d&#039;utiliser directement le numéro d&#039;interruption, par exemple, la broche deux sur un UNO correspond à l&#039;interruption 0.&lt;br /&gt;
**&#039;&#039;ISR&#039;&#039;: correspond à la &#039;&#039;routine&#039;&#039; (fonction) appelée lorsque l&#039;interruption est levée.&lt;br /&gt;
**&#039;&#039;mode&#039;&#039;: correspond au type d&#039;interruption et peut prendre les valeurs suivantes :  LOW (état bas), CHANGE (changement d&#039;état), RISING (état bas vers haut) et FALLING (état haut vers bas). &lt;br /&gt;
*[https://www.arduino.cc/en/Reference/DetachInterrupt detachInterrupt(interrupt)]:&lt;br /&gt;
**interrupt: correspond au numéro d&#039;interruption, en relation avec une broche, généralement déterminé par la fonction &#039;&#039;digitalPinToInterrupt(pin)&#039;&#039;. Cependant, il est possible d&#039;utiliser directement le numéro d&#039;interruption, par exemple, la broche deux sur un UNO correspond à l&#039;interruption 0.&lt;br /&gt;
&lt;br /&gt;
Ci-contre, un code qui permet d&#039;allumer une LED quand on appuie sur un bouton:&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
const uint8_t ledPin = 13;&lt;br /&gt;
const uint8_t interruptPin = 2;&lt;br /&gt;
 &lt;br /&gt;
void setup() {&lt;br /&gt;
  pinMode(ledPin, OUTPUT);&lt;br /&gt;
  pinMode(interruptPin, INPUT_PULLUP);&lt;br /&gt;
  attachInterrupt(digitalPinToInterrupt(interruptPin), toggle, FALLING);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void loop() {}&lt;br /&gt;
 &lt;br /&gt;
// Change l&#039;état de la LED&lt;br /&gt;
void toggle() { &lt;br /&gt;
  uint8_t pinState = !digitalRead(ledPin);&lt;br /&gt;
  digitalWrite(ledPin, pinState);  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===interrupts() / noInterrupts()===&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Permet de désactiver ([https://www.arduino.cc/en/Reference/NoInterrupts noInterrupts]) et réactiver ([https://www.arduino.cc/en/Reference/Interrupts interrupts]) les interruptions qui sont actives par défaut. Cela peut être intéressant pour une portion de code &#039;&#039;critique&#039;&#039; ou l&#039;on ne souhaite pas être dérangé par un événement externe. Il faut garder à l&#039;esprit que l&#039;ATMega fonctionne correctement grâce aux interruptions, il ne faut pas les désactiver trop longtemps !&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void setup() {}&lt;br /&gt;
&lt;br /&gt;
void loop(){&lt;br /&gt;
  noInterrupts();&lt;br /&gt;
  // code sensible ici&lt;br /&gt;
  interrupts();&lt;br /&gt;
  // retour à la normale !&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Cas de l&#039;ESP8266 ==&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Pour la carte ESP8266 il faut préciser, pour les fonctions qui servent de vecteurs d&#039;interruption, qu&#039;elles doivent être stockées dans la mémoire RAM et non la flash grâce au spécificateur &#039;&#039;ICACHE_RAM_ATTR&#039;&#039;&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void ICACHE_RAM_ATTR toggle() { &lt;br /&gt;
 ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Communication==&lt;br /&gt;
===Serial===&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Les communications sur les broches &#039;&#039;Rx&#039;&#039; et &#039;&#039;Tx&#039;&#039; utilise les niveaux logiques [https://fr.wikipedia.org/wiki/Transistor-Transistor_logic TTL] (3.3v ou 5v). Touts les &#039;&#039;Arduino&#039;&#039; possèdent au moins un contrôleur [https://fr.wikipedia.org/wiki/UART UART] qui permet de communiquer avec un périphérique tier (PC, autre Arduino, module radio, etc...).&lt;br /&gt;
* [https://www.arduino.cc/en/Reference/Serial Serial(speed)] : &#039;&#039;speed&#039;&#039; correspond à la vitesse du port série et peut prendre différentes valeurs comme &#039;&#039;9600&#039;&#039; ou &#039;&#039;115200&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ci-contre un exemple qui utilise le port série :&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void setup(){&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  Serial.println(F(&amp;quot;Hello World&amp;quot;));&lt;br /&gt;
}&lt;br /&gt;
void loop(){&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Gestion mémoire sur Arduino=&lt;br /&gt;
==Les différents types de mémoires==&lt;br /&gt;
{|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
===La flash===&lt;br /&gt;
La mémoire flash est utilisée pour stocker le programme qui va s&#039;exécuter sur l&#039;Arduino. Cette mémoire permet l&#039;exécution du code mais pour pouvoir le modifier il faut d&#039;abord le copier en SRAM. La mémoire flash est non volatile et permet la conservation du programme même en l&#039;absence de source d’énergie mais possède un nombre de cycle d&#039;écriture limité à 100.000.&lt;br /&gt;
&lt;br /&gt;
===La SRAM===&lt;br /&gt;
La &#039;&#039;Static Random Access Memory&#039;&#039; ou SRAM, peut être lue et modifiée par le programme en cours d&#039;exécution dans plusieurs buts :&lt;br /&gt;
*Les données statiques – Il existe un bloc réservé dans la SRAM pour toutes les données globales et statiques. Pour les variables avec une valeur initiale, le système copie les données de la flash vers la SRAM au démarrage du programme.&lt;br /&gt;
*&#039;&#039;Heap&#039;&#039; – La &#039;&#039;heap&#039;&#039; sert pour les données dynamiquement allouée et commence à partir des données statique et s’étend au fur et à mesure que des données dynamiques sont allouée.&lt;br /&gt;
*&#039;&#039;Stack&#039;&#039; – La &#039;&#039;stack&#039;&#039; sert pour les variables locales et pour mémoriser les interruptions et les appels de fonction. Elle grossie du haut de la mémoire vers la &#039;&#039;stack&#039;&#039;. Chaque interruption, appel de fonction, allocation de variable locales fait grossir la &#039;&#039;stack&#039;&#039; vers la &#039;&#039;heap&#039;&#039;. A la fin de l&#039;interruption ou de l&#039;exécution d&#039;une fonction, l&#039;espace alloué redevient libre.&lt;br /&gt;
&lt;br /&gt;
La plupart des problèmes mémoire surviennent quand la &#039;&#039;heap&#039;&#039; recontre la &#039;&#039;stack&#039;&#039;. Lorsque cela survient, les deux mémoires deviennent corrompues ce qui, dans la majorité des cas, se conclu par un crash. Si la collision n&#039;est pas détectée et que l&#039;ATMega ne redémarre pas, le comportement du programme devient erratique.&lt;br /&gt;
&lt;br /&gt;
===l&#039;EEPROM===&lt;br /&gt;
L&#039;EEPROM est une autre mémoire non-volatile avec un nombre de cycle d&#039;écriture limité à 100.000. Cette mémoire se lit bit par bit, ce qui peut rendre son utilisation un peu fastidieuse. Elle sert principalement à enregistrer des données de configuration ensuite utilisées par le programme pour modifier son comportement.&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Arduino sram diagram.jpg|centré|500px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Architectures mémoire==&lt;br /&gt;
===Deux concepts===&lt;br /&gt;
Au début de l&#039;ère de l&#039;électronique ont émergé deux types d&#039;architectures mémoire : Harvard et Princeton.&lt;br /&gt;
{|align=&amp;quot;center&amp;quot; cellspacing=&amp;quot;0&amp;quot; style=&amp;quot;width:300px border-collapse:collapse&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|align=&amp;quot;center&amp;quot; style=&amp;quot;border:1px solid black&amp;quot;|Harvard&lt;br /&gt;
|align=&amp;quot;center&amp;quot; style=&amp;quot;border:1px solid black&amp;quot;|Princeton (Von Neumann)&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; style=&amp;quot;border:1px solid black&amp;quot;|Principe&lt;br /&gt;
|style=&amp;quot;border:1px solid black&amp;quot;|Utilise deux types d&#039;espaces mémoire : un pour le programme et un pour les données dynamiques.&lt;br /&gt;
|style=&amp;quot;border:1px solid black&amp;quot;|Utilise un seul espace mémoire pour les programmes et les données dynamiques.&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; style=&amp;quot;border:1px solid black&amp;quot;|Avantage&lt;br /&gt;
|align=&amp;quot;center&amp;quot; style=&amp;quot;border:1px solid black&amp;quot;|Rapidité&lt;br /&gt;
|align=&amp;quot;center&amp;quot; style=&amp;quot;border:1px solid black&amp;quot;|Fléxibilité&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Les microcontrôleurs===&lt;br /&gt;
Les  microcontrôleurs comme l&#039;ATMega sont étudiés pour les applications embarquées et, à l&#039;inverse des ordinateurs de bureau, ont une tâche bien définie qu&#039;ils doivent effectuer de manière sûre et efficace. Le design des microcontrôleur est plutôt spartiate, bien éloigné des mise en cache multicouche ou encore des disques virtuels en mémoire, il se concentre sur ce qui est essentiel à son fonctionnement.&lt;br /&gt;
&lt;br /&gt;
Le modèle Harvard convient tout à fait pour une utilisation dans le domaine de l&#039;embarqué et d’ailleurs, sur un ATMega, on a bien le programme qui est stocké en mémoire flash et les données qui se trouvent en &#039;&#039;SRAM&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
La plupart du temps, le compilateur et le système gèrent ces choses automatiquement mais, quand l&#039;espace commence à devenir exiguë, il est bon de savoir comment les choses se passent &#039;&#039;sous le capot&#039;&#039;.&lt;br /&gt;
===Différence avec un ordinateur classique===&lt;br /&gt;
Les ordinateurs classique, MAC et PC, sont de conception hybride et profitent du meilleur des deux architecture. Au cœur du processeur ont est sur une concpetion Harvard qui compartimente les caches pour les instructions et les données pour maximiser les performance. Cependant ces instructions et données sont chargées d&#039;un espace mémoire commum. Du point de vue du programmeur, cela apparaît comme une architecture &#039;&#039;Von Neumann&#039;&#039; avec plusieurs giga d&#039;espace de stockage mémoire.&lt;br /&gt;
&lt;br /&gt;
La différence la plus flagrante qui existe entre les ordinateurs et les microcontrôleurs est la quantité de mémoire disponible. L&#039;Arduino Uno ne dispose que de 32Ko de mémoire flash et de 2Ko de SRAM ce qui est environ 100.000 fois plus petit qu&#039;un PC bas de gamme !&lt;br /&gt;
&lt;br /&gt;
En travaillant dans un environnement minimaliste, il faut utiliser intelligemment les ressources disponible !  &lt;br /&gt;
&lt;br /&gt;
==Mesurer la consommation de SRAM==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Lorsqu&#039;on écrit un programme destiné à tourner sur un microcontrôleur comme l&#039;ATMega, l&#039;utilisation de la mémoire est très importante et surtout la détection de fuites !&lt;br /&gt;
Le programme peut très bien s&#039;exécuter pendant 1, 2 voire 3 jours puis, d&#039;un coup, plus rien...&lt;br /&gt;
Très souvent, les fuites viennent de l&#039;utilisation de fonction d&#039;allocation mémoire comme  &#039;&#039;malloc&#039;&#039; ou &#039;&#039;new&#039;&#039; et qui doivent être suivie de &#039;&#039;free&#039;&#039; ou &#039;&#039;delete&#039;&#039;.&lt;br /&gt;
Le temps de correction d&#039;un bug de ce type est très rapide, encore faut-il s&#039;en rendre compte !&lt;br /&gt;
&lt;br /&gt;
La fonction ci-contre permet de mesurer la quantité de mémoire disponible, c&#039;est à dire, l&#039;espace qui sépare la &#039;&#039;heap&#039;&#039; de la &#039;&#039;stack&#039;&#039;.&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int freeRam () {&lt;br /&gt;
  extern int __heap_start, *__brkval;&lt;br /&gt;
  int v;&lt;br /&gt;
  return (int) &amp;amp;v - (__brkval == 0 ? (int) &amp;amp;__heap_start : (int) __brkval);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
{|width=&amp;quot;90%&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
Pour suivre l&#039;évolution de la consommation mémoire il faut appeler, de manière régulière, la fonction précédente à des endroits choisi dans le code (là où on pense que la fuite se situe).&lt;br /&gt;
|}&lt;br /&gt;
Prenons l&#039;exemple suivant :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Pointeur accessible de manière globale&lt;br /&gt;
char* str;&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  printFreeRam();&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
  // Affectation d&#039;un tableau de taille 50&lt;br /&gt;
  str = (char*) malloc(50);&lt;br /&gt;
  printFreeRam();&lt;br /&gt;
  delay(200);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void printFreeRam () {&lt;br /&gt;
  extern int __heap_start, *__brkval;&lt;br /&gt;
  int v;&lt;br /&gt;
  v = (int) &amp;amp;v - (__brkval == 0 ? (int) &amp;amp;__heap_start : (int) __brkval);&lt;br /&gt;
  Serial.print(F(&amp;quot;Free ram : &amp;quot;));&lt;br /&gt;
  Serial.println(v);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
&lt;br /&gt;
On voit clairement la quantité de mémoire diminuer...&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Free ram : 1836&lt;br /&gt;
Free ram : 1784&lt;br /&gt;
Free ram : 1732&lt;br /&gt;
Free ram : 1680&lt;br /&gt;
Free ram : 1628&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Si on ajoute la ligne suivante après le &#039;&#039;malloc&#039;&#039; :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
free(str);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Plus aucun problème. &lt;br /&gt;
&lt;br /&gt;
Il ne faut pas oublier de libérer l&#039;espace réservè en mémoire ou, encore mieux, de reposer sur les des-allocation naturelle du système (en n&#039;utilisant pas &#039;&#039;malloc&#039;&#039; ou &#039;&#039;new&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
De plus, il ne faut pas oublier qu&#039;utiliser trop l&#039;allocation dynamique conduit à la fragmentation de la &#039;&#039;heap&#039;&#039;...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Limiter l&#039;utilisation de la mémoire==&lt;br /&gt;
===Mémoire Flash===&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Lorsque le programme est conséquent, par exemple à cause de l&#039;import de libraires, la mémoire flash commence à manquer cruellement. Il n&#039;y a pas vraiment de miracle mais, suivre les préceptes suivants peut aider :&lt;br /&gt;
*Utiliser des fonctions : lorsque c&#039;est possible, il faut essayer de factoriser le code qui se répète dans des fonctions. En plus d&#039;augmenter la lisibilité, cela permet de faciliter la correction de bug.&lt;br /&gt;
*Supprimer le code mort : le plus compliqué, dans des programmes de plusieurs millier de lignes, c&#039;est de s’apercevoir du code qui ne sert plus.&lt;br /&gt;
*Supprimer le &#039;&#039;bootloader&#039;&#039; : quand on à atteint les limites et que plus aucune optimisation n&#039;est possible, il faut penser à supprimer le &#039;&#039;bootloader&#039;&#039;. Cela va permettre à votre programme de s&#039;exécuter plus rapidement mais il n&#039;est plus possible d&#039;utiliser l&#039;Arduino tel quel, il faut utiliser un Arduino ISP (In-System Programer) comme celui ci-contre.&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Arduino isp.jpg|centré|250px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Mémoire flash et PROGMEM===&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_socket&amp;diff=4171</id>
		<title>C socket</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_socket&amp;diff=4171"/>
		<updated>2025-12-20T16:13:30Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
||&lt;br /&gt;
Les sockets permettent de connecter deux programmes qui s&#039;exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l&#039;utilisation d&#039;un port (TCP ou UDP) et d&#039;une adresse IP. &lt;br /&gt;
&lt;br /&gt;
Il y a deux types de sockets, une qui est démarrée par la partie serveur en &#039;&#039;écoute&#039;&#039; et l&#039;autre démarrée par la partie cliente qui se connecte à la première.&lt;br /&gt;
&lt;br /&gt;
Ci-contre une image résumant les différentes étapes pour arriver à l&#039;envoi de données.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Socket workflow.png|centré|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Création  =&lt;br /&gt;
== Côté serveur ==&lt;br /&gt;
Tout d&#039;abord il faut créer l&#039;objet socket:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int socket(int domain, int type, int protocol)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*domain &amp;amp;rarr; integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)&lt;br /&gt;
*type &amp;amp;rarr; type de communication&lt;br /&gt;
**SOCK_STREAM: TCP&lt;br /&gt;
**SOCK_DGRAM: UDP&lt;br /&gt;
**SOCK_RAW: socket à l&#039;état brut (bas niveau)&lt;br /&gt;
*protocol &amp;amp;rarr; valeur du champ &#039;&#039;protocol&#039;&#039; de l&#039;en-tête de niveau 3 (généralement &#039;&#039;0&#039;&#039;)&lt;br /&gt;
*la valeur de retour est le fichier descripteur de la socket ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur de socket créé, il est possible de le configurer:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* level &amp;amp;rarr; niveau d&#039;application de l&#039;option (SOL_SOCKET, TCP, UDP, ...);&lt;br /&gt;
* optname &amp;amp;rarr; le nom de l&#039;option (&#039;&#039;SO_REUSEADDR&#039;&#039;, &#039;&#039;SO_REUSEPORT&#039;&#039;, &#039;&#039;SO_KEEPALIVE&#039;&#039;, ...)&lt;br /&gt;
* optval, optlen  &amp;amp;rarr; utilisé pour accéder aux options de la socket;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
Il existe plusieurs options de socket, les plus utilisées étant :&lt;br /&gt;
* &#039;&#039;SO_REUSEADDR&#039;&#039; &amp;amp;rarr; permet de réutiliser l&#039;adresse de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_REUSEPORT&#039;&#039; &amp;amp;rarr; permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_KEEPALIVE&#039;&#039; &amp;amp;rarr; envoie des informations périodiquement pour tester si l&#039;extrémité du tunnel est toujours présente;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in {&lt;br /&gt;
    short            sin_family;   // e.g: AF_INET&lt;br /&gt;
    unsigned short   sin_port;     // e.g: htons(3490)&lt;br /&gt;
    struct in_addr   sin_addr;     // détaillé ci-dessous&lt;br /&gt;
    char             sin_zero[8];  // &#039;0&#039; habituellement&lt;br /&gt;
};&lt;br /&gt;
struct in_addr {&lt;br /&gt;
    unsigned long s_addr;  // initialiser avec inet_aton()&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant passer la socket en état d&#039;écoute, elle est prête à recevoir des connexions:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int listen(int sockfd, int backlog)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* backlog &amp;amp;rarr; taille maximum de la file d&#039;attente de la socket après laquelle le système répond avec &#039;&#039;ECONNREFUSED&#039;&#039;;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant attendre une connexion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; l&#039;adresse du client;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de la structure addr;&lt;br /&gt;
* le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
== Côté client ==&lt;br /&gt;
Après avoir créé la socket avec &#039;&#039;socket&#039;&#039; on peut se connecter à la partie serveur:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
= Lecture / écriture =&lt;br /&gt;
== Fonctions génériques ==&lt;br /&gt;
Tout d&#039;abord, on peut dire que la socket est un fichier et se manipule donc comme un fichier !&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;read&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;write&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Modification===&lt;br /&gt;
Pour modifier un descripteur fichier de socket on utilise &#039;&#039;fcntl&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fcntl(int fd, int cmd, ... /* int args */ );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* cmd &amp;amp;rarr; la commande de modification (descripteur, drapeaux, ...);&lt;br /&gt;
* args &amp;amp;rarr; les arguments correspondant à la commande&lt;br /&gt;
&lt;br /&gt;
Cette commande va nous permettre de modifier le drapeau du descripteur de la socket (&#039;&#039;F_SETFL&#039;&#039;) pour le rendre non bloquant (&#039;&#039;O_NONBLOCK&#039;&#039;) !&lt;br /&gt;
&lt;br /&gt;
== Fonctions spécifiques ==&lt;br /&gt;
Il existe des fonctions spécifiques pour manipuler les sockets. Ces fonctions ressemblent aux précédentes mais acceptent des drapeaux en plus pour pouvoir modifier le descripteur &#039;&#039;à la volée&#039;&#039;.&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recv(int fd, void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;send&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t send(int fd, const void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_CONFIRM&#039;&#039;, &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Cas d&#039;utilisation =&lt;br /&gt;
== Création ==&lt;br /&gt;
Pour commencer, il faut créer le descripteur de fichier:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int fdsocket;&lt;br /&gt;
if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
	printf(&amp;quot;Echec de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Paramétrage des options ==&lt;br /&gt;
Il faut maintenant configurer la réutilisation de l&#039;adresse et du port&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int opt = 1;&lt;br /&gt;
if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,	sizeof(opt)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echec de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Attachement ==&lt;br /&gt;
On peut maintenant attacher la socket à un port et une adresse:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in adresse;&lt;br /&gt;
&lt;br /&gt;
adresse.sin_family = AF_INET;&lt;br /&gt;
// Ecoute sur toutes les adresses (INADDR_ANY &amp;lt;=&amp;gt; 0.0.0.0)&lt;br /&gt;
adresse.sin_addr.s_addr = INADDR_ANY;&lt;br /&gt;
// Conversion du port en valeur réseaux (Host TO Network Short)&lt;br /&gt;
adresse.sin_port = htons(8080);&lt;br /&gt;
&lt;br /&gt;
if (bind(fdsocket, (struct sockaddr *) &amp;amp;adresse, sizeof(adresse)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echec d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mise en écoute ==&lt;br /&gt;
La socket est prête à passer à l&#039;écoute des connexions des clients:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
&lt;br /&gt;
if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echec de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Acceptation des connexions ==&lt;br /&gt;
Il est possible d&#039;accepter les nouvelles connexions:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int clientSocket;&lt;br /&gt;
// Structure contenant l&#039;adresse du client&lt;br /&gt;
struct sockaddr_in clientAdresse;&lt;br /&gt;
unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
if ((clientSocket = accept(fdsocket, (struct sockaddr *) &amp;amp;clientAdresse, &amp;amp;addrLen)) != -1) {&lt;br /&gt;
	// Convertion de l&#039;IP en texte&lt;br /&gt;
	char ip[INET_ADDRSTRLEN];&lt;br /&gt;
	inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
	printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Il est possible d&#039;utiliser &#039;&#039;fcntl&#039;&#039; pour passer le descripteur de la socket en non bloquant, avant l&#039;appel à la fonction &#039;&#039;accept&#039;&#039;. Si le descripteur reste bloquant, la fonction &#039;&#039;accept&#039;&#039; ne rendra pas la main tant qu&#039;un client ne se sera connecté, ayant pour résultat le blocage complet du programme !&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Passage en mode non bloquant&lt;br /&gt;
fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
On peut remarquer, au passage, l&#039;utilisation de &#039;&#039;inet_ntop&#039;&#039; pour convertir l&#039;adresse du client du format binaire au format text.&lt;br /&gt;
&lt;br /&gt;
== Lecture / Écriture ==&lt;br /&gt;
=== Mode connecté (TCP) ===&lt;br /&gt;
On peut maintenant utiliser le descripteur de la socket du client pour lire et écrire.&lt;br /&gt;
* avec les fonctions génériques:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
// Passage en mode non bloquant, sinon read attend&lt;br /&gt;
fcntl(clientSocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
int len = read(clientSocket, buffer, BUFFER_LEN);&lt;br /&gt;
&lt;br /&gt;
write(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* avec les fonctions spécifiques:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analysons le retour de la fonction &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039; lorsque l&#039;on utilise un descripteur non bloquant d&#039;après la [http://man7.org/linux/man-pages/man2/recvmsg.2.html documentation] :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
NAME &lt;br /&gt;
       recv, recvfrom, recvmsg - receive a message from a socket&lt;br /&gt;
[...]&lt;br /&gt;
MSG_DONTWAIT (since Linux 2.2)&lt;br /&gt;
    Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)). &lt;br /&gt;
[...]&lt;br /&gt;
RETURN VALUE&lt;br /&gt;
[...]&lt;br /&gt;
These calls return the number of bytes received, or -1 if an error&lt;br /&gt;
       occurred.  In the event of an error, errno is set to indicate the&lt;br /&gt;
       error.&lt;br /&gt;
When a stream socket peer has performed an orderly shutdown, the&lt;br /&gt;
       return value will be 0 (the traditional &amp;quot;end-of-file&amp;quot; return).&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le retour est :&lt;br /&gt;
* lorsque le descripteur est vide (aucune donnée de la part du client) &#039;&#039;-1&#039;&#039; et &#039;&#039;errno&#039;&#039; sera positionné à la valeur &#039;&#039;EAGAIN&#039;&#039;, il ne faut donc pas fermer la socket si le retour est &#039;&#039;-1&#039;&#039; ! Il faut d&#039;abord vérifier que &#039;&#039;errno&#039;&#039; &#039;&#039;&#039;n&#039;a pas&#039;&#039;&#039; la valeur &#039;&#039;EAGAIN&#039;&#039;;&lt;br /&gt;
* lorsque le retour est &#039;&#039;0&#039;&#039;, cela signifie que l&#039;extrémité est fermée, il faut donc fermer le descripteur local;&lt;br /&gt;
* lorsque la valeur est supérieure à &#039;&#039;0&#039;&#039; elle correspond au nombre d&#039;octets reçus.&lt;br /&gt;
&lt;br /&gt;
On peut donc en déduire le morceau de code suivant pour gérer le retour de &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
	// Une erreur est survenue&lt;br /&gt;
} else if (len == 0) {&lt;br /&gt;
	// Le client s&#039;est déconnecté (extrémité de la socket fermée)			&lt;br /&gt;
} else if (len &amp;gt; 0) {&lt;br /&gt;
	// Le client à envoyé des données	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mode datagramme (UDP) ===&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recvfrom&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recvfrom(int fd, void *buf, size_t count, int flags, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
*  addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;sendto&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t sendto(int fd, const void *buf, size_t count, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Exemples =&lt;br /&gt;
== Serveur TCP ==&lt;br /&gt;
Voici un exemple de serveur &#039;&#039;echo&#039;&#039; qui renvoie le message au client. Les connexions utilisent TCP et un simple client comme telnet suffit pour l&#039;utiliser.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_CLIENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]);&lt;br /&gt;
void manageClient(int clients[]);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création et initialisation du tableau contenant les descripteurs des sockets clients&lt;br /&gt;
	int clients[NB_CLIENTS];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		clients[i] = -1;&lt;br /&gt;
	}&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
			// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
			addClientToTab(clientSocket, clients);&lt;br /&gt;
		}&lt;br /&gt;
		manageClient(clients);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]) {&lt;br /&gt;
	// On vérifie si on à de la place de libre&lt;br /&gt;
	int found = -1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On cherche de la place&lt;br /&gt;
		if (clients[i] == -1) {&lt;br /&gt;
			// On ajoute le client&lt;br /&gt;
			printf(&amp;quot;Ajout du client à l&#039;index %i\n&amp;quot;, i);&lt;br /&gt;
			clients[i] = clientSocket;&lt;br /&gt;
			// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
			send(clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;, 	strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;),&lt;br /&gt;
					MSG_DONTWAIT);&lt;br /&gt;
			found = 0;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (found == -1) {&lt;br /&gt;
		// On a plus de place de libre&lt;br /&gt;
		puts(&amp;quot;Plus de place, désolé&amp;quot;);&lt;br /&gt;
		close(clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int clients[]) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On vérifie les messages&lt;br /&gt;
		int clientSocket = clients[i];&lt;br /&gt;
		if (clientSocket == -1) {&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
		// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
		int isClosed = 0;&lt;br /&gt;
		if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
			// Une erreur est survenue&lt;br /&gt;
			printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len == 0) {&lt;br /&gt;
			// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len &amp;gt; 0) {&lt;br /&gt;
			// Ajout du terminateur de chaîne&lt;br /&gt;
			buffer[len] = &#039;\0&#039;;&lt;br /&gt;
			if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
				// Le client veut se déconnecter&lt;br /&gt;
				send(clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
				isClosed = 1;&lt;br /&gt;
			} else {&lt;br /&gt;
				// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
				int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
				char response[len];&lt;br /&gt;
				strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
				strcat(response, buffer);&lt;br /&gt;
				// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
				send(clientSocket, response, strlen(response), 0);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		if (isClosed == 1) {&lt;br /&gt;
			// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
			printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
			// Fermeture de la socket&lt;br /&gt;
			close(clientSocket);&lt;br /&gt;
			// On fait de la place dans le tableau&lt;br /&gt;
			clients[i] = -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;telnet&#039;&#039; (&#039;&#039;yum -y install telnet&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# telnet 127.0.0.1 8080&lt;br /&gt;
Trying 127.0.0.1...&lt;br /&gt;
Connected to 127.0.0.1.&lt;br /&gt;
Escape character is &#039;^]&#039;.&lt;br /&gt;
Entrez &#039;exit&#039; pour quitter&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Serveur UDP ==&lt;br /&gt;
Si on essaie de reproduire le même type de serveur mais cette fois-ci avec une socket en UDP, cela devient beaucoup plus simple. En effet, en UDP, plus besoin de tenir un tableau avec les descripteurs des sockets des clients car... on est en mode déconnecté (datagramme) ! Entendez par là que les clients envoient un message sans attendre de réponse et sans se soucier de savoir si le message est arrivé au serveur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
void manageClient(int serverSocket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		manageClient(serverSocket);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en UDP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int serverSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	// On vérifie si on a reçu un message&lt;br /&gt;
	int len = recvfrom(serverSocket, buffer, BUFFER_LEN, MSG_DONTWAIT,&lt;br /&gt;
			(struct sockaddr*) &amp;amp;clientAdresse, &amp;amp;addrLen);&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		printf(&amp;quot;Problème de socket %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		printf(&amp;quot;Message reçu de %s:%i\n&amp;quot;, inet_ntoa(clientAdresse.sin_addr),&lt;br /&gt;
				ntohs(clientAdresse.sin_port));&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
		len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
		char response[len];&lt;br /&gt;
		strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
		strcat(response, buffer);&lt;br /&gt;
		// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
		sendto(serverSocket, response, strlen(response), MSG_DONTWAIT,&lt;br /&gt;
				(struct sockaddr*) &amp;amp;clientAdresse, addrLen);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;netcat&#039;&#039; (&#039;&#039;yum -y install nc&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nc -u 127.0.0.1 8080&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Cas du multicast ==&lt;br /&gt;
Il est intéressant parfois de s&#039;abonner à un groupe multicast pour recevoir des messages.&lt;br /&gt;
&lt;br /&gt;
Pour cela il suffit d&#039;utiliser la structure &#039;&#039;mreq&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct ip_mreq mreq;&lt;br /&gt;
mreq.imr_multiaddr.s_addr = inet_addr(ip); // ip correspond à l&#039;adresse multicast eg. &amp;quot;235.0.0.1&amp;quot;&lt;br /&gt;
mreq.imr_interface.s_addr = htonl(INADDR_ANY); // cette ip correspond à l&#039;adresse locale&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il faut ensuite positionner l&#039;option sur le descripteur de fichier de la socket concernée:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
if (setsockopt(fdsocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &amp;amp;mreq,	sizeof(mreq)) &amp;lt; 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Notification évènementielle avec epoll =&lt;br /&gt;
Depuis la version 2.5 du noyau Linux il est possible d&#039;utiliser une série de fonctions, appartenant à l&#039;espace utilisateur (user-space), permettant de suivre l&#039;état d&#039;un descripteur de fichier.&lt;br /&gt;
La différence avec read, recv ou encore recvfrom c&#039;est que l&#039;on va pouvoir surveiller plusieurs descripteurs de fichiers en même temps, laissant le CPU libre pour effectuer autre chose !&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
Tout d&#039;abord il faut créer un descripteur de fichier de type &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_create1(int flags);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* flags &amp;amp;rarr; EPOLL_CLOEXEC pour éviter les situations de compétition en environnement parallélisé;&lt;br /&gt;
* le code retour correspond au descripteur &#039;&#039;epoll&#039;&#039; ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur &#039;&#039;epoll&#039;&#039; créé, il faut lui associer un événement:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* op &amp;amp;rarr; une opération parmi:&lt;br /&gt;
** EPOLL_CTL_ADD &amp;amp;rarr; pour ajouter un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_MOD &amp;amp;rarr; pour modifier un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_DEL &amp;amp;rarr; pour supprimer un événement sur un descripteur de fichier;&lt;br /&gt;
* fd &amp;amp;rarr; un descripteur de fichier à surveiller;&lt;br /&gt;
* event &amp;amp;rarr; la structure contenant l&#039;événement à surveiller (&#039;&#039;EPOLLIN&#039;&#039;, &#039;&#039;EPOLLOUT&#039;&#039;, EPOLLRDHUP&#039;&#039;, ...);&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; en cas de succès ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Voici la structure &#039;&#039;epoll_event&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct epoll_event {&lt;br /&gt;
	uint32_t events;    /* Epoll events */&lt;br /&gt;
	epoll_data_t data;  /* User data variable */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
typedef union epoll_data {&lt;br /&gt;
	void    *ptr;&lt;br /&gt;
	int      fd;&lt;br /&gt;
	uint32_t u32;&lt;br /&gt;
	uint64_t u64;&lt;br /&gt;
} epoll_data_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enfin, il ne reste plus qu&#039;à &#039;&#039;attendre&#039;&#039; des événements:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* events &amp;amp;rarr; un tableau de &#039;&#039;epoll_event contenant les événements;&lt;br /&gt;
* maxevents &amp;amp;rarr; le nombre maximum d&#039;événements à traiter (selon la taille du tableau);&lt;br /&gt;
* timeout &amp;amp;rarr; le temps, en millisecondes que la fonction attend:&lt;br /&gt;
** &#039;&#039;-1&#039;&#039; &amp;amp;rarr; la fonction devient bloquante;&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; retourne immédiatement;&lt;br /&gt;
** &#039;&#039;&amp;gt; 0&#039;&#039; &amp;amp;rarr; attend le nombre de millisecondes spécifiées;&lt;br /&gt;
* le code retour correspond au nombre d&#039;événements ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
== Améliorations architecturales ==&lt;br /&gt;
=== Gestion des utilisateurs===&lt;br /&gt;
Tout d&#039;abord, le fait de laisser &#039;&#039;epoll&#039;&#039; gérer les descripteurs de socket nous permet de supprimer le tableau &#039;&#039;clients&#039;&#039; ainsi que la variable &#039;&#039;NB_CLIENTS&#039;&#039;. En effet, la référence vers le descripteur est maintenue par &#039;&#039;epoll&#039;&#039; et nous pouvons gérer maintenant un nombre &#039;&#039;illimité&#039;&#039; de clients.&lt;br /&gt;
&lt;br /&gt;
=== Performances ===&lt;br /&gt;
La boucle &#039;&#039;while&#039;&#039; de notre serveur consomme énormément de CPU, environ 100% de CPU par client ! On aurait pu utiliser un temporisateur avec &#039;&#039;nanosleep&#039;&#039; pour baisser la consommation CPU entre 5% et 10%...&lt;br /&gt;
Le fait de basculer sur une fonction bloquante comme &#039;&#039;epoll_wait&#039;&#039; qui utilise des signaux (interruptions) permet de réduire considérablement la consommation CPU. Gardez à l&#039;esprit qu&#039;il n&#039;y a rien de mieux que les interruptions, la preuve, lorsque l&#039;on fait un &#039;&#039;top&#039;&#039;, notre programme a complètement disparu de la liste !&lt;br /&gt;
&lt;br /&gt;
== Modification du serveur ==&lt;br /&gt;
Modifions le code du serveur TCP pour y intégrer &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_EVENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket);&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket);&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket);&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création du descripteur EPOLL&lt;br /&gt;
	int epfd = epoll_create1(0);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event events[NB_EVENTS];&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	addEpollEvent(&amp;amp;epfd, &amp;amp;serverSocket);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		int nfds = epoll_wait(epfd, events, NB_EVENTS, -1);&lt;br /&gt;
		for (int i = 0; i &amp;lt; nfds; i++) {&lt;br /&gt;
			if (events[i].data.fd == serverSocket) {&lt;br /&gt;
				if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
					// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
					addClientToTab(&amp;amp;epfd, &amp;amp;clientSocket);&lt;br /&gt;
				}&lt;br /&gt;
			} else if (events[i].events &amp;amp; EPOLLIN) {&lt;br /&gt;
				// On a reçu quelque chose&lt;br /&gt;
				manageClient(&amp;amp;epfd, &amp;amp;events[i].data.fd);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	close(serverSocket);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket) {&lt;br /&gt;
	addEpollEvent(epfd, clientSocket);&lt;br /&gt;
	// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
	send(*clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;,&lt;br /&gt;
			strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	int len = recv(*clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
	// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
	int isClosed = 0;&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		// Une erreur est survenue&lt;br /&gt;
		printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len == 0) {&lt;br /&gt;
		// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
			// Le client veut se déconnecter&lt;br /&gt;
			send(*clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else {&lt;br /&gt;
			// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
			int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
			char response[len];&lt;br /&gt;
			strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
			strcat(response, buffer);&lt;br /&gt;
			// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
			send(*clientSocket, response, strlen(response), 0);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (isClosed == 1) {&lt;br /&gt;
		// On enlève la socket des événements EPOLL&lt;br /&gt;
		delEpollEvent(epfd, clientSocket);&lt;br /&gt;
		// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
		printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
		// Fermeture de la socket&lt;br /&gt;
		close(*clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Ajoute un descripteur de socket aux événements EPOLL&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Ajout de la socket %i dans les événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// Initialisation de la structure&lt;br /&gt;
	memset(&amp;amp;event, 0, sizeof(struct epoll_event));&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_ADD, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
// Efface un descripteur de socket des événements EPOLL&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Suppression de la socket %i des événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// Initialisation de la structure&lt;br /&gt;
	memset(&amp;amp;event, 0, sizeof(struct epoll_event));&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_DEL, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Voici ce que l&#039;on peut observer dans la console:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création de la socket&lt;br /&gt;
Paramètrage de la socket&lt;br /&gt;
Attachement de la socket sur le port 8080&lt;br /&gt;
Mise en écoute de la socket&lt;br /&gt;
Ajout de la socket 4 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:17607&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
Connexion de 127.0.0.1:18119&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:18631&lt;br /&gt;
Ajout de la socket 6 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Nagios&amp;diff=4170</id>
		<title>Nagios</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Nagios&amp;diff=4170"/>
		<updated>2025-12-20T16:12:06Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Prérequis =&lt;br /&gt;
Dans un premier temps, il faudra avoir une connexion à Internet, utiliser un serveur DNS et désactiver SELinux.&lt;br /&gt;
&lt;br /&gt;
Pour ceux qui auraient manqué des étapes, les voici:&lt;br /&gt;
* [[resolv.conf|Configuration du client DNS]]&lt;br /&gt;
* [[ifcfg-ethX|Paramétrer sa carte réseau]]&lt;br /&gt;
* [[SELinux#Changement_d.27.C3.A9tat|Désactiver SELinux]]&lt;br /&gt;
&lt;br /&gt;
Une fois ces étapes effectuées, assurez-vous d&#039;avoir installé le [[Linux_repository | dépôt EPEL]] car la majeure partie de nos paquets viennent de cette source !&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
Nagios a besoin d&#039;un serveur web pour fonctionner, ce qui nous donne :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
yum -y install httpd nagios nagios-plugins-all&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Avant d&#039;aller plus loin, intéressons-nous au fonctionnement de Nagios.&lt;br /&gt;
&lt;br /&gt;
= Fonctionnement =&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
Plusieurs éléments de configuration sont présents dans Nagios:&lt;br /&gt;
===timeperiods===&lt;br /&gt;
Elles permettent de fixer les plages de notifications des contacts et de contrôle des hôtes et services.&lt;br /&gt;
===contact===&lt;br /&gt;
Ceux sont les personnes qu&#039;il faut alerter par la supervision.&lt;br /&gt;
===contactgroup===&lt;br /&gt;
Ceux sont des groupes de plusieurs &#039;&#039;contact&#039;&#039; qui vont être alertés en même temps. Ceux sont souvent des personnes occupant le même poste (administrateur système, webmestre&lt;br /&gt;
, responsable d&#039;exploitation, etc...)&lt;br /&gt;
===hosts===&lt;br /&gt;
Ils représentent les machines physiques à superviser.&lt;br /&gt;
===hostgroup===&lt;br /&gt;
Permettent de rassembler plusieurs &#039;&#039;host&#039;&#039; occupant le même rôle ou faisant tourner une même application.&lt;br /&gt;
===services===&lt;br /&gt;
Ceux sont les contrôles à effectuer sur un &#039;&#039;host&#039;&#039; (DNS, est-ce que le démon SSH tourne ?, % d&#039;utilisation CPU, % d&#039;utilisation mémoire, l’IO disque, ...)&lt;br /&gt;
===servicegroup===&lt;br /&gt;
Permet de rassembler des services pour les considérer comme un bloc comme c&#039;est souvent le cas dans un cluster applicatif.&lt;br /&gt;
===template===&lt;br /&gt;
Ils permettent d&#039;éviter les redondances au niveau des définitions d’hôtes et de services en regroupant des variables communes.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:nagios_config_operation.png|centré]]&lt;br /&gt;
&lt;br /&gt;
== Notifications ==&lt;br /&gt;
#Au début le service est disponible, le voyant est au vert (état OK)&lt;br /&gt;
#Quand le service ne répond plus, Nagios le passe de l’état &#039;&#039;OK&#039;&#039; à &#039;&#039;Warning&#039;&#039;. Le service passe en état &#039;&#039;SOFT&#039;&#039;, Nagios va déclencher le cycle de vérification de la fiabilité de l’incident (utilisation de retry_check et max_check_attemps)&lt;br /&gt;
# Au bout du cycle de vérification, Nagios passe le service en état &#039;&#039;HARD&#039;&#039;, l’incident est certifié. Le cycle de notification va commencer (tous les notification_interval).&lt;br /&gt;
# Quand le service répond de nouveau, Nagios envoie une dernière notification pour signaler que le service est repassé à l&#039;état &#039;&#039;OK&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Fichier:nagios_service_notification.png|centré]]&lt;br /&gt;
&lt;br /&gt;
== Vérifications distantes ==&lt;br /&gt;
Pour assurer des vérifications &#039;&#039;locales&#039;&#039; sur des machines distantes, il faut un moyen d&#039;exécuter les &#039;&#039;plugins&#039;&#039; sur la machine distante. Ce moyen c&#039;est &#039;&#039;NRPE&#039;&#039; pour &#039;&#039;Nagios Remote Plugins Executor&#039;&#039;.&lt;br /&gt;
[[Fichier:nagios_nrpe_operation.png|centré]]&lt;br /&gt;
&lt;br /&gt;
= Fichiers de configuration =&lt;br /&gt;
Commençons par observer le contenu du répertoire &#039;&#039;/etc/nagios/objects&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ll /etc/nagios/objects&lt;br /&gt;
total 48&lt;br /&gt;
-rw-rw-r--. 1 root root  7704 31 août   2013 commands.cfg&lt;br /&gt;
-rw-rw-r--. 1 root root  2166 31 août   2013 contacts.cfg&lt;br /&gt;
-rw-rw-r--. 1 root root  5403 31 août   2013 localhost.cfg&lt;br /&gt;
-rw-rw-r--. 1 root root  3124 31 août   2013 printer.cfg&lt;br /&gt;
-rw-rw-r--. 1 root root  3293 31 août   2013 switch.cfg&lt;br /&gt;
-rw-rw-r--. 1 root root 11158 31 août   2013 templates.cfg&lt;br /&gt;
-rw-rw-r--. 1 root root  3208 31 août   2013 timeperiods.cfg&lt;br /&gt;
-rw-rw-r--. 1 root root  4019 31 août   2013 windows.cfg&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Timeperiods.cfg ==&lt;br /&gt;
Dans ce fichier sont déclarés les périodes que nous allons pouvoir utiliser pour la vérification des hôtes ou des services.&lt;br /&gt;
Ci-dessous un exemple :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define timeperiod{&lt;br /&gt;
   timeperiod_name 24x7&lt;br /&gt;
   alias           24 Hours A Day, 7 Days A Week&lt;br /&gt;
   sunday          00:00-24:00&lt;br /&gt;
   monday          00:00-24:00&lt;br /&gt;
   tuesday         00:00-24:00&lt;br /&gt;
   wednesday       00:00-24:00&lt;br /&gt;
   thursday        00:00-24:00&lt;br /&gt;
   friday          00:00-24:00&lt;br /&gt;
   saturday        00:00-24:00&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit qu&#039;une période se définit comme suit :&lt;br /&gt;
* &#039;&#039;timeperiod_name&#039;&#039; : identifiant de la période, doit être unique dans toute la configuration de &#039;Nagios&#039;&#039;;&lt;br /&gt;
* &#039;&#039;alias&#039;&#039; : appellation longue, permettant de mieux identifier la période;&lt;br /&gt;
* &#039;&#039;monday 00:00-24:00&#039;&#039;, &#039;&#039;tuesday 00:00-24:00&#039;&#039;, ... : les jours de la semaine suivis de l&#039;heure de début et de l&#039;heure de fin.&lt;br /&gt;
&lt;br /&gt;
Pas très compliqué de créer sa propre période. Imaginons que nous voulions une période qui correspond aux lundis matin entre 8h et 9h :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define timeperiod{&lt;br /&gt;
   timeperiod_name week_start&lt;br /&gt;
   alias           Lundi matin de 8h à 9h&lt;br /&gt;
   monday          08:00-09:00&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== contact.cfg ==&lt;br /&gt;
=== Contact ===&lt;br /&gt;
Dans ce fichier sont déclarés les contacts qui vont pouvoir être contactés quand un problème surviendra. Ci-dessous un exemple : &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define contact{&lt;br /&gt;
   contact_name                    nagiosadmin             ; Short name of user&lt;br /&gt;
   use                             generic-contact         ; Inherit default values from generic-contact template (defined above)&lt;br /&gt;
   alias                           Nagios Admin            ; Full name of user&lt;br /&gt;
   email                           nagios@localhost        ; &amp;lt;&amp;lt;***** CHANGE THIS TO YOUR EMAIL ADDRESS ******&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si on regarde bien, ce contact utilise un template qui contient les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define contact{&lt;br /&gt;
   name                            generic-contact         ; The name of this contact template&lt;br /&gt;
   service_notification_period     24x7                    ; service notifications can be sent anytime&lt;br /&gt;
   host_notification_period        24x7                    ; host notifications can be sent anytime&lt;br /&gt;
   service_notification_options    w,u,c,r,f,s             ; send notifications for all service states, flapping events, and scheduled downtime events&lt;br /&gt;
   host_notification_options       d,u,r,f,s               ; send notifications for all host states, flapping events, and scheduled downtime events&lt;br /&gt;
   service_notification_commands   notify-service-by-email ; send service notifications via email&lt;br /&gt;
   host_notification_commands      notify-host-by-email    ; send host notifications via email&lt;br /&gt;
   register                        0                       ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL CONTACT, JUST A TEMPLATE!&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Quelques précisions : &lt;br /&gt;
* les &#039;&#039;notification_options&#039;&#039; prennent les paramètres suivants : &lt;br /&gt;
**w: notification sur les états &#039;&#039;WARNING&#039;&#039;;&lt;br /&gt;
**u: notification sur les états &#039;&#039;UNKNOWN&#039;&#039;;&lt;br /&gt;
**c: notification sur les états &#039;&#039;CRITICAL&#039;&#039;;&lt;br /&gt;
**r: notification quand le service repasse à l&#039;état &#039;&#039;OK&#039;&#039; (&#039;&#039;RECOVERY&#039;&#039;);&lt;br /&gt;
**f: notification quand il y a du &#039;&#039;flapping&#039;&#039; (quand le service démarre et s&#039;arrête sans cesse);&lt;br /&gt;
**s: notification quand la période d&#039;arrêt programmée (&#039;&#039;SCHEDULED DOWNTIME&#039;&#039;) est terminée;&lt;br /&gt;
**n: pas de notification (&#039;&#039;NONE&#039;&#039;).&lt;br /&gt;
=== Notifications ===&lt;br /&gt;
*les &#039;&#039;notification_commands&#039;&#039; sont déclarées dans le fichier &#039;&#039;/etc/nagios/objects/commands.cfg:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# &#039;notify-host-by-email&#039; command definition&lt;br /&gt;
define command{&lt;br /&gt;
   command_name    notify-host-by-email&lt;br /&gt;
   command_line    /usr/bin/printf &amp;quot;%b&amp;quot; &amp;quot;***** Nagios *****\n&lt;br /&gt;
   \nNotification Type: $NOTIFICATIONTYPE$&lt;br /&gt;
   \nHost: $HOSTNAME$ &lt;br /&gt;
   \nState: $HOSTSTATE$&lt;br /&gt;
   \nAddress: $HOSTADDRESS$&lt;br /&gt;
   \nInfo: $HOSTOUTPUT$&lt;br /&gt;
   \n\nDate/Time: $LONGDATETIME$\n&amp;quot; | /bin/mail -s &amp;quot;** $NOTIFICATIONTYPE$ Host Alert: $HOSTNAME$ is $HOSTSTATE$ **&amp;quot; $CONTACTEMAIL$&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# &#039;notify-service-by-email&#039; command definition&lt;br /&gt;
define command{&lt;br /&gt;
   command_name    notify-service-by-email&lt;br /&gt;
   command_line    /usr/bin/printf &amp;quot;%b&amp;quot; &amp;quot;***** Nagios *****\n&lt;br /&gt;
   \nNotification Type: $NOTIFICATIONTYPE$&lt;br /&gt;
   \n\nService: $SERVICEDESC$\nHost: $HOSTALIAS$&lt;br /&gt;
   \nAddress: $HOSTADDRESS$\nState: $SERVICESTATE$&lt;br /&gt;
   \n\nDate/Time: $LONGDATETIME$&lt;br /&gt;
   \n\nAdditional Info:\n\n$SERVICEOUTPUT$\n&amp;quot; | /bin/mail -s &amp;quot;** $NOTIFICATIONTYPE$ Service Alert: $HOSTALIAS$/$SERVICEDESC$ is $SERVICESTATE$ **&amp;quot; $CONTACTEMAIL$&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ces deux commandes envoient un mail (&#039;&#039;/bin/mail&#039;&#039;) au contact désigné.&lt;br /&gt;
=== Groupe ===&lt;br /&gt;
Toujours dans le fichier &#039;&#039;/etc/nagios/objects/contact.cfg&#039;&#039; on peut trouver la déclaration d&#039;un groupe de contact :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define contactgroup{&lt;br /&gt;
   contactgroup_name       admins&lt;br /&gt;
   alias                   Nagios Administrators&lt;br /&gt;
   members                 nagiosadmin&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Dans ce groupe figure uniquement le contact &#039;&#039;nagiosadmin&#039;&#039; mais il est possible d&#039;en ajouter en les séparant par une virgule.&lt;br /&gt;
&lt;br /&gt;
==commands.cfg==&lt;br /&gt;
=== Déclaration d&#039;une commande===&lt;br /&gt;
Dans ce fichier sont écrites différentes commandes que l&#039;on peut utiliser pour faire des vérifications sur des hôtes.&lt;br /&gt;
Ces commandes utilisent des &#039;plugins&#039; qui se trouvent dans le répertoire &#039;&#039;/usr/lib64/nagios/plugins&#039;&#039; et nous allons plutôt détailler l&#039;usage de ces &#039;plugins&#039;.&lt;br /&gt;
&lt;br /&gt;
Pour terminer avec ce fichier, nous allons prendre un exemple. Le plugin &#039;&#039;check_procs&#039;&#039;, qui permet de vérifier combien de processus tournent sur une machine et utilisé dans le fichier commande comme suit :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define command{&lt;br /&gt;
   command_name    check_local_procs&lt;br /&gt;
   command_line    $USER1$/check_procs -w $ARG1$ -c $ARG2$ -s $ARG3$&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Seulement, ce n&#039;est pas la seule façon d&#039;utiliser &#039;&#039;check_procs&#039;&#039;, et on peut s&#039;en rendre compte en demandant le &#039;&#039;help&#039;&#039; du &#039;plugin&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# /usr/lib64/nagios/plugins/check_procs -h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut utiliser l&#039;option &#039;&#039;-u&#039;&#039; pour vérifier si un utilisateur a lancé un processus. Cela peut être pratique si vous voulez vérifier que certain démon fonctionnent, comme par exemple &#039;&#039;ntp&#039;&#039; : &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ps -ef | grep ntp | grep -v grep&lt;br /&gt;
ntp       10458      1  0 07:55 ?        00:00:00 ntpd -u ntp:ntp -p /var/run/ntpd.pid -g&lt;br /&gt;
# ./check_procs -u ntp&lt;br /&gt;
PROCS OK: 1 processus avec UID = 38 (ntp)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Cependant, la commande &#039;&#039;check_local_procs&#039;&#039; ne permet pas d&#039;utiliser le &#039;plugin&#039; avec cet argument... Déclarons une nouvelle commande !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define command{&lt;br /&gt;
   command_name    check_user_process&lt;br /&gt;
   command_line    $USER1$/check_procs -w $ARG1$ -c $ARG2$ -u $ARG3$&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On laisse les options &#039;&#039;-w&#039;&#039; et &#039;&#039;-c&#039;&#039; pour nous pouvoir mettre des paliers sur le nombre de processus lancés. &lt;br /&gt;
Dans le cas d&#039;un serveur Apache, cela peut être intéressant de déclencher des alarmes en fonction du nombre de processus lancés.&lt;br /&gt;
En effet, si trop de processus sont démarrés, le serveur est peut-être trop sollicité ou sous en train de subir une attaque DDOS.&lt;br /&gt;
On peut de toute façon, passer la valeur &#039;&#039;-1&#039;&#039; à &#039;&#039;-w&#039;&#039; ou &#039;&#039;-c&#039;&#039; pour les désactiver.&lt;br /&gt;
&lt;br /&gt;
===Listes des commandes ===&lt;br /&gt;
*notify-host-by-email&lt;br /&gt;
*notify-service-by-email&lt;br /&gt;
*check-host-alive&lt;br /&gt;
*check_local_disk&lt;br /&gt;
*check_local_load&lt;br /&gt;
*check_local_procs&lt;br /&gt;
*check_local_users&lt;br /&gt;
*check_local_swap&lt;br /&gt;
*check_local_mrtgtraf&lt;br /&gt;
*check_ftp&lt;br /&gt;
*check_hpjd&lt;br /&gt;
*check_snmp&lt;br /&gt;
*check_http&lt;br /&gt;
*check_ssh&lt;br /&gt;
*check_dhcp&lt;br /&gt;
*check_ping&lt;br /&gt;
*check_pop&lt;br /&gt;
*check_imap&lt;br /&gt;
*check_smtp&lt;br /&gt;
*check_tcp&lt;br /&gt;
*check_udp&lt;br /&gt;
*check_nt&lt;br /&gt;
*process-host-perfdata&lt;br /&gt;
*process-service-perfdata&lt;br /&gt;
&lt;br /&gt;
== localhost.cfg ==&lt;br /&gt;
===Définition d&#039;un hôte===&lt;br /&gt;
Au début du fichier, on trouve la déclaration de l&#039;hôte:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define host{&lt;br /&gt;
   use                     linux-server            ; Name of host template to use&lt;br /&gt;
   host_name               localhost&lt;br /&gt;
   alias                   localhost&lt;br /&gt;
   address                 127.0.0.1&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut remarquer que cette déclaration utilise le template &#039;&#039;linux-server&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define host{&lt;br /&gt;
   name                       linux-server    ; The name of this host template&lt;br /&gt;
   use                        generic-host    ; This template inherits other values from the generic-host template&lt;br /&gt;
   check_period               24x7            ; By default, Linux hosts are checked round the clock&lt;br /&gt;
   check_interval             5               ; Actively check the host every 5 minutes&lt;br /&gt;
   retry_interval             1               ; Schedule host check retries at 1 minute intervals&lt;br /&gt;
   max_check_attempts         10              ; Check each Linux host 10 times (max)&lt;br /&gt;
   check_command              check-host-alive ; Default command to check Linux hosts&lt;br /&gt;
   notification_period        workhours       ; Linux admins hate to be woken up, so we only notify during the day&lt;br /&gt;
   notification_interval      120             ; Resend notifications every 2 hours&lt;br /&gt;
   notification_options       d,u,r           ; Only send notifications for specific host states&lt;br /&gt;
   contact_groups             admins          ; Notifications get sent to the admins by default&lt;br /&gt;
   register                   0               ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL HOST, JUST A TEMPLATE!&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Qui lui-même utilise le template &#039;&#039;generic-host&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define host{&lt;br /&gt;
   name                            generic-host    ; The name of this host template&lt;br /&gt;
   notifications_enabled           1               ; Host notifications are enabled&lt;br /&gt;
   event_handler_enabled           1               ; Host event handler is enabled&lt;br /&gt;
   flap_detection_enabled          1               ; Flap detection is enabled&lt;br /&gt;
   failure_prediction_enabled      1               ; Failure prediction is enabled&lt;br /&gt;
   process_perf_data               1               ; Process performance data&lt;br /&gt;
   retain_status_information       1               ; Retain status information across program restarts&lt;br /&gt;
   retain_nonstatus_information    1               ; Retain non-status information across program restarts&lt;br /&gt;
   notification_period             24x7            ; Send host notifications at any time&lt;br /&gt;
   register                        0               ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL HOST, JUST A TEMPLATE!&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Définition des services ===&lt;br /&gt;
Plus loin dans le fichier, nous pouvons voir la déclaration des services. Les services utilisent quatre paramètres:&lt;br /&gt;
* un &#039;&#039;template&#039; ;&lt;br /&gt;
* un &#039;&#039;host&#039;&#039; ;&lt;br /&gt;
* une description ;&lt;br /&gt;
* une commande.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define service{&lt;br /&gt;
   use                             local-service         ; Name of service template to use&lt;br /&gt;
   host_name                       localhost&lt;br /&gt;
   service_description             Root Partition&lt;br /&gt;
   check_command                   check_local_disk!20%!10%!/&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut remarquer que cette déclaration utilise le template &#039;&#039;local-service&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define service{&lt;br /&gt;
   name                            local-service           ; The name of this service template&lt;br /&gt;
   use                             generic-service         ; Inherit default values from the generic-service definition&lt;br /&gt;
   max_check_attempts              4                       ; Re-check the service up to 4 times in order to determine its final (hard) state&lt;br /&gt;
   normal_check_interval           5                       ; Check the service every 5 minutes under normal conditions&lt;br /&gt;
   retry_check_interval            1                       ; Re-check the service every minute until a hard state can be determined&lt;br /&gt;
   register                        0                       ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL SERVICE, JUST A TEMPLATE!&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Qui lui même utilise le template &#039;&#039;generic-host&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define service{&lt;br /&gt;
   name                            generic-service         ; The &#039;name&#039; of this service template&lt;br /&gt;
   active_checks_enabled           1                       ; Active service checks are enabled&lt;br /&gt;
   passive_checks_enabled          1                       ; Passive service checks are enabled/accepted&lt;br /&gt;
   parallelize_check               1                       ; Active service checks should be parallelized (disabling this can lead to major performance problems)&lt;br /&gt;
   obsess_over_service             1                       ; We should obsess over this service (if necessary)&lt;br /&gt;
   check_freshness                 0                       ; Default is to NOT check service &#039;freshness&#039;&lt;br /&gt;
   notifications_enabled           1                       ; Service notifications are enabled&lt;br /&gt;
   event_handler_enabled           1                       ; Service event handler is enabled&lt;br /&gt;
   flap_detection_enabled          1                       ; Flap detection is enabled&lt;br /&gt;
   failure_prediction_enabled      1                       ; Failure prediction is enabled&lt;br /&gt;
   process_perf_data               1                       ; Process performance data&lt;br /&gt;
   retain_status_information       1                       ; Retain status information across program restarts&lt;br /&gt;
   retain_nonstatus_information    1                       ; Retain non-status information across program restarts&lt;br /&gt;
   is_volatile                     0                       ; The service is not volatile&lt;br /&gt;
   check_period                    24x7                    ; The service can be checked at any time of the day&lt;br /&gt;
   max_check_attempts              3                       ; Re-check the service up to 3 times in order to determine its final (hard) state&lt;br /&gt;
   normal_check_interval           10                      ; Check the service every 10 minutes under normal conditions&lt;br /&gt;
   retry_check_interval            2                       ; Re-check the service every two minutes until a hard state can be determined&lt;br /&gt;
   contact_groups                  admins                  ; Notifications get sent out to everyone in the &#039;admins&#039; group&lt;br /&gt;
   notification_options            w,u,c,r                 ; Send notifications about warning, unknown, critical, and recovery events&lt;br /&gt;
   notification_interval           60                      ; Re-notify about service problems every hour&lt;br /&gt;
   notification_period             24x7                    ; Notifications can be sent out at any time&lt;br /&gt;
   register                        0                      ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL SERVICE, JUST A TEMPLATE!&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration =&lt;br /&gt;
Les ajouts de configuration se font dans le répertoire &#039;&#039;/etc/nagios/conf.d&#039;&#039;.&lt;br /&gt;
== Hôtes ==&lt;br /&gt;
Dans cet exemple, nous considérerons un serveur DHCP à l&#039;adresse &#039;&#039;192.168.3.251&#039;&#039;. Les lignes de configuration seront écrites dans le fichier &#039;&#039;/etc/nagios/conf.d/sdhcpd.conf&#039;&#039;&lt;br /&gt;
===Déclaration de l&#039;hôte===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define host{&lt;br /&gt;
   use linux-server&lt;br /&gt;
   host_name dhcp.tala-informatique.fr&lt;br /&gt;
   alias sdhcpd&lt;br /&gt;
   address 192.168.3.251&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Vérifications externes ===&lt;br /&gt;
Dans un premier temps, nous allons déclarer des vérifications externes qui ne nécessitent aucune intervention sur la machine à surveiller.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
## SERVICE DEFINITION ##&lt;br /&gt;
define service{&lt;br /&gt;
   use                    local-service&lt;br /&gt;
   host_name              dhcp.tala-informatique.fr&lt;br /&gt;
   service_description    Host alive&lt;br /&gt;
   check_command          check-host-alive&lt;br /&gt;
}&lt;br /&gt;
define service{&lt;br /&gt;
   use                    local-service&lt;br /&gt;
   host_name              dhcp.tala-informatique.fr&lt;br /&gt;
   service_description    Check dhcpd&lt;br /&gt;
   check_command          check_dhcp&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Pour que &#039;&#039;check_dhcp&#039;&#039; fonctionne, n&#039;oubliez pas d&#039;ouvrir le pare-feu :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# iptables -I INPUT 2 -p udp --dport 68 -j ACCEPT&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Pre fly check =&lt;br /&gt;
Avant de démarrer &#039;&#039;Nagios&#039;&#039;, il est intéressant de faire une vérification de la configuration pour voir s&#039;il n&#039;y a pas d&#039;erreur:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nagios -v /etc/nagios/nagios.cfg&lt;br /&gt;
&lt;br /&gt;
Nagios Core 3.5.1&lt;br /&gt;
Copyright (c) 2009-2011 Nagios Core Development Team and Community Contributors&lt;br /&gt;
Copyright (c) 1999-2009 Ethan Galstad&lt;br /&gt;
Last Modified: 08-30-2013&lt;br /&gt;
License: GPL&lt;br /&gt;
&lt;br /&gt;
Website: http://www.nagios.org&lt;br /&gt;
Reading configuration data...&lt;br /&gt;
   Read main config file okay...&lt;br /&gt;
Processing object config file &#039;/etc/nagios/objects/commands.cfg&#039;...&lt;br /&gt;
Processing object config file &#039;/etc/nagios/objects/contacts.cfg&#039;...&lt;br /&gt;
Processing object config file &#039;/etc/nagios/objects/timeperiods.cfg&#039;...&lt;br /&gt;
Processing object config file &#039;/etc/nagios/objects/templates.cfg&#039;...&lt;br /&gt;
Processing object config file &#039;/etc/nagios/objects/localhost.cfg&#039;...&lt;br /&gt;
Processing object config directory &#039;/etc/nagios/conf.d&#039;...&lt;br /&gt;
Processing object config file &#039;/etc/nagios/conf.d/www.cfg&#039;...&lt;br /&gt;
Processing object config file &#039;/etc/nagios/conf.d/sdhcpd.cfg&#039;...&lt;br /&gt;
   Read object config files okay...&lt;br /&gt;
&lt;br /&gt;
Running pre-flight check on configuration data...&lt;br /&gt;
&lt;br /&gt;
Checking services...&lt;br /&gt;
        Checked 11 services.&lt;br /&gt;
Checking hosts...&lt;br /&gt;
        Checked 3 hosts.&lt;br /&gt;
Checking host groups...&lt;br /&gt;
        Checked 1 host groups.&lt;br /&gt;
Checking service groups...&lt;br /&gt;
        Checked 0 service groups.&lt;br /&gt;
Checking contacts...&lt;br /&gt;
        Checked 1 contacts.&lt;br /&gt;
Checking contact groups...&lt;br /&gt;
        Checked 1 contact groups.&lt;br /&gt;
Checking service escalations...&lt;br /&gt;
        Checked 0 service escalations.&lt;br /&gt;
Checking service dependencies...&lt;br /&gt;
        Checked 0 service dependencies.&lt;br /&gt;
Checking host escalations...&lt;br /&gt;
        Checked 0 host escalations.&lt;br /&gt;
Checking host dependencies...&lt;br /&gt;
        Checked 0 host dependencies.&lt;br /&gt;
Checking commands...&lt;br /&gt;
        Checked 24 commands.&lt;br /&gt;
Checking time periods...&lt;br /&gt;
        Checked 5 time periods.&lt;br /&gt;
Checking for circular paths between hosts...&lt;br /&gt;
Checking for circular host and service dependencies...&lt;br /&gt;
Checking global event handlers...&lt;br /&gt;
Checking obsessive compulsive processor commands...&lt;br /&gt;
Checking misc settings...&lt;br /&gt;
&lt;br /&gt;
Total Warnings: 0&lt;br /&gt;
Total Errors:   0&lt;br /&gt;
&lt;br /&gt;
Things look okay - No serious problems were detected during the pre-flight check&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=Démarrage =&lt;br /&gt;
Une fois la configuration terminée, on démarre &#039;&#039;Nagios&#039;&#039; et on l&#039;enregistre dans le chargeur de démarrage:&lt;br /&gt;
* Pour SystemVInit:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
service nagios start&lt;br /&gt;
chkconfig nagios on&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Pour SystemD:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
systemctl start nagios.service&lt;br /&gt;
systemctl enable nagios.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Accès Web=&lt;br /&gt;
== Accès administrateur ==&lt;br /&gt;
Pour accéder à l&#039;interface Web de &#039;&#039;Nagios&#039;&#039;, il faut avant tout paramétrer un mot de passe et autoriser l&#039;accès depuis une autre machine que &#039;&#039;127.0.0.1&#039;&#039;:&lt;br /&gt;
Cela se passe dans le fichier &#039;&#039;/etc/httpd/conf.d/nagios.conf&#039;&#039;, où il faut commenter les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#  Order deny,allow&lt;br /&gt;
#  Deny from all&lt;br /&gt;
#  Allow from 127.0.0.1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Maintenant, il ne nous reste plus qu&#039;à définir un mot de passe:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# htpasswd /etc/nagios/passwd nagiosadmin&lt;br /&gt;
New password:&lt;br /&gt;
Re-type new password:&lt;br /&gt;
Updating password for user nagiosadmin&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Une fois que vous avez configuré et démarré [[HTTPD#Configuration_de_base | HTTPD]], vous avez accès à l’interface Web:&lt;br /&gt;
[[Fichier:Web interface nagios intro.png|centré]]&lt;br /&gt;
&lt;br /&gt;
== Autres accès ==&lt;br /&gt;
Si vous décidez de créer d&#039;autres utilisateurs que &#039;&#039;nagiosadmin&#039;&#039;, il faudra leur donner accès à certaines informations.&lt;br /&gt;
Cela se passe dans le fichier &#039;&#039;/etc/nagios/cgi.conf&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use_authentication=1&lt;br /&gt;
#edit username&lt;br /&gt;
authorized_for_all_host_commands=username&lt;br /&gt;
authorized_for_all_hosts=username&lt;br /&gt;
authorized_for_all_service_commands=username&lt;br /&gt;
authorized_for_all_services=username&lt;br /&gt;
authorized_for_configuration_information=username&lt;br /&gt;
authorized_for_system_commands=username&lt;br /&gt;
authorized_for_system_information=username&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il suffit d&#039;ajouter les noms d&#039;utilisateurs séparés d&#039;une virgule.&lt;br /&gt;
=NRPE : vérifications locales sur hôte distant =&lt;br /&gt;
Les vérifications externes sont très intéressantes mais, malheureusement, pas suffisantes pour garantir le bon fonctionnement d&#039;un hôte.&lt;br /&gt;
&lt;br /&gt;
Pour faire des vérifications locales sur un hôte distant, nous allons utiliser &#039;&#039;NRPE&#039;&#039; sur le serveur &#039;&#039;DHCP&#039;&#039;.&lt;br /&gt;
== Installation ==&lt;br /&gt;
Pour faire des vérifications sur des machines distantes (autre que le serveur &#039;&#039;Nagios&#039;&#039;), il faut installer le module &#039;&#039;NRPE&#039;&#039; sur ces machines:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# yum -y install nrpe nagios-plugins-all&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On n&#039;oublie pas d&#039;ouvrir le pare-feu:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# iptables -I INPUT 2 -p tcp --dport 5666 -j ACCEPT&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
=== Autorisation ===&lt;br /&gt;
Nous allons modifier son fichier de configuration &#039;&#039;/etc/nagios/nrpe.cfg&#039;&#039; pour y ajouter l&#039;adresse du serveur &#039;&#039;Nagios&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
allowed_hosts=127.0.0.1, 192.168.3.242&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Commandes ===&lt;br /&gt;
À la fin du fichier on peut voir les commandes qui sont déclarées :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
command[check_users]=/usr/lib64/nagios/plugins/check_users -w 5 -c 10&lt;br /&gt;
command[check_load]=/usr/lib64/nagios/plugins/check_load -w 15,10,5 -c 30,25,20&lt;br /&gt;
command[check_hda1]=/usr/lib64/nagios/plugins/check_disk -w 20% -c 10% -p /dev/hda1&lt;br /&gt;
command[check_zombie_procs]=/usr/lib64/nagios/plugins/check_procs -w 5 -c 10 -s Z&lt;br /&gt;
command[check_total_procs]=/usr/lib64/nagios/plugins/check_procs -w 150 -c 200&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut modifier &#039;&#039;check_hda1&#039;&#039; en :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
command[check_sda]=/usr/lib64/nagios/plugins/check_disk -w 20% -c 10% -p /dev/sda&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Démarrage ==&lt;br /&gt;
Maintenant on peut démarrer &#039;&#039;NRPE&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# service nrpe start&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Et on l&#039;enregistre dans le chargeur de démarrage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chkconfig nrpe on&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Intégration dans &#039;&#039;Nagios&#039;&#039; ==&lt;br /&gt;
=== Installation du plugin ===&lt;br /&gt;
De retour sur le serveur &#039;&#039;Nagios&#039;&#039;, nous allons tout d&#039;abord installer le plugin &#039;&#039;NRPE&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# yum -y install nagios-plugins-nrpe&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Test de connectivité ==&lt;br /&gt;
Toujours sur le serveur &#039;&#039;Nagios&#039;&#039;, pour être sûr que la connectivité est bonne, on peut utiliser le plugin:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# /usr/lib64/nagios/plugins/check_nrpe -H 192.168.3.251&lt;br /&gt;
NRPE v2.15&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Pourquoi pas une commande:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# /usr/lib64/nagios/plugins/check_nrpe -H 192.168.3.251 -c check_sda&lt;br /&gt;
DISK OK - free space: / 5472 MB (85% inode=95%);| /=947MB;5410;6086;0;6763&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Tout semble fonctionner, intégrons cela à la configuration !&lt;br /&gt;
=== Modification de la configuration ===&lt;br /&gt;
Tout d&#039;abord, il faut ajouter une commande soit dans le fichier &#039;&#039;/etc/nagios/objects/commands.cfg&#039;&#039; soit dans le fichier &#039;&#039;/etc/nagios/conf.d/nrpe_plugin.cfg&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#######################################################&lt;br /&gt;
#&lt;br /&gt;
# Ajout pour NRPE&lt;br /&gt;
#&lt;br /&gt;
#######################################################&lt;br /&gt;
define command{&lt;br /&gt;
   command_name    check_nrpe&lt;br /&gt;
   command_line    $USER1$/check_nrpe -H $HOSTADDRESS$ -c $ARG1$&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nous pouvons maintenant modifier le fichier &#039;&#039;/etc/nagios/conf.d/sdhcp.cfg&#039;&#039; pour prendre en compte ces vérifications :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define service{&lt;br /&gt;
   use                    local-service&lt;br /&gt;
   host_name              dhcp.tala-informatique.fr&lt;br /&gt;
   service_description    Check users&lt;br /&gt;
   check_command          check_nrpe!check_users&lt;br /&gt;
}&lt;br /&gt;
define service{&lt;br /&gt;
   use                    local-service&lt;br /&gt;
   host_name              dhcp.tala-informatique.fr&lt;br /&gt;
   service_description    Check disk /dev/sda&lt;br /&gt;
   check_command          check_nrpe!check_sda&lt;br /&gt;
}&lt;br /&gt;
define service{&lt;br /&gt;
   use                    local-service&lt;br /&gt;
   host_name              dhcp.tala-informatique.fr&lt;br /&gt;
   service_description    Check load&lt;br /&gt;
   check_command          check_nrpe!check_load&lt;br /&gt;
}&lt;br /&gt;
define service{&lt;br /&gt;
   use                    local-service&lt;br /&gt;
   host_name              dhcp.tala-informatique.fr&lt;br /&gt;
   service_description    Check zombies&lt;br /&gt;
   check_command          check_nrpe!check_zombie_procs&lt;br /&gt;
}&lt;br /&gt;
define service{&lt;br /&gt;
   use                    local-service&lt;br /&gt;
   host_name              dhcp.tala-informatique.fr&lt;br /&gt;
   service_description    Check total process&lt;br /&gt;
   check_command          check_nrpe!check_total_procs&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si on retourne sur l&#039;interface Web, on peut observer ces nouvelles vérifications:&lt;br /&gt;
[[Fichier:Nagios check nrpe web.png|centré]]&lt;br /&gt;
&lt;br /&gt;
= Développement d&#039;un plugin =&lt;br /&gt;
Que faire lorsque rien ne permet de vérifier ce que vous voulez vérifier ?&lt;br /&gt;
&lt;br /&gt;
Pas de panique, il suffit de développer son propre plugin. Avant d&#039;aller plus loin, intéressons-nous au fonctionnement des plugins :&lt;br /&gt;
* les plugins renvoient trois états : 0 = OK ; 1 = WARNING ; 2 = CRITICAL&lt;br /&gt;
* les plugins peuvent renvoyer &#039;&#039;&#039;une&#039;&#039;&#039; (1) ligne de retour&lt;br /&gt;
&lt;br /&gt;
Développons un script pour retourner le nombre de machines qui ont eu une adresse IP dans la journée.&lt;br /&gt;
== La date ==&lt;br /&gt;
Sur nos machines françaises la date est la suivante :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# date&lt;br /&gt;
Thu Jan 14 04:28:04 CET 2016&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Or, dans le &#039;&#039;/var/log/message&#039;&#039;, la date est formatée comme suit :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Jan 14 00:54:08 dhcp dhcpd: added reverse map from 242.3.168.192.in-addr.arpa. to nagios.tala-informatique.fr&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Les mois ne correspondent pas... Il faut changer de langue pour que les mois s&#039;affichent comme dans le &#039;&#039;/var/log/message&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# LC_ALL=en_US.utf8&lt;br /&gt;
# export LC_ALL&lt;br /&gt;
# date&lt;br /&gt;
Thu Jan 14 04:28:04 CET 2016&lt;br /&gt;
# date +&amp;quot;%b %d&amp;quot;&lt;br /&gt;
Jan 14&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On a la date du jour, maintenant filtrons les entrées qui concernent le serveur dhcpd:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# cat /var/log/message | grep &amp;quot;$(date +&amp;quot;%b %d&amp;quot;)&amp;quot; | grep dhcpd | grep DHCPACK&lt;br /&gt;
Jan 14 00:17:12 dhcp dhcpd: DHCPACK on 192.168.3.242 to 00:0c:29:32:07:4a via eth0&lt;br /&gt;
Jan 14 00:54:08 dhcp dhcpd: DHCPACK on 192.168.3.242 to 00:0c:29:32:07:4a via eth0&lt;br /&gt;
Jan 14 01:00:27 dhcp dhcpd: DHCPACK on 192.168.3.242 to 00:0c:29:32:07:4a via eth0&lt;br /&gt;
Jan 14 03:41:37 dhcp dhcpd: DHCPACK on 192.168.3.242 to 00:0c:29:32:07:4a via eth0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Les IPs ==&lt;br /&gt;
Ceux sont vraiment les adresses IP qui nous intéressent, nous allons utiliser un filtre en colonne:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#  cat /var/log/messages | grep &amp;quot;$(date +&amp;quot;%b %d&amp;quot;)&amp;quot; | grep dhcpd | grep DHCPACK | awk -F &#039; &#039; &#039;{ print $8 }&#039;&lt;br /&gt;
192.168.3.242&lt;br /&gt;
192.168.3.242&lt;br /&gt;
192.168.3.242&lt;br /&gt;
192.168.3.242&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Enfin, nous allons utiliser la commande &#039;&#039;sort&#039;&#039; pour faire ressortir uniquement les adresses différentes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# cat /var/log/messages | grep &amp;quot;$(date +&amp;quot;%b %d&amp;quot;)&amp;quot; | grep dhcpd | grep DHCPACK | awk -F &#039; &#039; &#039;{ print $8 }&#039; | sort -u&lt;br /&gt;
192.168.3.242&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nous pourrions retourner l&#039;adresse MAC associée à cette IP:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# cat /var/log/messages | grep &amp;quot;$(date +&amp;quot;%b %d&amp;quot;)&amp;quot; | grep dhcpd | grep DHCPACK | grep 192.168.3.242 | awk -F &#039; &#039; &#039;{ print $10 }&#039; | sort -u&lt;br /&gt;
00:0c:29:32:07:4a&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Le plugin ==&lt;br /&gt;
Ce qui nous donne les lignes suivantes que nous placerons dans &#039;&#039;/opt/nagios/check_dhcp_ip.sh&#039;&#039; :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
## Setting local time lang&lt;br /&gt;
LC_ALL=en_US.utf8&lt;br /&gt;
RET=&amp;quot;&amp;quot;&lt;br /&gt;
export LC_ALL&lt;br /&gt;
## Getting IPs&lt;br /&gt;
IPS=$(cat /var/log/messages | grep &amp;quot;$(date +&amp;quot;%b %d&amp;quot;)&amp;quot; | grep dhcpd | grep DHCPACK | awk -F &#039; &#039; &#039;{ print $8 }&#039; | sort -u)&lt;br /&gt;
NBIP=0&lt;br /&gt;
## Getting MACs&lt;br /&gt;
for IP in ${IPS};do&lt;br /&gt;
   MAC=$(cat /var/log/messages | grep &amp;quot;$(date +&amp;quot;%b %d&amp;quot;)&amp;quot; | grep dhcpd | grep DHCPACK | grep $IP | awk -F &#039; &#039; &#039;{ print $10 }&#039; | sort -u)&lt;br /&gt;
   if [ &amp;quot;${RET}&amp;quot; != &amp;quot;&amp;quot; ];then&lt;br /&gt;
      RET=&amp;quot;${RET} ;&amp;quot;&lt;br /&gt;
   fi&lt;br /&gt;
   RET=&amp;quot;${RET} ${IP} &amp;lt;-&amp;gt; ${MAC}&amp;quot;&lt;br /&gt;
   let NBIP++&lt;br /&gt;
done&lt;br /&gt;
## Echoing return&lt;br /&gt;
echo &amp;quot;${NBIP} ip delivered by dhcp : ${RET}&amp;quot;&lt;br /&gt;
exit 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Cela donne le résultat suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ./check_dhcp_ip.sh&lt;br /&gt;
2 ip delivered by dhcp :  192.168.3.242 &amp;lt;-&amp;gt; 00:0c:29:32:07:4a ; 192.168.3.243 &amp;lt;-&amp;gt; 00:0c:29:8f:03:25&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Les droits ==&lt;br /&gt;
Si on fait un :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ps -ef | grep nrpe | grep -v grep&lt;br /&gt;
nrpe       3518      1  0 05:39 ?        00:00:00 /usr/sbin/nrpe -c /etc/nagios/nrpe.cfg -d&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On constate que &#039;&#039;NRPE&#039;&#039; se lance avec l&#039;utilisateur &#039;&#039;nrpe&#039;&#039;, et si on fait un :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ll /var/log/messages&lt;br /&gt;
-rw------- 1 root root 33596 14 janv. 05:46 /var/log/messages&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On constate que le fichier &#039;&#039;/var/log/messages&#039;&#039; est lisible uniquement par &#039;&#039;root&#039;&#039;. On va donc &#039;&#039;étendre&#039;&#039; les droits du fichier :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod 555 /var/log/messages&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
De la sorte notre plugin sera fonctionnel, même exécuté par l&#039;utilisateur &#039;&#039;nrpe&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Ajout dans nrpe.cfg ==&lt;br /&gt;
Nous allons maintenant déclarer notre plugin dans le fichier &#039;&#039;/etc/nagios/nrpe.cfg&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#####################&lt;br /&gt;
#&lt;br /&gt;
# SPECIFIC PLUGINS&lt;br /&gt;
#&lt;br /&gt;
#####################&lt;br /&gt;
command[check_dhcp_ip]=/opt/nagios/check_dhcp_ip.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On n&#039;oublie pas de redémarrer &#039;&#039;NRPE&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# service nrpe restart&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Ajout sur le serveur Nagios ==&lt;br /&gt;
Nous allons faire de même sur le serveur Nagios dans le fichier &#039;&#039;/etc/nagios/conf.d/sdhcpd.cfg&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define service{&lt;br /&gt;
   use                    local-service&lt;br /&gt;
   host_name              dhcp.tala-informatique.fr&lt;br /&gt;
   service_description    Check DHCP IP&lt;br /&gt;
   check_command          check_nrpe!check_dhcp_ip&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On n&#039;oublie pas de redémarrer &#039;&#039;Nagios&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# service nagios restart&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Et on vérifie sur l&#039;interface web :&lt;br /&gt;
[[Fichier:Check dhcp ip web.png|centré]]&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4169</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4169"/>
		<updated>2025-12-20T16:07:32Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Modification de AnyEvent.pm */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps où l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;à spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Tests =&lt;br /&gt;
Il faut maintenant tester le filtre et pour ça on peut utiliser un partage de connexion et réaliser le nombre d&#039;échecs spécifié par la directive &amp;quot;maxretry&amp;quot; (ici 3) :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# fail2ban-client status proxmox&lt;br /&gt;
Status for the jail: proxmox&lt;br /&gt;
|- Filter&lt;br /&gt;
|  |- Currently failed:	0&lt;br /&gt;
|  |- Total failed:	3&lt;br /&gt;
|  `- Journal matches:	_SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
`- Actions&lt;br /&gt;
   |- Currently banned:	1&lt;br /&gt;
   |- Total banned:	1&lt;br /&gt;
   `- Banned IP list:	37.167.23.159&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit bien l&#039;IP 37.167.23.159 qui est bannie !&lt;br /&gt;
N&#039;oubliez pas de l&#039;ajouter à &#039;&#039;ignoreip&#039;&#039; dans le fichier &#039;&#039;jails.local&#039;&#039; ou de faire &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# fail2ban-client unban 37.167.23.159&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilise l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
Même si le filtre &#039;&#039;fail2ban&#039;&#039; est configuré pour utiliser la commande &#039;&#039;journactl&#039;&#039; sur le démon &#039;&#039;pvedaemon&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
les adresses viennent de pveproxy qui journalise tout dans &#039;&#039;/var/log/pveproxy/access.log&#039;&#039; et &#039;&#039;pvedaemon&#039;&#039; ne journalise que les échecs d&#039;authentification. &lt;br /&gt;
Il est donc plus simple de faire un &#039;&#039;tail&#039;&#039; ou un &#039;&#039;cat&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Que d&#039;utiliser &#039;&#039;journalctl&#039;&#039; pour voir les IPs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
Dec 14 10:43:01 labo unix_chkpwd[1326506]: password check failed for user (root)&lt;br /&gt;
Dec 14 10:43:01 labo IPCC.xs[1320783]: pam_unix(proxmox-ve-auth:auth): authentication failure; logname= uid=0 euid=0 tty= ruser= rhost=37.167.23.159  user=root&lt;br /&gt;
Dec 14 10:43:03 labo pvedaemon[1320783]: authentication failure; rhost=37.167.23.159 user=root@pam msg=Authentication failure&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;&#039;uniquement&#039;&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrait ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jour d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modification peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Double IPv4 ou préfixe IPv6 ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/resources HTTP/1.1&amp;quot; 200 1226&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/tasks HTTP/1.1&amp;quot; 200 1090&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
::ffff:192.168.20.22 - root@pam [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/nodes/labo/lxc/100/status/current HTTP/1.1&amp;quot; 200 523&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il nous reste un peu de nettoyage à faire, dans certains cas :&lt;br /&gt;
* l&#039;adresse apparaît non pas une fois mais deux, séparées par une virgule&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
37.167.182.207, 37.167.182.207&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* l&#039;adresse IPv4 (32 bits) est affichée dans sa représentation IPv6 (128 bits), avec le préfixe &#039;&#039;::ffff:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
::ffff:192.168.20.22&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour faire le nettoyage :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   # Remove trailing ip &lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
if($reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
   # Remove IPv6 subnet for IPv4&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=.*:?&amp;lt;HOST&amp;gt;,? user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant, que l&#039;on peut créer dans /root/remoteip_fix.sh :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
    if(\$reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
        # Remove IPv6 subnet for IPv4&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$1;&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à le rendre exécutable et le lancer :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x /root/remoteip_fix.sh&lt;br /&gt;
# /root/remoteip_fix.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne faudra pas oublier de redémarrer &#039;&#039;pveproxy&#039;&#039; pour appliquer les changements !&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Arduino_SR501&amp;diff=4168</id>
		<title>Arduino SR501</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Arduino_SR501&amp;diff=4168"/>
		<updated>2025-12-20T16:06:16Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Récupérer la présence de mouvement */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Partie électronique =&lt;br /&gt;
== Le composant ==&lt;br /&gt;
Le &#039;&#039;SR501&#039;&#039; est généralement monté sur une platine.&lt;br /&gt;
[[Fichier:SR501.jpg|centré|150px]]&lt;br /&gt;
&lt;br /&gt;
Ce composant possède une sortie numérique qui est à &#039;&#039;0v&#039;&#039; en l&#039;absence de mouvement et &#039;&#039;5v&#039;&#039; sinon.&lt;br /&gt;
&lt;br /&gt;
D&#039;autres réglages sont possibles comme: &lt;br /&gt;
*la sensibilité&lt;br /&gt;
*le délais&lt;br /&gt;
*le type de déclenchement (impulsion simple ou répétée)&lt;br /&gt;
[[Fichier:SR501_config.png|centré|250px]]&lt;br /&gt;
&lt;br /&gt;
== Le montage ==&lt;br /&gt;
Le montage suivant prévoit un fil &#039;&#039;data&#039;&#039; qui nous permettra de lire le retour du &#039;&#039;SR501&#039;&#039; sur le PIN 2 de &#039;&#039;l&#039;Arduino&#039;&#039;.&lt;br /&gt;
[[Fichier:SR501_diagram.png|centré|400px]]&lt;br /&gt;
&lt;br /&gt;
= Partie logicielle =&lt;br /&gt;
== Récupérer la présence de mouvement ==&lt;br /&gt;
Un exemple de code qui permet de récupérer la présence de mouvement. Notez l&#039;utilisation d&#039;une résistance de &#039;&#039;pull-up&#039;&#039; sur le PIN 2 ! &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pirPin = 2;&lt;br /&gt;
int pirState = LOW;&lt;br /&gt;
 &lt;br /&gt;
void setup() {&lt;br /&gt;
  // Met la broche 2 en entrée&lt;br /&gt;
  pinMode(pirPin, INPUT);&lt;br /&gt;
  // Utilisation d&#039;une résistance de pull-up sur le PIN 2&lt;br /&gt;
  digitalWrite(pirPin, HIGH);&lt;br /&gt;
  // Active le port série&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void loop(){&lt;br /&gt;
  // Vérification de l&#039;état du capteur&lt;br /&gt;
  if (digitalRead(pirPin ) == HIGH) {  &lt;br /&gt;
    if (pirState == LOW) {&lt;br /&gt;
      // Mouvement !&lt;br /&gt;
      Serial.println(&amp;quot;Motion !&amp;quot;);&lt;br /&gt;
      pirState = HIGH;&lt;br /&gt;
    }&lt;br /&gt;
  } else {&lt;br /&gt;
    if (pirState == HIGH){&lt;br /&gt;
      // Pas de mouvement...&lt;br /&gt;
      Serial.println(&amp;quot;No motion...&amp;quot;);&lt;br /&gt;
      pirState = LOW;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Résultat ==&lt;br /&gt;
Voila le résultat :&lt;br /&gt;
[[Fichier:Arduino SR501 reading serial.png|centré|400px]]&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=MediaWiki:Common.css&amp;diff=4167</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=MediaWiki:Common.css&amp;diff=4167"/>
		<updated>2025-12-20T15:56:18Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* Le CSS placé ici sera appliqué à tous les habillages. */&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=MediaWiki:Common.css&amp;diff=4166</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=MediaWiki:Common.css&amp;diff=4166"/>
		<updated>2025-12-20T15:43:06Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : Page créée avec « /* Le CSS placé ici sera appliqué à tous les habillages. */ // Source - https://stackoverflow.com/a // Posted by Jinlye // Retrieved 2025-12-20, License - CC BY-SA 3.0  /***** Overcome mediawiki bug whereby the WikiEdit sprite is a no-show *****/ .wikiEditor-toolbar-spritedButton {   background-image: linear-gradient(transparent, transparent), url(&amp;quot;/extensions/WikiEditor/modules/images/toolbar/button-sprite.svg?v=001&amp;quot;) !important;   background-position: 0px 0p... »&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* Le CSS placé ici sera appliqué à tous les habillages. */&lt;br /&gt;
// Source - https://stackoverflow.com/a&lt;br /&gt;
// Posted by Jinlye&lt;br /&gt;
// Retrieved 2025-12-20, License - CC BY-SA 3.0&lt;br /&gt;
&lt;br /&gt;
/***** Overcome mediawiki bug whereby the WikiEdit sprite is a no-show *****/&lt;br /&gt;
.wikiEditor-toolbar-spritedButton {&lt;br /&gt;
  background-image: linear-gradient(transparent, transparent), url(&amp;quot;/extensions/WikiEditor/modules/images/toolbar/button-sprite.svg?v=001&amp;quot;) !important;&lt;br /&gt;
  background-position: 0px 0px; /* This gets over-ridden with inline style to move sprite into view of relevant part */&lt;br /&gt;
  background-repeat: no-repeat !important;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Php_$get_$post_$session&amp;diff=4164</id>
		<title>Php $get $post $session</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Php_$get_$post_$session&amp;diff=4164"/>
		<updated>2025-12-16T14:32:12Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
== Les concepts ==&lt;br /&gt;
Lorsque l&#039;on conçoit une application Web, il y a un moment ou on est obligé de communiquer des informations entre les différentes pages de cette application. &lt;br /&gt;
Les pages sont demandées par la partie cliente, généralement un navigateur (eg. Firefox, Chrome, Safari, ...), et sont distribuées par la partie serveur (eg. Apache HTTPd, Nginx, IIS, ...).&lt;br /&gt;
&lt;br /&gt;
Cette échange est encadré par l&#039;utilisation du protocole [[:Media:HTTP.pdf|HTTP]] et doit donc utiliser des &#039;&#039;&#039;méthodes HTTP&#039;&#039;&#039;, aussi appellées &#039;&#039;&#039;verbes HTTP&#039;&#039;&#039;, consacrés :&lt;br /&gt;
* &#039;&#039;GET&#039;&#039;;&lt;br /&gt;
* &#039;&#039;POST&#039;&#039;;&lt;br /&gt;
* &#039;&#039;PUT&#039;&#039;;&lt;br /&gt;
* &#039;&#039;DELETE&#039;&#039;;&lt;br /&gt;
Les deux premiers sont utilisés dans les applications Web au travers de formulaires et les deux derniers sont plutôt utilisés dans le cadre de [[:Media:webservices.pdf|Web services RESTful]].&lt;br /&gt;
&lt;br /&gt;
On distingue donc deux cas de figure :&lt;br /&gt;
*l&#039;envoie d&#039;informations du client au serveur;&lt;br /&gt;
*la conservation d&#039;information côté serveur.&lt;br /&gt;
Dans le premier cas de figure, on peut utiliser les méthodes &#039;&#039;GET&#039;&#039; ou &#039;&#039;POST&#039;&#039;, alors que dans le deuxième cas de figure on utilisera les sessions.&lt;br /&gt;
&lt;br /&gt;
== Création d&#039;un projet == &lt;br /&gt;
Pour illustrer ces propos nous allons créer un projet dans [[eclipse_install|Eclipse]] et pour cela assurez-vous d&#039;avoir la perspective Php ainsi que d&#039;avoir suivi le tutoriel [[Php_xdebug | Xdebug]]. &lt;br /&gt;
&lt;br /&gt;
Commençons par la création du projet:  &lt;br /&gt;
* Première écran de l&#039;assistant :&lt;br /&gt;
** on créé le projet sur le [[Php_xdebug#Ajout_d.27un_serveur_Web|serveur local]];&lt;br /&gt;
** on active le support de JavaScript&lt;br /&gt;
[[Fichier:Eclipse php project create first screen.png|centré|450px]]&lt;br /&gt;
* Deuxième écran de l&#039;assistant :&lt;br /&gt;
** On créé un repertoire &#039;&#039;src&#039;&#039;&lt;br /&gt;
[[Fichier:Eclipse php project create second screen.png|centré|450px]]&lt;br /&gt;
* On clique sur &#039;&#039;finish&#039;&#039; pour terminer l&#039;assistant&lt;br /&gt;
&lt;br /&gt;
Une fois le projet créé, on va y ajouter une page &#039;&#039;index.php&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;title&amp;gt;Formulaire&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
		&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;form action=&amp;quot;php/traitement.php&amp;quot; method=&amp;quot;get&amp;quot;&amp;gt;&lt;br /&gt;
				Login :&lt;br /&gt;
				&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;login&amp;quot;&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
				Password : &lt;br /&gt;
				&amp;lt;input type=&amp;quot;password&amp;quot;  name=&amp;quot;password&amp;quot;&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
				&amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Connexion&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;/form&amp;gt;&lt;br /&gt;
		&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comme vous pouvez le constater, cette page ne contient pas le code Php en charge du traitement du formulaire. Elle envoie les informations à la page &#039;&#039;traitement.php&#039;&#039; qui se trouve dans le répertoire &#039;&#039;php&#039;&#039;. &lt;br /&gt;
Voici le code de cette page :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
var_dump($_GET);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= La méthode &#039;&#039;GET&#039;&#039; =&lt;br /&gt;
Pour l&#039;instant, en phase de découverte, nous allons utiliser la fonction &#039;&#039;var_dump&#039;&#039; qui permet d&#039;afficher le contenu d&#039;une variable, peu importe son type.&lt;br /&gt;
On pourrait également utiliser la vue &#039;&#039;Debug&#039;&#039; d&#039;Eclipse pour voir le contenu de la variable en mémoire. &lt;br /&gt;
Peu importe la solution retenue, l&#039;important et de voir la corrélation entre :&lt;br /&gt;
*les données du formulaire;&lt;br /&gt;
*l&#039;URL;&lt;br /&gt;
*le tableau &#039;&#039;$_GET&#039;&#039; créé par l&#039;interpréteur Php.&lt;br /&gt;
== Envoie d&#039;information ==&lt;br /&gt;
Lorsque l&#039;on valide le formulaire, le navigateur va construivre l&#039;URL suivante :&lt;br /&gt;
&lt;br /&gt;
http://localhost/Form/src/php/traitement.php?login=toto&amp;amp;password=titi&lt;br /&gt;
&lt;br /&gt;
Et l&#039;interpréteur Php va remplir le tableau &#039;&#039;$_GET&#039;&#039; de la manière suivante (résultat du &#039;&#039;var_dump&#039;&#039;) :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
array (size=2)&lt;br /&gt;
  &#039;login&#039; =&amp;gt; string &#039;toto&#039; (length=4)&lt;br /&gt;
  &#039;password&#039; =&amp;gt; string &#039;titi&#039; (length=4)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
On peut s&#039;amuser à modifier les valeurs dans l&#039;URL pour comprendre l&#039;impact sur le contenu du tableau &#039;&#039;$_GET&#039;&#039; : l&#039;interpréteur construit un tableau associatif ou figurera chacun des tuples présent dans l&#039;URL.&lt;br /&gt;
Le caractère &#039;&#039;?&#039;&#039; sert à séparer la ressource demandée de la liste des tuples et le caractère &#039;&#039;&amp;amp;&#039;&#039; sert à séparer chacun des tuples dans la liste.&lt;br /&gt;
&lt;br /&gt;
== Réception d&#039;information ==&lt;br /&gt;
La récupération d&#039;une valeur semble simple : il suffit d&#039;y accéder en spécifiant son nom.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
$login = $_GET[&amp;quot;login&amp;quot;];&lt;br /&gt;
$password = $_GET[&amp;quot;password&amp;quot;];&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;Votre login est : &amp;quot;.$login;&lt;br /&gt;
echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
echo &amp;quot;Votre mot de passe est : &amp;quot;.$password;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= La méthode &#039;&#039;POST&#039;&#039; =&lt;br /&gt;
== Envoie d&#039;information ==&lt;br /&gt;
Très similaire à la méthode &#039;&#039;GET&#039;&#039; à la différence que les tuples sont envoyé dans le corps de la requête &#039;&#039;HTTP&#039;&#039; et non dans l&#039;URL.&lt;br /&gt;
Pour l&#039;utiliser, il suffit de modifier la ligne suivante du fichier &#039;&#039;index.php&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;form action=&amp;quot;php/traitement.php&amp;quot; method=&amp;quot;get&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
en &lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;form action=&amp;quot;php/traitement.php&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Réception d&#039;information ==&lt;br /&gt;
La récupération des informations ce fait non plus dans le tableau &#039;&#039;$_GET&#039;&#039; mais dans le tableau &#039;&#039;$_POST&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
$login = $_POST[&amp;quot;login&amp;quot;];&lt;br /&gt;
$password = $_POST[&amp;quot;password&amp;quot;];&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;Votre login est : &amp;quot;.$login;&lt;br /&gt;
echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
echo &amp;quot;Votre mot de passe est : &amp;quot;.$password;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Limitations =&lt;br /&gt;
==Une variable manque à l&#039;appel !==&lt;br /&gt;
Que se passe t-il si l&#039;un des paramètres manque ? Si, par exemple, il manque le tuple &#039;&#039;password&#039;&#039; comme c&#039;est le cas avec l&#039;URL suivante:&lt;br /&gt;
&lt;br /&gt;
http://localhost/Form/src/php/traitement.php?login=toto&lt;br /&gt;
&lt;br /&gt;
Ci-dessous un extrait du fichier &#039;&#039;/var/log/httpd/error_log&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Mon Jun 27 04:49:56 2016] [error] [client 192.168.100.1] PHP Notice:  Undefined index: password in /var/www/html/Form/src/php/traitement.php on line 4&lt;br /&gt;
[Mon Jun 27 04:49:56 2016] [error] [client 192.168.100.1] PHP Stack trace:&lt;br /&gt;
[Mon Jun 27 04:49:56 2016] [error] [client 192.168.100.1] PHP   1. {main}() /var/www/html/Form/src/php/traitement.php:0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour ne pas prendre de risque il faudrait tester si l&#039;index existe en utilisant la fonction &#039;&#039;isset&#039;&#039; : &lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
if (isset ( $_GET [&amp;quot;login&amp;quot;] )) {&lt;br /&gt;
	$login = $_GET [&amp;quot;login&amp;quot;];&lt;br /&gt;
	echo &amp;quot;Votre login est : &amp;quot; . $login;&lt;br /&gt;
	echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
if (isset ( $_GET [&amp;quot;password&amp;quot;] )) {&lt;br /&gt;
	$password = $_GET [&amp;quot;password&amp;quot;];&lt;br /&gt;
	echo &amp;quot;Votre mot de passe est : &amp;quot; . $password;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
==Ma variable est vide !==&lt;br /&gt;
Que se passe t-il si la variable est vide ? Si, par exemple, le tuple &#039;&#039;password&#039;&#039; ne possède pas de valeur comme c&#039;est le cas avec l&#039;URL suivante:&lt;br /&gt;
&lt;br /&gt;
http://localhost/Form/src/php/traitement.php?login=toto&amp;amp;password&lt;br /&gt;
&lt;br /&gt;
Aucune erreur n&#039;est générée mais le reste du code risque de ne pas fonctionner correctement...&lt;br /&gt;
Il faudrait tester si la variable n&#039;est pas vide grâce à la fonction &#039;&#039;empty&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (isset ( $_GET [&amp;quot;login&amp;quot;] )) {&lt;br /&gt;
	$login = $_GET [&amp;quot;login&amp;quot;];&lt;br /&gt;
	if (! empty ( $login )) {&lt;br /&gt;
		echo &amp;quot;Votre login est : &amp;quot; . $login;&lt;br /&gt;
		echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
if (isset ( $_GET [&amp;quot;password&amp;quot;] )) {&lt;br /&gt;
	$password = $_GET [&amp;quot;password&amp;quot;];&lt;br /&gt;
	if (! empty ( $password )) {&lt;br /&gt;
		echo &amp;quot;Votre mot de passe est : &amp;quot; . $password;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Réfléxion... ==&lt;br /&gt;
Pour ce formulaire composé de seulement deux champs on constate que la quantité de code qu&#039;il faut écrire, si on veut faire un traitement efficace des valeurs, est colossale. &lt;br /&gt;
On imagine facilement que plus il y aura de champs plus cela sera pénible...&lt;br /&gt;
&lt;br /&gt;
Il faudrait un moyen, pas trop compliqué, de savoir si une variable existe et si elle à une valeur. On va en profiter pour gérer le cas des nombre, en effet, le chiffre &#039;&#039;0&#039;&#039; est considéré comme &#039;&#039;empty&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons écrire deux fonctions pour cela dans le fichier &#039;&#039;php/helper.php&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
function getVar($name) {&lt;br /&gt;
	if (isset ( $_GET [$name] )) {&lt;br /&gt;
		if(is_numeric($tab[$name])){&lt;br /&gt;
			return $tab[$name];&lt;br /&gt;
		}&lt;br /&gt;
		if (! empty ( $_GET [$name] )) {&lt;br /&gt;
			return $_GET [$name];&lt;br /&gt;
		}&lt;br /&gt;
		return TRUE;&lt;br /&gt;
	}&lt;br /&gt;
	return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function postVar($name) {&lt;br /&gt;
	if (isset ( $_POST [$name] )) {&lt;br /&gt;
		if(is_numeric($tab[$name])){&lt;br /&gt;
			return $tab[$name];&lt;br /&gt;
		}&lt;br /&gt;
		if (! empty ( $_POST[$name] )) {&lt;br /&gt;
			return $_POST[$name];&lt;br /&gt;
		}&lt;br /&gt;
		return TRUE;&lt;br /&gt;
	}&lt;br /&gt;
	return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Bon d&#039;accord ! On peut encore compresser :&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
function getVar($name) {&lt;br /&gt;
	return retrieveVar($name, $_GET);&lt;br /&gt;
}&lt;br /&gt;
function postVar($name) {&lt;br /&gt;
	return retrieveVar($name, $_POST);&lt;br /&gt;
}&lt;br /&gt;
function retrieveVar($name, $tab){&lt;br /&gt;
	if (isset($tab[$name])) {&lt;br /&gt;
		if(is_numeric($tab[$name])){&lt;br /&gt;
			return $tab[$name];&lt;br /&gt;
		}&lt;br /&gt;
		if (! empty($tab[$name])) {&lt;br /&gt;
			return $tab[$name];&lt;br /&gt;
		}&lt;br /&gt;
		return TRUE;&lt;br /&gt;
	}&lt;br /&gt;
	return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ces fonctions renvoies :&lt;br /&gt;
* faux si la variable n&#039;existe pas ;&lt;br /&gt;
* vrai si elle existe mais est vide ;&lt;br /&gt;
* sinon sa valeur.&lt;br /&gt;
&lt;br /&gt;
La philosophie est simple:&lt;br /&gt;
* soit on à besoin que la variable &#039;&#039;&#039;existe&#039;&#039;&#039; et on fait le test suivant :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	$var = getVar(&amp;quot;var&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	// Si différent de faux, la variable existe avec une valeur ou non !&lt;br /&gt;
	if($var !== FALSE){&lt;br /&gt;
		...&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* soit il faut &#039;&#039;&#039;absolument&#039;&#039;&#039; une valeur on fait le test suivant :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	$var = getVar(&amp;quot;var&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	// Si la variable n&#039;est pas un booleen, elle à une valeur !&lt;br /&gt;
	if(!is_bool($var)){&lt;br /&gt;
		...&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voyez comme le code est plus clair, simple, efficace :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
include &#039;helper.php&#039;;&lt;br /&gt;
&lt;br /&gt;
$login = getVar ( &amp;quot;login&amp;quot; );&lt;br /&gt;
$password = getVar ( &amp;quot;password&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
if (! is_bool ( $login )) {&lt;br /&gt;
	echo &amp;quot;Votre login est : &amp;quot; . $login;&lt;br /&gt;
	echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
if (! is_bool ( $password )) {&lt;br /&gt;
	echo &amp;quot;Votre mot de passe est : &amp;quot; . $password;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Interaction =&lt;br /&gt;
==Redirection après vérification==&lt;br /&gt;
Il faudrait maintenant tester les valeurs des champs pour confirmer ou infirmer l&#039;authentification. Pour ce faire, nous allons modifier la page &#039;&#039;traitement.php&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
include &#039;helper.php&#039;;&lt;br /&gt;
&lt;br /&gt;
// Valeur par défaut&lt;br /&gt;
$stored_login = &amp;quot;root&amp;quot;;&lt;br /&gt;
$stored_password = &amp;quot;toor&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
$login = getVar ( &amp;quot;login&amp;quot; );&lt;br /&gt;
$password = getVar ( &amp;quot;password&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
if (! is_bool ( $login ) &amp;amp;&amp;amp; ! is_bool ( $password )) {&lt;br /&gt;
	if($login == $stored_login &amp;amp;&amp;amp; $password == $stored_password){&lt;br /&gt;
		// Authentification réussie&lt;br /&gt;
		return header(&amp;quot;Location: ../logged.php&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Authentification échoué&lt;br /&gt;
header(&amp;quot;Location: ../index.php&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Dans ce script :&lt;br /&gt;
* on définit au début les valeurs du couple login / mot de passe mais on aurait pu récupérer ces valeur dans une base de données;&lt;br /&gt;
* on utilise la fonction &#039;&#039;header&#039;&#039; avec la valeur &#039;&#039;Location&#039;&#039; qui permet de &#039;&#039;&#039;rediriger le navigateur&#039;&#039;&#039; en ajoutant un &#039;&#039;header HTTP&#039;&#039; à la réponse.&lt;br /&gt;
Si l&#039;authentification se passe correctement, on est redirigé vers la page &#039;&#039;logged.php&#039;&#039; (que nous allons créer), si elle échoue on revient sur la page &#039;&#039;index.php&#039;&#039; pour faire une nouvelle tentative de connexion.&lt;br /&gt;
Voici le code de la page &#039;&#039;logged.php&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;Authentification réussie !&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
==User eXperience==&lt;br /&gt;
En UX, on s&#039;intéresse beaucoup à l&#039;utilisateur et son ressenti. Que va t-il se passer si l&#039;authentification échoue ? Il sera redirigé tellement vite sur la page &#039;&#039;index.php&#039;&#039; qu&#039;il aura l&#039;impression que rien ne s&#039;est passé... Il faut absolument que la page &#039;&#039;index.php&#039;&#039; affiche un message qui informe de l&#039;échec de l&#039;authentification !&lt;br /&gt;
&lt;br /&gt;
C&#039;est &#039;&#039;traitement.php&#039;&#039; qui détient l&#039;information de l’échec ou de la réussite de l&#039;authentification... Il faudrait que cette page transmette l&#039;information à &#039;&#039;index.php&#039;&#039; ! Comment faire... avec un &#039;&#039;GET&#039;&#039; mais, cette fois, sans formulaire et uniquement en modifiant l&#039;URL de redirection en cas d&#039;échec. Dans le fichier &#039;&#039;traitement.php&#039;&#039; modifiez la ligne suivante :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Authentification échoué&lt;br /&gt;
header(&amp;quot;Location: ../index.php&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
en &lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Authentification échoué&lt;br /&gt;
header(&amp;quot;Location: ../index.php?echec&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il suffit maintenant de récupérer l&#039;information dans la page &#039;&#039;index.php&#039;&#039;. Ajouter, au dessus de la &#039;&#039;div&#039;&#039; existante, le code suivant :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;?php&lt;br /&gt;
		include &#039;php/helper.php&#039;;&lt;br /&gt;
		$echec = getVar(&amp;quot;echec&amp;quot;);&lt;br /&gt;
		if($echec !== FALSE){&lt;br /&gt;
			echo &amp;quot;Echec de l&#039;authentification !&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
	?&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Lorsque l&#039;on échoue l&#039;authentification, un message s&#039;affiche !&lt;br /&gt;
= Les sessions =&lt;br /&gt;
Les sessions en PHP permettent de mémoriser des informations dans le tableau $_SESSION accessible après l&#039;appel de la fonction &#039;&#039;session_start&#039;&#039;.&lt;br /&gt;
Ce tableau est unique pour chaque connexion et ne peut être partagé entre plusieurs clients.&lt;br /&gt;
== Mémoire d&#039;éléphant ==&lt;br /&gt;
Si on essaye d&#039;accéder à la page &#039;&#039;logged.php&#039;&#039; sans passer par la page &#039;&#039;index.php&#039;&#039;... cela fonctionne.&lt;br /&gt;
&lt;br /&gt;
Il faut absolument que notre application web se souvienne si la personne est authentifiée sinon elle doit rediriger vers &#039;&#039;index.php&#039;&#039;. Nous allons utiliser les &#039;&#039;sessions&#039;&#039; pour doter notre application Web d&#039;une mémoire !&lt;br /&gt;
&lt;br /&gt;
Dans la page &#039;&#039;traitement.php&#039;&#039; modifiez les lignes suivantes :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Authentification réussie&lt;br /&gt;
session_start();&lt;br /&gt;
$_SESSION[&amp;quot;logged&amp;quot;] = true;&lt;br /&gt;
return header(&amp;quot;Location: ../logged.php&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour accéder à cette variable nous allons utiliser une fonction, comme pour &#039;&#039;GET&#039;&#039; et &#039;&#039;POST&#039;&#039;, que nous allons placer dans le fichier &#039;&#039;helper.php&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function sessionVar($name) {&lt;br /&gt;
	if (session_status() !== PHP_SESSION_ACTIVE) {&lt;br /&gt;
		session_start();&lt;br /&gt;
	}&lt;br /&gt;
	if (isset ( $_SESSION [$name] )) {&lt;br /&gt;
		if (! empty ( $_SESSION [$name] )) {&lt;br /&gt;
			return $_SESSION [$name];&lt;br /&gt;
		}&lt;br /&gt;
		return TRUE;&lt;br /&gt;
	}&lt;br /&gt;
	return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
ou pour reprendre l&#039;exemple plus haut :&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
function sessionVar($name) {&lt;br /&gt;
	if (session_status() !== PHP_SESSION_ACTIVE) {&lt;br /&gt;
		session_start();&lt;br /&gt;
	}&lt;br /&gt;
	return retrieveVar($name, $_SESSION);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
La fonction &#039;&#039;session_status&#039;&#039; permet de connaître le statut d&#039;une session est peut prendre plusieurs valeurs :&lt;br /&gt;
*PHP_SESSION_DISABLED : si les sessions sont désactivées sur le serveur;&lt;br /&gt;
*PHP_SESSION_NONE : si les sessions sont actives mais qu&#039;aucune n&#039;existe;&lt;br /&gt;
*PHP_SESSION_ACTIVE : si une session existe&lt;br /&gt;
Cette fonction est très utile car elle nous permet de tester l’existence d&#039;une session avant de la démarrer. Php lèvera une notification si on appel deux fois de suite à la fonction &#039;&#039;session_start&#039;&#039; : &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PHP Notice:  A session had already been started - ignoring session_start()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne nous reste plus qu&#039;à récupérer cette valeur dans la page &#039;&#039;logged.php&#039;&#039; et à rediriger les &#039;&#039;petits malin&#039;&#039; non authentifiés vers la page &#039;&#039;index.php&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
include &#039;php/helper.php&#039;;&lt;br /&gt;
$logged = sessionVar(&amp;quot;logged&amp;quot;);&lt;br /&gt;
if($logged === FALSE){&lt;br /&gt;
	return header(&amp;quot;Location: index.php&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;Authentification réussie !&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Perte de mémoire !==&lt;br /&gt;
Si on veut déconnecter l&#039;utilisateur de l&#039;application il faut &#039;&#039;oublier&#039;&#039; qu&#039;il est connecté et, pour cela, il faut utiliser la fonction &#039;&#039;session_destroy&#039;&#039;.&lt;br /&gt;
Nous allons créer une page &#039;&#039;php/unlog.php&#039;&#039; qui contiendra les lignes suivantes :&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
session_start();&lt;br /&gt;
session_destroy();&lt;br /&gt;
header(&amp;quot;Location: index.php&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Dans cette page on :&lt;br /&gt;
* démarre la session;&lt;br /&gt;
* détruit la session;&lt;br /&gt;
* redirige l&#039;utilisateur vers &#039;&#039;index.php&#039;&#039;&lt;br /&gt;
Il ne reste plus qu&#039;à mettre un lien dans la page &#039;&#039;logged.php&#039;&#039;, a la fin du fichier :&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
echo &amp;quot;&amp;lt;a href=&#039;php/unlog.php&#039; &amp;gt;déconnexion&amp;lt;/a&amp;gt;&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Pour résumer :&lt;br /&gt;
* session_start : permet de démarrer une session;&lt;br /&gt;
* session _status : permet de connaître l&#039;état d&#039;une session;&lt;br /&gt;
* session_destroy : permet de détruire une session.&lt;br /&gt;
&lt;br /&gt;
=Pour aller plus loin=&lt;br /&gt;
On pourrait même modifier l&#039;URL pour que la page &#039;&#039;index.php&#039;&#039; affiche le message suivant : &#039;&#039;Vous devez être connecté pour voir cette page !&#039;&#039; :&lt;br /&gt;
* sur la page &#039;&#039;logged.php&#039;&#039; modifiez la ligne suivante :&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
return header(&amp;quot;Location: index.php?unlogged&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sur la page &#039;&#039;index.php&#039;&#039; modifiez les lignes suivantes :&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
include &#039;php/helper.php&#039;;&lt;br /&gt;
if(getVar(&amp;quot;echec&amp;quot;)!== FALSE){&lt;br /&gt;
	echo &amp;quot;Echec de l&#039;authentification !&amp;quot;;&lt;br /&gt;
}else if(getVar(&amp;quot;logged&amp;quot;)!== FALSE){&lt;br /&gt;
	echo &amp;quot;Vous devez être connecté pour voir cette page !&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Php_$get_$post_$session&amp;diff=4163</id>
		<title>Php $get $post $session</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Php_$get_$post_$session&amp;diff=4163"/>
		<updated>2025-12-16T14:24:42Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Réception d&amp;#039;information */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
== Les concepts ==&lt;br /&gt;
Lorsque l&#039;on conçoit une application Web, il y a un moment ou on est obligé de communiquer des informations entre les différentes pages de cette application. &lt;br /&gt;
Les pages sont demandées par la partie cliente, généralement un navigateur (eg. Firefox, Chrome, Safari, ...), et sont distribuées par la partie serveur (eg. Apache HTTPd, Nginx, IIS, ...).&lt;br /&gt;
&lt;br /&gt;
Cette échange est encadré par l&#039;utilisation du protocole [[:Media:HTTP.pdf|HTTP]] et doit donc utiliser des &#039;&#039;&#039;méthodes HTTP&#039;&#039;&#039;, aussi appellées &#039;&#039;&#039;verbes HTTP&#039;&#039;&#039;, consacrés :&lt;br /&gt;
* &#039;&#039;GET&#039;&#039;;&lt;br /&gt;
* &#039;&#039;POST&#039;&#039;;&lt;br /&gt;
* &#039;&#039;PUT&#039;&#039;;&lt;br /&gt;
* &#039;&#039;DELETE&#039;&#039;;&lt;br /&gt;
Les deux premiers sont utilisés dans les applications Web au travers de formulaires et les deux derniers sont plutôt utilisés dans le cadre de [[:Media:webservices.pdf|Web services RESTful]].&lt;br /&gt;
&lt;br /&gt;
On distingue donc deux cas de figure :&lt;br /&gt;
*l&#039;envoie d&#039;informations du client au serveur;&lt;br /&gt;
*la conservation d&#039;information côté serveur.&lt;br /&gt;
Dans le premier cas de figure, on peut utiliser les méthodes &#039;&#039;GET&#039;&#039; ou &#039;&#039;POST&#039;&#039;, alors que dans le deuxième cas de figure on utilisera les sessions.&lt;br /&gt;
&lt;br /&gt;
== Création d&#039;un projet == &lt;br /&gt;
Pour illustrer ces propos nous allons créer un projet dans [[eclipse_install|Eclipse]] et pour cela assurez-vous d&#039;avoir la perspective Php ainsi que d&#039;avoir suivi le tutoriel [[Php_xdebug | Xdebug]]. &lt;br /&gt;
&lt;br /&gt;
Commençons par la création du projet:  &lt;br /&gt;
* Première écran de l&#039;assistant :&lt;br /&gt;
** on créé le projet sur le [[Php_xdebug#Ajout_d.27un_serveur_Web|serveur local]];&lt;br /&gt;
** on active le support de JavaScript&lt;br /&gt;
[[Fichier:Eclipse php project create first screen.png|centré|450px]]&lt;br /&gt;
* Deuxième écran de l&#039;assistant :&lt;br /&gt;
** On créé un repertoire &#039;&#039;src&#039;&#039;&lt;br /&gt;
[[Fichier:Eclipse php project create second screen.png|centré|450px]]&lt;br /&gt;
* On clique sur &#039;&#039;finish&#039;&#039; pour terminer l&#039;assistant&lt;br /&gt;
&lt;br /&gt;
Une fois le projet créé, on va y ajouter une page &#039;&#039;index.php&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;title&amp;gt;Formulaire&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
		&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;form action=&amp;quot;php/traitement.php&amp;quot; method=&amp;quot;get&amp;quot;&amp;gt;&lt;br /&gt;
				Login :&lt;br /&gt;
				&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;login&amp;quot;&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
				Password : &lt;br /&gt;
				&amp;lt;input type=&amp;quot;password&amp;quot;  name=&amp;quot;password&amp;quot;&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
				&amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Connexion&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;/form&amp;gt;&lt;br /&gt;
		&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comme vous pouvez le constater, cette page ne contient pas le code Php en charge du traitement du formulaire. Elle envoie les informations à la page &#039;&#039;traitement.php&#039;&#039; qui se trouve dans le répertoire &#039;&#039;php&#039;&#039;. &lt;br /&gt;
Voici le code de cette page :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
var_dump($_GET);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= La méthode &#039;&#039;GET&#039;&#039; =&lt;br /&gt;
Pour l&#039;instant, en phase de découverte, nous allons utiliser la fonction &#039;&#039;var_dump&#039;&#039; qui permet d&#039;afficher le contenu d&#039;une variable, peu importe son type.&lt;br /&gt;
On pourrait également utiliser la vue &#039;&#039;Debug&#039;&#039; d&#039;Eclipse pour voir le contenu de la variable en mémoire. &lt;br /&gt;
Peu importe la solution retenue, l&#039;important et de voir la corrélation entre :&lt;br /&gt;
*les données du formulaire;&lt;br /&gt;
*l&#039;URL;&lt;br /&gt;
*le tableau &#039;&#039;$_GET&#039;&#039; créé par l&#039;interpréteur Php.&lt;br /&gt;
== Envoie d&#039;information ==&lt;br /&gt;
Lorsque l&#039;on valide le formulaire, le navigateur va construivre l&#039;URL suivante :&lt;br /&gt;
&lt;br /&gt;
http://localhost/Form/src/php/traitement.php?login=toto&amp;amp;password=titi&lt;br /&gt;
&lt;br /&gt;
Et l&#039;interpréteur Php va remplir le tableau &#039;&#039;$_GET&#039;&#039; de la manière suivante (résultat du &#039;&#039;var_dump&#039;&#039;) :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
array (size=2)&lt;br /&gt;
  &#039;login&#039; =&amp;gt; string &#039;toto&#039; (length=4)&lt;br /&gt;
  &#039;password&#039; =&amp;gt; string &#039;titi&#039; (length=4)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
On peut s&#039;amuser à modifier les valeurs dans l&#039;URL pour comprendre l&#039;impact sur le contenu du tableau &#039;&#039;$_GET&#039;&#039; : l&#039;interpréteur construit un tableau associatif ou figurera chacun des tuples présent dans l&#039;URL.&lt;br /&gt;
Le caractère &#039;&#039;?&#039;&#039; sert à séparer la ressource demandée de la liste des tuples et le caractère &#039;&#039;&amp;amp;&#039;&#039; sert à séparer chacun des tuples dans la liste.&lt;br /&gt;
&lt;br /&gt;
== Réception d&#039;information ==&lt;br /&gt;
La récupération d&#039;une valeur semble simple : il suffit d&#039;y accéder en spécifiant son nom.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
$login = $_GET[&amp;quot;login&amp;quot;];&lt;br /&gt;
$password = $_GET[&amp;quot;password&amp;quot;];&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;Votre login est : &amp;quot;.$login;&lt;br /&gt;
echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
echo &amp;quot;Votre mot de passe est : &amp;quot;.$password;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= La méthode &#039;&#039;POST&#039;&#039; =&lt;br /&gt;
== Envoie d&#039;information ==&lt;br /&gt;
Très similaire à la méthode &#039;&#039;GET&#039;&#039; à la différence que les tuples sont envoyé dans le corps de la requête &#039;&#039;HTTP&#039;&#039; et non dans l&#039;URL.&lt;br /&gt;
Pour l&#039;utiliser, il suffit de modifier la ligne suivante du fichier &#039;&#039;index.php&#039;&#039; :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;form action=&amp;quot;php/traitement.php&amp;quot; method=&amp;quot;get&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
en &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;form action=&amp;quot;php/traitement.php&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== Réception d&#039;information ==&lt;br /&gt;
La récupération des informations ce fait non plus dans le tableau &#039;&#039;$_GET&#039;&#039; mais dans le tableau &#039;&#039;$_POST&#039;&#039; :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
$login = $_POST[&amp;quot;login&amp;quot;];&lt;br /&gt;
$password = $_POST[&amp;quot;password&amp;quot;];&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;Votre login est : &amp;quot;.$login;&lt;br /&gt;
echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
echo &amp;quot;Votre mot de passe est : &amp;quot;.$password;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Limitations =&lt;br /&gt;
==Une variable manque à l&#039;appel !==&lt;br /&gt;
Que se passe t-il si l&#039;un des paramètres manque ? Si, par exemple, il manque le tuple &#039;&#039;password&#039;&#039; comme c&#039;est le cas avec l&#039;URL suivante:&lt;br /&gt;
&lt;br /&gt;
http://localhost/Form/src/php/traitement.php?login=toto&lt;br /&gt;
&lt;br /&gt;
Ci-dessous un extrait du fichier &#039;&#039;/var/log/httpd/error_log&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Mon Jun 27 04:49:56 2016] [error] [client 192.168.100.1] PHP Notice:  Undefined index: password in /var/www/html/Form/src/php/traitement.php on line 4&lt;br /&gt;
[Mon Jun 27 04:49:56 2016] [error] [client 192.168.100.1] PHP Stack trace:&lt;br /&gt;
[Mon Jun 27 04:49:56 2016] [error] [client 192.168.100.1] PHP   1. {main}() /var/www/html/Form/src/php/traitement.php:0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour ne pas prendre de risque il faudrait tester si l&#039;index existe en utilisant la fonction &#039;&#039;isset&#039;&#039; : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
if (isset ( $_GET [&amp;quot;login&amp;quot;] )) {&lt;br /&gt;
	$login = $_GET [&amp;quot;login&amp;quot;];&lt;br /&gt;
	echo &amp;quot;Votre login est : &amp;quot; . $login;&lt;br /&gt;
	echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
if (isset ( $_GET [&amp;quot;password&amp;quot;] )) {&lt;br /&gt;
	$password = $_GET [&amp;quot;password&amp;quot;];&lt;br /&gt;
	echo &amp;quot;Votre mot de passe est : &amp;quot; . $password;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==Ma variable est vide !==&lt;br /&gt;
Que se passe t-il si la variable est vide ? Si, par exemple, le tuple &#039;&#039;password&#039;&#039; ne possède pas de valeur comme c&#039;est le cas avec l&#039;URL suivante:&lt;br /&gt;
&lt;br /&gt;
http://localhost/Form/src/php/traitement.php?login=toto&amp;amp;password&lt;br /&gt;
&lt;br /&gt;
Aucune erreur n&#039;est générée mais le reste du code risque de ne pas fonctionner correctement...&lt;br /&gt;
Il faudrait tester si la variable n&#039;est pas vide grâce à la fonction &#039;&#039;empty&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (isset ( $_GET [&amp;quot;login&amp;quot;] )) {&lt;br /&gt;
	$login = $_GET [&amp;quot;login&amp;quot;];&lt;br /&gt;
	if (! empty ( $login )) {&lt;br /&gt;
		echo &amp;quot;Votre login est : &amp;quot; . $login;&lt;br /&gt;
		echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
if (isset ( $_GET [&amp;quot;password&amp;quot;] )) {&lt;br /&gt;
	$password = $_GET [&amp;quot;password&amp;quot;];&lt;br /&gt;
	if (! empty ( $password )) {&lt;br /&gt;
		echo &amp;quot;Votre mot de passe est : &amp;quot; . $password;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== Réfléxion... ==&lt;br /&gt;
Pour ce formulaire composé de seulement deux champs on constate que la quantité de code qu&#039;il faut écrire, si on veut faire un traitement efficace des valeurs, est colossale. &lt;br /&gt;
On imagine facilement que plus il y aura de champs plus cela sera pénible...&lt;br /&gt;
&lt;br /&gt;
Il faudrait un moyen, pas trop compliqué, de savoir si une variable existe et si elle à une valeur. On va en profiter pour gérer le cas des nombre, en effet, le chiffre &#039;&#039;0&#039;&#039; est considéré comme &#039;&#039;empty&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons écrire deux fonctions pour cela dans le fichier &#039;&#039;php/helper.php&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
function getVar($name) {&lt;br /&gt;
	if (isset ( $_GET [$name] )) {&lt;br /&gt;
		if(is_numeric($tab[$name])){&lt;br /&gt;
			return $tab[$name];&lt;br /&gt;
		}&lt;br /&gt;
		if (! empty ( $_GET [$name] )) {&lt;br /&gt;
			return $_GET [$name];&lt;br /&gt;
		}&lt;br /&gt;
		return TRUE;&lt;br /&gt;
	}&lt;br /&gt;
	return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function postVar($name) {&lt;br /&gt;
	if (isset ( $_POST [$name] )) {&lt;br /&gt;
		if(is_numeric($tab[$name])){&lt;br /&gt;
			return $tab[$name];&lt;br /&gt;
		}&lt;br /&gt;
		if (! empty ( $_POST[$name] )) {&lt;br /&gt;
			return $_POST[$name];&lt;br /&gt;
		}&lt;br /&gt;
		return TRUE;&lt;br /&gt;
	}&lt;br /&gt;
	return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bon d&#039;accord ! On peut encore compresser :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
function getVar($name) {&lt;br /&gt;
	return retrieveVar($name, $_GET);&lt;br /&gt;
}&lt;br /&gt;
function postVar($name) {&lt;br /&gt;
	return retrieveVar($name, $_POST);&lt;br /&gt;
}&lt;br /&gt;
function retrieveVar($name, $tab){&lt;br /&gt;
	if (isset($tab[$name])) {&lt;br /&gt;
		if(is_numeric($tab[$name])){&lt;br /&gt;
			return $tab[$name];&lt;br /&gt;
		}&lt;br /&gt;
		if (! empty($tab[$name])) {&lt;br /&gt;
			return $tab[$name];&lt;br /&gt;
		}&lt;br /&gt;
		return TRUE;&lt;br /&gt;
	}&lt;br /&gt;
	return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ces fonctions renvoies :&lt;br /&gt;
* faux si la variable n&#039;existe pas ;&lt;br /&gt;
* vrai si elle existe mais est vide ;&lt;br /&gt;
* sinon sa valeur.&lt;br /&gt;
&lt;br /&gt;
La philosophie est simple:&lt;br /&gt;
* soit on à besoin que la variable &#039;&#039;&#039;existe&#039;&#039;&#039; et on fait le test suivant :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	$var = getVar(&amp;quot;var&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	// Si différent de faux, la variable existe avec une valeur ou non !&lt;br /&gt;
	if($var !== FALSE){&lt;br /&gt;
		...&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* soit il faut &#039;&#039;&#039;absolument&#039;&#039;&#039; une valeur on fait le test suivant :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	$var = getVar(&amp;quot;var&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	// Si la variable n&#039;est pas un booleen, elle à une valeur !&lt;br /&gt;
	if(!is_bool($var)){&lt;br /&gt;
		...&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voyez comme le code est plus clair, simple, efficace :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
include &#039;helper.php&#039;;&lt;br /&gt;
&lt;br /&gt;
$login = getVar ( &amp;quot;login&amp;quot; );&lt;br /&gt;
$password = getVar ( &amp;quot;password&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
if (! is_bool ( $login )) {&lt;br /&gt;
	echo &amp;quot;Votre login est : &amp;quot; . $login;&lt;br /&gt;
	echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
if (! is_bool ( $password )) {&lt;br /&gt;
	echo &amp;quot;Votre mot de passe est : &amp;quot; . $password;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Interaction =&lt;br /&gt;
==Redirection après vérification==&lt;br /&gt;
Il faudrait maintenant tester les valeurs des champs pour confirmer ou infirmer l&#039;authentification. Pour ce faire, nous allons modifier la page &#039;&#039;traitement.php&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
include &#039;helper.php&#039;;&lt;br /&gt;
&lt;br /&gt;
// Valeur par défaut&lt;br /&gt;
$stored_login = &amp;quot;root&amp;quot;;&lt;br /&gt;
$stored_password = &amp;quot;toor&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
$login = getVar ( &amp;quot;login&amp;quot; );&lt;br /&gt;
$password = getVar ( &amp;quot;password&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
if (! is_bool ( $login ) &amp;amp;&amp;amp; ! is_bool ( $password )) {&lt;br /&gt;
	if($login == $stored_login &amp;amp;&amp;amp; $password == $stored_password){&lt;br /&gt;
		// Authentification réussie&lt;br /&gt;
		return header(&amp;quot;Location: ../logged.php&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Authentification échoué&lt;br /&gt;
header(&amp;quot;Location: ../index.php&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dans ce script :&lt;br /&gt;
* on définit au début les valeurs du couple login / mot de passe mais on aurait pu récupérer ces valeur dans une base de données;&lt;br /&gt;
* on utilise la fonction &#039;&#039;header&#039;&#039; avec la valeur &#039;&#039;Location&#039;&#039; qui permet de &#039;&#039;&#039;rediriger le navigateur&#039;&#039;&#039; en ajoutant un &#039;&#039;header HTTP&#039;&#039; à la réponse.&lt;br /&gt;
Si l&#039;authentification se passe correctement, on est redirigé vers la page &#039;&#039;logged.php&#039;&#039; (que nous allons créer), si elle échoue on revient sur la page &#039;&#039;index.php&#039;&#039; pour faire une nouvelle tentative de connexion.&lt;br /&gt;
Voici le code de la page &#039;&#039;logged.php&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;Authentification réussie !&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==User eXperience==&lt;br /&gt;
En UX, on s&#039;intéresse beaucoup à l&#039;utilisateur et son ressenti. Que va t-il se passer si l&#039;authentification échoue ? Il sera redirigé tellement vite sur la page &#039;&#039;index.php&#039;&#039; qu&#039;il aura l&#039;impression que rien ne s&#039;est passé... Il faut absolument que la page &#039;&#039;index.php&#039;&#039; affiche un message qui informe de l&#039;échec de l&#039;authentification !&lt;br /&gt;
&lt;br /&gt;
C&#039;est &#039;&#039;traitement.php&#039;&#039; qui détient l&#039;information de l’échec ou de la réussite de l&#039;authentification... Il faudrait que cette page transmette l&#039;information à &#039;&#039;index.php&#039;&#039; ! Comment faire... avec un &#039;&#039;GET&#039;&#039; mais, cette fois, sans formulaire et uniquement en modifiant l&#039;URL de redirection en cas d&#039;échec. Dans le fichier &#039;&#039;traitement.php&#039;&#039; modifiez la ligne suivante :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Authentification échoué&lt;br /&gt;
header(&amp;quot;Location: ../index.php&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
en &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Authentification échoué&lt;br /&gt;
header(&amp;quot;Location: ../index.php?echec&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Il suffit maintenant de récupérer l&#039;information dans la page &#039;&#039;index.php&#039;&#039;. Ajouter, au dessus de la &#039;&#039;div&#039;&#039; existante, le code suivant :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;?php&lt;br /&gt;
		include &#039;php/helper.php&#039;;&lt;br /&gt;
		$echec = getVar(&amp;quot;echec&amp;quot;);&lt;br /&gt;
		if($echec !== FALSE){&lt;br /&gt;
			echo &amp;quot;Echec de l&#039;authentification !&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
	?&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Lorsque l&#039;on échoue l&#039;authentification, un message s&#039;affiche !&lt;br /&gt;
= Les sessions =&lt;br /&gt;
Les sessions en PHP permettent de mémoriser des informations dans le tableau $_SESSION accessible après l&#039;appel de la fonction &#039;&#039;session_start&#039;&#039;.&lt;br /&gt;
Ce tableau est unique pour chaque connexion et ne peut être partagé entre plusieurs clients.&lt;br /&gt;
== Mémoire d&#039;éléphant ==&lt;br /&gt;
Si on essaye d&#039;accéder à la page &#039;&#039;logged.php&#039;&#039; sans passer par la page &#039;&#039;index.php&#039;&#039;... cela fonctionne.&lt;br /&gt;
&lt;br /&gt;
Il faut absolument que notre application web se souvienne si la personne est authentifiée sinon elle doit rediriger vers &#039;&#039;index.php&#039;&#039;. Nous allons utiliser les &#039;&#039;sessions&#039;&#039; pour doter notre application Web d&#039;une mémoire !&lt;br /&gt;
&lt;br /&gt;
Dans la page &#039;&#039;traitement.php&#039;&#039; modifiez les lignes suivantes :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Authentification réussie&lt;br /&gt;
session_start();&lt;br /&gt;
$_SESSION[&amp;quot;logged&amp;quot;] = true;&lt;br /&gt;
return header(&amp;quot;Location: ../logged.php&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour accéder à cette variable nous allons utiliser une fonction, comme pour &#039;&#039;GET&#039;&#039; et &#039;&#039;POST&#039;&#039;, que nous allons placer dans le fichier &#039;&#039;helper.php&#039;&#039; :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function sessionVar($name) {&lt;br /&gt;
	if (session_status() !== PHP_SESSION_ACTIVE) {&lt;br /&gt;
		session_start();&lt;br /&gt;
	}&lt;br /&gt;
	if (isset ( $_SESSION [$name] )) {&lt;br /&gt;
		if (! empty ( $_SESSION [$name] )) {&lt;br /&gt;
			return $_SESSION [$name];&lt;br /&gt;
		}&lt;br /&gt;
		return TRUE;&lt;br /&gt;
	}&lt;br /&gt;
	return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ou pour reprendre l&#039;exemple plus haut :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=php&amp;gt;&lt;br /&gt;
function sessionVar($name) {&lt;br /&gt;
	if (session_status() !== PHP_SESSION_ACTIVE) {&lt;br /&gt;
		session_start();&lt;br /&gt;
	}&lt;br /&gt;
	return retrieveVar($name, $_SESSION);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
La fonction &#039;&#039;session_status&#039;&#039; permet de connaître le statut d&#039;une session est peut prendre plusieurs valeurs :&lt;br /&gt;
*PHP_SESSION_DISABLED : si les sessions sont désactivées sur le serveur;&lt;br /&gt;
*PHP_SESSION_NONE : si les sessions sont actives mais qu&#039;aucune n&#039;existe;&lt;br /&gt;
*PHP_SESSION_ACTIVE : si une session existe&lt;br /&gt;
Cette fonction est très utile car elle nous permet de tester l’existence d&#039;une session avant de la démarrer. Php lèvera une notification si on appel deux fois de suite à la fonction &#039;&#039;session_start&#039;&#039; : &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PHP Notice:  A session had already been started - ignoring session_start()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne nous reste plus qu&#039;à récupérer cette valeur dans la page &#039;&#039;logged.php&#039;&#039; et à rediriger les &#039;&#039;petits malin&#039;&#039; non authentifiés vers la page &#039;&#039;index.php&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
include &#039;php/helper.php&#039;;&lt;br /&gt;
$logged = sessionVar(&amp;quot;logged&amp;quot;);&lt;br /&gt;
if($logged === FALSE){&lt;br /&gt;
	return header(&amp;quot;Location: index.php&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;Authentification réussie !&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Perte de mémoire !==&lt;br /&gt;
Si on veut déconnecter l&#039;utilisateur de l&#039;application il faut &#039;&#039;oublier&#039;&#039; qu&#039;il est connecté et, pour cela, il faut utiliser la fonction &#039;&#039;session_destroy&#039;&#039;.&lt;br /&gt;
Nous allons créer une page &#039;&#039;php/unlog.php&#039;&#039; qui contiendra les lignes suivantes :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
session_start();&lt;br /&gt;
session_destroy();&lt;br /&gt;
header(&amp;quot;Location: index.php&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dans cette page on :&lt;br /&gt;
* démarre la session;&lt;br /&gt;
* détruit la session;&lt;br /&gt;
* redirige l&#039;utilisateur vers &#039;&#039;index.php&#039;&#039;&lt;br /&gt;
Il ne reste plus qu&#039;à mettre un lien dans la page &#039;&#039;logged.php&#039;&#039;, a la fin du fichier :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=php&amp;gt;&lt;br /&gt;
echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
echo &amp;quot;&amp;lt;a href=&#039;php/unlog.php&#039; &amp;gt;déconnexion&amp;lt;/a&amp;gt;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Pour résumer :&lt;br /&gt;
* session_start : permet de démarrer une session;&lt;br /&gt;
* session _status : permet de connaître l&#039;état d&#039;une session;&lt;br /&gt;
* session_destroy : permet de détruire une session.&lt;br /&gt;
&lt;br /&gt;
=Pour aller plus loin=&lt;br /&gt;
On pourrait même modifier l&#039;URL pour que la page &#039;&#039;index.php&#039;&#039; affiche le message suivant : &#039;&#039;Vous devez être connecté pour voir cette page !&#039;&#039; :&lt;br /&gt;
* sur la page &#039;&#039;logged.php&#039;&#039; modifiez la ligne suivante :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=php&amp;gt;&lt;br /&gt;
return header(&amp;quot;Location: index.php?unlogged&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* sur la page &#039;&#039;index.php&#039;&#039; modifiez les lignes suivantes :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
include &#039;php/helper.php&#039;;&lt;br /&gt;
if(getVar(&amp;quot;echec&amp;quot;)!== FALSE){&lt;br /&gt;
	echo &amp;quot;Echec de l&#039;authentification !&amp;quot;;&lt;br /&gt;
}else if(getVar(&amp;quot;logged&amp;quot;)!== FALSE){&lt;br /&gt;
	echo &amp;quot;Vous devez être connecté pour voir cette page !&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Php_$get_$post_$session&amp;diff=4162</id>
		<title>Php $get $post $session</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Php_$get_$post_$session&amp;diff=4162"/>
		<updated>2025-12-16T14:22:50Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Création d&amp;#039;un projet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
== Les concepts ==&lt;br /&gt;
Lorsque l&#039;on conçoit une application Web, il y a un moment ou on est obligé de communiquer des informations entre les différentes pages de cette application. &lt;br /&gt;
Les pages sont demandées par la partie cliente, généralement un navigateur (eg. Firefox, Chrome, Safari, ...), et sont distribuées par la partie serveur (eg. Apache HTTPd, Nginx, IIS, ...).&lt;br /&gt;
&lt;br /&gt;
Cette échange est encadré par l&#039;utilisation du protocole [[:Media:HTTP.pdf|HTTP]] et doit donc utiliser des &#039;&#039;&#039;méthodes HTTP&#039;&#039;&#039;, aussi appellées &#039;&#039;&#039;verbes HTTP&#039;&#039;&#039;, consacrés :&lt;br /&gt;
* &#039;&#039;GET&#039;&#039;;&lt;br /&gt;
* &#039;&#039;POST&#039;&#039;;&lt;br /&gt;
* &#039;&#039;PUT&#039;&#039;;&lt;br /&gt;
* &#039;&#039;DELETE&#039;&#039;;&lt;br /&gt;
Les deux premiers sont utilisés dans les applications Web au travers de formulaires et les deux derniers sont plutôt utilisés dans le cadre de [[:Media:webservices.pdf|Web services RESTful]].&lt;br /&gt;
&lt;br /&gt;
On distingue donc deux cas de figure :&lt;br /&gt;
*l&#039;envoie d&#039;informations du client au serveur;&lt;br /&gt;
*la conservation d&#039;information côté serveur.&lt;br /&gt;
Dans le premier cas de figure, on peut utiliser les méthodes &#039;&#039;GET&#039;&#039; ou &#039;&#039;POST&#039;&#039;, alors que dans le deuxième cas de figure on utilisera les sessions.&lt;br /&gt;
&lt;br /&gt;
== Création d&#039;un projet == &lt;br /&gt;
Pour illustrer ces propos nous allons créer un projet dans [[eclipse_install|Eclipse]] et pour cela assurez-vous d&#039;avoir la perspective Php ainsi que d&#039;avoir suivi le tutoriel [[Php_xdebug | Xdebug]]. &lt;br /&gt;
&lt;br /&gt;
Commençons par la création du projet:  &lt;br /&gt;
* Première écran de l&#039;assistant :&lt;br /&gt;
** on créé le projet sur le [[Php_xdebug#Ajout_d.27un_serveur_Web|serveur local]];&lt;br /&gt;
** on active le support de JavaScript&lt;br /&gt;
[[Fichier:Eclipse php project create first screen.png|centré|450px]]&lt;br /&gt;
* Deuxième écran de l&#039;assistant :&lt;br /&gt;
** On créé un repertoire &#039;&#039;src&#039;&#039;&lt;br /&gt;
[[Fichier:Eclipse php project create second screen.png|centré|450px]]&lt;br /&gt;
* On clique sur &#039;&#039;finish&#039;&#039; pour terminer l&#039;assistant&lt;br /&gt;
&lt;br /&gt;
Une fois le projet créé, on va y ajouter une page &#039;&#039;index.php&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;title&amp;gt;Formulaire&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
		&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;form action=&amp;quot;php/traitement.php&amp;quot; method=&amp;quot;get&amp;quot;&amp;gt;&lt;br /&gt;
				Login :&lt;br /&gt;
				&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;login&amp;quot;&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
				Password : &lt;br /&gt;
				&amp;lt;input type=&amp;quot;password&amp;quot;  name=&amp;quot;password&amp;quot;&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
				&amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Connexion&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;/form&amp;gt;&lt;br /&gt;
		&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comme vous pouvez le constater, cette page ne contient pas le code Php en charge du traitement du formulaire. Elle envoie les informations à la page &#039;&#039;traitement.php&#039;&#039; qui se trouve dans le répertoire &#039;&#039;php&#039;&#039;. &lt;br /&gt;
Voici le code de cette page :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
var_dump($_GET);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= La méthode &#039;&#039;GET&#039;&#039; =&lt;br /&gt;
Pour l&#039;instant, en phase de découverte, nous allons utiliser la fonction &#039;&#039;var_dump&#039;&#039; qui permet d&#039;afficher le contenu d&#039;une variable, peu importe son type.&lt;br /&gt;
On pourrait également utiliser la vue &#039;&#039;Debug&#039;&#039; d&#039;Eclipse pour voir le contenu de la variable en mémoire. &lt;br /&gt;
Peu importe la solution retenue, l&#039;important et de voir la corrélation entre :&lt;br /&gt;
*les données du formulaire;&lt;br /&gt;
*l&#039;URL;&lt;br /&gt;
*le tableau &#039;&#039;$_GET&#039;&#039; créé par l&#039;interpréteur Php.&lt;br /&gt;
== Envoie d&#039;information ==&lt;br /&gt;
Lorsque l&#039;on valide le formulaire, le navigateur va construivre l&#039;URL suivante :&lt;br /&gt;
&lt;br /&gt;
http://localhost/Form/src/php/traitement.php?login=toto&amp;amp;password=titi&lt;br /&gt;
&lt;br /&gt;
Et l&#039;interpréteur Php va remplir le tableau &#039;&#039;$_GET&#039;&#039; de la manière suivante (résultat du &#039;&#039;var_dump&#039;&#039;) :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
array (size=2)&lt;br /&gt;
  &#039;login&#039; =&amp;gt; string &#039;toto&#039; (length=4)&lt;br /&gt;
  &#039;password&#039; =&amp;gt; string &#039;titi&#039; (length=4)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
On peut s&#039;amuser à modifier les valeurs dans l&#039;URL pour comprendre l&#039;impact sur le contenu du tableau &#039;&#039;$_GET&#039;&#039; : l&#039;interpréteur construit un tableau associatif ou figurera chacun des tuples présent dans l&#039;URL.&lt;br /&gt;
Le caractère &#039;&#039;?&#039;&#039; sert à séparer la ressource demandée de la liste des tuples et le caractère &#039;&#039;&amp;amp;&#039;&#039; sert à séparer chacun des tuples dans la liste.&lt;br /&gt;
&lt;br /&gt;
== Réception d&#039;information ==&lt;br /&gt;
La récupération d&#039;une valeur semble simple : il suffit d&#039;y accéder en spécifiant son nom.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
$login = $_GET[&amp;quot;login&amp;quot;];&lt;br /&gt;
$password = $_GET[&amp;quot;password&amp;quot;];&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;Votre login est : &amp;quot;.$login;&lt;br /&gt;
echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
echo &amp;quot;Votre mot de passe est : &amp;quot;.$password;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= La méthode &#039;&#039;POST&#039;&#039; =&lt;br /&gt;
== Envoie d&#039;information ==&lt;br /&gt;
Très similaire à la méthode &#039;&#039;GET&#039;&#039; à la différence que les tuples sont envoyé dans le corps de la requête &#039;&#039;HTTP&#039;&#039; et non dans l&#039;URL.&lt;br /&gt;
Pour l&#039;utiliser, il suffit de modifier la ligne suivante du fichier &#039;&#039;index.php&#039;&#039; :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;form action=&amp;quot;php/traitement.php&amp;quot; method=&amp;quot;get&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
en &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;form action=&amp;quot;php/traitement.php&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== Réception d&#039;information ==&lt;br /&gt;
La récupération des informations ce fait non plus dans le tableau &#039;&#039;$_GET&#039;&#039; mais dans le tableau &#039;&#039;$_POST&#039;&#039; :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
$login = $_POST[&amp;quot;login&amp;quot;];&lt;br /&gt;
$password = $_POST[&amp;quot;password&amp;quot;];&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;Votre login est : &amp;quot;.$login;&lt;br /&gt;
echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
echo &amp;quot;Votre mot de passe est : &amp;quot;.$password;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Limitations =&lt;br /&gt;
==Une variable manque à l&#039;appel !==&lt;br /&gt;
Que se passe t-il si l&#039;un des paramètres manque ? Si, par exemple, il manque le tuple &#039;&#039;password&#039;&#039; comme c&#039;est le cas avec l&#039;URL suivante:&lt;br /&gt;
&lt;br /&gt;
http://localhost/Form/src/php/traitement.php?login=toto&lt;br /&gt;
&lt;br /&gt;
Ci-dessous un extrait du fichier &#039;&#039;/var/log/httpd/error_log&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Mon Jun 27 04:49:56 2016] [error] [client 192.168.100.1] PHP Notice:  Undefined index: password in /var/www/html/Form/src/php/traitement.php on line 4&lt;br /&gt;
[Mon Jun 27 04:49:56 2016] [error] [client 192.168.100.1] PHP Stack trace:&lt;br /&gt;
[Mon Jun 27 04:49:56 2016] [error] [client 192.168.100.1] PHP   1. {main}() /var/www/html/Form/src/php/traitement.php:0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour ne pas prendre de risque il faudrait tester si l&#039;index existe en utilisant la fonction &#039;&#039;isset&#039;&#039; : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
if (isset ( $_GET [&amp;quot;login&amp;quot;] )) {&lt;br /&gt;
	$login = $_GET [&amp;quot;login&amp;quot;];&lt;br /&gt;
	echo &amp;quot;Votre login est : &amp;quot; . $login;&lt;br /&gt;
	echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
if (isset ( $_GET [&amp;quot;password&amp;quot;] )) {&lt;br /&gt;
	$password = $_GET [&amp;quot;password&amp;quot;];&lt;br /&gt;
	echo &amp;quot;Votre mot de passe est : &amp;quot; . $password;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==Ma variable est vide !==&lt;br /&gt;
Que se passe t-il si la variable est vide ? Si, par exemple, le tuple &#039;&#039;password&#039;&#039; ne possède pas de valeur comme c&#039;est le cas avec l&#039;URL suivante:&lt;br /&gt;
&lt;br /&gt;
http://localhost/Form/src/php/traitement.php?login=toto&amp;amp;password&lt;br /&gt;
&lt;br /&gt;
Aucune erreur n&#039;est générée mais le reste du code risque de ne pas fonctionner correctement...&lt;br /&gt;
Il faudrait tester si la variable n&#039;est pas vide grâce à la fonction &#039;&#039;empty&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if (isset ( $_GET [&amp;quot;login&amp;quot;] )) {&lt;br /&gt;
	$login = $_GET [&amp;quot;login&amp;quot;];&lt;br /&gt;
	if (! empty ( $login )) {&lt;br /&gt;
		echo &amp;quot;Votre login est : &amp;quot; . $login;&lt;br /&gt;
		echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
if (isset ( $_GET [&amp;quot;password&amp;quot;] )) {&lt;br /&gt;
	$password = $_GET [&amp;quot;password&amp;quot;];&lt;br /&gt;
	if (! empty ( $password )) {&lt;br /&gt;
		echo &amp;quot;Votre mot de passe est : &amp;quot; . $password;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== Réfléxion... ==&lt;br /&gt;
Pour ce formulaire composé de seulement deux champs on constate que la quantité de code qu&#039;il faut écrire, si on veut faire un traitement efficace des valeurs, est colossale. &lt;br /&gt;
On imagine facilement que plus il y aura de champs plus cela sera pénible...&lt;br /&gt;
&lt;br /&gt;
Il faudrait un moyen, pas trop compliqué, de savoir si une variable existe et si elle à une valeur. On va en profiter pour gérer le cas des nombre, en effet, le chiffre &#039;&#039;0&#039;&#039; est considéré comme &#039;&#039;empty&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons écrire deux fonctions pour cela dans le fichier &#039;&#039;php/helper.php&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
function getVar($name) {&lt;br /&gt;
	if (isset ( $_GET [$name] )) {&lt;br /&gt;
		if(is_numeric($tab[$name])){&lt;br /&gt;
			return $tab[$name];&lt;br /&gt;
		}&lt;br /&gt;
		if (! empty ( $_GET [$name] )) {&lt;br /&gt;
			return $_GET [$name];&lt;br /&gt;
		}&lt;br /&gt;
		return TRUE;&lt;br /&gt;
	}&lt;br /&gt;
	return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function postVar($name) {&lt;br /&gt;
	if (isset ( $_POST [$name] )) {&lt;br /&gt;
		if(is_numeric($tab[$name])){&lt;br /&gt;
			return $tab[$name];&lt;br /&gt;
		}&lt;br /&gt;
		if (! empty ( $_POST[$name] )) {&lt;br /&gt;
			return $_POST[$name];&lt;br /&gt;
		}&lt;br /&gt;
		return TRUE;&lt;br /&gt;
	}&lt;br /&gt;
	return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bon d&#039;accord ! On peut encore compresser :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
function getVar($name) {&lt;br /&gt;
	return retrieveVar($name, $_GET);&lt;br /&gt;
}&lt;br /&gt;
function postVar($name) {&lt;br /&gt;
	return retrieveVar($name, $_POST);&lt;br /&gt;
}&lt;br /&gt;
function retrieveVar($name, $tab){&lt;br /&gt;
	if (isset($tab[$name])) {&lt;br /&gt;
		if(is_numeric($tab[$name])){&lt;br /&gt;
			return $tab[$name];&lt;br /&gt;
		}&lt;br /&gt;
		if (! empty($tab[$name])) {&lt;br /&gt;
			return $tab[$name];&lt;br /&gt;
		}&lt;br /&gt;
		return TRUE;&lt;br /&gt;
	}&lt;br /&gt;
	return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ces fonctions renvoies :&lt;br /&gt;
* faux si la variable n&#039;existe pas ;&lt;br /&gt;
* vrai si elle existe mais est vide ;&lt;br /&gt;
* sinon sa valeur.&lt;br /&gt;
&lt;br /&gt;
La philosophie est simple:&lt;br /&gt;
* soit on à besoin que la variable &#039;&#039;&#039;existe&#039;&#039;&#039; et on fait le test suivant :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	$var = getVar(&amp;quot;var&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	// Si différent de faux, la variable existe avec une valeur ou non !&lt;br /&gt;
	if($var !== FALSE){&lt;br /&gt;
		...&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* soit il faut &#039;&#039;&#039;absolument&#039;&#039;&#039; une valeur on fait le test suivant :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
	$var = getVar(&amp;quot;var&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	// Si la variable n&#039;est pas un booleen, elle à une valeur !&lt;br /&gt;
	if(!is_bool($var)){&lt;br /&gt;
		...&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voyez comme le code est plus clair, simple, efficace :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
include &#039;helper.php&#039;;&lt;br /&gt;
&lt;br /&gt;
$login = getVar ( &amp;quot;login&amp;quot; );&lt;br /&gt;
$password = getVar ( &amp;quot;password&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
if (! is_bool ( $login )) {&lt;br /&gt;
	echo &amp;quot;Votre login est : &amp;quot; . $login;&lt;br /&gt;
	echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
if (! is_bool ( $password )) {&lt;br /&gt;
	echo &amp;quot;Votre mot de passe est : &amp;quot; . $password;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Interaction =&lt;br /&gt;
==Redirection après vérification==&lt;br /&gt;
Il faudrait maintenant tester les valeurs des champs pour confirmer ou infirmer l&#039;authentification. Pour ce faire, nous allons modifier la page &#039;&#039;traitement.php&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
include &#039;helper.php&#039;;&lt;br /&gt;
&lt;br /&gt;
// Valeur par défaut&lt;br /&gt;
$stored_login = &amp;quot;root&amp;quot;;&lt;br /&gt;
$stored_password = &amp;quot;toor&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
$login = getVar ( &amp;quot;login&amp;quot; );&lt;br /&gt;
$password = getVar ( &amp;quot;password&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
if (! is_bool ( $login ) &amp;amp;&amp;amp; ! is_bool ( $password )) {&lt;br /&gt;
	if($login == $stored_login &amp;amp;&amp;amp; $password == $stored_password){&lt;br /&gt;
		// Authentification réussie&lt;br /&gt;
		return header(&amp;quot;Location: ../logged.php&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Authentification échoué&lt;br /&gt;
header(&amp;quot;Location: ../index.php&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dans ce script :&lt;br /&gt;
* on définit au début les valeurs du couple login / mot de passe mais on aurait pu récupérer ces valeur dans une base de données;&lt;br /&gt;
* on utilise la fonction &#039;&#039;header&#039;&#039; avec la valeur &#039;&#039;Location&#039;&#039; qui permet de &#039;&#039;&#039;rediriger le navigateur&#039;&#039;&#039; en ajoutant un &#039;&#039;header HTTP&#039;&#039; à la réponse.&lt;br /&gt;
Si l&#039;authentification se passe correctement, on est redirigé vers la page &#039;&#039;logged.php&#039;&#039; (que nous allons créer), si elle échoue on revient sur la page &#039;&#039;index.php&#039;&#039; pour faire une nouvelle tentative de connexion.&lt;br /&gt;
Voici le code de la page &#039;&#039;logged.php&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;Authentification réussie !&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==User eXperience==&lt;br /&gt;
En UX, on s&#039;intéresse beaucoup à l&#039;utilisateur et son ressenti. Que va t-il se passer si l&#039;authentification échoue ? Il sera redirigé tellement vite sur la page &#039;&#039;index.php&#039;&#039; qu&#039;il aura l&#039;impression que rien ne s&#039;est passé... Il faut absolument que la page &#039;&#039;index.php&#039;&#039; affiche un message qui informe de l&#039;échec de l&#039;authentification !&lt;br /&gt;
&lt;br /&gt;
C&#039;est &#039;&#039;traitement.php&#039;&#039; qui détient l&#039;information de l’échec ou de la réussite de l&#039;authentification... Il faudrait que cette page transmette l&#039;information à &#039;&#039;index.php&#039;&#039; ! Comment faire... avec un &#039;&#039;GET&#039;&#039; mais, cette fois, sans formulaire et uniquement en modifiant l&#039;URL de redirection en cas d&#039;échec. Dans le fichier &#039;&#039;traitement.php&#039;&#039; modifiez la ligne suivante :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Authentification échoué&lt;br /&gt;
header(&amp;quot;Location: ../index.php&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
en &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Authentification échoué&lt;br /&gt;
header(&amp;quot;Location: ../index.php?echec&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Il suffit maintenant de récupérer l&#039;information dans la page &#039;&#039;index.php&#039;&#039;. Ajouter, au dessus de la &#039;&#039;div&#039;&#039; existante, le code suivant :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;?php&lt;br /&gt;
		include &#039;php/helper.php&#039;;&lt;br /&gt;
		$echec = getVar(&amp;quot;echec&amp;quot;);&lt;br /&gt;
		if($echec !== FALSE){&lt;br /&gt;
			echo &amp;quot;Echec de l&#039;authentification !&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
	?&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Lorsque l&#039;on échoue l&#039;authentification, un message s&#039;affiche !&lt;br /&gt;
= Les sessions =&lt;br /&gt;
Les sessions en PHP permettent de mémoriser des informations dans le tableau $_SESSION accessible après l&#039;appel de la fonction &#039;&#039;session_start&#039;&#039;.&lt;br /&gt;
Ce tableau est unique pour chaque connexion et ne peut être partagé entre plusieurs clients.&lt;br /&gt;
== Mémoire d&#039;éléphant ==&lt;br /&gt;
Si on essaye d&#039;accéder à la page &#039;&#039;logged.php&#039;&#039; sans passer par la page &#039;&#039;index.php&#039;&#039;... cela fonctionne.&lt;br /&gt;
&lt;br /&gt;
Il faut absolument que notre application web se souvienne si la personne est authentifiée sinon elle doit rediriger vers &#039;&#039;index.php&#039;&#039;. Nous allons utiliser les &#039;&#039;sessions&#039;&#039; pour doter notre application Web d&#039;une mémoire !&lt;br /&gt;
&lt;br /&gt;
Dans la page &#039;&#039;traitement.php&#039;&#039; modifiez les lignes suivantes :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Authentification réussie&lt;br /&gt;
session_start();&lt;br /&gt;
$_SESSION[&amp;quot;logged&amp;quot;] = true;&lt;br /&gt;
return header(&amp;quot;Location: ../logged.php&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour accéder à cette variable nous allons utiliser une fonction, comme pour &#039;&#039;GET&#039;&#039; et &#039;&#039;POST&#039;&#039;, que nous allons placer dans le fichier &#039;&#039;helper.php&#039;&#039; :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function sessionVar($name) {&lt;br /&gt;
	if (session_status() !== PHP_SESSION_ACTIVE) {&lt;br /&gt;
		session_start();&lt;br /&gt;
	}&lt;br /&gt;
	if (isset ( $_SESSION [$name] )) {&lt;br /&gt;
		if (! empty ( $_SESSION [$name] )) {&lt;br /&gt;
			return $_SESSION [$name];&lt;br /&gt;
		}&lt;br /&gt;
		return TRUE;&lt;br /&gt;
	}&lt;br /&gt;
	return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ou pour reprendre l&#039;exemple plus haut :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=php&amp;gt;&lt;br /&gt;
function sessionVar($name) {&lt;br /&gt;
	if (session_status() !== PHP_SESSION_ACTIVE) {&lt;br /&gt;
		session_start();&lt;br /&gt;
	}&lt;br /&gt;
	return retrieveVar($name, $_SESSION);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
La fonction &#039;&#039;session_status&#039;&#039; permet de connaître le statut d&#039;une session est peut prendre plusieurs valeurs :&lt;br /&gt;
*PHP_SESSION_DISABLED : si les sessions sont désactivées sur le serveur;&lt;br /&gt;
*PHP_SESSION_NONE : si les sessions sont actives mais qu&#039;aucune n&#039;existe;&lt;br /&gt;
*PHP_SESSION_ACTIVE : si une session existe&lt;br /&gt;
Cette fonction est très utile car elle nous permet de tester l’existence d&#039;une session avant de la démarrer. Php lèvera une notification si on appel deux fois de suite à la fonction &#039;&#039;session_start&#039;&#039; : &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PHP Notice:  A session had already been started - ignoring session_start()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne nous reste plus qu&#039;à récupérer cette valeur dans la page &#039;&#039;logged.php&#039;&#039; et à rediriger les &#039;&#039;petits malin&#039;&#039; non authentifiés vers la page &#039;&#039;index.php&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
include &#039;php/helper.php&#039;;&lt;br /&gt;
$logged = sessionVar(&amp;quot;logged&amp;quot;);&lt;br /&gt;
if($logged === FALSE){&lt;br /&gt;
	return header(&amp;quot;Location: index.php&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;Authentification réussie !&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Perte de mémoire !==&lt;br /&gt;
Si on veut déconnecter l&#039;utilisateur de l&#039;application il faut &#039;&#039;oublier&#039;&#039; qu&#039;il est connecté et, pour cela, il faut utiliser la fonction &#039;&#039;session_destroy&#039;&#039;.&lt;br /&gt;
Nous allons créer une page &#039;&#039;php/unlog.php&#039;&#039; qui contiendra les lignes suivantes :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
session_start();&lt;br /&gt;
session_destroy();&lt;br /&gt;
header(&amp;quot;Location: index.php&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dans cette page on :&lt;br /&gt;
* démarre la session;&lt;br /&gt;
* détruit la session;&lt;br /&gt;
* redirige l&#039;utilisateur vers &#039;&#039;index.php&#039;&#039;&lt;br /&gt;
Il ne reste plus qu&#039;à mettre un lien dans la page &#039;&#039;logged.php&#039;&#039;, a la fin du fichier :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=php&amp;gt;&lt;br /&gt;
echo &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
echo &amp;quot;&amp;lt;a href=&#039;php/unlog.php&#039; &amp;gt;déconnexion&amp;lt;/a&amp;gt;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Pour résumer :&lt;br /&gt;
* session_start : permet de démarrer une session;&lt;br /&gt;
* session _status : permet de connaître l&#039;état d&#039;une session;&lt;br /&gt;
* session_destroy : permet de détruire une session.&lt;br /&gt;
&lt;br /&gt;
=Pour aller plus loin=&lt;br /&gt;
On pourrait même modifier l&#039;URL pour que la page &#039;&#039;index.php&#039;&#039; affiche le message suivant : &#039;&#039;Vous devez être connecté pour voir cette page !&#039;&#039; :&lt;br /&gt;
* sur la page &#039;&#039;logged.php&#039;&#039; modifiez la ligne suivante :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=php&amp;gt;&lt;br /&gt;
return header(&amp;quot;Location: index.php?unlogged&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* sur la page &#039;&#039;index.php&#039;&#039; modifiez les lignes suivantes :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
include &#039;php/helper.php&#039;;&lt;br /&gt;
if(getVar(&amp;quot;echec&amp;quot;)!== FALSE){&lt;br /&gt;
	echo &amp;quot;Echec de l&#039;authentification !&amp;quot;;&lt;br /&gt;
}else if(getVar(&amp;quot;logged&amp;quot;)!== FALSE){&lt;br /&gt;
	echo &amp;quot;Vous devez être connecté pour voir cette page !&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4161</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4161"/>
		<updated>2025-12-14T10:06:20Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps où l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;à spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Tests =&lt;br /&gt;
Il faut maintenant tester le filtre et pour ça on peut utiliser un partage de connexion et réaliser le nombre d&#039;échecs spécifié par la directive &amp;quot;maxretry&amp;quot; (ici 3) :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# fail2ban-client status proxmox&lt;br /&gt;
Status for the jail: proxmox&lt;br /&gt;
|- Filter&lt;br /&gt;
|  |- Currently failed:	0&lt;br /&gt;
|  |- Total failed:	3&lt;br /&gt;
|  `- Journal matches:	_SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
`- Actions&lt;br /&gt;
   |- Currently banned:	1&lt;br /&gt;
   |- Total banned:	1&lt;br /&gt;
   `- Banned IP list:	37.167.23.159&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit bien l&#039;IP 37.167.23.159 qui est bannie !&lt;br /&gt;
N&#039;oubliez pas de l&#039;ajouter à &#039;&#039;ignoreip&#039;&#039; dans le fichier &#039;&#039;jails.local&#039;&#039; ou de faire &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# fail2ban-client unban 37.167.23.159&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilise l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
Même si le filtre &#039;&#039;fail2ban&#039;&#039; est configuré pour utiliser la commande &#039;&#039;journactl&#039;&#039; sur le démon &#039;&#039;pvedaemon&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
les adresses viennent de pveproxy qui journalise tout dans &#039;&#039;/var/log/pveproxy/access.log&#039;&#039; et &#039;&#039;pvedaemon&#039;&#039; ne journalise que les échecs d&#039;authentification. &lt;br /&gt;
Il est donc plus simple de faire un &#039;&#039;tail&#039;&#039; ou un &#039;&#039;cat&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Que d&#039;utiliser &#039;&#039;journalctl&#039;&#039; pour voir les IPs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
Dec 14 10:43:01 labo unix_chkpwd[1326506]: password check failed for user (root)&lt;br /&gt;
Dec 14 10:43:01 labo IPCC.xs[1320783]: pam_unix(proxmox-ve-auth:auth): authentication failure; logname= uid=0 euid=0 tty= ruser= rhost=37.167.23.159  user=root&lt;br /&gt;
Dec 14 10:43:03 labo pvedaemon[1320783]: authentication failure; rhost=37.167.23.159 user=root@pam msg=Authentication failure&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;&#039;uniquement&#039;&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrait ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jour d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modification peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Double IPv4 ou préfixe IPv6 ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/resources HTTP/1.1&amp;quot; 200 1226&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/tasks HTTP/1.1&amp;quot; 200 1090&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
::ffff:192.168.20.22 - root@pam [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/nodes/labo/lxc/100/status/current HTTP/1.1&amp;quot; 200 523&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il nous reste un peu de nettoyage à faire, dans certains cas :&lt;br /&gt;
* l&#039;adresse apparaît non pas une fois mais deux, séparées par une virgule&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
37.167.182.207, 37.167.182.207&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* l&#039;adresse IPv4 (32 bits) est affichée dans sa représentation IPv6 (128 bits), avec le préfixe &#039;&#039;::ffff:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
::ffff:192.168.20.22&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour faire le nettoyage :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   # Remove trailing ip &lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
if($reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
   # Remove IPv6 subnet for IPv4&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=.*:?&amp;lt;HOST&amp;gt;,? user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant, que l&#039;on peut créer dans /root/remoteip_fix.sh :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
    if(\$reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
        # Remove IPv6 subnet for IPv4&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$1;&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à le rendre exécutable et le lancer :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x /root/remoteip_fix.sh&lt;br /&gt;
# /root/remoteip_fix.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne faudra pas oublier de redémarrer &#039;&#039;pveproxy&#039;&#039; pour appliquer les changements !&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4160</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4160"/>
		<updated>2025-12-14T10:02:32Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Tests */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps où l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;à spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Tests =&lt;br /&gt;
Il faut maintenant tester le filtre est pour ça on peut utiliser un partage de connexion et réaliser le nombre d&#039;échecs spécifié par la directive &amp;quot;maxretry&amp;quot; (ici 3) :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# fail2ban-client status proxmox&lt;br /&gt;
Status for the jail: proxmox&lt;br /&gt;
|- Filter&lt;br /&gt;
|  |- Currently failed:	0&lt;br /&gt;
|  |- Total failed:	3&lt;br /&gt;
|  `- Journal matches:	_SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
`- Actions&lt;br /&gt;
   |- Currently banned:	1&lt;br /&gt;
   |- Total banned:	1&lt;br /&gt;
   `- Banned IP list:	37.167.23.159&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit bien l&#039;IP 37.167.23.159 qui est bannie !&lt;br /&gt;
N&#039;oubliez pas de l&#039;ajouter à &#039;&#039;ignoreip&#039;&#039; dans le fichier &#039;&#039;jails.local&#039;&#039; ou de faire &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# fail2ban-client unban 37.167.23.159&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilise l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
Même si le filtre &#039;&#039;fail2ban&#039;&#039; est configuré pour utiliser la commande &#039;&#039;journactl&#039;&#039; sur le démon &#039;&#039;pvedaemon&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
les adresses viennent de pveproxy qui journalise tout dans &#039;&#039;/var/log/pveproxy/access.log&#039;&#039; et &#039;&#039;pvedaemon&#039;&#039; ne journalise que les échecs d&#039;authentification. &lt;br /&gt;
Il est donc plus simple de faire un &#039;&#039;tail&#039;&#039; ou un &#039;&#039;cat&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Que d&#039;utiliser &#039;&#039;journalctl&#039;&#039; pour voir les IPs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
Dec 14 10:43:01 labo unix_chkpwd[1326506]: password check failed for user (root)&lt;br /&gt;
Dec 14 10:43:01 labo IPCC.xs[1320783]: pam_unix(proxmox-ve-auth:auth): authentication failure; logname= uid=0 euid=0 tty= ruser= rhost=37.167.23.159  user=root&lt;br /&gt;
Dec 14 10:43:03 labo pvedaemon[1320783]: authentication failure; rhost=37.167.23.159 user=root@pam msg=Authentication failure&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;uniquement&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrait ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jour d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modification peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Double IPv4 ou préfixe IPv6 ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/resources HTTP/1.1&amp;quot; 200 1226&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/tasks HTTP/1.1&amp;quot; 200 1090&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
::ffff:192.168.20.22 - root@pam [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/nodes/labo/lxc/100/status/current HTTP/1.1&amp;quot; 200 523&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il nous reste un peu de nettoyage à faire, dans certains cas :&lt;br /&gt;
* l&#039;adresse apparaît non pas une fois mais deux, séparées par une virgule&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
37.167.182.207, 37.167.182.207&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* l&#039;adresse IPv4 (32 bits) est affichée dans sa représentation IPv6 (128 bits), avec le préfixe &#039;&#039;::ffff:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
::ffff:192.168.20.22&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour faire le nettoyage :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   # Remove trailing ip &lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
if($reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
   # Remove IPv6 subnet for IPv4&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=.*:?&amp;lt;HOST&amp;gt;,? user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant, que l&#039;on peut créer dans /root/remoteip_fix.sh :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
    if(\$reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
        # Remove IPv6 subnet for IPv4&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$1;&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne plus qu&#039;a le rendre exécutable et le lancer :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x /root/remoteip_fix.sh&lt;br /&gt;
# /root/remoteip_fix.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne faudra pas oublier de redémarrer &#039;&#039;pveproxy&#039;&#039; pour appliquer les changements !&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4159</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4159"/>
		<updated>2025-12-14T10:00:17Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Filtre pour PVEPROXY */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps où l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;à spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Tests =&lt;br /&gt;
Il faut maintenant tester le filtre est pour ça on peut utiliser un partage de connexion et réaliser le nombre d&#039;échecs spécifié par la directive &amp;quot;maxretry&amp;quot;&lt;br /&gt;
&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilise l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
Même si le filtre &#039;&#039;fail2ban&#039;&#039; est configuré pour utiliser la commande &#039;&#039;journactl&#039;&#039; sur le démon &#039;&#039;pvedaemon&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
les adresses viennent de pveproxy qui journalise tout dans &#039;&#039;/var/log/pveproxy/access.log&#039;&#039; et &#039;&#039;pvedaemon&#039;&#039; ne journalise que les échecs d&#039;authentification. &lt;br /&gt;
Il est donc plus simple de faire un &#039;&#039;tail&#039;&#039; ou un &#039;&#039;cat&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Que d&#039;utiliser &#039;&#039;journalctl&#039;&#039; pour voir les IPs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
Dec 14 10:43:01 labo unix_chkpwd[1326506]: password check failed for user (root)&lt;br /&gt;
Dec 14 10:43:01 labo IPCC.xs[1320783]: pam_unix(proxmox-ve-auth:auth): authentication failure; logname= uid=0 euid=0 tty= ruser= rhost=37.167.23.159  user=root&lt;br /&gt;
Dec 14 10:43:03 labo pvedaemon[1320783]: authentication failure; rhost=37.167.23.159 user=root@pam msg=Authentication failure&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;uniquement&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrait ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jour d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modification peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Double IPv4 ou préfixe IPv6 ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/resources HTTP/1.1&amp;quot; 200 1226&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/tasks HTTP/1.1&amp;quot; 200 1090&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
::ffff:192.168.20.22 - root@pam [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/nodes/labo/lxc/100/status/current HTTP/1.1&amp;quot; 200 523&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il nous reste un peu de nettoyage à faire, dans certains cas :&lt;br /&gt;
* l&#039;adresse apparaît non pas une fois mais deux, séparées par une virgule&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
37.167.182.207, 37.167.182.207&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* l&#039;adresse IPv4 (32 bits) est affichée dans sa représentation IPv6 (128 bits), avec le préfixe &#039;&#039;::ffff:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
::ffff:192.168.20.22&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour faire le nettoyage :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   # Remove trailing ip &lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
if($reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
   # Remove IPv6 subnet for IPv4&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=.*:?&amp;lt;HOST&amp;gt;,? user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant, que l&#039;on peut créer dans /root/remoteip_fix.sh :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
    if(\$reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
        # Remove IPv6 subnet for IPv4&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$1;&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne plus qu&#039;a le rendre exécutable et le lancer :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x /root/remoteip_fix.sh&lt;br /&gt;
# /root/remoteip_fix.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne faudra pas oublier de redémarrer &#039;&#039;pveproxy&#039;&#039; pour appliquer les changements !&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4158</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4158"/>
		<updated>2025-12-14T09:56:21Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Proxification */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps où l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;à spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilise l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
Même si le filtre &#039;&#039;fail2ban&#039;&#039; est configuré pour utiliser la commande &#039;&#039;journactl&#039;&#039; sur le démon &#039;&#039;pvedaemon&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
les adresses viennent de pveproxy qui journalise tout dans &#039;&#039;/var/log/pveproxy/access.log&#039;&#039; et &#039;&#039;pvedaemon&#039;&#039; ne journalise que les échecs d&#039;authentification. &lt;br /&gt;
Il est donc plus simple de faire un &#039;&#039;tail&#039;&#039; ou un &#039;&#039;cat&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Que d&#039;utiliser &#039;&#039;journalctl&#039;&#039; pour voir les IPs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
Dec 14 10:43:01 labo unix_chkpwd[1326506]: password check failed for user (root)&lt;br /&gt;
Dec 14 10:43:01 labo IPCC.xs[1320783]: pam_unix(proxmox-ve-auth:auth): authentication failure; logname= uid=0 euid=0 tty= ruser= rhost=37.167.23.159  user=root&lt;br /&gt;
Dec 14 10:43:03 labo pvedaemon[1320783]: authentication failure; rhost=37.167.23.159 user=root@pam msg=Authentication failure&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;uniquement&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrait ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jour d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modification peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Double IPv4 ou préfixe IPv6 ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/resources HTTP/1.1&amp;quot; 200 1226&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/tasks HTTP/1.1&amp;quot; 200 1090&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
::ffff:192.168.20.22 - root@pam [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/nodes/labo/lxc/100/status/current HTTP/1.1&amp;quot; 200 523&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il nous reste un peu de nettoyage à faire, dans certains cas :&lt;br /&gt;
* l&#039;adresse apparaît non pas une fois mais deux, séparées par une virgule&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
37.167.182.207, 37.167.182.207&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* l&#039;adresse IPv4 (32 bits) est affichée dans sa représentation IPv6 (128 bits), avec le préfixe &#039;&#039;::ffff:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
::ffff:192.168.20.22&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour faire le nettoyage :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   # Remove trailing ip &lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
if($reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
   # Remove IPv6 subnet for IPv4&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=.*:?&amp;lt;HOST&amp;gt;,? user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant, que l&#039;on peut créer dans /root/remoteip_fix.sh :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
    if(\$reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
        # Remove IPv6 subnet for IPv4&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$1;&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne plus qu&#039;a le rendre exécutable et le lancer :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x /root/remoteip_fix.sh&lt;br /&gt;
# /root/remoteip_fix.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne faudra pas oublier de redémarrer &#039;&#039;pveproxy&#039;&#039; pour appliquer les changements !&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4157</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4157"/>
		<updated>2025-12-14T09:55:56Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Proxification */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps où l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;à spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilise l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
{|style=&amp;quot;width:100%&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning-icon.png|centré|60px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Même si le filtre &#039;&#039;fail2ban&#039;&#039; est configuré pour utiliser la commande &#039;&#039;journactl&#039;&#039; sur le démon &#039;&#039;pvedaemon&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
les adresses viennent de pveproxy qui journalise tout dans &#039;&#039;/var/log/pveproxy/access.log&#039;&#039; et &#039;&#039;pvedaemon&#039;&#039; ne journalise que les échecs d&#039;authentification. &lt;br /&gt;
Il est donc plus simple de faire un &#039;&#039;tail&#039;&#039; ou un &#039;&#039;cat&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Que d&#039;utiliser &#039;&#039;journalctl&#039;&#039; pour voir les IPs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
Dec 14 10:43:01 labo unix_chkpwd[1326506]: password check failed for user (root)&lt;br /&gt;
Dec 14 10:43:01 labo IPCC.xs[1320783]: pam_unix(proxmox-ve-auth:auth): authentication failure; logname= uid=0 euid=0 tty= ruser= rhost=37.167.23.159  user=root&lt;br /&gt;
Dec 14 10:43:03 labo pvedaemon[1320783]: authentication failure; rhost=37.167.23.159 user=root@pam msg=Authentication failure&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;uniquement&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrait ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jour d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modification peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Double IPv4 ou préfixe IPv6 ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/resources HTTP/1.1&amp;quot; 200 1226&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/tasks HTTP/1.1&amp;quot; 200 1090&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
::ffff:192.168.20.22 - root@pam [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/nodes/labo/lxc/100/status/current HTTP/1.1&amp;quot; 200 523&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il nous reste un peu de nettoyage à faire, dans certains cas :&lt;br /&gt;
* l&#039;adresse apparaît non pas une fois mais deux, séparées par une virgule&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
37.167.182.207, 37.167.182.207&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* l&#039;adresse IPv4 (32 bits) est affichée dans sa représentation IPv6 (128 bits), avec le préfixe &#039;&#039;::ffff:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
::ffff:192.168.20.22&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour faire le nettoyage :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   # Remove trailing ip &lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
if($reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
   # Remove IPv6 subnet for IPv4&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=.*:?&amp;lt;HOST&amp;gt;,? user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant, que l&#039;on peut créer dans /root/remoteip_fix.sh :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
    if(\$reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
        # Remove IPv6 subnet for IPv4&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$1;&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne plus qu&#039;a le rendre exécutable et le lancer :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x /root/remoteip_fix.sh&lt;br /&gt;
# /root/remoteip_fix.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne faudra pas oublier de redémarrer &#039;&#039;pveproxy&#039;&#039; pour appliquer les changements !&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4156</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4156"/>
		<updated>2025-12-14T09:54:15Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Proxification */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps où l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;à spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilise l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
{|style=&amp;quot;width:100%&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning-icon.png|centré|60px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Même si le filtre &#039;&#039;fail2ban&#039;&#039; est configuré pour utiliser la commande &#039;&#039;journactl&#039;&#039; sur le démon &#039;&#039;pvedaemon&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
les adresses viennent de pveproxy qui journalise tout dans &#039;&#039;/var/log/pveproxy/access.log&#039;&#039; et &#039;&#039;pvedaemon&#039;&#039; ne journalise que les échecs d&#039;authentification. Il est donc plus simple de faire un &#039;&#039;tail&#039;&#039; que d&#039;utiliser &#039;&#039;journalctl&#039;&#039; pour voir les IPs&lt;br /&gt;
|}&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;uniquement&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrait ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jour d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modification peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Double IPv4 ou préfixe IPv6 ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/resources HTTP/1.1&amp;quot; 200 1226&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/tasks HTTP/1.1&amp;quot; 200 1090&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
::ffff:192.168.20.22 - root@pam [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/nodes/labo/lxc/100/status/current HTTP/1.1&amp;quot; 200 523&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il nous reste un peu de nettoyage à faire, dans certains cas :&lt;br /&gt;
* l&#039;adresse apparaît non pas une fois mais deux, séparées par une virgule&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
37.167.182.207, 37.167.182.207&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* l&#039;adresse IPv4 (32 bits) est affichée dans sa représentation IPv6 (128 bits), avec le préfixe &#039;&#039;::ffff:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
::ffff:192.168.20.22&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour faire le nettoyage :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   # Remove trailing ip &lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
if($reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
   # Remove IPv6 subnet for IPv4&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=.*:?&amp;lt;HOST&amp;gt;,? user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant, que l&#039;on peut créer dans /root/remoteip_fix.sh :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
    if(\$reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
        # Remove IPv6 subnet for IPv4&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$1;&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne plus qu&#039;a le rendre exécutable et le lancer :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x /root/remoteip_fix.sh&lt;br /&gt;
# /root/remoteip_fix.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne faudra pas oublier de redémarrer &#039;&#039;pveproxy&#039;&#039; pour appliquer les changements !&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4155</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4155"/>
		<updated>2025-12-14T09:53:34Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Proxification */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps où l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;à spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilise l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
{|style=&amp;quot;width:100%&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning-icon.png|centré|60px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Même si le filtre &#039;&#039;fail2ban&#039;&#039; est configuré pour utiliser la commande &#039;&#039;journactl&#039;&#039; sur le démon &#039;&#039;pvedaemon&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
les adresses viennent de pveproxy qui journalise tout dans &#039;&#039;/var/log/pveproxy/access.log&#039;&#039; et &#039;&#039;pvedaemon&#039;&#039; ne journalise que les échecs d&#039;authentification &lt;br /&gt;
|}&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;uniquement&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrait ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jour d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modification peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Double IPv4 ou préfixe IPv6 ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/resources HTTP/1.1&amp;quot; 200 1226&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/tasks HTTP/1.1&amp;quot; 200 1090&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
::ffff:192.168.20.22 - root@pam [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/nodes/labo/lxc/100/status/current HTTP/1.1&amp;quot; 200 523&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il nous reste un peu de nettoyage à faire, dans certains cas :&lt;br /&gt;
* l&#039;adresse apparaît non pas une fois mais deux, séparées par une virgule&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
37.167.182.207, 37.167.182.207&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* l&#039;adresse IPv4 (32 bits) est affichée dans sa représentation IPv6 (128 bits), avec le préfixe &#039;&#039;::ffff:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
::ffff:192.168.20.22&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour faire le nettoyage :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   # Remove trailing ip &lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
if($reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
   # Remove IPv6 subnet for IPv4&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=.*:?&amp;lt;HOST&amp;gt;,? user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant, que l&#039;on peut créer dans /root/remoteip_fix.sh :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
    if(\$reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
        # Remove IPv6 subnet for IPv4&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$1;&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne plus qu&#039;a le rendre exécutable et le lancer :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x /root/remoteip_fix.sh&lt;br /&gt;
# /root/remoteip_fix.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne faudra pas oublier de redémarrer &#039;&#039;pveproxy&#039;&#039; pour appliquer les changements !&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4154</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4154"/>
		<updated>2025-12-14T09:53:25Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Proxification */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps où l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;à spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilise l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
{|style=&amp;quot;width:100%&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning-icon.png|centré|60px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Même si le filtre &#039;&#039;fail2ban&#039;&#039; est configuré pour utiliser la commande &#039;&#039;journactl&#039;&#039; sur le démon &#039;&#039;pvedaemon&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
les adresses viennent de pveproxy qui journalise tout dans &#039;&#039;/var/log/pveproxy/access.log&#039;&#039; et &#039;&#039;pvedaemon&#039;&#039; ne journalise que les échecs d&#039;authentification &lt;br /&gt;
|}&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;uniquement&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrait ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jour d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modification peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Double IPv4 ou préfixe IPv6 ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/resources HTTP/1.1&amp;quot; 200 1226&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/tasks HTTP/1.1&amp;quot; 200 1090&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
::ffff:192.168.20.22 - root@pam [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/nodes/labo/lxc/100/status/current HTTP/1.1&amp;quot; 200 523&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il nous reste un peu de nettoyage à faire, dans certains cas :&lt;br /&gt;
* l&#039;adresse apparaît non pas une fois mais deux, séparées par une virgule&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
37.167.182.207, 37.167.182.207&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* l&#039;adresse IPv4 (32 bits) est affichée dans sa représentation IPv6 (128 bits), avec le préfixe &#039;&#039;::ffff:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
::ffff:192.168.20.22&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour faire le nettoyage :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   # Remove trailing ip &lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
if($reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
   # Remove IPv6 subnet for IPv4&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=.*:?&amp;lt;HOST&amp;gt;,? user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant, que l&#039;on peut créer dans /root/remoteip_fix.sh :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
    if(\$reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
        # Remove IPv6 subnet for IPv4&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$1;&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne plus qu&#039;a le rendre exécutable et le lancer :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x /root/remoteip_fix.sh&lt;br /&gt;
# /root/remoteip_fix.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne faudra pas oublier de redémarrer &#039;&#039;pveproxy&#039;&#039; pour appliquer les changements !&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4153</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4153"/>
		<updated>2025-12-14T09:53:04Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Proxification */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps où l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;à spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilise l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
{|style=&amp;quot;width:100%&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning-icon.png|centré|60px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Même si le filtre &#039;&#039;fail2ban&#039;&#039; est configuré pour utiliser la commande &#039;&#039;journactl&#039;&#039; sur le démon &#039;&#039;pvedaemon&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
les adresses viennent de pveproxy qui journalise tout dans &#039;&#039;/var/log/pveproxy/access.log&#039;&#039; et &#039;&#039;pvedaemon&#039;&#039; ne journalise que les échecs d&#039;authentification &lt;br /&gt;
|}&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;uniquement&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrait ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jour d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modification peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Double IPv4 ou préfixe IPv6 ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/resources HTTP/1.1&amp;quot; 200 1226&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/tasks HTTP/1.1&amp;quot; 200 1090&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
::ffff:192.168.20.22 - root@pam [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/nodes/labo/lxc/100/status/current HTTP/1.1&amp;quot; 200 523&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il nous reste un peu de nettoyage à faire, dans certains cas :&lt;br /&gt;
* l&#039;adresse apparaît non pas une fois mais deux, séparées par une virgule&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
37.167.182.207, 37.167.182.207&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* l&#039;adresse IPv4 (32 bits) est affichée dans sa représentation IPv6 (128 bits), avec le préfixe &#039;&#039;::ffff:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
::ffff:192.168.20.22&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour faire le nettoyage :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   # Remove trailing ip &lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
if($reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
   # Remove IPv6 subnet for IPv4&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=.*:?&amp;lt;HOST&amp;gt;,? user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant, que l&#039;on peut créer dans /root/remoteip_fix.sh :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
    if(\$reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
        # Remove IPv6 subnet for IPv4&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$1;&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne plus qu&#039;a le rendre exécutable et le lancer :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x /root/remoteip_fix.sh&lt;br /&gt;
# /root/remoteip_fix.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne faudra pas oublier de redémarrer &#039;&#039;pveproxy&#039;&#039; pour appliquer les changements !&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4152</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4152"/>
		<updated>2025-12-14T09:50:47Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Proxification */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps où l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;à spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilise l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
{|style=&amp;quot;width:800px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning-icon.png|centré|30px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Même si le filtre &#039;&#039;fail2ban&#039;&#039; est configuré pour utiliser la commande &#039;&#039;journactl&#039;&#039; sur le démon &#039;&#039;pvedaemon&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Les adresses viennent de pveproxy qui journalise tout dans &#039;&#039;/var/log/pveproxy/access.log&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;uniquement&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrait ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jour d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modification peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Double IPv4 ou préfixe IPv6 ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/resources HTTP/1.1&amp;quot; 200 1226&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/tasks HTTP/1.1&amp;quot; 200 1090&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
::ffff:192.168.20.22 - root@pam [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/nodes/labo/lxc/100/status/current HTTP/1.1&amp;quot; 200 523&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il nous reste un peu de nettoyage à faire, dans certains cas :&lt;br /&gt;
* l&#039;adresse apparaît non pas une fois mais deux, séparées par une virgule&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
37.167.182.207, 37.167.182.207&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* l&#039;adresse IPv4 (32 bits) est affichée dans sa représentation IPv6 (128 bits), avec le préfixe &#039;&#039;::ffff:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
::ffff:192.168.20.22&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour faire le nettoyage :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   # Remove trailing ip &lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
if($reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
   # Remove IPv6 subnet for IPv4&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=.*:?&amp;lt;HOST&amp;gt;,? user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant, que l&#039;on peut créer dans /root/remoteip_fix.sh :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
    if(\$reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
        # Remove IPv6 subnet for IPv4&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$1;&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne plus qu&#039;a le rendre exécutable et le lancer :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x /root/remoteip_fix.sh&lt;br /&gt;
# /root/remoteip_fix.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne faudra pas oublier de redémarrer &#039;&#039;pveproxy&#039;&#039; pour appliquer les changements !&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4151</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4151"/>
		<updated>2025-12-14T09:44:52Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Double IPs ou IPv6 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps où l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;à spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilise l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;uniquement&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrait ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jour d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modification peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Double IPv4 ou préfixe IPv6 ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/resources HTTP/1.1&amp;quot; 200 1226&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/tasks HTTP/1.1&amp;quot; 200 1090&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
::ffff:192.168.20.22 - root@pam [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/nodes/labo/lxc/100/status/current HTTP/1.1&amp;quot; 200 523&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il nous reste un peu de nettoyage à faire, dans certains cas :&lt;br /&gt;
* l&#039;adresse apparaît non pas une fois mais deux, séparées par une virgule&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
37.167.182.207, 37.167.182.207&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* l&#039;adresse IPv4 (32 bits) est affichée dans sa représentation IPv6 (128 bits), avec le préfixe &#039;&#039;::ffff:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
::ffff:192.168.20.22&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour faire le nettoyage :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   # Remove trailing ip &lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
if($reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
   # Remove IPv6 subnet for IPv4&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=.*:?&amp;lt;HOST&amp;gt;,? user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant, que l&#039;on peut créer dans /root/remoteip_fix.sh :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
    if(\$reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
        # Remove IPv6 subnet for IPv4&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$1;&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne plus qu&#039;a le rendre exécutable et le lancer :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x /root/remoteip_fix.sh&lt;br /&gt;
# /root/remoteip_fix.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne faudra pas oublier de redémarrer &#039;&#039;pveproxy&#039;&#039; pour appliquer les changements !&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4150</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4150"/>
		<updated>2025-12-14T09:41:19Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Automatisation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps où l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;à spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilise l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;uniquement&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrait ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jour d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modification peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Double IPs ou IPv6 ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/resources HTTP/1.1&amp;quot; 200 1226&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/tasks HTTP/1.1&amp;quot; 200 1090&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
::ffff:192.168.20.22 - root@pam [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/nodes/labo/lxc/100/status/current HTTP/1.1&amp;quot; 200 523&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il nous reste un peu de nettoyage à faire, dans certains cas :&lt;br /&gt;
* l&#039;adresse apparaît non pas une fois mais deux, séparées par une virgule&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
37.167.182.207, 37.167.182.207&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* l&#039;adresse IPv4 (32 bits) est affichée dans sa représentation IPv6 (128 bits), avec le préfixe &#039;&#039;::ffff:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
::ffff:192.168.20.22&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour faire le nettoyage :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   # Remove trailing ip &lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
if($reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
   # Remove IPv6 subnet for IPv4&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=.*:?&amp;lt;HOST&amp;gt;,? user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant, que l&#039;on peut créer dans /root/remoteip_fix.sh :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
    if(\$reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
        # Remove IPv6 subnet for IPv4&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$1;&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne plus qu&#039;a le rendre exécutable et le lancer :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x /root/remoteip_fix.sh&lt;br /&gt;
# /root/remoteip_fix.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne faudra pas oublier de redémarrer &#039;&#039;pveproxy&#039;&#039; pour appliquer les changements !&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4149</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4149"/>
		<updated>2025-12-14T09:40:51Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps où l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;à spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilise l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;uniquement&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrait ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jour d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modification peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Double IPs ou IPv6 ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/resources HTTP/1.1&amp;quot; 200 1226&lt;br /&gt;
::ffff:192.168.20.22 - - [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/cluster/tasks HTTP/1.1&amp;quot; 200 1090&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
::ffff:192.168.20.22 - root@pam [14/12/2025:09:34:42 +0100] &amp;quot;GET /api2/json/nodes/labo/lxc/100/status/current HTTP/1.1&amp;quot; 200 523&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il nous reste un peu de nettoyage à faire, dans certains cas :&lt;br /&gt;
* l&#039;adresse apparaît non pas une fois mais deux, séparées par une virgule&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
37.167.182.207, 37.167.182.207&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* l&#039;adresse IPv4 (32 bits) est affichée dans sa représentation IPv6 (128 bits), avec le préfixe &#039;&#039;::ffff:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
::ffff:192.168.20.22&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour faire le nettoyage :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   # Remove trailing ip &lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
if($reqstate-&amp;gt;{peer_host} =~ /.*\:([\d]*\..*)/) {&lt;br /&gt;
   # Remove IPv6 subnet for IPv4&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=.*:?&amp;lt;HOST&amp;gt;,? user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant, que l&#039;on peut créer dans /root/remoteip_fix.sh :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne plus qu&#039;a le rendre exécutable et le lancer :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x /root/remoteip_fix.sh&lt;br /&gt;
# /root/remoteip_fix.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne faudra pas oublier de redémarrer &#039;&#039;pveproxy&#039;&#039; pour appliquer les changements !&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4148</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4148"/>
		<updated>2025-12-14T08:30:46Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Automatisation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps où l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;à spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilise l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;uniquement&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrait ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jour d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modification peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Double IPs ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_2.css HTTP/1.1&amp;quot; 200 6217&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
L&#039;adresse apparaît non pas une fois mais deux, séparées par une virgule...&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour se débarrasser de la virgule ainsi que de la deuxième adresse :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf pour ajouter &#039;&#039;,.*&#039;&#039; après &#039;&#039;&amp;lt;HOST&amp;gt;&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt;,.* user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant, que l&#039;on peut créer dans /root/remoteip_fix.sh :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne plus qu&#039;a le rendre exécutable et le lancer :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# chmod +x /root/remoteip_fix.sh&lt;br /&gt;
# /root/remoteip_fix.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne faudra pas oublier de redémarrer &#039;&#039;pveproxy&#039;&#039; pour appliquer les changements !&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=The_Linux_Craftsman&amp;diff=4147</id>
		<title>The Linux Craftsman</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=The_Linux_Craftsman&amp;diff=4147"/>
		<updated>2025-12-14T08:28:06Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* La pratique */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Ce wiki =&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;100%&amp;quot;&lt;br /&gt;
! Le contenu !! Public visé !! L&#039;auteur&lt;br /&gt;
|-valign=top&lt;br /&gt;
|width=&amp;quot;33%&amp;quot;|&lt;br /&gt;
Ce wiki propose des articles, cours, TPs et TDs sur des sujets gravitant autour des technologies du système d&#039;information, notamment sur le système Linux, sur la distribution CentOS et maintenant Rocky Linux.&lt;br /&gt;
&lt;br /&gt;
Vous y trouverez des articles traitant de la mise en place de services réseaux tels que DHCP et DNS mais également des articles sur la mise en place de pare-feu, site Web, etc... Il y a un peu de tout et je vous encourage à utiliser le champ de recherche pour trouver ce dont vous avez besoin.&lt;br /&gt;
|width=&amp;quot;33%&amp;quot;|&lt;br /&gt;
&lt;br /&gt;
Ce wiki s&#039;adresse principalement à mes élèves mais il peut également servir à des enseignants qui désirent monter leurs cours sans se &amp;quot;prendre la tête&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Tout est disponible sous [http://www.gnu.org/copyleft/fdl.html licence GNU Free Documentation License 1.3] ou ultérieure et vous pouvez récupérer les contenus et en faire ce que vous voulez !&lt;br /&gt;
|width=&amp;quot;33%&amp;quot;|&lt;br /&gt;
&lt;br /&gt;
Je m&#039;appelle Jean-Christophe FORTON et je suis professeur d&#039;informatique depuis 2011 mais cela n&#039;a pas toujours été mon métier, plus d&#039;info [[jcf|ici]]...&lt;br /&gt;
&lt;br /&gt;
N&#039;hésitez pas à faire un tour sur :&lt;br /&gt;
{|&lt;br /&gt;
|-valign=middle&lt;br /&gt;
|align=center|&lt;br /&gt;
[[Image:Logo-YouTube-rouge.png|30px|link=https://www.youtube.com/@tala-informatique]]&lt;br /&gt;
|| ma chaîne Youtube pour découvrir mes aventures ou (re)visionner certains cours&lt;br /&gt;
|-valign=middle&lt;br /&gt;
|align=center|&lt;br /&gt;
[[Fichier:Logo-Tipeee.png|27px|link=https://fr.tipeee.com/tala-informatique]]&lt;br /&gt;
||mon Tipeee pour me remercier&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Les cours =&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;100%&amp;quot;&lt;br /&gt;
! Système  !! Sécurité !! Réseaux !! Développement !! Général&lt;br /&gt;
|-valign=top&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Linux&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:intro_linux.pdf|Introduction au système Linux]]&lt;br /&gt;
* [[:Media:tp_linux_commandes_de_bases.pdf|TP Linux : commandes de bases]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Windows&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Architecture des ordinateurs en environnement Windows&lt;br /&gt;
** [[:Media:materiel.pdf|Le matériel]]&lt;br /&gt;
** [[:Media:system_exploitation.pdf|Le système d&#039;exploitation]]&lt;br /&gt;
** [[:Media:gestion_disques.pdf|La gestion des disques]]&lt;br /&gt;
** [[:Media:sauvegarde.pdf|La sauvegarde]]&lt;br /&gt;
* Active Directory&lt;br /&gt;
** [[:Media:ad_intro.pdf|Les principes fondamentaux]]&lt;br /&gt;
** [[:Media:ad_gpo.pdf|Gestion des stratégies]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Réseaux&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:Les pare-feux.pdf|Les pare-feux]]&lt;br /&gt;
* [[:Media:Les pare-feux_tech.pdf|Les pare-feux (fiche technique)]]&lt;br /&gt;
* [[:Media:Les VPN.pdf|Les VPN]]&lt;br /&gt;
* [[:Media:Haute_dispo.pdf|Haute disponibilité]]&lt;br /&gt;
* [[:Media:sauvegarde_pca.pdf|La sauvegarde]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Sécurité des Systèmes d’Information&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:principes_SSI.pdf|Les principes fondamentaux]]&lt;br /&gt;
* [[:Media:implémentation_concrete_SSI.pdf|Implémentation concrète de la sécurité]]&lt;br /&gt;
* [[:Media:cryptographie.pdf|Cryptographie]]&lt;br /&gt;
* [[:Media:TD1_SSI.pdf|TD1: Principes Fondamentaux]]&lt;br /&gt;
* [[:Media:TP1_SSI.pdf|TP1: Écoute d&#039;une connexion]]&lt;br /&gt;
* [[:Media:TP2_SSI.pdf|TP2: Chiffrement asymétrique avec PGP]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Web&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:apache server.pdf|Le serveur Apache]]&lt;br /&gt;
* [[:Media:pilotage web bdd.pdf|Pilotage d&#039;une page web]]&lt;br /&gt;
* [[:Media:web app security.pdf|Sécurité des applications Web]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Modèle OSI&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
*[[:Media:OSI.pdf|Le modèle OSI en version cours]]&lt;br /&gt;
*[[:Media:OSI_Slides.pdf|Le modèle OSI en version slides]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 1 &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:Normes_cablages.pdf|Les normes de câblage]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 2&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:Ethernet-802.3.pdf|La norme Ethernet (802.3)]]&lt;br /&gt;
* [[:Media:WiFi-802.11.pdf|La norme Wi-Fi (802.11)]]&lt;br /&gt;
* [[:Media:Bridge.pdf|Les bridges]]&lt;br /&gt;
* [[:Media:STP.pdf|Spanning Tree Protocol]]&lt;br /&gt;
* [[:Media:VLAN.pdf|Les Vlans]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 3+&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Routage:&lt;br /&gt;
** [[:Media:CIDR.pdf|Classless Inter-Domain Routing]]&lt;br /&gt;
** [[:Media:resume_protocoles_vecteur_distance.pdf|Résumé sur les protocoles à vecteur de distance]]&lt;br /&gt;
** [[:Media:EIGRP.pdf|Enhanced Interior Gateway Routing Protocol]]&lt;br /&gt;
** [[:Media:OSPF.pdf|Open Shortest Path First]]&lt;br /&gt;
** [[:Media:BGP.pdf|Border Gateway Protocol]]&lt;br /&gt;
*Services:&lt;br /&gt;
** [[:Media:DHCP.pdf|DHCP]]&lt;br /&gt;
** [[:Media:DNS.pdf|DNS]]&lt;br /&gt;
** [[:Media:SAMBA.pdf|Samba]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Qualité de service&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:QoS.pdf|La QoS]]&lt;br /&gt;
* [[:Media:Iproute2_QoS.pdf|Iproute2 et la QoS]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Protocole&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:HTTP.pdf|HTTP]]&lt;br /&gt;
* [[:Media:SNMP.pdf|SNMP]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;L&#039;algorithmique&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[:Media:algo_intro.pdf|Introduction]]&lt;br /&gt;
* [[:Media:algo_langage.pdf|Le Langage]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;SQL&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:conception_bdd.pdf|Conception de base de données]]&lt;br /&gt;
* [[:Media:conception_bdd_simple.pdf|Conception de base de données (simplifié)]]&lt;br /&gt;
* [[:Media:langage_de_requête.pdf|Langage de requête]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Langages du Web&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:HTML xHTML intro.pdf|Le langage HTML]]&lt;br /&gt;
* [[:Media:css.pdf|Le langage CSS]]&lt;br /&gt;
* [[:Media:javascript.pdf|Le langage JavaScript]]&lt;br /&gt;
* [[:Media:php.pdf|Le langage PHP]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Langage bas niveau&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:C lang.pdf|Le langage C]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Architecture SOA&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:webservices.pdf|Les Web Services RESTful]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Informatique embarquée&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:arduino_avr.pdf|Arduino et AVR]]&lt;br /&gt;
* [[:Media:IOT.pdf|L&#039;Internet des objets]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Gestion&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:gestion_projet.pdf|Gestion de projet]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Bureautique&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:powerpoint.pdf|PowerPoint]]&lt;br /&gt;
* [[:Media:excel.pdf|Excel]]&lt;br /&gt;
* [[:Media:bdd_com.pdf|Les bases de données]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Histoire / évolutions&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt; &lt;br /&gt;
* [[:Media:expertise_codage.pdf|Expertise codage]]&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= La pratique =&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; width=&amp;quot;100%&amp;quot;&lt;br /&gt;
! Système !! Sécurité !! Réseaux !! Développement !! Embarquée !! Virtualisation&lt;br /&gt;
|-valign=&amp;quot;top&amp;quot;&lt;br /&gt;
|align=left|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Général&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[Installation de CentOS]]&lt;br /&gt;
* [[Installation de Rocky]]&lt;br /&gt;
* [[migration_centos8torocky8| Migration CentOS 8 &amp;amp;rarr; Rocky 8]]&lt;br /&gt;
* [[upgrade_rocky8to9| Upgrade Rocky 8 &amp;amp;rarr; 9]]&lt;br /&gt;
* [[WSL | WSL : Installation de Linux sur Windows 10]]&lt;br /&gt;
* [[Vi / Vim]]&lt;br /&gt;
* [[linux_repository|Les dépots (EPEL, EL, ...)]]&lt;br /&gt;
* [[cron| Gestionnaire des tâches: cron]]&lt;br /&gt;
* [[rpm_yum| Gestionnaires de paquetages: RPM &amp;amp; YUM]]&lt;br /&gt;
* [[Les logs]]&lt;br /&gt;
* [[users_groups|Utilisateurs et groupes]]&lt;br /&gt;
* [[Gestion des disques]]&lt;br /&gt;
* CentOS &amp;amp;#8805; 7 (&amp;amp;asymp; Rocky) [[Fichier:Warning-icon.png|20px]]&lt;br /&gt;
** [[systemctl|Systemctl sur SystemD]]&lt;br /&gt;
** [[systemctl service|Créer un service avec Systemctl (démon)]]&lt;br /&gt;
** [[iptables_on_systemd | Firewalld ?!? Rendez moi Iptables ! ]]&lt;br /&gt;
** [[chrony|&#039;&#039;NTP&#039;&#039; est mort, vive &#039;&#039;Chrony&#039;&#039;]]&lt;br /&gt;
* CentOS 6&lt;br /&gt;
** [[Gestionnaire de démarrage|Gestionnaire de démarrage SysVInit]]&lt;br /&gt;
** [[start_stop_daemon|Création d&#039;un service avec start-stop-daemon (SysVInit)]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Réseaux&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[ifcfg-ethX|Les interfaces réseaux]]&lt;br /&gt;
* [[sysconfig-network|Les paramètres réseaux]]&lt;br /&gt;
* [[resolv.conf|Configuration du client DNS]]&lt;br /&gt;
* [[ntpd|Configuration du client NTP]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Haute disponibilité&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[drbd|Réplication à chaud avec DRBD]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Multimédia&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[DLNA|Partage de contenu cross-platform avec DLNA]]&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Réseaux&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[Iptables]]&lt;br /&gt;
* [[Squid]]&lt;br /&gt;
* [[Snort]]&lt;br /&gt;
* [[:Media:Chillispot.pdf|Hotspot avec Chillispot]]&lt;br /&gt;
* [[Sécuriser un service avec Fail2ban]]&lt;br /&gt;
* [[Openvpn]]&lt;br /&gt;
* [[Wireguard]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Système&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[SSH]]&lt;br /&gt;
* [[SELinux]]&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Les outils&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[tcpdump | Le scanner &#039;&#039;tcpdump&#039;&#039;]]&lt;br /&gt;
* [[iproute2 | Contrôle réseau avec la commande &#039;&#039;ip&#039;&#039;]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 2&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[source routing|Le routage source]]&lt;br /&gt;
* [[bridge|Les bridges]]&lt;br /&gt;
* [[alias|Les alias]]&lt;br /&gt;
* [[vlan|Les vlans]]&lt;br /&gt;
* [[one-arm_router|La passerelle &#039;&#039;one-arm&#039;&#039;]]&lt;br /&gt;
* [[transparent_firewall|Le proxy &#039;&#039;transparent&#039;&#039;]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 3+&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Services:&lt;br /&gt;
** [[DHCP]]&lt;br /&gt;
** [[DNS]]&lt;br /&gt;
*Haute-disponibilité&lt;br /&gt;
** [[Ucarp]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;OSI 7&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Services:&lt;br /&gt;
** [[SAMBA]]&lt;br /&gt;
** [[HTTPD]]&lt;br /&gt;
** [[xmpp | Serveur XMPP avec Ejabberd]]&lt;br /&gt;
** [[vnc|Installer un serveur VNC]]&lt;br /&gt;
* Industrialisation:&lt;br /&gt;
** [[:Media:pxe.pdf|Monter un serveur PXE (pdf)]]&lt;br /&gt;
** [[PXE|Monter un serveur PXE]]&lt;br /&gt;
* Supervision&lt;br /&gt;
** [[Nagios| Nagios]]&lt;br /&gt;
** [[Cacti| Installer un serveur Cacti]]&lt;br /&gt;
** [[SNMP | Utiliser SNMP]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Cisco ISR (routeur)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[ISR-basics|Les bases]]&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Général&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[eclipse_install|Mise en place de l&#039;environnement de développement]]&lt;br /&gt;
* [[svn|Serveur de version SVN]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;PHP&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
*Les basiques :&lt;br /&gt;
** [[php_httpd_install | Installation sous Linux]]&lt;br /&gt;
** [[php_devel| Premier projet en &#039;&#039;PHP&#039;&#039;]]&lt;br /&gt;
** [[php_algo|Un peu d&#039;algorithmique]]&lt;br /&gt;
** [[php_object| Les objets]]&lt;br /&gt;
** [[php_xdebug| Debugger avec &#039;&#039;Xdebug&#039;&#039;]]&lt;br /&gt;
** [[phpunit| Les tests en PHP]]&lt;br /&gt;
* La partie CLI :&lt;br /&gt;
** [[php_memcached| Memcached un serveur de cache]]&lt;br /&gt;
** [[php_daemon | Écriture d&#039;un démon]]&lt;br /&gt;
** [[php_socket | Utilisation des sockets]]&lt;br /&gt;
* La partie Web :&lt;br /&gt;
** [[php_$get_$post_$session| Passer des informations entres pages]]&lt;br /&gt;
** [[php_pdo| Utiliser une base MySQL avec PDO]]&lt;br /&gt;
** [[php_slim | Le framework SLIM]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;JavaScript&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
*Les basiques :&lt;br /&gt;
** [[js_devel| Premier projet en &#039;&#039;JavaScript&#039;&#039;]]&lt;br /&gt;
** [[js_algo| Un peu d&#039;algorithmique]]&lt;br /&gt;
** [[js_object| Les objets]]&lt;br /&gt;
*Les RIA (&#039;&#039;R&#039;&#039;ich &#039;&#039;I&#039;&#039;nternet &#039;&#039;A&#039;&#039;pplication) :&lt;br /&gt;
** [[js_AJAX| Client AJAX pour utiliser des Web Services]]&lt;br /&gt;
** [[js_AJAX_auth | Authentification avec un client AJAX]]&lt;br /&gt;
** [[js_AJAX_fetch_api | Découverte de l&#039;API &#039;&#039;fetch&#039;&#039;]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Java&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
*Les basiques :&lt;br /&gt;
** [[java_devel| Premier projet en &#039;&#039;Java&#039;&#039;]]&lt;br /&gt;
** [[java_jar|Mon premier objet]]&lt;br /&gt;
** [[java_algo| Un peu d&#039;algorithmique]]&lt;br /&gt;
** [[junit| Les tests en Java]]&lt;br /&gt;
* La partie Web:&lt;br /&gt;
** [[java_servlet|Les Servlets]]&lt;br /&gt;
** [[java_ws_restful| Web Service Restful]]&lt;br /&gt;
* Stocker des informations:&lt;br /&gt;
** [[java_mysql| Utiliser une base MySQL avec Java]]&lt;br /&gt;
** [[java_memcached| Memcached un serveur de cache]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;C&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
*Les basiques :&lt;br /&gt;
** [[c_devel| Premier projet en &#039;&#039;C&#039;&#039;]]&lt;br /&gt;
** [[c_devel_cross| Compilation croisée (Cross Compilation)]]&lt;br /&gt;
** [[c_algo| Un peu d&#039;algorithmique]]&lt;br /&gt;
*Execution parallèle :&lt;br /&gt;
** [[c_pthread| Les threads]]&lt;br /&gt;
** [[c_fork| Les forks]]&lt;br /&gt;
* IPC:&lt;br /&gt;
** [[c_pipe| Les tubes]]&lt;br /&gt;
** [[c_semaphore| Les sémaphores]]&lt;br /&gt;
** [[c_signals| Les signaux POSIX]]&lt;br /&gt;
** [[c_socket| Les sockets]]&lt;br /&gt;
* GP-GPU avec CUDA:&lt;br /&gt;
** [[cuda_install| Installation de CUDA]]&lt;br /&gt;
** [[cuda_hello_world| Les concepts et bases]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Python&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[python_devel| Premier projet en &#039;&#039;Python&#039;&#039;]]&lt;br /&gt;
* [[python_algo|Un peu d&#039;algorithmique]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;SQL&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[sql_install| Installation d&#039;un SGBDR]]&lt;br /&gt;
* [[SQL_import| Importation d&#039;une base de données]]&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Général&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[Arduino_sketch_writing | Écriture d&#039;un sketch]]&lt;br /&gt;
* [[Arduino_CH340_driver_install | Installation du pilote CH340 (Serial TTL)]]&lt;br /&gt;
* [[Arduino_FTDI_driver_install | Installation du pilote FTDI(Serial TTL)]]&lt;br /&gt;
* [[Arduino_CP210X_driver_install | Installation du pilote CP210X(Serial TTL)]]&lt;br /&gt;
* [[Arduino_Eclipse_sketch | Création d&#039;un projet sous Eclipse]]&lt;br /&gt;
* [[arduino_diagram | Schémas des cartes Arduino]]&lt;br /&gt;
* [[esp8266_diagram | Schémas des cartes ESP8266]]&lt;br /&gt;
* [[esp32_diagram | Schémas des cartes ESP32]]&lt;br /&gt;
* [[sbc_diagram | Schémas des SBC]]&lt;br /&gt;
* [[µc_datasheet | Fiches techniques des microcontrôleurs]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Capteurs&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[Arduino_LDR | Luminosité avec une photorésistance]]&lt;br /&gt;
* [[Arduino_BH1750 | Luminosité avec le BH1750]]&lt;br /&gt;
* [[Arduino_DS18B20 | Température avec le DS18B20]]&lt;br /&gt;
* [[Arduino_LM35DZ | Température avec le LM35]]&lt;br /&gt;
* [[Arduino_DHT11 | Température et humidité avec le DHT11]]&lt;br /&gt;
* [[Arduino_BMP280 | Température, pression et altitude avec le BMP280]]&lt;br /&gt;
* [[Arduino_A3144 | Effet de Hall (magnétisme) avec le A3144 ]]&lt;br /&gt;
* [[Arduino_SR501 | Détection de mouvement avec le SR501 (PIR) ]]&lt;br /&gt;
* [[Arduino_soil_moisture | Capteur d&#039;humidité du sol ]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Communication&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Sans-fil:&lt;br /&gt;
** [[Arduino_HC12 | Communication RF433 avec un HC12]]&lt;br /&gt;
** [[Arduino_NRF24L01 | Communication 2.4Ghz avec un NRF24L01]]&lt;br /&gt;
** [[Arduino_ESP05 | Communication Wi-Fi avec un ESP-05]]&lt;br /&gt;
* Shield Ethernet (W5100):&lt;br /&gt;
** [[Arduino_W5100_intro | Présentation du shield]]&lt;br /&gt;
** [[Arduino_W5100_OSI3 | Configuration OSI 3]]&lt;br /&gt;
** [[Arduino_W5100_web_server | Utilisation du Shield Ethernet pour faire un serveur Web]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Composants et montages divers&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[ potentiometre | Potentiomètre ]]&lt;br /&gt;
* [[ shift_register | Registre à décalage ]]&lt;br /&gt;
* [[ Arduino_2axis_joystick_button | Joystick 2 axes avec bouton ]]&lt;br /&gt;
* [[ Arduino_SD_CARD | Module pour cartes SD ]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;ESP8266 / ESP32&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[esp_ide_arduino | Cartes ESP et IDE Arduino]]&lt;br /&gt;
* [[esp8266_wifi | Utilisation du WiFi ]]&lt;br /&gt;
* [[esp8266_webserver | Utilisation du serveur web ]]&lt;br /&gt;
* [[esp8266_udp_server | Serveur UDP ]]&lt;br /&gt;
* [[esp8266_ntp_client | Client NTP ]]&lt;br /&gt;
* [[:Media:esp8266_tp_meteo_dht11.pdf | TP station météo avec le DHT11 ]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Raspberry / Banana / Orange Pi&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[iso_install_sdcard | Installation d&#039;une image ]]&lt;br /&gt;
* [[pi_java_install | Mise en place de Java ]]&lt;br /&gt;
* [[sbc_qemu_emulation | Émulation avec Qemu ]]&lt;br /&gt;
* [[Linux sunxi armbian gpio | Manipulation des GPIO sous Linux]]&lt;br /&gt;
* [[Linux sunxi armbian w1 | Utilisation du protocole OneWire sous Linux]]&lt;br /&gt;
* [[Linux uart sunxi armbian | Utilisation du protocole UART sous Linux]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Notions avancées&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* Arduino (ATmega328)&lt;br /&gt;
** [[atmega328_registers | Manipulation de registres ]]&lt;br /&gt;
** [[atmega328_timers | Les timers ]]&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;VmWare&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[vmware_install | Installation ]]&lt;br /&gt;
* [[vmware_network | La partie réseau ]]&lt;br /&gt;
* [[vmware_create_vm | Création d&#039;une machine virtuelle ]]&lt;br /&gt;
* [[vmware_debug_vm | Débogage réseau d&#039;une machine virtuelle]]&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Proxmox&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* [[proxmox_install | Installation ]]&lt;br /&gt;
* [[proxmox_iptables | Utilisation d&#039;iptables ]]&lt;br /&gt;
* [[proxmox_fail2ban | Fail2ban ]]&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;visibility:hidden&amp;quot;&amp;gt;&lt;br /&gt;
__TOC__&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
__NOTOC__&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=S%C3%A9curiser_un_service_avec_Fail2ban&amp;diff=4146</id>
		<title>Sécuriser un service avec Fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=S%C3%A9curiser_un_service_avec_Fail2ban&amp;diff=4146"/>
		<updated>2025-12-13T18:30:54Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Choix de la punition */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Le but de fail2ban est d&#039;empêcher une attaque par force brute, c&#039;est à dire qu&#039;un individu trouve un tuple identifiant/mot de passe permettant l&#039;accès à un serveur.&lt;br /&gt;
Fail2ban va analyser les logs pour compter le nombre de tentatives et bannir l&#039;IP qui essaye de se connecter si elle dépasse le nombre maximal d’essais.&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
Une fois l&#039;installation du [[Linux_repository#Installation | dépôt EPEL]] terminée, nous pouvons procéder à celle de &#039;&#039;fail2ban&#039;&#039;: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
yum -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration =&lt;br /&gt;
&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps ou l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Configuration de la section Jail ==&lt;br /&gt;
* Sur CentOS 6:&lt;br /&gt;
Pour sécuriser le service, il faut repérer sa section jail. Dans cet exemple, nous allons sécuriser &#039;&#039;SSH&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons avoir besoin des variables suivantes:&lt;br /&gt;
*enabled : permet d&#039;activer le filtrage&lt;br /&gt;
*filter  : donne un nom au filtre&lt;br /&gt;
*action  : permet de bloquer le port (ssh) avec une règles Iptables et d&#039;envoyer un mail&lt;br /&gt;
*logpath : indique le fichier de log&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[ssh-iptables]&lt;br /&gt;
&lt;br /&gt;
enabled  = true&lt;br /&gt;
filter   = sshd&lt;br /&gt;
action   = iptables[name=SSH, port=ssh, protocol=tcp]&lt;br /&gt;
           sendmail-whois[name=SSH, dest=MON_EMAIL, sender=fail2ban@MON_DOMAINE, sendername=&amp;quot;Fail2Ban&amp;quot;]&lt;br /&gt;
logpath  = /var/log/secure&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remplacez &#039;&#039;MON_EMAIL&#039;&#039; par votre mail ainsi que &#039;&#039;MON_DOMAINE&#039;&#039; par votre domaine !&lt;br /&gt;
&lt;br /&gt;
*Sur CentOS 8:&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
* Pour SystemVInit:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# service fail2ban start&lt;br /&gt;
# chkconfig fail2ban on&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Pour SystemD:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Ajout automatique de règles dans Iptables =&lt;br /&gt;
Vous pouvez constater que fail2ban ajoute des règles dans la chaîne &#039;&#039;fail2ban-SSH&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Chain fail2ban-SSH (1 references)&lt;br /&gt;
 pkts bytes target     prot opt in     out     source               destination&lt;br /&gt;
    0     0 REJECT     all  --  *      *       122.225.109.214      0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       122.225.109.115      0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       61.174.51.212        0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       61.174.51.213        0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       125.70.0.44          0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       61.174.51.210        0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       122.226.140.158      0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       122.225.109.210      0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       222.186.34.117       0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       210.14.69.244        0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       122.225.109.206      0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       74.208.209.116       0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       61.174.51.229        0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       222.186.34.119       0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
   14   928 REJECT     all  --  *      *       61.55.135.41         0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
 1049 66520 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Parcours des Logs =&lt;br /&gt;
{{#lst:Les_logs|log_fail2ban}}&lt;br /&gt;
&lt;br /&gt;
= Apache =&lt;br /&gt;
&lt;br /&gt;
Voici quelques ajouts intéressants pour le serveur Apache:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[apache]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = http,https&lt;br /&gt;
filter = apache-auth&lt;br /&gt;
logpath = %(apache_error_log)s&lt;br /&gt;
&lt;br /&gt;
[apache-overflows]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = http,https&lt;br /&gt;
filter = apache-overflows&lt;br /&gt;
logpath = %(apache_error_log)s&lt;br /&gt;
&lt;br /&gt;
[apache-dos]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = http,https&lt;br /&gt;
filter = apache-dos&lt;br /&gt;
logpath = %(apache_access_log)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=S%C3%A9curiser_un_service_avec_Fail2ban&amp;diff=4145</id>
		<title>Sécuriser un service avec Fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=S%C3%A9curiser_un_service_avec_Fail2ban&amp;diff=4145"/>
		<updated>2025-12-13T18:30:09Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Choix de la punition */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Le but de fail2ban est d&#039;empêcher une attaque par force brute, c&#039;est à dire qu&#039;un individu trouve un tuple identifiant/mot de passe permettant l&#039;accès à un serveur.&lt;br /&gt;
Fail2ban va analyser les logs pour compter le nombre de tentatives et bannir l&#039;IP qui essaye de se connecter si elle dépasse le nombre maximal d’essais.&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
Une fois l&#039;installation du [[Linux_repository#Installation | dépôt EPEL]] terminée, nous pouvons procéder à celle de &#039;&#039;fail2ban&#039;&#039;: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
yum -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration =&lt;br /&gt;
&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps ou l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentative durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Configuration de la section Jail ==&lt;br /&gt;
* Sur CentOS 6:&lt;br /&gt;
Pour sécuriser le service, il faut repérer sa section jail. Dans cet exemple, nous allons sécuriser &#039;&#039;SSH&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons avoir besoin des variables suivantes:&lt;br /&gt;
*enabled : permet d&#039;activer le filtrage&lt;br /&gt;
*filter  : donne un nom au filtre&lt;br /&gt;
*action  : permet de bloquer le port (ssh) avec une règles Iptables et d&#039;envoyer un mail&lt;br /&gt;
*logpath : indique le fichier de log&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[ssh-iptables]&lt;br /&gt;
&lt;br /&gt;
enabled  = true&lt;br /&gt;
filter   = sshd&lt;br /&gt;
action   = iptables[name=SSH, port=ssh, protocol=tcp]&lt;br /&gt;
           sendmail-whois[name=SSH, dest=MON_EMAIL, sender=fail2ban@MON_DOMAINE, sendername=&amp;quot;Fail2Ban&amp;quot;]&lt;br /&gt;
logpath  = /var/log/secure&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remplacez &#039;&#039;MON_EMAIL&#039;&#039; par votre mail ainsi que &#039;&#039;MON_DOMAINE&#039;&#039; par votre domaine !&lt;br /&gt;
&lt;br /&gt;
*Sur CentOS 8:&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
* Pour SystemVInit:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# service fail2ban start&lt;br /&gt;
# chkconfig fail2ban on&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Pour SystemD:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Ajout automatique de règles dans Iptables =&lt;br /&gt;
Vous pouvez constater que fail2ban ajoute des règles dans la chaîne &#039;&#039;fail2ban-SSH&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Chain fail2ban-SSH (1 references)&lt;br /&gt;
 pkts bytes target     prot opt in     out     source               destination&lt;br /&gt;
    0     0 REJECT     all  --  *      *       122.225.109.214      0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       122.225.109.115      0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       61.174.51.212        0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       61.174.51.213        0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       125.70.0.44          0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       61.174.51.210        0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       122.226.140.158      0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       122.225.109.210      0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       222.186.34.117       0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       210.14.69.244        0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       122.225.109.206      0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       74.208.209.116       0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       61.174.51.229        0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
    0     0 REJECT     all  --  *      *       222.186.34.119       0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
   14   928 REJECT     all  --  *      *       61.55.135.41         0.0.0.0/0           reject-with icmp-port-unreachable&lt;br /&gt;
 1049 66520 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Parcours des Logs =&lt;br /&gt;
{{#lst:Les_logs|log_fail2ban}}&lt;br /&gt;
&lt;br /&gt;
= Apache =&lt;br /&gt;
&lt;br /&gt;
Voici quelques ajouts intéressants pour le serveur Apache:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[apache]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = http,https&lt;br /&gt;
filter = apache-auth&lt;br /&gt;
logpath = %(apache_error_log)s&lt;br /&gt;
&lt;br /&gt;
[apache-overflows]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = http,https&lt;br /&gt;
filter = apache-overflows&lt;br /&gt;
logpath = %(apache_error_log)s&lt;br /&gt;
&lt;br /&gt;
[apache-dos]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = http,https&lt;br /&gt;
filter = apache-dos&lt;br /&gt;
logpath = %(apache_access_log)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4144</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4144"/>
		<updated>2025-12-13T18:29:44Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps où l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcul permet de trouver le nombre maximal de tentatives durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;à spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilise l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;uniquement&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrait ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jour d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modification peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Double IPs ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_2.css HTTP/1.1&amp;quot; 200 6217&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
L&#039;adresse apparaît non pas une fois mais deux, séparées par une virgule...&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour se débarrasser de la virgule ainsi que de la deuxième adresse :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf pour ajouter &#039;&#039;,.*&#039;&#039; après &#039;&#039;&amp;lt;HOST&amp;gt;&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt;,.* user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4143</id>
		<title>Proxmox fail2ban</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Proxmox_fail2ban&amp;diff=4143"/>
		<updated>2025-12-13T18:22:05Z</updated>

		<summary type="html">&lt;p&gt;Jc.forton : /* Automatisation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Proxmox utilise une interface web d&#039;administration qui utilise le port TCP 8006 et c&#039;est un vecteur possible d&#039;attaque par force brut. Nous allons donc &#039;&#039;dresser&#039;&#039; fail2ban pour agir en cas d&#039;attaque !&lt;br /&gt;
&lt;br /&gt;
Proxmox utilise Debian, nous allons principalement reproduire les étapes décrites dans le tutoriel de [[Sécuriser_un_service_avec_Fail2ban| Fail2ban pour Rocky]] et adapter à Debian.&lt;br /&gt;
&lt;br /&gt;
Avant d&#039;aller plus loin, assurez-vous d&#039;avoir correctement installé et configuré [[proxmox_iptables |iptables sur Proxmox]]&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# apt -y install fail2ban&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Configuration =&lt;br /&gt;
Tout d&#039;abord il faut copier le fichier &#039;&#039;/etc/fail2ban/jail.conf&#039;&#039; en &#039;&#039;/etc/fail2ban/jail.local&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans ce fichier, on va s&#039;intéresser aux variables suivantes:&lt;br /&gt;
*ignoreip : correspond à la suite d&#039;adresses IP qui ne se feront jamais bannir;&lt;br /&gt;
*maxretry : correspond au nombre d&#039;essais;&lt;br /&gt;
*findtime : correspond à la période pendant laquelle les essais vont incrémenter maxretry;&lt;br /&gt;
*bantime  : correspond au temps ou l&#039;adresse IP ne peut pas se connecter.&lt;br /&gt;
&lt;br /&gt;
= Configuration de base =&lt;br /&gt;
== Choix de la punition ==&lt;br /&gt;
Il faut choisir quelque chose de cohérent (une punition suffisante) pour ne pas permettre de se faire cracker son mot de passe:&lt;br /&gt;
*maxretry = 3&lt;br /&gt;
*findtime = 86400 (correspond à 1 journée)&lt;br /&gt;
*bantime = 604800 (correspond à 1 semaine)&lt;br /&gt;
&lt;br /&gt;
Cela signifie que si une adresse IP se trompe 3 fois en 1 journée (86400s) elle se fait bannir pendant 1 semaine (604800s).&lt;br /&gt;
&lt;br /&gt;
Un rapide calcule permet de trouver le nombre maximal de tentative durant une année:&lt;br /&gt;
&lt;br /&gt;
365 jours / 7 jours par semaine * 3 tentatives = 156 essais, ce qui reste raisonnable. &lt;br /&gt;
&lt;br /&gt;
Si le pirate possède un [http://fr.wikipedia.org/wiki/Botnet botnet], il faut bien sûr multiplier ce nombre par le nombre de machines dans le botnet...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention : fail2ban parcours les logs de connexion pour connaître le numéro de la tentative, ce qui a pour conséquence, si le &#039;&#039;findtime&#039;&#039; est grand, de prendre un certain temps...&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Jail pour SSH ==&lt;br /&gt;
Il suffit d&#039;ajouter la ligne &#039;&#039;enabled=true&#039;&#039; dans la section à activer !&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[sshd]&lt;br /&gt;
&lt;br /&gt;
# To use more aggressive sshd modes set filter parameter &amp;quot;mode&amp;quot; in jail.local:&lt;br /&gt;
# normal (default), ddos, extra or aggressive (combines all).&lt;br /&gt;
# See &amp;quot;tests/files/logs/sshd&amp;quot; or &amp;quot;filter.d/sshd.conf&amp;quot; for usage example and details.&lt;br /&gt;
#mode   = normal&lt;br /&gt;
enabled = true&lt;br /&gt;
port    = ssh&lt;br /&gt;
logpath = %(sshd_log)s&lt;br /&gt;
backend = %(sshd_backend)s&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuration spécifique =&lt;br /&gt;
== Jail pour PVEPROXY ==&lt;br /&gt;
Pveproxy écoute sur le port 8006 et ne fonctionne pas exactement comme un serveur [[HTTPD | Apache httpd]] qui mettrait ces logs dans le fichier &#039;&#039;error_log&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nous allons mettre cette configuration spécifique dans le fichier &#039;&#039;/etc/fail2ban/jail.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[proxmox]&lt;br /&gt;
enabled = true&lt;br /&gt;
port = https,http,8006&lt;br /&gt;
filter = proxmox&lt;br /&gt;
backend = systemd&lt;br /&gt;
maxretry = 3&lt;br /&gt;
findtime = 2d&lt;br /&gt;
bantime = 1h&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le backend utilisé est systemd, exactement comme si vous utilisiez la commande :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# journalctl -fu pvedaemon&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Filtre pour PVEPROXY ==&lt;br /&gt;
Il ne nous reste plus qu&#039;a spécifier le filtre &#039;&#039;proxmox&#039;&#039; dans le fichier &#039;&#039;/etc/fail2ban/filter.d/proxmox.conf&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Definition]&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt; user=.* msg=.*&lt;br /&gt;
ignoreregex =&lt;br /&gt;
journalmatch = _SYSTEMD_UNIT=pvedaemon.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Démarrage et enregistrement dans le chargeur de démarrage =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl start fail2ban.service&lt;br /&gt;
# systemctl enable fail2ban.service&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vous pouvez maintenant démarrer fail2ban&lt;br /&gt;
&lt;br /&gt;
= Proxification=&lt;br /&gt;
Lorsque votre serveur Proxmox se trouve derrière un reverse-proxy (Apache, Nginx, Traefik, ...), au niveau 3 OSI, l&#039;adresse IP du client est remplacée par celle du proxy et le serveur utilse l&#039;entêtes &#039;&#039;X-Forwarded-For&#039;&#039; ou &#039;&#039;X-Real-IP&#039;&#039; pour transporter l&#039;adresse du client &lt;br /&gt;
[[Fichier:Reverse proxy proxmox.png|700px|centré]]&lt;br /&gt;
Le problème est que la seule adresse que fail2ban va voir apparaître dans les logs est celle du reverse-proxy (&#039;&#039;192.168.4.100&#039;&#039;) :&lt;br /&gt;
[[Fichier:Reverse proxy ip logs.png|600px|centré]]&lt;br /&gt;
== Configuration de pveproxy ==&lt;br /&gt;
Si on veut voir l&#039;adresse du client, il faut dire au [https://fr.wikipedia.org/wiki/Daemon_(informatique)| démon] &#039;&#039;pveproxy&#039;&#039; d&#039;utiliser l&#039;une des deux entêtes précédentes ainsi que préciser l&#039;adresse du proxy. &lt;br /&gt;
&lt;br /&gt;
Préciser l&#039;adresse du proxy est essentiel pour que &#039;&#039;pveproxy&#039;&#039; n&#039;accepte de lire l&#039;adresse du client présente dans l&#039;entête &#039;&#039;uniquement&#039;&#039; si cela provient du proxy, qui est une machine de confiance. Le cas contraire, tout le monde pourrais ajouter une adresse IP dans l&#039;entête pour la faire bannir...&lt;br /&gt;
&lt;br /&gt;
Nous allons créer le fichier &#039;&#039;/etc/default/pveproxy&#039;&#039; avec les lignes suivantes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Forwarded-For&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_HEADER=&amp;quot;X-Real-IP&amp;quot;&lt;br /&gt;
PROXY_REAL_IP_ALLOW_FROM=&amp;quot;192.168.4.100&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il faut maintenant redémarrer &#039;&#039;pveproxy&#039;&#039; pour que les changements s&#039;appliquent. Si vous êtes connecté via l&#039;interface web, cela va vous déconnecter, le temps que le démon redémarre, il faudra ensuite certainement rafraîchir la page web.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# systemctl restart pveproxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;a vérifier dans les logs si les bonnes adresses s&#039;affichent:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Si jamais ce n&#039;est pas le cas, poursuivez avec la modification du fichier &#039;&#039;AnyEvent.pm&#039;&#039;&lt;br /&gt;
== Modification de AnyEvent.pm ==&lt;br /&gt;
=== Application de la modification ===&lt;br /&gt;
Au jours d&#039;aujourd&#039;hui (13/12/25) la proxification ne fonctionne plus et il faut apporter une modification dans le fichier &#039;&#039;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&#039;&#039; pour que les bonnes adresses apparaissent !&lt;br /&gt;
En fonction des versions cette modifications peut avoir lieu soit ligne &#039;&#039;&#039;1504&#039;&#039;&#039;, soit ligne &#039;&#039;&#039;1554&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Il faut repérer la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
sub authenticate_and_handle_request {&lt;br /&gt;
    my ($self, $reqstate) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $request = $reqstate-&amp;gt;{request};&lt;br /&gt;
    my $method = $request-&amp;gt;method();&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
C&#039;est un peu plus loin qu&#039;il faut insérer le code :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
 ### INSERTION ICI ###&lt;br /&gt;
&lt;br /&gt;
    if ($self-&amp;gt;{spiceproxy}) {&lt;br /&gt;
        my $connect_str = $request-&amp;gt;header(&#039;Host&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il faut modifier l&#039;attribut &#039;&#039;peer_host&#039;&#039; de l&#039;objet &#039;&#039;reqstate&#039;&#039; si l&#039;une des deux entêtes est présente :&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if ($request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
} elsif ($request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Double IPs ===&lt;br /&gt;
Après redémarrage, cela permet de voir l&#039;adresse du client dans les logs :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# tail -f /var/log/pveproxy/access.log&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/ext-all.js?ver=7.0.0 HTTP/1.1&amp;quot; 200 683505&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/js/u2f-api.js HTTP/1.1&amp;quot; 200 4898&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /proxmoxlib.js?ver=v5.0.4-t1754316706 HTTP/1.1&amp;quot; 200 157086&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_1.css HTTP/1.1&amp;quot; 200 32919&lt;br /&gt;
37.167.182.207, 37.167.182.207 - - [13/12/2025:18:48:18 +0100] &amp;quot;GET /pve2/ext6/theme-crisp/resources/theme-crisp-all_2.css HTTP/1.1&amp;quot; 200 6217&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
L&#039;adresses apparaît non pas une fois mais deux, séparée par une virgule...&lt;br /&gt;
&lt;br /&gt;
On a deux possibilités pour se débarrasser de la virgule ainsi que de la deuxième adresse :&lt;br /&gt;
*Soit on ajoute les lignes suivantes dans &#039;&#039;AnyEvent.pm&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=perl&amp;gt;&lt;br /&gt;
if (index($reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
   my @fields = split /,/, $reqstate-&amp;gt;{peer_host};&lt;br /&gt;
   $reqstate-&amp;gt;{peer_host} = $fields[0];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*Soit on modifie le filtre /etc/fail2ban/filter.d/proxmox.conf pour ajouter &#039;&#039;,.*&#039;&#039; après &#039;&#039;&amp;lt;HOST&amp;gt;&#039;&#039; :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
failregex = pvedaemon\[.*authentication failure; rhost=&amp;lt;HOST&amp;gt;,.* user=.* msg=.*&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Automatisation ===&lt;br /&gt;
On peut automatiser le processus précédent avec le script suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
ANYEVENT_PATH=&amp;quot;/usr/share/perl5/PVE/APIServer/AnyEvent.pm&amp;quot;&lt;br /&gt;
ANYEVENT_REGEX=&#039;$self-&amp;gt;{spiceproxy}&#039;&lt;br /&gt;
TMP_FILE=&amp;quot;/tmp/AnyEvent.pm.tmp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
if [ ! -f $ANYEVENT_PATH ]; then&lt;br /&gt;
  echo &amp;quot;$ANYEVENT_PATH does not exists !&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
if [ $(grep &#039;### BUGFIX REMOTE_IP FOR FAIL2BAN ###&#039; $ANYEVENT_PATH | wc -l) -eq 1 ]; then&lt;br /&gt;
  echo &amp;quot;AnyEvent.pm already patched !&amp;quot;&lt;br /&gt;
  exit 0&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
LINE_NUMBER_TOP=$(nl -ba $ANYEVENT_PATH | grep $ANYEVENT_REGEX | head -n 1 | awk -F &#039; &#039; &#039;{print $1}&#039;)&lt;br /&gt;
let LINE_NUMBER_TOP-=1&lt;br /&gt;
&lt;br /&gt;
if [ $LINE_NUMBER_TOP -lt 0 ]; then&lt;br /&gt;
  echo &amp;quot;Did not find the $ANYEVENT_REGEX expr in $ANYEVENT_PATH&amp;quot;&lt;br /&gt;
  exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo -n &amp;quot;Applying bugfix: &amp;quot;&lt;br /&gt;
trap &amp;quot;rm -f $TMP_FILE&amp;quot; 0 1 2 5 13 15&lt;br /&gt;
head -n $LINE_NUMBER_TOP $ANYEVENT_PATH &amp;gt;$TMP_FILE&lt;br /&gt;
cat &amp;gt;&amp;gt;$TMP_FILE &amp;lt;&amp;lt;EOF&lt;br /&gt;
### BUGFIX REMOTE_IP FOR FAIL2BAN ###&lt;br /&gt;
    if (\$request-&amp;gt;header(&#039;X-Forwarded-For&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Forwarded-For&#039;);&lt;br /&gt;
    } elsif (\$request-&amp;gt;header(&#039;X-Real-IP&#039;)) {&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$request-&amp;gt;header(&#039;X-Real-IP&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    if (index(\$reqstate-&amp;gt;{peer_host}, &#039;,&#039;) != -1) {&lt;br /&gt;
        my @fields = split /,/, \$reqstate-&amp;gt;{peer_host};&lt;br /&gt;
        \$reqstate-&amp;gt;{peer_host} = \$fields[0];&lt;br /&gt;
    }&lt;br /&gt;
#####################################&lt;br /&gt;
EOF&lt;br /&gt;
echo &amp;quot;$ANYEVENT_MODIF&amp;quot; &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cp $ANYEVENT_PATH $ANYEVENT_PATH.ori&lt;br /&gt;
LINE_NUMBER_BOTTOM=$(cat $ANYEVENT_PATH | wc -l)&lt;br /&gt;
let LINE_NUMBER_BOTTOM-=LINE_NUMBER_TOP&lt;br /&gt;
tail -n $LINE_NUMBER_BOTTOM $ANYEVENT_PATH &amp;gt;&amp;gt;$TMP_FILE&lt;br /&gt;
cat $TMP_FILE &amp;gt;$ANYEVENT_PATH&lt;br /&gt;
rm -f $TMP_FILE&lt;br /&gt;
trap 0&lt;br /&gt;
echo &amp;quot;done.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jc.forton</name></author>
	</entry>
</feed>