auto: modifications serveur
@@ -1 +1 @@
|
|||||||
{"readable":true,"books":[],"as":[{"asn":"","name":"LAN","country":"","hits":6099}]}
|
{"readable":true,"books":[],"as":[{"asn":"","name":"LAN","country":"","hits":6210}]}
|
||||||
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 97 KiB |
@@ -1,107 +0,0 @@
|
|||||||
<svg width="100%" viewBox="0 0 690 430" xmlns="http://www.w3.org/2000/svg" role="img" style=""><defs><mask id="imagine-text-gaps-m5xkrk" maskUnits="userSpaceOnUse"><rect x="0" y="0" width="690" height="430" fill="white"/><rect x="48" y="42.75" width="107.25" height="19" fill="black" rx="2"/><rect x="36" y="70.5" width="378.3333435058594" height="51.5" fill="black" rx="2"/><rect x="36" y="110.5" width="382.29998779296875" height="51.5" fill="black" rx="2"/><rect x="36" y="167" width="378.7166748046875" height="22.75" fill="black" rx="2"/><rect x="39.68333435058594" y="-58.25" width="20.633333206176758" height="31.5" fill="black" rx="2"/><rect x="31.016666412353516" y="-29.75" width="37.96666717529297" height="15.25" fill="black" rx="2"/><rect x="-4" y="-14.5" width="110.31666564941406" height="19" fill="black" rx="2"/><rect x="351" y="-4.5" width="31.66666603088379" height="20.25" fill="black" rx="2"/><rect x="-4" y="-14.5" width="101.98332977294922" height="19" fill="black" rx="2"/><rect x="116" y="-4.75" width="25.016666412353516" height="21.5" fill="black" rx="2"/><rect x="36" y="382.5" width="260.53334045410156" height="21.5" fill="black" rx="2"/><rect x="448.29998779296875" y="382.5" width="196.23333740234375" height="21.5" fill="black" rx="2"/></mask></defs>
|
|
||||||
<title style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Certificats Let's Encrypt 6 jours — illustration d'article</title>
|
|
||||||
<desc style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Bannière illustrant le passage des certificats Let's Encrypt à une durée de 6 jours, avec un cadenas central et un compteur de jours.</desc>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Background -->
|
|
||||||
<rect x="0" y="0" width="680" height="420" style="fill:rgb(245, 239, 230);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<!-- Top tag/eyebrow -->
|
|
||||||
<rect x="40" y="40" width="140" height="24" rx="4" style="fill:rgb(249, 115, 22);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="52" y="56" style="fill:rgb(245, 239, 230);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:11px;font-weight:500;text-anchor:start;dominant-baseline:auto">DEVOPS / WEBOPS</text>
|
|
||||||
|
|
||||||
<!-- Main title -->
|
|
||||||
<text x="40" y="110" font-size="38" style="fill:rgb(31, 41, 55);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:38px;font-weight:500;text-anchor:start;dominant-baseline:auto">Certificats à 6 jours :</text>
|
|
||||||
<text x="40" y="150" font-size="38" style="fill:rgb(31, 41, 55);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:38px;font-weight:500;text-anchor:start;dominant-baseline:auto">faut-il sauter le pas ?</text>
|
|
||||||
|
|
||||||
<!-- Subtitle -->
|
|
||||||
<text x="40" y="184" font-size="15" style="fill:rgb(75, 85, 99);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:15px;font-weight:400;text-anchor:start;dominant-baseline:auto">Le profil shortlived de Let's Encrypt expliqué aux ops</text>
|
|
||||||
|
|
||||||
<!-- Decorative line under title -->
|
|
||||||
<line x1="40" y1="210" x2="180" y2="210" stroke-width="2" style="fill:none;stroke:rgb(249, 115, 22);color:rgb(0, 0, 0);stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<!-- Logo / Padlock illustration on the right -->
|
|
||||||
<!-- Padlock body -->
|
|
||||||
<g transform="translate(480, 220)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<!-- Shackle -->
|
|
||||||
<path d="M -40 -10 Q -40 -60 0 -60 Q 40 -60 40 -10 L 40 10" stroke-width="8" stroke-linecap="round" mask="url(#imagine-text-gaps-m5xkrk)" style="fill:none;stroke:rgb(31, 41, 55);color:rgb(0, 0, 0);stroke-width:8px;stroke-linecap:round;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- Body -->
|
|
||||||
<rect x="-58" y="0" width="116" height="90" rx="10" style="fill:rgb(31, 41, 55);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- Keyhole -->
|
|
||||||
<circle cx="0" cy="35" r="8" fill="#F97316" style="fill:rgb(249, 115, 22);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="-3" y="35" width="6" height="20" fill="#F97316" style="fill:rgb(249, 115, 22);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<!-- Day counter badge on padlock -->
|
|
||||||
<circle cx="50" cy="-30" r="32" style="fill:rgb(249, 115, 22);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="50" y="-35" text-anchor="middle" font-family="var(--font-sans)" font-weight="500" font-size="22" fill="#F5EFE6" style="fill:rgb(245, 239, 230);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:22px;font-weight:500;text-anchor:middle;dominant-baseline:auto">6</text>
|
|
||||||
<text x="50" y="-19" text-anchor="middle" font-family="var(--font-sans)" font-weight="400" font-size="9" fill="#F5EFE6" style="fill:rgb(245, 239, 230);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:9px;font-weight:400;text-anchor:middle;dominant-baseline:auto">JOURS</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Calendar dots showing the contrast: 90 days vs 6 days -->
|
|
||||||
<g transform="translate(40, 260)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<!-- 90 days row -->
|
|
||||||
<text x="0" y="0" font-size="12" style="fill:rgb(75, 85, 99);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto">AVANT — 90 jours</text>
|
|
||||||
<g transform="translate(0, 12)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<!-- Generate many tiny dots representing 90 days -->
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<circle cx="0" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="10" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="20" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="30" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="40" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="50" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="60" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="70" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="80" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="90" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="100" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="110" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="120" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="130" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="140" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="150" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="160" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="170" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="180" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="190" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="200" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="210" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="220" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="230" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="240" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="250" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="260" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="270" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="280" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="290" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="300" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="310" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="320" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="330" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="340" cy="6" r="3" opacity="0.4" style="fill:rgb(16, 185, 129);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
<text x="355" y="10" font-size="13" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:13px;font-weight:500;text-anchor:start;dominant-baseline:auto">×90</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(40, 320)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<!-- 6 days row -->
|
|
||||||
<text x="0" y="0" font-size="12" style="fill:rgb(31, 41, 55);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:12px;font-weight:500;text-anchor:start;dominant-baseline:auto">APRÈS — 6 jours</text>
|
|
||||||
<g transform="translate(0, 12)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<circle cx="0" cy="6" r="6" style="fill:rgb(249, 115, 22);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="20" cy="6" r="6" style="fill:rgb(249, 115, 22);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="40" cy="6" r="6" style="fill:rgb(249, 115, 22);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="60" cy="6" r="6" style="fill:rgb(249, 115, 22);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="80" cy="6" r="6" style="fill:rgb(249, 115, 22);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="100" cy="6" r="6" style="fill:rgb(249, 115, 22);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="120" y="11" font-size="14" style="fill:rgb(31, 41, 55);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:14px;font-weight:500;text-anchor:start;dominant-baseline:auto">×6</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Bottom footer -->
|
|
||||||
<line x1="40" y1="375" x2="640" y2="375" stroke-width="0.5" opacity="0.3" style="fill:none;stroke:rgb(31, 41, 55);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.3;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="40" y="397" font-size="12" style="fill:rgb(75, 85, 99);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto">Let's Encrypt · Profil shortlived · Certbot 4.0+</text>
|
|
||||||
<text x="640" y="397" text-anchor="end" font-size="12" font-style="italic" style="fill:rgb(75, 85, 99);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:12px;font-weight:400;font-style:italic;text-anchor:end;dominant-baseline:auto">Guide pour les ops auto-hébergés</text>
|
|
||||||
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 26 KiB |
@@ -1,200 +0,0 @@
|
|||||||
# Certificats Let's Encrypt à 6 jours : faut-il sauter le pas ?
|
|
||||||
|
|
||||||
> Guide DevOps / WebOps pour comprendre les certificats à durée de vie courte (`shortlived`) et décider si vous devez migrer.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## TL;DR
|
|
||||||
|
|
||||||
Let's Encrypt propose désormais au grand public des certificats valides **6 jours** (profil `shortlived`), en plus du classique 90 jours. Pour les certificats sur **adresse IP**, c'est même obligatoire. La question n'est pas "est-ce que c'est bien ?" — techniquement oui — mais "est-ce que mon infra est prête ?". Si votre renouvellement automatique fonctionne sans accroc depuis 6 mois, foncez. Sinon, fiabilisez d'abord, migrez ensuite.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. De quoi on parle
|
|
||||||
|
|
||||||
Depuis janvier 2026, Let's Encrypt émet en disponibilité générale deux nouveautés couplées : les certificats pour adresses IP, et les certificats à durée de vie de 6 jours via un nouveau profil ACME nommé `shortlived`. Certbot 4.0 a introduit le flag `--preferred-profile` pour les sélectionner, et Certbot 5.3 a ajouté `--ip-address` pour demander un cert sur IP.
|
|
||||||
|
|
||||||
Concrètement, un certificat `shortlived` a une validité de ~6 jours au lieu de 90. Tout le reste — chaîne de confiance, algorithmes, format — est identique. Pour le navigateur, c'est un certificat Let's Encrypt standard.
|
|
||||||
|
|
||||||
### Les profils disponibles
|
|
||||||
|
|
||||||
| Profil | Durée | Usage |
|
|
||||||
|---|---|---|
|
|
||||||
| `classic` (défaut) | 90 jours | Tout le monde, par défaut |
|
|
||||||
| `shortlived` | ~6 jours | Adopters précoces, certs sur IP (obligatoire) |
|
|
||||||
| `tlsserver` | 90 jours | Variante optimisée serveur web (sans clientAuth) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Pourquoi Let's Encrypt pousse vers le court
|
|
||||||
|
|
||||||
Quatre raisons techniques, par ordre d'importance.
|
|
||||||
|
|
||||||
### 2.1 La révocation TLS est cassée — autant l'éviter
|
|
||||||
|
|
||||||
C'est le vrai sujet. Le mécanisme de révocation des certificats (CRL, OCSP) n'a jamais fonctionné correctement à grande échelle :
|
|
||||||
|
|
||||||
- **OCSP soft-fail** : si le serveur OCSP est injoignable, la plupart des navigateurs acceptent quand même le certificat. Un attaquant qui contrôle le réseau bloque l'OCSP et le cert révoqué passe.
|
|
||||||
- **OCSP stapling** mal configuré sur beaucoup de serveurs.
|
|
||||||
- **CRLite, OneCRL** : couvertures partielles, déploiement client incohérent.
|
|
||||||
- **OCSP retiré** : Let's Encrypt a arrêté OCSP en 2025, justement parce que ça ne servait quasiment à rien tout en posant des problèmes de vie privée.
|
|
||||||
|
|
||||||
Avec un cert à 6 jours, la révocation devient cosmétique : on attend l'expiration. La fenêtre d'exploitation d'une clé compromise passe de plusieurs semaines (cert 90 jours, OCSP douteux) à quelques jours maximum.
|
|
||||||
|
|
||||||
### 2.2 Réduire la fenêtre de compromission
|
|
||||||
|
|
||||||
Si votre clé privée fuite (backup mal protégé, faille serveur, employé qui part avec une copie, vulnérabilité type Heartbleed), l'attaquant peut usurper votre site tant que le cert est valide. À 90 jours, c'est trois mois d'exposition dans le pire cas. À 6 jours, c'est une semaine.
|
|
||||||
|
|
||||||
C'est encore plus critique pour les **certs sur IP** : une IP peut changer de propriétaire (cloud, VPS recyclé, réattribution FAI). Un cert long pour une IP qui ne vous appartient plus, c'est un risque que LE refuse de prendre — d'où l'obligation du profil court pour cet usage.
|
|
||||||
|
|
||||||
### 2.3 Forcer une automatisation propre
|
|
||||||
|
|
||||||
Personne ne renouvelle un cert à la main tous les 6 jours. C'est mécaniquement infaisable. Le profil `shortlived` est donc un **filtre qualité** : si votre renouvellement n'est pas blindé, vous le saurez vite.
|
|
||||||
|
|
||||||
L'effet de bord positif : ça élimine les pannes classiques type "le cert a expiré parce que le cron était cassé depuis trois mois et personne ne s'en est rendu compte". À 6 jours, un cron cassé devient visible immédiatement.
|
|
||||||
|
|
||||||
### 2.4 Agilité cryptographique
|
|
||||||
|
|
||||||
Si une vulnérabilité majeure impose de déprécier un algorithme en urgence (RSA, transition post-quantique, faille découverte sur SHA-256), un parc avec des certs à 6 jours bascule en une semaine. Un parc 90 jours met trois mois. C'est la raison qui motive aussi le CA/Browser Forum à pousser globalement vers des durées plus courtes (45 jours d'ici 2029 dans la baseline).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Pourquoi vous pourriez ne *pas* migrer
|
|
||||||
|
|
||||||
Soyons honnêtes : pour la plupart des infras web classiques, le 90 jours suffit largement. Le 6 jours a des coûts réels.
|
|
||||||
|
|
||||||
### 3.1 Pression sur le rate limiting
|
|
||||||
|
|
||||||
Let's Encrypt limite à **300 nouveaux certificats par compte par 3 heures** et **5 duplicatas de cert par semaine**. Avec des certs 90 jours, vous renouvelez ~4 fois par an. Avec des 6 jours, c'est ~60 fois par an et par cert. Si vous avez 50 services derrière 50 certs distincts, vous explosez votre budget de requêtes ACME.
|
|
||||||
|
|
||||||
**Mitigation :** regrouper les domaines dans des certs SAN (un seul cert pour `app.a5l.fr`, `api.a5l.fr`, `admin.a5l.fr` plutôt que trois certs).
|
|
||||||
|
|
||||||
### 3.2 Dépendance critique au CA et au réseau
|
|
||||||
|
|
||||||
À 90 jours, si Let's Encrypt est down 48h, vous ne le remarquez même pas. À 6 jours, une panne de 48h sur LE *et* votre fenêtre de renouvellement (typiquement à 1/3 de la durée restante, soit ~2 jours), et votre cert expire. Vos services tombent.
|
|
||||||
|
|
||||||
Conséquences concrètes :
|
|
||||||
- Il faut un **monitoring sérieux** de l'expiration des certs (Prometheus blackbox exporter, `check_ssl_cert`, etc.).
|
|
||||||
- Il faut un **fallback** : second client ACME, second account, ou cert de secours d'une autre CA.
|
|
||||||
- Il faut absolument que la résolution DNS et le port 80/443 sortants depuis votre serveur soient fiables.
|
|
||||||
|
|
||||||
### 3.3 Charge sur les systèmes de déploiement
|
|
||||||
|
|
||||||
Chaque renouvellement déclenche : appel ACME, validation HTTP-01 ou DNS-01, écriture des fichiers, **rechargement du serveur web** (Nginx, Apache, HAProxy, etc.). À ~60 fois par an au lieu de 4, ça multiplie par 15 le nombre de reloads.
|
|
||||||
|
|
||||||
Sur un serveur web basique, un `nginx -s reload` est gratuit. Sur des architectures plus complexes (load balancers stateful, terminations TLS distribuées, certs poussés vers un CDN, configs multi-nœuds avec Ansible/Salt), chaque renouvellement déclenche une cascade. À évaluer.
|
|
||||||
|
|
||||||
### 3.4 Logs, audit, conformité
|
|
||||||
|
|
||||||
Certains contextes réglementaires demandent une traçabilité des certificats (PCI-DSS, ISO 27001, HDS). Multiplier par 15 le volume d'événements de renouvellement à archiver et auditer, ça représente du stockage et du tooling à adapter.
|
|
||||||
|
|
||||||
### 3.5 Le cas "monitoring TLS externe"
|
|
||||||
|
|
||||||
Si vous avez des outils tiers (uptime monitors, scanners de conformité) qui vérifient l'expiration de vos certs, ils risquent de hurler en permanence : un cert qui montre toujours "expire dans 6 jours" déclenche les alertes "cert expirant bientôt" sur la plupart des outils mal configurés. Il faut soit ajuster les seuils, soit changer d'outil.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Décision : grille de lecture
|
|
||||||
|
|
||||||
| Situation | Recommandation |
|
|
||||||
|---|---|
|
|
||||||
| Serveurs web classiques, renouvellement Certbot qui marche, < 20 certs | Restez en 90 jours. Le bénéfice marginal ne justifie pas le risque. |
|
|
||||||
| Vous gérez des **certs sur IP** | Pas le choix : `shortlived` est obligatoire. |
|
|
||||||
| Architecture critique avec rotation de clés agressive (banque, santé, infra publique) | Migrez. Le 6 jours est aligné avec vos exigences de sécurité. |
|
|
||||||
| Infra dev/staging interne | Excellent terrain de test. Migrez d'abord ici pour valider votre pipeline. |
|
|
||||||
| Vous avez déjà eu une expiration cert non détectée en prod | **Ne migrez pas tout de suite.** Fiabilisez d'abord le monitoring et le renouvellement, puis migrez. Sinon vous transformez un incident annuel en incident hebdomadaire. |
|
|
||||||
| Vous publiez via reverse proxy unique (un seul cert SAN pour plusieurs services) | Bon candidat. Un seul renouvellement à fiabiliser. |
|
|
||||||
| Vous avez un parc hétérogène (Apache + Nginx + HAProxy + Traefik...) avec hooks custom | Auditez chaque hook avant de migrer. C'est là que ça casse. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Comment migrer concrètement (Certbot)
|
|
||||||
|
|
||||||
### 5.1 Pré-requis
|
|
||||||
|
|
||||||
Avant tout :
|
|
||||||
|
|
||||||
1. **Certbot 4.0+** pour `--preferred-profile`, **5.3+** pour `--ip-address`, **5.4+** pour `webroot` avec IP.
|
|
||||||
2. Un renouvellement automatique opérationnel et **vérifié** (timer systemd ou cron actif, testé avec `certbot renew --dry-run`).
|
|
||||||
3. Un monitoring d'expiration des certs en place. Si vous n'en avez pas, installez-le **avant** de migrer.
|
|
||||||
4. Un hook de reload du serveur web qui fonctionne (`--deploy-hook`).
|
|
||||||
|
|
||||||
### 5.2 Test sur le staging Let's Encrypt
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo certbot certonly --staging \
|
|
||||||
--preferred-profile shortlived \
|
|
||||||
--webroot \
|
|
||||||
--webroot-path /var/www/html \
|
|
||||||
-d test.example.com
|
|
||||||
```
|
|
||||||
|
|
||||||
Vérifier que le cert obtenu a bien une durée de 6 jours :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openssl x509 -in /etc/letsencrypt/live/test.example.com/cert.pem -noout -dates
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.3 Renouvellement plus fréquent
|
|
||||||
|
|
||||||
Par défaut, Certbot renouvelle quand il reste 1/3 de la durée. Pour un cert 6 jours, ça veut dire renouveler à 2 jours restants. Ça laisse peu de marge en cas de panne. Vous pouvez forcer un renouvellement plus tôt :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Renouvelle si moins de 4 jours restants (au lieu de 2 par défaut)
|
|
||||||
certbot renew --deploy-hook "systemctl reload nginx"
|
|
||||||
```
|
|
||||||
|
|
||||||
Le timer Certbot tourne deux fois par jour par défaut, ce qui est suffisant. Pas besoin de l'accélérer.
|
|
||||||
|
|
||||||
### 5.4 Cas d'un certificat sur IP
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo certbot certonly \
|
|
||||||
--preferred-profile shortlived \
|
|
||||||
--webroot \
|
|
||||||
--webroot-path /var/www/html \
|
|
||||||
--ip-address 203.0.113.42
|
|
||||||
```
|
|
||||||
|
|
||||||
Note importante : Certbot ne sait pas encore *installer* automatiquement les certs IP dans Nginx ou Apache. Il faut éditer la config manuellement pour pointer vers `/etc/letsencrypt/live/<ip>/fullchain.pem` et `privkey.pem`, et configurer un `--deploy-hook` pour le reload.
|
|
||||||
|
|
||||||
### 5.5 Plan de bascule recommandé
|
|
||||||
|
|
||||||
1. **Semaine 1-2** : un domaine non critique (un sous-domaine de test, un service interne) en `shortlived`. Surveillez les renouvellements.
|
|
||||||
2. **Semaine 3-4** : étendez à la moitié de votre dev/staging.
|
|
||||||
3. **Semaine 5-6** : migration progressive en prod, en commençant par les services les moins critiques.
|
|
||||||
4. **À tout moment** : possibilité de retour arrière en supprimant `--preferred-profile shortlived` du fichier de config Certbot dans `/etc/letsencrypt/renewal/<domain>.conf`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Pièges à éviter
|
|
||||||
|
|
||||||
- **Ne migrez pas tout en même temps.** Si votre hook de reload a un bug, vous le découvrez sur un seul service, pas sur 50.
|
|
||||||
- **Ne désactivez pas le monitoring d'expiration sous prétexte que c'est automatisé.** L'automatisation peut casser silencieusement. Un check externe qui hurle à J-2 reste indispensable.
|
|
||||||
- **Attention aux secrets stockés dans des configs autres que Certbot.** Si vous avez des certs poussés manuellement vers un CDN, un load balancer cloud ou un firewall TLS-inspectant, le passage à 6 jours impose d'automatiser cette propagation aussi.
|
|
||||||
- **Pas de cert IP pour un service exposé publiquement à long terme.** Si l'IP change, le cert devient inutilisable instantanément. Préférez le DNS quand c'est possible.
|
|
||||||
- **Vérifiez votre client ACME.** Tous les clients ACME ne supportent pas encore les profils. acme.sh, Caddy, lego, Traefik : checkez la version. Certbot 4.0 minimum.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. Verdict
|
|
||||||
|
|
||||||
Le profil `shortlived` est **techniquement supérieur** au 90 jours sur le plan sécuritaire. Mais il déplace le coût : moins de risques liés aux clés compromises et à la révocation cassée, **plus** de risques liés à la chaîne de renouvellement.
|
|
||||||
|
|
||||||
La règle simple : **si votre renouvellement automatique est fiable, migrez. Sinon, fiabilisez-le d'abord — la migration n'en sera que la conséquence naturelle.**
|
|
||||||
|
|
||||||
Pour la majorité des infras web auto-hébergées (typiquement, un Proxmox + reverse proxy + une dizaine de services derrière), le 90 jours reste un excellent compromis. Le `shortlived` devient pertinent quand :
|
|
||||||
- Vous avez besoin de certs sur IP (obligatoire).
|
|
||||||
- Vous exploitez des services à forte exigence de sécurité (clés très sensibles).
|
|
||||||
- Vous voulez tester votre résilience opérationnelle (le 6 jours est un excellent test de fiabilité de votre stack).
|
|
||||||
|
|
||||||
Le reste du temps, gardez le 90 jours, dormez tranquille, et ressortez ce document quand le CA/Browser Forum imposera 45 jours par défaut (vers 2027-2028).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Sources
|
|
||||||
|
|
||||||
- [Let's Encrypt — Six-Day and IP Address Certificates Available in Certbot](https://letsencrypt.org/2026/03/11/shorter-certs-certbot) (mars 2026)
|
|
||||||
- [Let's Encrypt — 6-day and IP address certs in general availability](https://letsencrypt.org/2026/01/15/6day-and-ip-general-availability) (janvier 2026)
|
|
||||||
- [Documentation Certbot — Hooks](https://eff-certbot.readthedocs.io/en/stable/using.html#hooks)
|
|
||||||
- [CA/Browser Forum Baseline Requirements](https://cabforum.org/baseline-requirements-documents/)
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "093711bf-4e60-4ea8-ba73-928d2d67776c",
|
|
||||||
"slug": "certificats-let-s-encrypt-a-6-jours-faut-il-sauter-le-pas",
|
|
||||||
"title": "Certificats Let's Encrypt à 6 jours : faut-il sauter le pas ?",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2026-05-07 22:46",
|
|
||||||
"created_at": "2026-05-12 08:47:27",
|
|
||||||
"updated_at": "2026-05-12 08:59:58",
|
|
||||||
"revisions": [],
|
|
||||||
"cover": "cover.svg",
|
|
||||||
"files_meta": {
|
|
||||||
"447e17138517246a-26778.svg": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"_thumb_3007bde7fd56be76-3567.png": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"_thumb_58a97ee03002ccd0-21513.png": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"_thumb_7fa3a5beb503fead-99693.png": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"cover.svg": {
|
|
||||||
"author": "Cédrix / Générée par IA",
|
|
||||||
"source_url": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"external_links": [
|
|
||||||
{
|
|
||||||
"url": "https://letsencrypt.org/2026/03/11/shorter-certs-certbot",
|
|
||||||
"name": "Six-Day and IP Address Certificates Available in Certbot - Let's Encrypt",
|
|
||||||
"added_at": "2026-05-12 08:51:48",
|
|
||||||
"meta": {
|
|
||||||
"mime": "text/html",
|
|
||||||
"size": 30330,
|
|
||||||
"description": " This was also posted on EFF’s blog.\nAs we announced earlier this year, Let’s Encrypt now issues IP address and six-day certificates to the general public. The Certbot team at the Electronic Frontier Foundation has been working on two improvements to support these features: the --preferred-profile flag released last year in Certbot 4.0, and the --ip-address flag, new in Certbot 5.3. With these improvements together, you can now use Certbot to get those IP address certificates!\n",
|
|
||||||
"og_image": "/file?uuid=093711bf-4e60-4ea8-ba73-928d2d67776c&name=_thumb_58a97ee03002ccd0-21513.png",
|
|
||||||
"og_type": "website"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://eff-certbot.readthedocs.io/en/stable/using.html#hooks",
|
|
||||||
"name": "User Guide — Certbot 5.6.0 documentation",
|
|
||||||
"added_at": "2026-05-12 08:52:14",
|
|
||||||
"meta": {
|
|
||||||
"mime": "text/html",
|
|
||||||
"size": 191138,
|
|
||||||
"og_image": "/file?uuid=093711bf-4e60-4ea8-ba73-928d2d67776c&name=_thumb_3007bde7fd56be76-3567.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://cabforum.org/baseline-requirements-documents/",
|
|
||||||
"name": "https://cabforum.org/working-groups/server/baseline-requirements/documents/",
|
|
||||||
"added_at": "2026-05-12 08:52:42",
|
|
||||||
"meta": {
|
|
||||||
"mime": "text/html",
|
|
||||||
"size": 209,
|
|
||||||
"og_image": "/file?uuid=093711bf-4e60-4ea8-ba73-928d2d67776c&name=_thumb_7fa3a5beb503fead-99693.png"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "",
|
|
||||||
"og_image": "",
|
|
||||||
"category": "actualité"
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 12 KiB |
@@ -1,136 +0,0 @@
|
|||||||
# Deuxièmes pas DevOps : durcir et fiabiliser un serveur Debian
|
|
||||||
|
|
||||||
Une fois le système de base configuré (dépôts, mises à jour, `sudo`, identification — sujets traités dans l'article précédent), la machine est fonctionnelle mais encore vulnérable et un peu fragile pour un usage sérieux. Ce deuxième article s'attaque aux gestes qui transforment un serveur « qui marche » en un serveur sur lequel on peut raisonnablement faire tourner quelque chose : sécuriser l'accès SSH, mettre en place un pare-feu, automatiser les correctifs de sécurité et soigner quelques détails opérationnels.
|
|
||||||
|
|
||||||
## Sécuriser l'accès SSH
|
|
||||||
|
|
||||||
SSH est la porte d'entrée principale d'un serveur Linux. C'est aussi, statistiquement, la cible la plus attaquée : n'importe quelle IP publique reçoit en permanence des tentatives de connexion automatisées. Deux gestes simples changent radicalement la donne.
|
|
||||||
|
|
||||||
### Passer à l'authentification par clé
|
|
||||||
|
|
||||||
Les mots de passe, même longs, restent vulnérables aux attaques par force brute et au phishing. Une paire de clés cryptographiques est à la fois plus sûre et plus pratique au quotidien.
|
|
||||||
|
|
||||||
Côté poste de travail, on génère une paire de clés modernes :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ssh-keygen -t ed25519 -C "cedrix@laptop"
|
|
||||||
```
|
|
||||||
|
|
||||||
L'algorithme `ed25519` est aujourd'hui le choix par défaut recommandé : clés courtes, signatures rapides, sécurité solide. Le commentaire (`-C`) facilite l'identification de la clé quand on en gère plusieurs.
|
|
||||||
|
|
||||||
On copie ensuite la clé publique sur le serveur :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ssh-copy-id utilisateur@serveur
|
|
||||||
```
|
|
||||||
|
|
||||||
Cette commande dépose la clé publique dans `~/.ssh/authorized_keys` côté serveur avec les bonnes permissions. À partir de là, la connexion se fait sans saisir de mot de passe — il faut tester depuis une nouvelle session avant de passer à l'étape suivante, sous peine de risquer de se retrouver enfermé dehors.
|
|
||||||
|
|
||||||
### Désactiver la connexion root et les mots de passe
|
|
||||||
|
|
||||||
Une fois la connexion par clé validée, on durcit la configuration SSH. Le fichier à modifier est `/etc/ssh/sshd_config` :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo nano /etc/ssh/sshd_config
|
|
||||||
```
|
|
||||||
|
|
||||||
Les directives importantes à positionner (ou décommenter) sont :
|
|
||||||
|
|
||||||
```
|
|
||||||
PermitRootLogin no
|
|
||||||
PasswordAuthentication no
|
|
||||||
PubkeyAuthentication yes
|
|
||||||
```
|
|
||||||
|
|
||||||
La première interdit toute connexion directe en `root` via SSH : on devra obligatoirement se connecter avec un utilisateur normal puis élever ses droits via `sudo`. La deuxième supprime complètement l'authentification par mot de passe, ne laissant plus que les clés. La troisième confirme explicitement que l'authentification par clé est active.
|
|
||||||
|
|
||||||
On recharge ensuite le service pour appliquer les changements :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo systemctl reload ssh
|
|
||||||
```
|
|
||||||
|
|
||||||
Important : garder la session SSH actuelle ouverte et tester la nouvelle configuration depuis un autre terminal avant de fermer la première. En cas de problème, on peut encore corriger le tir.
|
|
||||||
|
|
||||||
Pour aller un cran plus loin, changer le port SSH par défaut (22) vers un port moins évident réduit considérablement le bruit dans les logs. Ce n'est pas de la sécurité au sens strict (un scan le retrouvera), mais c'est un filtre efficace contre les attaques automatisées.
|
|
||||||
|
|
||||||
## Mettre en place un pare-feu
|
|
||||||
|
|
||||||
Par défaut, Debian n'a aucun pare-feu actif. Tout port ouvert par un service installé sera donc directement exposé. Deux outils standards existent : `nftables` (le successeur officiel d'`iptables`, bas niveau et puissant) et `ufw` (une surcouche pensée pour la simplicité). Pour démarrer, `ufw` est le bon compromis.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install ufw
|
|
||||||
```
|
|
||||||
|
|
||||||
La logique consiste à tout bloquer en entrée par défaut, puis à n'ouvrir explicitement que ce qui doit l'être :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo ufw default deny incoming
|
|
||||||
sudo ufw default allow outgoing
|
|
||||||
sudo ufw allow ssh
|
|
||||||
sudo ufw enable
|
|
||||||
```
|
|
||||||
|
|
||||||
Si SSH écoute sur un port non standard, remplacer `ufw allow ssh` par `ufw allow 2222/tcp` (ou le port choisi). Oublier cette étape avant un `ufw enable` est un grand classique du verrouillage involontaire.
|
|
||||||
|
|
||||||
Pour les services web, on ouvrira typiquement les ports 80 et 443 :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo ufw allow 80/tcp
|
|
||||||
sudo ufw allow 443/tcp
|
|
||||||
```
|
|
||||||
|
|
||||||
L'état du pare-feu se vérifie avec :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo ufw status verbose
|
|
||||||
```
|
|
||||||
|
|
||||||
Sur une architecture où la machine est derrière un reverse proxy (cas fréquent quand on expose plusieurs services sur un même domaine), seuls les ports utiles côté proxy doivent être ouverts au monde extérieur. Les services applicatifs eux-mêmes restent accessibles uniquement depuis le réseau interne.
|
|
||||||
|
|
||||||
## Automatiser les correctifs de sécurité
|
|
||||||
|
|
||||||
Les failles de sécurité ne préviennent pas, et personne n'a envie de lancer manuellement `apt upgrade` chaque matin sur dix machines. Le paquet `unattended-upgrades` applique automatiquement les mises à jour du dépôt `security`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install unattended-upgrades
|
|
||||||
sudo dpkg-reconfigure --priority=low unattended-upgrades
|
|
||||||
```
|
|
||||||
|
|
||||||
La configuration se trouve ensuite dans `/etc/apt/apt.conf.d/50unattended-upgrades`. Par défaut, seuls les correctifs de sécurité sont appliqués automatiquement, ce qui est généralement le bon compromis : on profite des patches critiques sans risquer qu'une mise à jour fonctionnelle introduise une régression sur un service en production.
|
|
||||||
|
|
||||||
Quelques options qui méritent l'attention dans ce fichier :
|
|
||||||
|
|
||||||
- `Unattended-Upgrade::Automatic-Reboot` : à régler sur `"true"` si l'on accepte les redémarrages automatiques après une mise à jour de noyau, ou `"false"` si l'on préfère les piloter à la main. La directive `Automatic-Reboot-Time` permet alors de choisir l'horaire.
|
|
||||||
- `Unattended-Upgrade::Mail` : pour recevoir un rapport par mail des mises à jour appliquées, à condition d'avoir un MTA configuré sur la machine.
|
|
||||||
|
|
||||||
Le bon réflexe consiste à vérifier de temps en temps les logs dans `/var/log/unattended-upgrades/` pour s'assurer que tout se déroule sans heurts.
|
|
||||||
|
|
||||||
## Soigner les détails opérationnels
|
|
||||||
|
|
||||||
Quelques outils complémentaires améliorent significativement le confort et la résilience d'un serveur.
|
|
||||||
|
|
||||||
**Fail2ban** surveille les logs d'authentification et bannit temporairement les IP qui tentent trop de connexions échouées. Même avec SSH par clé uniquement, le service réduit considérablement le bruit dans les journaux :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install fail2ban
|
|
||||||
```
|
|
||||||
|
|
||||||
La configuration par défaut surveille déjà SSH ; elle peut être étendue à d'autres services (nginx, Postfix, etc.) via des fichiers dans `/etc/fail2ban/jail.d/`.
|
|
||||||
|
|
||||||
**Logwatch** ou **journalctl** méritent qu'on s'y attarde. Sur une Debian récente, `journalctl` est l'outil central pour consulter les logs systemd :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
journalctl -u ssh --since "1 hour ago"
|
|
||||||
journalctl -p err --since today
|
|
||||||
```
|
|
||||||
|
|
||||||
Prendre l'habitude de jeter un œil aux logs régulièrement — ou de mettre en place une remontée centralisée si l'on gère plusieurs machines — change beaucoup de choses en exploitation.
|
|
||||||
|
|
||||||
**Un swap raisonnable**, sur une VM ou un serveur dédié, évite que la machine ne devienne complètement injoignable en cas de pic de consommation mémoire. Sur un conteneur LXC en revanche, c'est généralement géré au niveau de l'hyperviseur.
|
|
||||||
|
|
||||||
## Et après ?
|
|
||||||
|
|
||||||
Avec ces réglages, le serveur est dans un état correct pour accueillir des services réels : la surface d'attaque est réduite, les correctifs s'appliquent tout seuls, et les logs racontent ce qui se passe. La suite logique est l'installation de la pile applicative proprement dite (serveur web, base de données, runtime) et la mise en place d'un reverse proxy pour exposer plusieurs services derrière un même point d'entrée.
|
|
||||||
|
|
||||||
Comme évoqué dans le premier article, le moment où l'on commence à enchaîner ces étapes sur plusieurs machines est exactement celui où il faut basculer vers de l'automatisation : un script shell bien rangé pour commencer, puis Ansible ou un équivalent quand le parc grossit. Une bonne pratique consiste à versionner ces scripts dans un dépôt Git dédié à l'infrastructure, au même titre que le code applicatif.
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "0bba1ad7-e4cb-49a6-9467-fcfac2e09a93",
|
|
||||||
"slug": "deuxiemes-pas-devops-durcir-et-fiabiliser-un-serveur-debian",
|
|
||||||
"title": "Deuxièmes pas DevOps : durcir et fiabiliser un serveur Debian",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2026-06-08 07:00",
|
|
||||||
"created_at": "2026-05-12 23:01:34",
|
|
||||||
"updated_at": "2026-05-12 23:29:17",
|
|
||||||
"revisions": [],
|
|
||||||
"cover": "cover.jpg",
|
|
||||||
"files_meta": {
|
|
||||||
"cover.jpg": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"external_links": [
|
|
||||||
{
|
|
||||||
"url": "https://varlog.a5l.fr/post/post-install",
|
|
||||||
"name": "Premiers pas DevOps : préparer un système Debian fraîchement installé",
|
|
||||||
"added_at": "2026-05-12 23:18:02",
|
|
||||||
"author": "Cédrix",
|
|
||||||
"meta": {
|
|
||||||
"mime": "text/html",
|
|
||||||
"description": "Lorsqu'on vient de provisionner une machine Debian — que ce soit un conteneur LXC, une VM ou un serveur dédié — quelques étapes initiales sont incontourna…"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "",
|
|
||||||
"og_image": "https://varlog.a5l.fr/file?uuid=0bba1ad7-e4cb-49a6-9467-fcfac2e09a93&name=cover.jpg",
|
|
||||||
"category": "informatique"
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
<svg width="100%" viewBox="0 0 680 520" role="img" xmlns="http://www.w3.org/2000/svg" style="">
|
|
||||||
<title style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">ImageMagick sur Debian : convert-im6, magick, et leur lien avec les binaires sous-jacents</title>
|
|
||||||
<desc style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Schéma illustrant comment les commandes convert et magick sont des symlinks vers les binaires versionnés convert-im6.q16 et magick-im7.q16 sur Debian.</desc>
|
|
||||||
<defs>
|
|
||||||
<marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></marker>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<text x="340" y="36" text-anchor="middle" font-size="16" style="fill:rgb(20, 20, 19);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">ImageMagick sur Debian</text>
|
|
||||||
<text x="340" y="56" text-anchor="middle" style="fill:rgb(61, 61, 58);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Ce que tu tapes, ce que le système exécute</text>
|
|
||||||
|
|
||||||
<rect x="40" y="90" width="290" height="180" rx="8" fill="none" stroke="var(--color-border-tertiary)" stroke-width="0.5" stroke-dasharray="4 3" style="fill:none;stroke:rgba(31, 30, 29, 0.15);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-dasharray:4px, 3px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="60" y="112" style="fill:rgb(61, 61, 58);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto">Debian 11 / 12 (bookworm)</text>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="80" y="130" width="120" height="44" rx="8" style="fill:rgb(230, 241, 251);stroke:rgb(24, 95, 165);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="140" y="152" text-anchor="middle" style="fill:rgb(12, 68, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">convert</text>
|
|
||||||
<text x="140" y="168" text-anchor="middle" style="fill:rgb(24, 95, 165);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">tu tapes</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<line x1="200" y1="152" x2="240" y2="152" stroke="var(--color-text-tertiary)" stroke-width="0.5" marker-end="url(#arrow)" stroke-dasharray="3 2" style="fill:none;stroke:rgb(115, 114, 108);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-dasharray:3px, 2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="240" y="130" width="70" height="44" rx="8" style="fill:rgb(241, 239, 232);stroke:rgb(95, 94, 90);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="275" y="148" text-anchor="middle" style="fill:rgb(68, 68, 65);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:14px;font-weight:400;text-anchor:middle;dominant-baseline:auto">indispo</text>
|
|
||||||
<text x="275" y="162" text-anchor="middle" style="fill:rgb(95, 94, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">par défaut</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="80" y="200" width="120" height="44" rx="8" style="fill:rgb(230, 241, 251);stroke:rgb(24, 95, 165);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="140" y="222" text-anchor="middle" style="fill:rgb(12, 68, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">convert-im6</text>
|
|
||||||
<text x="140" y="238" text-anchor="middle" style="fill:rgb(24, 95, 165);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">tu tapes</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<line x1="200" y1="222" x2="240" y2="222" stroke="var(--color-text-secondary)" stroke-width="0.5" marker-end="url(#arrow)" style="fill:none;stroke:rgb(115, 114, 108);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="240" y="200" width="80" height="44" rx="8" style="fill:rgb(225, 245, 238);stroke:rgb(15, 110, 86);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="280" y="220" text-anchor="middle" style="fill:rgb(8, 80, 65);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">IM 6</text>
|
|
||||||
<text x="280" y="234" text-anchor="middle" style="fill:rgb(15, 110, 86);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">.q16</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<rect x="350" y="90" width="290" height="180" rx="8" fill="none" stroke="var(--color-border-tertiary)" stroke-width="0.5" stroke-dasharray="4 3" style="fill:none;stroke:rgba(31, 30, 29, 0.15);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-dasharray:4px, 3px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="370" y="112" style="fill:rgb(61, 61, 58);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto">Debian 13 (trixie)</text>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="380" y="130" width="120" height="44" rx="8" style="fill:rgb(230, 241, 251);stroke:rgb(24, 95, 165);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="440" y="152" text-anchor="middle" style="fill:rgb(12, 68, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">convert</text>
|
|
||||||
<text x="440" y="168" text-anchor="middle" style="fill:rgb(24, 95, 165);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">symlink</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<line x1="500" y1="152" x2="540" y2="152" stroke="var(--color-text-secondary)" stroke-width="0.5" marker-end="url(#arrow)" style="fill:none;stroke:rgb(115, 114, 108);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="540" y="130" width="80" height="44" rx="8" style="fill:rgb(250, 238, 218);stroke:rgb(133, 79, 11);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="580" y="150" text-anchor="middle" style="fill:rgb(99, 56, 6);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">IM 7</text>
|
|
||||||
<text x="580" y="164" text-anchor="middle" style="fill:rgb(133, 79, 11);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">.q16</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="380" y="200" width="120" height="44" rx="8" style="fill:rgb(230, 241, 251);stroke:rgb(24, 95, 165);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="440" y="222" text-anchor="middle" style="fill:rgb(12, 68, 124);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">magick</text>
|
|
||||||
<text x="440" y="238" text-anchor="middle" style="fill:rgb(24, 95, 165);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">recommandé</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<line x1="500" y1="222" x2="540" y2="222" stroke="var(--color-text-secondary)" stroke-width="0.5" marker-end="url(#arrow)" style="fill:none;stroke:rgb(115, 114, 108);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="540" y="200" width="80" height="44" rx="8" style="fill:rgb(250, 238, 218);stroke:rgb(133, 79, 11);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="580" y="220" text-anchor="middle" style="fill:rgb(99, 56, 6);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">IM 7</text>
|
|
||||||
<text x="580" y="234" text-anchor="middle" style="fill:rgb(133, 79, 11);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">.q16</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<text x="340" y="310" text-anchor="middle" style="fill:rgb(20, 20, 19);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">Pourquoi ce renommage ?</text>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="100" y="335" width="200" height="80" rx="8" style="fill:rgb(225, 245, 238);stroke:rgb(15, 110, 86);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="200" y="362" text-anchor="middle" style="fill:rgb(8, 80, 65);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">ImageMagick 6</text>
|
|
||||||
<text x="200" y="382" text-anchor="middle" style="fill:rgb(15, 110, 86);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">convert, identify,</text>
|
|
||||||
<text x="200" y="398" text-anchor="middle" style="fill:rgb(15, 110, 86);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">mogrify, composite</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="380" y="335" width="200" height="80" rx="8" style="fill:rgb(250, 238, 218);stroke:rgb(133, 79, 11);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="480" y="362" text-anchor="middle" style="fill:rgb(99, 56, 6);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:auto">ImageMagick 7</text>
|
|
||||||
<text x="480" y="382" text-anchor="middle" style="fill:rgb(133, 79, 11);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">magick</text>
|
|
||||||
<text x="480" y="398" text-anchor="middle" style="fill:rgb(133, 79, 11);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">(commande unique)</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<text x="340" y="365" text-anchor="middle" style="fill:rgb(20, 20, 19);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:14px;font-weight:400;text-anchor:middle;dominant-baseline:auto">+</text>
|
|
||||||
<text x="340" y="385" text-anchor="middle" style="fill:rgb(61, 61, 58);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">noms en</text>
|
|
||||||
<text x="340" y="400" text-anchor="middle" style="fill:rgb(61, 61, 58);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">conflit</text>
|
|
||||||
|
|
||||||
<rect x="100" y="445" width="480" height="50" rx="8" fill="var(--color-background-secondary)" stroke="var(--color-border-tertiary)" stroke-width="0.5" style="fill:rgb(245, 244, 237);stroke:rgba(31, 30, 29, 0.15);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="340" y="467" text-anchor="middle" style="fill:rgb(20, 20, 19);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:14px;font-weight:400;text-anchor:middle;dominant-baseline:auto">Solution Debian : suffixer chaque binaire</text>
|
|
||||||
<text x="340" y="484" text-anchor="middle" style="fill:rgb(61, 61, 58);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">convert-im6.q16 / magick-im7.q16 → cohabitation possible</text>
|
|
||||||
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 23 KiB |
@@ -1,115 +0,0 @@
|
|||||||
<svg width="100%" viewBox="0 0 690 490" role="img" xmlns="http://www.w3.org/2000/svg" style="">
|
|
||||||
<title style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">magick : la commande unifiée d'ImageMagick 7</title>
|
|
||||||
<desc style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Illustration représentant la commande magick comme une baguette magique transformant une image source en plusieurs formats.</desc>
|
|
||||||
|
|
||||||
<defs>
|
|
||||||
<radialGradient id="bgGlow" cx="50%" cy="45%" r="60%">
|
|
||||||
<stop offset="0%" stop-color="#2a1f4d"/>
|
|
||||||
<stop offset="60%" stop-color="#1a1530"/>
|
|
||||||
<stop offset="100%" stop-color="#0f0b1f"/>
|
|
||||||
</radialGradient>
|
|
||||||
<radialGradient id="starGlow" cx="50%" cy="50%" r="50%">
|
|
||||||
<stop offset="0%" stop-color="#ffe89a" stop-opacity="1"/>
|
|
||||||
<stop offset="40%" stop-color="#f4a949" stop-opacity="0.8"/>
|
|
||||||
<stop offset="100%" stop-color="#f4a949" stop-opacity="0"/>
|
|
||||||
</radialGradient>
|
|
||||||
<linearGradient id="wand" x1="0" y1="0" x2="1" y2="1">
|
|
||||||
<stop offset="0%" stop-color="#3d2a5c"/>
|
|
||||||
<stop offset="50%" stop-color="#5a3d7a"/>
|
|
||||||
<stop offset="100%" stop-color="#2a1a3d"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="wandTip" x1="0" y1="0" x2="1" y2="1">
|
|
||||||
<stop offset="0%" stop-color="#f4d489"/>
|
|
||||||
<stop offset="100%" stop-color="#c89149"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<rect x="0" y="0" width="680" height="480" fill="url(#bgGlow)" style="stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<g opacity="0.6" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.6;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<circle cx="60" cy="80" r="1" fill="#e0d4ff" style="fill:rgb(224, 212, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="140" cy="50" r="0.8" fill="#e0d4ff" style="fill:rgb(224, 212, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="220" cy="100" r="1.2" fill="#fff4d4" style="fill:rgb(255, 244, 212);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="580" cy="60" r="1" fill="#e0d4ff" style="fill:rgb(224, 212, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="620" cy="120" r="0.8" fill="#fff4d4" style="fill:rgb(255, 244, 212);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="80" cy="380" r="1" fill="#e0d4ff" style="fill:rgb(224, 212, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="600" cy="400" r="1.2" fill="#fff4d4" style="fill:rgb(255, 244, 212);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="40" cy="240" r="0.8" fill="#e0d4ff" style="fill:rgb(224, 212, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="640" cy="280" r="1" fill="#e0d4ff" style="fill:rgb(224, 212, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="180" cy="420" r="0.8" fill="#fff4d4" style="fill:rgb(255, 244, 212);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="500" cy="440" r="1" fill="#e0d4ff" style="fill:rgb(224, 212, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(120, 200)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="0" y="0" width="140" height="100" rx="6" fill="#3d2a5c" stroke="#7a5fa3" stroke-width="1.5" style="fill:rgb(61, 42, 92);stroke:rgb(122, 95, 163);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="6" width="128" height="88" rx="3" fill="#1a1530" style="fill:rgb(26, 21, 48);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<circle cx="50" cy="40" r="14" fill="#f4a949" opacity="0.9" style="fill:rgb(244, 169, 73);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.9;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 6 80 L 40 55 L 70 70 L 100 45 L 134 65 L 134 94 L 6 94 Z" fill="#5a3d7a" style="fill:rgb(90, 61, 122);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 6 85 L 50 70 L 90 80 L 134 75 L 134 94 L 6 94 Z" fill="#7a5fa3" style="fill:rgb(122, 95, 163);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<text x="70" y="115" text-anchor="middle" fill="#a08fc4" font-family="ui-monospace, monospace" font-size="11" style="fill:rgb(160, 143, 196);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:11px;font-weight:400;text-anchor:middle;dominant-baseline:auto">photo.jpg</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(280, 230)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<line x1="0" y1="0" x2="120" y2="0" stroke="url(#wand)" stroke-width="8" stroke-linecap="round" style="fill:rgb(0, 0, 0);color:rgb(0, 0, 0);stroke-width:8px;stroke-linecap:round;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="0" y1="0" x2="120" y2="0" stroke="#8a6dad" stroke-width="2" stroke-linecap="round" opacity="0.5" style="fill:rgb(0, 0, 0);stroke:rgb(138, 109, 173);color:rgb(0, 0, 0);stroke-width:2px;stroke-linecap:round;stroke-linejoin:miter;opacity:0.5;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<g transform="translate(130, 0)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<circle cx="0" cy="0" r="35" fill="url(#starGlow)" opacity="0.7" style="stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="0" cy="0" r="22" fill="url(#starGlow)" style="stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<path d="M 0 -16 L 4 -4 L 16 0 L 4 4 L 0 16 L -4 4 L -16 0 L -4 -4 Z" fill="#fff4d4" style="fill:rgb(255, 244, 212);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 0 -10 L 2.5 -2.5 L 10 0 L 2.5 2.5 L 0 10 L -2.5 2.5 L -10 0 L -2.5 -2.5 Z" fill="#ffffff" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g fill="#ffe89a" style="fill:rgb(255, 232, 154);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<circle cx="155" cy="-25" r="2" style="fill:rgb(255, 232, 154);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="170" cy="-10" r="1.5" style="fill:rgb(255, 232, 154);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="175" cy="15" r="2" style="fill:rgb(255, 232, 154);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="160" cy="30" r="1.5" style="fill:rgb(255, 232, 154);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="145" cy="-35" r="1.2" style="fill:rgb(255, 232, 154);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="190" cy="0" r="1.8" style="fill:rgb(255, 232, 154);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="180" cy="-25" r="1" style="fill:rgb(255, 232, 154);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g stroke="#ffe89a" stroke-width="1.5" stroke-linecap="round" opacity="0.7" fill="none" style="fill:none;stroke:rgb(255, 232, 154);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<path d="M 150 -15 L 153 -22" style="fill:none;stroke:rgb(255, 232, 154);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 165 -5 L 172 -8" style="fill:none;stroke:rgb(255, 232, 154);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 165 12 L 173 18" style="fill:none;stroke:rgb(255, 232, 154);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 150 22 L 152 30" style="fill:none;stroke:rgb(255, 232, 154);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(460, 130)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="0" y="0" width="100" height="50" rx="5" fill="#2d4a3d" stroke="#7fb069" stroke-width="1.5" style="fill:rgb(45, 74, 61);stroke:rgb(127, 176, 105);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="50" y="22" text-anchor="middle" fill="#7fb069" font-family="ui-monospace, monospace" font-size="11" font-weight="500" style="fill:rgb(127, 176, 105);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">PNG</text>
|
|
||||||
<text x="50" y="38" text-anchor="middle" fill="#a8d089" font-family="ui-monospace, monospace" font-size="9" style="fill:rgb(168, 208, 137);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:9px;font-weight:400;text-anchor:middle;dominant-baseline:auto">photo.png</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(460, 215)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="0" y="0" width="100" height="50" rx="5" fill="#4a3d2a" stroke="#e8b059" stroke-width="1.5" style="fill:rgb(74, 61, 42);stroke:rgb(232, 176, 89);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="50" y="22" text-anchor="middle" fill="#e8b059" font-family="ui-monospace, monospace" font-size="11" font-weight="500" style="fill:rgb(232, 176, 89);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">WEBP</text>
|
|
||||||
<text x="50" y="38" text-anchor="middle" fill="#f4d489" font-family="ui-monospace, monospace" font-size="9" style="fill:rgb(244, 212, 137);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:9px;font-weight:400;text-anchor:middle;dominant-baseline:auto">photo.webp</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(460, 300)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="0" y="0" width="100" height="50" rx="5" fill="#4a2a3d" stroke="#d47a9a" stroke-width="1.5" style="fill:rgb(74, 42, 61);stroke:rgb(212, 122, 154);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="50" y="22" text-anchor="middle" fill="#d47a9a" font-family="ui-monospace, monospace" font-size="11" font-weight="500" style="fill:rgb(212, 122, 154);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">AVIF</text>
|
|
||||||
<text x="50" y="38" text-anchor="middle" fill="#e8a4bc" font-family="ui-monospace, monospace" font-size="9" style="fill:rgb(232, 164, 188);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:9px;font-weight:400;text-anchor:middle;dominant-baseline:auto">photo.avif</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g stroke="#7a5fa3" stroke-width="1" fill="none" opacity="0.5" stroke-dasharray="3 3" style="fill:none;stroke:rgb(122, 95, 163);color:rgb(0, 0, 0);stroke-width:1px;stroke-dasharray:3px, 3px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.5;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<path d="M 425 240 Q 445 200 460 155" style="fill:none;stroke:rgb(122, 95, 163);color:rgb(0, 0, 0);stroke-width:1px;stroke-dasharray:3px, 3px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 425 240 L 460 240" style="fill:none;stroke:rgb(122, 95, 163);color:rgb(0, 0, 0);stroke-width:1px;stroke-dasharray:3px, 3px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 425 240 Q 445 280 460 325" style="fill:none;stroke:rgb(122, 95, 163);color:rgb(0, 0, 0);stroke-width:1px;stroke-dasharray:3px, 3px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<text x="340" y="60" text-anchor="middle" fill="#e0d4ff" font-family="var(--font-sans)" font-size="32" font-weight="500" letter-spacing="2" style="fill:rgb(224, 212, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:32px;font-weight:500;text-anchor:middle;dominant-baseline:auto">magick</text>
|
|
||||||
<text x="340" y="84" text-anchor="middle" fill="#a08fc4" font-family="ui-monospace, monospace" font-size="12" style="fill:rgb(160, 143, 196);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:12px;font-weight:400;text-anchor:middle;dominant-baseline:auto">one command, every format</text>
|
|
||||||
|
|
||||||
<text x="340" y="430" text-anchor="middle" fill="#7a5fa3" font-family="ui-monospace, monospace" font-size="11" style="fill:rgb(122, 95, 163);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:11px;font-weight:400;text-anchor:middle;dominant-baseline:auto">$ magick photo.jpg -resize 1600 photo.webp</text>
|
|
||||||
<text x="340" y="450" text-anchor="middle" fill="#5a4a7a" font-family="var(--font-sans)" font-size="11" style="fill:rgb(90, 74, 122);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:11px;font-weight:400;text-anchor:middle;dominant-baseline:auto">ImageMagick 7</text>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 24 KiB |
@@ -1,80 +0,0 @@
|
|||||||
# ImageMagick sur Debian : pourquoi `convert-im6` et où trouver `magick`
|
|
||||||
|
|
||||||
Si tu as déjà installé ImageMagick sur un serveur Debian, tu es probablement tombé sur cette étrangeté : la commande `convert` historique est là, mais elle s'appelle `convert-im6`. Et la commande moderne `magick`, présente partout ailleurs, semble manquer à l'appel — sauf si tu es sur Debian 13, où elle est revenue.
|
|
||||||
|
|
||||||
Le sujet est un peu plus subtil qu'il n'y paraît, et beaucoup d'explications qui circulent sur le web sont fausses (notamment celle qui prétend que `convert` entrerait en conflit avec un binaire de `util-linux` — c'est un mythe). Voilà ce qui se passe réellement.
|
|
||||||
|
|
||||||
## Un peu de contexte sur ImageMagick
|
|
||||||
|
|
||||||
ImageMagick, c'est une suite d'outils en ligne de commande pour manipuler des images : conversion de formats, redimensionnement, compression, génération de vignettes, watermarks, lecture de métadonnées… Le genre d'outil qu'on retrouve aussi bien dans un script bash de cinq lignes que dans une chaîne de traitement industrielle ou un pipeline CI.
|
|
||||||
|
|
||||||
Historiquement, la suite est composée de plusieurs binaires distincts, chacun avec son rôle : `convert` pour la conversion, `identify` pour lire les métadonnées, `mogrify` pour le traitement par lot, `composite` pour combiner des images, `montage` pour les planches. C'est l'architecture d'**ImageMagick 6**, la version qui a régné en maître pendant une bonne quinzaine d'années.
|
|
||||||
|
|
||||||
Depuis 2016, **ImageMagick 7** est disponible. Le grand changement, c'est qu'il unifie tout derrière une seule commande : `magick`. Les anciennes commandes deviennent des sous-commandes (`magick convert`, `magick identify`, etc.), même si pour la rétrocompatibilité un binaire `magick` peut continuer à se comporter comme `convert` quand on l'appelle avec une syntaxe d'IM6.
|
|
||||||
|
|
||||||
## Pourquoi le suffixe `-im6` sur Debian
|
|
||||||
|
|
||||||
C'est ici que beaucoup d'articles racontent n'importe quoi. La vraie raison n'a rien à voir avec un conflit avec `util-linux` — je l'ai vérifié, aucun paquet système ne fournit de commande `convert`. Tu peux le vérifier toi-même : `dpkg -S /usr/bin/convert` ne renvoie rien qui vienne de util-linux.
|
|
||||||
|
|
||||||
La vraie raison est plus prosaïque. Pendant des années, Debian a voulu pouvoir **packager IM6 et IM7 en parallèle** dans la même distribution, pour permettre une transition en douceur. Le souci, c'est que les deux versions fournissent des binaires aux mêmes noms (`convert`, `identify`, `mogrify`…) avec des comportements légèrement différents. Impossible de les installer côte à côte sans renommer.
|
|
||||||
|
|
||||||
La solution adoptée par les mainteneurs Debian a été d'ajouter un suffixe explicite au nom de chaque binaire :
|
|
||||||
|
|
||||||
- les outils d'IM6 deviennent `convert-im6.q16`, `identify-im6.q16`, etc.
|
|
||||||
- les outils d'IM7 deviennent `magick-im7.q16` et compagnie
|
|
||||||
|
|
||||||
Le `.q16` indique la profondeur quantique du binaire (16 bits par canal, la valeur par défaut), et le `-im6` / `-im7` indique la version d'ImageMagick. Les noms classiques (`convert`, `magick`) ne sont alors que des symlinks gérés par `update-alternatives`, qui pointent vers la version active. C'est le même mécanisme que pour `java`, `editor`, ou `python` à une époque.
|
|
||||||
|
|
||||||
## Ce qui change entre Debian 11, 12 et 13
|
|
||||||
|
|
||||||
C'est l'autre point que la plupart des articles ratent : la situation n'est pas la même selon la version de Debian.
|
|
||||||
|
|
||||||
Sur **Debian 11 (bullseye) et 12 (bookworm)**, le paquet `imagemagick` installe IM6 (version 6.9.11.60). Tu n'as que `convert-im6` et ses copains, et `magick` n'existe pas dans les dépôts officiels (le paquet `imagemagick-7.q16` existe mais n'est pas le défaut). C'est cette situation que décrivent la plupart des tutoriels qui traînent sur le web.
|
|
||||||
|
|
||||||
Sur **Debian 13 (trixie)**, sorti en août 2025, le défaut a basculé sur IM7 (version 7.1.1.43). La commande `magick` est disponible, et `convert` est désormais un symlink vers `magick-im7.q16`. Tu peux le vérifier :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ readlink -f /usr/bin/convert
|
|
||||||
/usr/bin/magick-im7.q16
|
|
||||||
```
|
|
||||||
|
|
||||||
Autrement dit, sur Trixie, si tu écris `convert image.jpg image.png`, tu appelles en réalité IM7 sous un nom d'IM6. Ça fonctionne pour la plupart des usages, mais attention : IM7 est plus strict sur l'ordre des arguments en ligne de commande (`magick [INPUT-OPTIONS] INPUT [OUTPUT-OPTIONS] OUTPUT`), donc certains scripts anciens peuvent grogner.
|
|
||||||
|
|
||||||
## Correspondance entre les deux versions
|
|
||||||
|
|
||||||
| ImageMagick 6 (Debian 11/12) | ImageMagick 7 (Debian 13) |
|
|
||||||
| ---------------------------- | ------------------------- |
|
|
||||||
| `convert-im6` | `magick` |
|
|
||||||
| `identify-im6` | `magick identify` |
|
|
||||||
| `mogrify-im6` | `magick mogrify` |
|
|
||||||
| `composite-im6` | `magick composite` |
|
|
||||||
|
|
||||||
Pour les cas simples, le comportement est identique. Une commande de redimensionnement classique passe sans modification :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Debian 11/12
|
|
||||||
convert-im6 photo.jpg -resize 1600x1600 photo_reduite.jpg
|
|
||||||
|
|
||||||
# Debian 13
|
|
||||||
magick photo.jpg -resize 1600x1600 photo_reduite.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
## Faut-il s'inquiéter sur un serveur en production ?
|
|
||||||
|
|
||||||
Si tu administres une machine Debian 12 ou plus ancienne, non. IM6 est toujours activement maintenu pour les CVE (les correctifs sont régulièrement backportés dans les paquets stable), et la plupart des scripts existants continueront de fonctionner. Le `-im6` dans le nom du binaire est juste du marquage, pas une dépréciation.
|
|
||||||
|
|
||||||
Si tu migres vers Debian 13, prévois un peu de temps pour relire tes scripts. Les pièges classiques :
|
|
||||||
|
|
||||||
- l'ordre des options qui devient plus strict ;
|
|
||||||
- quelques comportements de couleur et d'alpha qui ont changé entre les deux versions, notamment sur les opérations chaînées ;
|
|
||||||
- le fichier `policy.xml` qui a déménagé : `/etc/ImageMagick-6/` devient `/etc/ImageMagick-7/`. Si tu avais assoupli les restrictions sur les PDF ou PostScript (un grand classique), il faut reporter la modification.
|
|
||||||
|
|
||||||
Pour un projet PHP comme les tiens, l'extension Imagick côté PHP est sensible à cette transition : la version compilée doit correspondre à la version d'IM installée, sinon `pecl install imagick` échoue. Sur Trixie, c'est IM7 qu'il faut lier.
|
|
||||||
|
|
||||||
## En pratique
|
|
||||||
|
|
||||||
Sur Debian 11/12, utilise `convert-im6`, `identify-im6`, etc. C'est la convention locale, pas une version dégradée. Si tu veux `magick` malgré tout, tu peux installer le paquet `imagemagick-7.q16` (présent dans les dépôts depuis bookworm) et basculer les alternatives manuellement, mais ce n'est presque jamais nécessaire.
|
|
||||||
|
|
||||||
Sur Debian 13, utilise `magick` directement. La commande `convert` reste disponible par compatibilité, mais elle pointe en réalité vers IM7 — autant utiliser le nom officiel.
|
|
||||||
|
|
||||||
Et dans tous les cas, évite les alias globaux qui réécrivent `convert` : ça finit toujours par mordre quelqu'un, soit toi dans six mois, soit le prochain qui reprendra le serveur.
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "0e0b8d1d-3352-4ab7-bc70-7bc1f02ee485",
|
|
||||||
"slug": "imagemagick-sur-debian-pourquoi-convert-im6-et-ou-trouver-magick",
|
|
||||||
"title": "ImageMagick sur Debian : pourquoi `convert-im6` et où trouver `magick`",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2025-12-28 15:32",
|
|
||||||
"created_at": "2025-12-28 15:32:01",
|
|
||||||
"updated_at": "2026-05-12 00:29:00",
|
|
||||||
"revisions": [
|
|
||||||
{
|
|
||||||
"n": 1,
|
|
||||||
"date": "2026-05-12 00:29:00",
|
|
||||||
"comment": "",
|
|
||||||
"title": "ImageMagick sur Debian : pourquoi `convert-im6` et où trouver `magick`"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cover": "cover.svg",
|
|
||||||
"files_meta": {
|
|
||||||
"45de275c5f174797-24653.svg": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"7ee02eb092b334c4-23450.svg": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"external_links": [],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "",
|
|
||||||
"og_image": "",
|
|
||||||
"category": "linux"
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
Si tu as déjà installé ImageMagick sur un serveur Debian, tu es probablement tombé sur cette étrangeté : la commande `convert` historique est là, mais elle s'appelle `convert-im6`. Et la commande moderne `magick`, présente partout ailleurs, semble manquer à l'appel — sauf si tu es sur Debian 13, où elle est revenue.
|
|
||||||
|
|
||||||
Le sujet est un peu plus subtil qu'il n'y paraît, et beaucoup d'explications qui circulent sur le web sont fausses (notamment celle qui prétend que `convert` entrerait en conflit avec un binaire de `util-linux` — c'est un mythe). Voilà ce qui se passe réellement.
|
|
||||||
|
|
||||||
## Un peu de contexte sur ImageMagick
|
|
||||||
|
|
||||||
ImageMagick, c'est une suite d'outils en ligne de commande pour manipuler des images : conversion de formats, redimensionnement, compression, génération de vignettes, watermarks, lecture de métadonnées… Le genre d'outil qu'on retrouve aussi bien dans un script bash de cinq lignes que dans une chaîne de traitement industrielle ou un pipeline CI.
|
|
||||||
|
|
||||||
Historiquement, la suite est composée de plusieurs binaires distincts, chacun avec son rôle : `convert` pour la conversion, `identify` pour lire les métadonnées, `mogrify` pour le traitement par lot, `composite` pour combiner des images, `montage` pour les planches. C'est l'architecture d'**ImageMagick 6**, la version qui a régné en maître pendant une bonne quinzaine d'années.
|
|
||||||
|
|
||||||
Depuis 2016, **ImageMagick 7** est disponible. Le grand changement, c'est qu'il unifie tout derrière une seule commande : `magick`. Les anciennes commandes deviennent des sous-commandes (`magick convert`, `magick identify`, etc.), même si pour la rétrocompatibilité un binaire `magick` peut continuer à se comporter comme `convert` quand on l'appelle avec une syntaxe d'IM6.
|
|
||||||
|
|
||||||
## Pourquoi le suffixe `-im6` sur Debian
|
|
||||||
|
|
||||||
C'est ici que beaucoup d'articles racontent n'importe quoi. La vraie raison n'a rien à voir avec un conflit avec `util-linux` — je l'ai vérifié, aucun paquet système ne fournit de commande `convert`. Tu peux le vérifier toi-même : `dpkg -S /usr/bin/convert` ne renvoie rien qui vienne de util-linux.
|
|
||||||
|
|
||||||
La vraie raison est plus prosaïque. Pendant des années, Debian a voulu pouvoir **packager IM6 et IM7 en parallèle** dans la même distribution, pour permettre une transition en douceur. Le souci, c'est que les deux versions fournissent des binaires aux mêmes noms (`convert`, `identify`, `mogrify`…) avec des comportements légèrement différents. Impossible de les installer côte à côte sans renommer.
|
|
||||||
|
|
||||||
La solution adoptée par les mainteneurs Debian a été d'ajouter un suffixe explicite au nom de chaque binaire :
|
|
||||||
|
|
||||||
- les outils d'IM6 deviennent `convert-im6.q16`, `identify-im6.q16`, etc.
|
|
||||||
- les outils d'IM7 deviennent `magick-im7.q16` et compagnie
|
|
||||||
|
|
||||||
Le `.q16` indique la profondeur quantique du binaire (16 bits par canal, la valeur par défaut), et le `-im6` / `-im7` indique la version d'ImageMagick. Les noms classiques (`convert`, `magick`) ne sont alors que des symlinks gérés par `update-alternatives`, qui pointent vers la version active. C'est le même mécanisme que pour `java`, `editor`, ou `python` à une époque.
|
|
||||||
|
|
||||||
## Ce qui change entre Debian 11, 12 et 13
|
|
||||||
|
|
||||||
C'est l'autre point que la plupart des articles ratent : la situation n'est pas la même selon la version de Debian.
|
|
||||||
|
|
||||||
Sur **Debian 11 (bullseye) et 12 (bookworm)**, le paquet `imagemagick` installe IM6 (version 6.9.11.60). Tu n'as que `convert-im6` et ses copains, et `magick` n'existe pas dans les dépôts officiels (le paquet `imagemagick-7.q16` existe mais n'est pas le défaut). C'est cette situation que décrivent la plupart des tutoriels qui traînent sur le web.
|
|
||||||
|
|
||||||
Sur **Debian 13 (trixie)**, sorti en août 2025, le défaut a basculé sur IM7 (version 7.1.1.43). La commande `magick` est disponible, et `convert` est désormais un symlink vers `magick-im7.q16`. Tu peux le vérifier :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ readlink -f /usr/bin/convert
|
|
||||||
/usr/bin/magick-im7.q16
|
|
||||||
```
|
|
||||||
|
|
||||||
Autrement dit, sur Trixie, si tu écris `convert image.jpg image.png`, tu appelles en réalité IM7 sous un nom d'IM6. Ça fonctionne pour la plupart des usages, mais attention : IM7 est plus strict sur l'ordre des arguments en ligne de commande (`magick [INPUT-OPTIONS] INPUT [OUTPUT-OPTIONS] OUTPUT`), donc certains scripts anciens peuvent grogner.
|
|
||||||
|
|
||||||
## Correspondance entre les deux versions
|
|
||||||
|
|
||||||
| ImageMagick 6 (Debian 11/12) | ImageMagick 7 (Debian 13) |
|
|
||||||
| ---------------------------- | ------------------------- |
|
|
||||||
| `convert-im6` | `magick` |
|
|
||||||
| `identify-im6` | `magick identify` |
|
|
||||||
| `mogrify-im6` | `magick mogrify` |
|
|
||||||
| `composite-im6` | `magick composite` |
|
|
||||||
|
|
||||||
Pour les cas simples, le comportement est identique. Une commande de redimensionnement classique passe sans modification :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Debian 11/12
|
|
||||||
convert-im6 photo.jpg -resize 1600x1600 photo_reduite.jpg
|
|
||||||
|
|
||||||
# Debian 13
|
|
||||||
magick photo.jpg -resize 1600x1600 photo_reduite.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
## Faut-il s'inquiéter sur un serveur en production ?
|
|
||||||
|
|
||||||
Si tu administres une machine Debian 12 ou plus ancienne, non. IM6 est toujours activement maintenu pour les CVE (les correctifs sont régulièrement backportés dans les paquets stable), et la plupart des scripts existants continueront de fonctionner. Le `-im6` dans le nom du binaire est juste du marquage, pas une dépréciation.
|
|
||||||
|
|
||||||
Si tu migres vers Debian 13, prévois un peu de temps pour relire tes scripts. Les pièges classiques :
|
|
||||||
|
|
||||||
- l'ordre des options qui devient plus strict ;
|
|
||||||
- quelques comportements de couleur et d'alpha qui ont changé entre les deux versions, notamment sur les opérations chaînées ;
|
|
||||||
- le fichier `policy.xml` qui a déménagé : `/etc/ImageMagick-6/` devient `/etc/ImageMagick-7/`. Si tu avais assoupli les restrictions sur les PDF ou PostScript (un grand classique), il faut reporter la modification.
|
|
||||||
|
|
||||||
Pour un projet PHP comme les tiens, l'extension Imagick côté PHP est sensible à cette transition : la version compilée doit correspondre à la version d'IM installée, sinon `pecl install imagick` échoue. Sur Trixie, c'est IM7 qu'il faut lier.
|
|
||||||
|
|
||||||
## En pratique
|
|
||||||
|
|
||||||
Sur Debian 11/12, utilise `convert-im6`, `identify-im6`, etc. C'est la convention locale, pas une version dégradée. Si tu veux `magick` malgré tout, tu peux installer le paquet `imagemagick-7.q16` (présent dans les dépôts depuis bookworm) et basculer les alternatives manuellement, mais ce n'est presque jamais nécessaire.
|
|
||||||
|
|
||||||
Sur Debian 13, utilise `magick` directement. La commande `convert` reste disponible par compatibilité, mais elle pointe en réalité vers IM7 — autant utiliser le nom officiel.
|
|
||||||
|
|
||||||
Et dans tous les cas, évite les alias globaux qui réécrivent `convert` : ça finit toujours par mordre quelqu'un, soit toi dans six mois, soit le prochain qui reprendra le serveur.
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# Clearview AI : quand l’intelligence artificielle dépasse les limites du « public »
|
|
||||||
|
|
||||||
En 2019, une start-up américaine du nom de **Clearview AI** fait irruption dans le monde de la reconnaissance faciale. Son idée paraît révolutionnaire : créer une base de données géante pour identifier n’importe qui à partir d’une simple photo. Pour nourrir son intelligence artificielle, l’entreprise collecte **des milliards d’images publiques** issues de plateformes comme **Facebook, LinkedIn, Twitter ou encore YouTube**. Chaque cliché, chaque visage devient une donnée utile à l’algorithme — mais sans que les personnes concernées n’en soient informées, ni qu’elles aient donné leur **consentement**.
|
|
||||||
|
|
||||||
Rapidement, l’ampleur du projet suscite la controverse. Des journalistes révèlent les pratiques de Clearview, et les autorités de protection des données s’en emparent. En **France**, la **CNIL** sanctionne l’entreprise pour traitement illégal de données biométriques. Le **régulateur britannique** fait de même, imposant des amendes et **interdisant l’usage de ces données en Europe**. Ce scandale devient un symbole : il montre que même à l’ère numérique, la **vie privée** reste un droit fondamental, et que la technologie ne peut pas s’affranchir des règles éthiques et juridiques.
|
|
||||||
|
|
||||||
L’affaire Clearview soulève un **enjeu majeur** : la frontière entre le **contenu public** et le **contenu libre d’usage**. Ce n’est pas parce qu’une image est visible en ligne qu’elle peut être exploitée pour entraîner une IA. Cette logique s’applique aussi à des plateformes comme **LinkedIn** : les informations qu’on y partage publiquement ne deviennent pas pour autant un matériau libre pour les algorithmes.
|
|
||||||
|
|
||||||
Ainsi, Clearview AI incarne à la fois la puissance et le danger de l’intelligence artificielle : un outil capable du meilleur, mais aussi du pire, lorsqu’il franchit la ligne fragile entre innovation et intrusion.
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "0eaa0f05-7f48-47b4-91d3-3ba4ac80fe50",
|
|
||||||
"slug": "clearview-ai-quand-l-intelligence-artificielle-depasse-les-limites-du-public",
|
|
||||||
"title": "Clearview AI : quand l’intelligence artificielle dépasse les limites du « public »",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2025-11-05 07:15:36",
|
|
||||||
"created_at": "2025-11-05 07:15:36",
|
|
||||||
"updated_at": "2025-11-05 07:15:36",
|
|
||||||
"revisions": [],
|
|
||||||
"cover": "cover.jpg",
|
|
||||||
"category": "actualité"
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 1.6 MiB |
@@ -1,143 +0,0 @@
|
|||||||
<svg width="100%" viewBox="0 0 690 490" role="img" xmlns="http://www.w3.org/2000/svg" style="">
|
|
||||||
<title style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Auto-héberger son serveur mail en 2026</title>
|
|
||||||
<desc style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Illustration de couverture pour un dossier technique sur l'auto-hébergement d'un serveur mail. Une grande enveloppe stylisée vue de face occupe le centre, avec un cachet rouge "@" et un timbre figurant un serveur. Le titre s'affiche au-dessus.</desc>
|
|
||||||
|
|
||||||
<defs>
|
|
||||||
<marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
|
||||||
<path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</marker>
|
|
||||||
<pattern id="diag" patternUnits="userSpaceOnUse" width="6" height="6" patternTransform="rotate(45)">
|
|
||||||
<line x1="0" y1="0" x2="0" y2="6" stroke="#1a3a5c" stroke-width="1.5"/>
|
|
||||||
</pattern>
|
|
||||||
<mask id="imagine-text-gaps-0383bf" maskUnits="userSpaceOnUse"><rect x="0" y="0" width="690" height="490" fill="white"/><rect x="148.75833129882812" y="72.75" width="382.48333740234375" height="17.75" fill="black" rx="2"/><rect x="175.93333435058594" y="89.25" width="326.9666748046875" height="52.75" fill="black" rx="2"/><rect x="178.8333282470703" y="131.25" width="323.3999938964844" height="52.75" fill="black" rx="2"/><rect x="192.73333740234375" y="195.5" width="294.5333251953125" height="20.25" fill="black" rx="2"/><rect x="8.096875190734863" y="47.4375" width="39.80624771118164" height="12.75" fill="black" rx="2"/><rect x="385.5289306640625" y="253.29542541503906" width="88.92898559570312" height="41.43055725097656" fill="black" rx="2"/><rect x="390.1529541015625" y="300.71087646484375" width="79.70211791992188" height="34.238037109375" fill="black" rx="2"/><rect x="-21.399999618530273" y="-26.75" width="42.79999923706055" height="46.5" fill="black" rx="2"/><rect x="206" y="355.5" width="24.866666793823242" height="20.25" fill="black" rx="2"/><rect x="36" y="460" width="111.19999694824219" height="16.5" fill="black" rx="2"/><rect x="536.0166625976562" y="460" width="107.98332977294922" height="16.5" fill="black" rx="2"/></mask></defs>
|
|
||||||
|
|
||||||
<!-- Fond papier -->
|
|
||||||
<rect x="0" y="0" width="680" height="480" fill="#f5efe0" style="fill:rgb(245, 239, 224);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<!-- Texture papier : grains discrets -->
|
|
||||||
<g opacity="0.08" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.08;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<circle cx="80" cy="60" r="1" fill="#3a2a18" style="fill:rgb(58, 42, 24);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="230" cy="40" r="1" fill="#3a2a18" style="fill:rgb(58, 42, 24);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="450" cy="90" r="1" fill="#3a2a18" style="fill:rgb(58, 42, 24);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="600" cy="50" r="1" fill="#3a2a18" style="fill:rgb(58, 42, 24);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="120" cy="430" r="1" fill="#3a2a18" style="fill:rgb(58, 42, 24);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="380" cy="455" r="1" fill="#3a2a18" style="fill:rgb(58, 42, 24);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="560" cy="440" r="1" fill="#3a2a18" style="fill:rgb(58, 42, 24);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="40" cy="240" r="1" fill="#3a2a18" style="fill:rgb(58, 42, 24);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="650" cy="280" r="1" fill="#3a2a18" style="fill:rgb(58, 42, 24);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Filets éditoriaux haut -->
|
|
||||||
<line x1="40" y1="58" x2="640" y2="58" stroke="#1a1a1a" stroke-width="2" mask="url(#imagine-text-gaps-0383bf)" style="fill:rgb(0, 0, 0);stroke:rgb(26, 26, 26);color:rgb(0, 0, 0);stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="40" y1="64" x2="640" y2="64" stroke="#1a1a1a" stroke-width="0.5" style="fill:rgb(0, 0, 0);stroke:rgb(26, 26, 26);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<!-- Surtitre -->
|
|
||||||
<text x="340" y="86" text-anchor="middle" font-family="Georgia, serif" font-size="11" letter-spacing="4" fill="#7a1a1a" font-weight="700" style="fill:rgb(122, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Georgia, serif;font-size:11px;font-weight:700;text-anchor:middle;dominant-baseline:auto">DOSSIER · CYBERSOUVERAINETÉ · 2026</text>
|
|
||||||
|
|
||||||
<!-- Titre principal -->
|
|
||||||
<text x="340" y="130" text-anchor="middle" font-family="Georgia, serif" font-size="42" font-weight="700" fill="#1a1a1a" style="fill:rgb(26, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Georgia, serif;font-size:42px;font-weight:700;text-anchor:middle;dominant-baseline:auto">Auto-héberger</text>
|
|
||||||
<text x="340" y="172" text-anchor="middle" font-family="Georgia, serif" font-size="42" font-style="italic" font-weight="400" fill="#1a1a1a" style="fill:rgb(26, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Georgia, serif;font-size:42px;font-weight:400;font-style:italic;text-anchor:middle;dominant-baseline:auto">son serveur mail</text>
|
|
||||||
|
|
||||||
<!-- Filet sous le titre -->
|
|
||||||
<line x1="280" y1="190" x2="400" y2="190" stroke="#7a1a1a" stroke-width="1.5" style="fill:rgb(0, 0, 0);stroke:rgb(122, 26, 26);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<!-- Sous-titre -->
|
|
||||||
<text x="340" y="210" text-anchor="middle" font-family="Georgia, serif" font-size="13" font-style="italic" fill="#5a4a3a" style="fill:rgb(90, 74, 58);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Georgia, serif;font-size:13px;font-weight:400;font-style:italic;text-anchor:middle;dominant-baseline:auto">survivre aux règles de Gmail, Outlook et consorts</text>
|
|
||||||
|
|
||||||
<!-- ============ ENVELOPPE ============ -->
|
|
||||||
<!-- Ombre portée (offset solide, pas de blur) -->
|
|
||||||
<rect x="186" y="246" width="308" height="180" fill="#2a2418" opacity="0.15" style="fill:rgb(42, 36, 24);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.15;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<!-- Corps de l'enveloppe -->
|
|
||||||
<rect x="180" y="240" width="308" height="180" fill="#fbf6e8" stroke="#1a1a1a" stroke-width="2" style="fill:rgb(251, 246, 232);stroke:rgb(26, 26, 26);color:rgb(0, 0, 0);stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<!-- Rabat triangulaire ouvert vers l'avant -->
|
|
||||||
<polygon points="180,240 488,240 334,346" fill="#ede4cc" stroke="#1a1a1a" stroke-width="2" style="fill:rgb(237, 228, 204);stroke:rgb(26, 26, 26);color:rgb(0, 0, 0);stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- Léger ombrage du rabat -->
|
|
||||||
<polygon points="180,240 488,240 334,346" fill="#1a1a1a" opacity="0.04" style="fill:rgb(26, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.04;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<!-- Ligne intérieure de l'enveloppe (V inversé) -->
|
|
||||||
<polyline points="180,420 334,346 488,420" fill="none" stroke="#1a1a1a" stroke-width="0.8" opacity="0.4" style="fill:none;stroke:rgb(26, 26, 26);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<!-- ===== TIMBRE en haut à droite ===== -->
|
|
||||||
<g transform="translate(420, 252)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<!-- Bord dentelé du timbre : ligne de petits cercles autour -->
|
|
||||||
<rect x="0" y="0" width="56" height="64" fill="#7a1a1a" style="fill:rgb(122, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- Crénelage : petites encoches blanches sur les bords -->
|
|
||||||
<g fill="#fbf6e8" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<circle cx="0" cy="8" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><circle cx="0" cy="20" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><circle cx="0" cy="32" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><circle cx="0" cy="44" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><circle cx="0" cy="56" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="56" cy="8" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><circle cx="56" cy="20" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><circle cx="56" cy="32" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><circle cx="56" cy="44" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><circle cx="56" cy="56" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="8" cy="0" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><circle cx="20" cy="0" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><circle cx="32" cy="0" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><circle cx="44" cy="0" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="8" cy="64" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><circle cx="20" cy="64" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><circle cx="32" cy="64" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/><circle cx="44" cy="64" r="2.5" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
<!-- Intérieur du timbre -->
|
|
||||||
<rect x="4" y="4" width="48" height="56" fill="#7a1a1a" stroke="#fbf6e8" stroke-width="0.8" style="fill:rgb(122, 26, 26);stroke:rgb(251, 246, 232);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- Petit serveur (rack) dans le timbre -->
|
|
||||||
<rect x="14" y="14" width="28" height="32" fill="#fbf6e8" stroke="#fbf6e8" stroke-width="0.5" style="fill:rgb(251, 246, 232);stroke:rgb(251, 246, 232);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="14" y1="22" x2="42" y2="22" stroke="#7a1a1a" stroke-width="1.2" style="fill:rgb(0, 0, 0);stroke:rgb(122, 26, 26);color:rgb(0, 0, 0);stroke-width:1.2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="14" y1="30" x2="42" y2="30" stroke="#7a1a1a" stroke-width="1.2" style="fill:rgb(0, 0, 0);stroke:rgb(122, 26, 26);color:rgb(0, 0, 0);stroke-width:1.2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="14" y1="38" x2="42" y2="38" stroke="#7a1a1a" stroke-width="1.2" style="fill:rgb(0, 0, 0);stroke:rgb(122, 26, 26);color:rgb(0, 0, 0);stroke-width:1.2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- LEDs -->
|
|
||||||
<circle cx="18" cy="18" r="0.8" fill="#7a1a1a" style="fill:rgb(122, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="38" cy="18" r="0.8" fill="#7a1a1a" style="fill:rgb(122, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="18" cy="26" r="0.8" fill="#7a1a1a" style="fill:rgb(122, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="38" cy="26" r="0.8" fill="#7a1a1a" style="fill:rgb(122, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="18" cy="34" r="0.8" fill="#7a1a1a" style="fill:rgb(122, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="38" cy="34" r="0.8" fill="#7a1a1a" style="fill:rgb(122, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- Valeur faciale -->
|
|
||||||
<text x="28" y="56" text-anchor="middle" font-family="Georgia, serif" font-size="7" font-weight="700" fill="#fbf6e8" style="fill:rgb(251, 246, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Georgia, serif;font-size:7px;font-weight:700;text-anchor:middle;dominant-baseline:auto">SMTP 25</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- ===== CACHET DE LA POSTE (oblitération circulaire) ===== -->
|
|
||||||
<g transform="translate(430, 296)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<!-- Cercles concentriques -->
|
|
||||||
<circle cx="0" cy="0" r="38" fill="none" stroke="#7a1a1a" stroke-width="1.5" opacity="0.85" style="fill:none;stroke:rgb(122, 26, 26);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.85;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="0" cy="0" r="32" fill="none" stroke="#7a1a1a" stroke-width="0.8" opacity="0.85" style="fill:none;stroke:rgb(122, 26, 26);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.85;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- Texte circulaire haut : on simule avec des tspans positionnés -->
|
|
||||||
<text font-family="Georgia, serif" font-size="6" font-weight="700" fill="#7a1a1a" opacity="0.85" letter-spacing="1.5" style="fill:rgb(122, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.85;font-family:Georgia, serif;font-size:6px;font-weight:700;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<textPath href="#topArc" startOffset="50%" text-anchor="middle" style="fill:rgb(122, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Georgia, serif;font-size:6px;font-weight:700;text-anchor:middle;dominant-baseline:auto">AUTO-HÉBERGÉ · 2026</textPath>
|
|
||||||
</text>
|
|
||||||
<text font-family="Georgia, serif" font-size="6" font-weight="700" fill="#7a1a1a" opacity="0.85" letter-spacing="1.5" style="fill:rgb(122, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.85;font-family:Georgia, serif;font-size:6px;font-weight:700;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<textPath href="#bottomArc" startOffset="50%" text-anchor="middle" style="fill:rgb(122, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Georgia, serif;font-size:6px;font-weight:700;text-anchor:middle;dominant-baseline:auto">SPF · DKIM · DMARC</textPath>
|
|
||||||
</text>
|
|
||||||
<!-- Le grand @ au centre -->
|
|
||||||
<text x="0" y="9" text-anchor="middle" font-family="Georgia, serif" font-size="36" font-weight="700" fill="#7a1a1a" opacity="0.9" style="fill:rgb(122, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.9;font-family:Georgia, serif;font-size:36px;font-weight:700;text-anchor:middle;dominant-baseline:auto">@</text>
|
|
||||||
<!-- Lignes d'oblitération qui sortent du cercle -->
|
|
||||||
<line x1="-60" y1="-12" x2="-38" y2="-8" stroke="#7a1a1a" stroke-width="1.5" opacity="0.7" style="fill:rgb(0, 0, 0);stroke:rgb(122, 26, 26);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="-62" y1="-4" x2="-38" y2="-2" stroke="#7a1a1a" stroke-width="1.5" opacity="0.7" style="fill:rgb(0, 0, 0);stroke:rgb(122, 26, 26);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="-60" y1="4" x2="-38" y2="4" stroke="#7a1a1a" stroke-width="1.5" opacity="0.7" style="fill:rgb(0, 0, 0);stroke:rgb(122, 26, 26);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="-58" y1="12" x2="-38" y2="10" stroke="#7a1a1a" stroke-width="1.5" opacity="0.7" style="fill:rgb(0, 0, 0);stroke:rgb(122, 26, 26);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="38" y1="-8" x2="62" y2="-12" stroke="#7a1a1a" stroke-width="1.5" opacity="0.7" style="fill:rgb(0, 0, 0);stroke:rgb(122, 26, 26);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="38" y1="-2" x2="64" y2="-4" stroke="#7a1a1a" stroke-width="1.5" opacity="0.7" style="fill:rgb(0, 0, 0);stroke:rgb(122, 26, 26);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="38" y1="4" x2="62" y2="6" stroke="#7a1a1a" stroke-width="1.5" opacity="0.7" style="fill:rgb(0, 0, 0);stroke:rgb(122, 26, 26);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="38" y1="10" x2="60" y2="14" stroke="#7a1a1a" stroke-width="1.5" opacity="0.7" style="fill:rgb(0, 0, 0);stroke:rgb(122, 26, 26);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Chemins invisibles pour le texte circulaire du cachet -->
|
|
||||||
<defs>
|
|
||||||
<path id="topArc" d="M 395,296 A 35,35 0 0 1 465,296" fill="none"/>
|
|
||||||
<path id="bottomArc" d="M 395,296 A 35,35 0 0 0 465,296" fill="none"/>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- ===== Adresse manuscrite sur l'enveloppe ===== -->
|
|
||||||
<!-- "À" stylisé -->
|
|
||||||
<text x="210" y="370" font-family="Georgia, serif" font-size="13" font-style="italic" fill="#1a1a1a" style="fill:rgb(26, 26, 26);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Georgia, serif;font-size:13px;font-weight:400;font-style:italic;text-anchor:start;dominant-baseline:auto">À :</text>
|
|
||||||
<!-- Lignes d'adresse stylisées (traits horizontaux qui simulent l'écriture) -->
|
|
||||||
<line x1="230" y1="370" x2="380" y2="370" stroke="#1a1a1a" stroke-width="0.8" mask="url(#imagine-text-gaps-0383bf)" style="fill:rgb(0, 0, 0);stroke:rgb(26, 26, 26);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="230" y1="386" x2="360" y2="386" stroke="#1a1a1a" stroke-width="0.8" style="fill:rgb(0, 0, 0);stroke:rgb(26, 26, 26);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="230" y1="402" x2="320" y2="402" stroke="#1a1a1a" stroke-width="0.8" style="fill:rgb(0, 0, 0);stroke:rgb(26, 26, 26);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<!-- ===== Marque "PAR AVION / VIA SMTP" en bas à gauche ===== -->
|
|
||||||
<g transform="translate(196, 396)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="0" y="0" width="16" height="14" fill="url(#diag)" style="stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="0" width="16" height="14" fill="none" stroke="#1a3a5c" stroke-width="0.8" style="fill:none;stroke:rgb(26, 58, 92);color:rgb(0, 0, 0);stroke-width:0.8px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- ============ Filet bas + mention ============ -->
|
|
||||||
<line x1="40" y1="448" x2="640" y2="448" stroke="#1a1a1a" stroke-width="0.5" style="fill:rgb(0, 0, 0);stroke:rgb(26, 26, 26);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="40" y1="454" x2="640" y2="454" stroke="#1a1a1a" stroke-width="2" style="fill:rgb(0, 0, 0);stroke:rgb(26, 26, 26);color:rgb(0, 0, 0);stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<text x="40" y="472" font-family="Georgia, serif" font-size="10" font-style="italic" fill="#5a4a3a" style="fill:rgb(90, 74, 58);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Georgia, serif;font-size:10px;font-weight:400;font-style:italic;text-anchor:start;dominant-baseline:auto">RFC 821 · 1982 — 2026</text>
|
|
||||||
<text x="640" y="472" text-anchor="end" font-family="Georgia, serif" font-size="10" font-style="italic" fill="#5a4a3a" style="fill:rgb(90, 74, 58);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Georgia, serif;font-size:10px;font-weight:400;font-style:italic;text-anchor:end;dominant-baseline:auto">postmaster@localhost</text>
|
|
||||||
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 34 KiB |
@@ -1,607 +0,0 @@
|
|||||||
# Auto-héberger son serveur mail en 2026
|
|
||||||
|
|
||||||
## Survivre aux règles de Gmail, Outlook et consorts
|
|
||||||
|
|
||||||
> **Contexte** — Cet article de Clubic ([lien](https://www.clubic.com/dossier-612034-proton-mail-infomaniak-la-souverainete-de-vos-emails-reste-une-illusion-face-aux-regles-dictees-par-google.html)) rappelle une vérité technique : SMTP date de 1982, n'a aucune sécurité native, et toutes les "rustines" (SPF, DKIM, DMARC, MTA-STS, DANE) ont été conçues par Yahoo, Cisco, Microsoft, Google. Depuis février 2024 (Google) et mai 2025 (Microsoft), tout expéditeur dépassant 5000 mails/jour vers Gmail/Outlook doit configurer SPF + DKIM + DMARC, maintenir un taux de spam < 0,1 %, et fournir un lien de désinscription en un clic.
|
|
||||||
>
|
|
||||||
> Mais même en dessous de 5000/jour, ces règles s'appliquent en pratique : sans elles, ton mail finit en spam ou est rejeté. Ce dossier décrit comment monter son propre serveur mail tout en passant à travers ces filtres.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Sommaire
|
|
||||||
|
|
||||||
1. [Avant de commencer : est-ce vraiment une bonne idée ?](#1-avant-de-commencer--est-ce-vraiment-une-bonne-idée-)
|
|
||||||
2. [Prérequis techniques](#2-prérequis-techniques)
|
|
||||||
3. [Architecture cible](#3-architecture-cible)
|
|
||||||
4. [Choix du fournisseur et de l'IP](#4-choix-du-fournisseur-et-de-lip)
|
|
||||||
5. [Configuration DNS complète](#5-configuration-dns-complète)
|
|
||||||
6. [Installation du stack mail](#6-installation-du-stack-mail)
|
|
||||||
7. [SPF, DKIM, DMARC : les rustines obligatoires](#7-spf-dkim-dmarc--les-rustines-obligatoires)
|
|
||||||
8. [MTA-STS, TLS-RPT, DANE : aller plus loin](#8-mta-sts-tls-rpt-dane--aller-plus-loin)
|
|
||||||
9. [PTR (reverse DNS) et HELO](#9-ptr-reverse-dns-et-helo)
|
|
||||||
10. [Warmup d'IP : la phase la plus délicate](#10-warmup-dip--la-phase-la-plus-délicate)
|
|
||||||
11. [Postmaster Tools, SNDS, FBL](#11-postmaster-tools-snds-fbl)
|
|
||||||
12. [Liste de désinscription en un clic (RFC 8058)](#12-liste-de-désinscription-en-un-clic-rfc-8058)
|
|
||||||
13. [Anti-spam entrant et hygiène](#13-anti-spam-entrant-et-hygiène)
|
|
||||||
14. [Monitoring, logs, alertes](#14-monitoring-logs-alertes)
|
|
||||||
15. [Que faire quand Gmail rejette quand même ?](#15-que-faire-quand-gmail-rejette-quand-même-)
|
|
||||||
16. [Checklist finale avant mise en prod](#16-checklist-finale-avant-mise-en-prod)
|
|
||||||
17. [Annexes : commandes utiles](#17-annexes--commandes-utiles)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Avant de commencer : est-ce vraiment une bonne idée ?
|
|
||||||
|
|
||||||
L'auto-hébergement mail est techniquement possible, mais c'est probablement le service le plus pénible à maintenir en 2026. Avant de te lancer, lis ça :
|
|
||||||
|
|
||||||
**Ce qui marche bien en auto-hébergé :**
|
|
||||||
- Recevoir du mail (presque tout le monde te livre).
|
|
||||||
- Envoyer vers d'autres serveurs auto-hébergés ou pros bien configurés.
|
|
||||||
- Garder le contrôle sur tes données, tes alias, tes domaines.
|
|
||||||
|
|
||||||
**Ce qui est dur :**
|
|
||||||
- Envoyer vers Gmail / Outlook / Yahoo / iCloud sans atterrir en spam.
|
|
||||||
- Sortir d'une blacklist une fois dedans.
|
|
||||||
- Maintenir un score de réputation IP correct sur la durée.
|
|
||||||
- Survivre à un changement unilatéral des règles côté gros acteurs (cf. février 2024 et mai 2025).
|
|
||||||
|
|
||||||
**Stratégie réaliste recommandée :**
|
|
||||||
- Réception entrante : auto-hébergée à 100 %. Aucun risque, full contrôle.
|
|
||||||
- Envoi sortant : deux options, selon ton volume et ton tolérance au risque.
|
|
||||||
- **Option A — Pure auto-hébergée** : tu envoies directement depuis ton serveur. Faisable, mais demande un warmup, une IP propre, et un suivi continu.
|
|
||||||
- **Option B — Smart host sortant** : tu envoies via un relais réputé (un autre de tes serveurs avec une IP qui a déjà sa réputation, ou un service type Mailjet/Sendgrid/SMTP2GO en bas volume gratuit). Tes mails sortent depuis l'IP du relais, qui a déjà sa réputation faite. C'est un compromis : tu perds une partie de la souveraineté technique, mais tu gagnes énormément en délivrabilité.
|
|
||||||
|
|
||||||
Le reste du dossier suit l'option A — tout en t'expliquant comment basculer en B si nécessaire.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Prérequis techniques
|
|
||||||
|
|
||||||
| Élément | Détail |
|
|
||||||
|---|---|
|
|
||||||
| Domaine | À toi, registrar peu importe, mais avec **DNSSEC activable** (cf. §8 pour DANE). |
|
|
||||||
| Serveur | VPS ou dédié, **2 vCPU / 4 Go RAM minimum**, Debian 12+ ou Ubuntu 24.04 LTS. |
|
|
||||||
| IP fixe v4 | Indispensable. **IP "résidentielle" ou IP de datacenter récemment recyclée = exclues**. |
|
|
||||||
| IP fixe v6 | Recommandée, mais désactivable si l'IPv6 du fournisseur est blacklistée. |
|
|
||||||
| PTR / reverse DNS | **Modifiable par toi**. Si l'hébergeur ne te le permet pas, change d'hébergeur. |
|
|
||||||
| Ports | 25, 465, 587, 993, 4190 ouverts sortants ET entrants. **Le port 25 sortant est bloqué chez beaucoup d'hébergeurs grand public** (OVH résidentiel, Free, etc.) : vérifie avant. |
|
|
||||||
| TLS | Certificat valide (Let's Encrypt suffit). |
|
|
||||||
|
|
||||||
**Compétences attendues** : Linux en ligne de commande, DNS (champs A/AAAA/MX/TXT/SRV/CAA/TLSA), notion de TLS, lecture de logs `journalctl` et `/var/log/mail.log`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Architecture cible
|
|
||||||
|
|
||||||
Un stack standard, éprouvé, en logiciels libres :
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────┐
|
|
||||||
│ Internet (mails entrants/sortants) │
|
|
||||||
└─────────────────────────────────────┘
|
|
||||||
│
|
|
||||||
▼ port 25
|
|
||||||
┌────────────────┐
|
|
||||||
│ Postfix │ ← MTA (envoi/réception SMTP)
|
|
||||||
│ (SMTP / 25/587)│
|
|
||||||
└────────────────┘
|
|
||||||
│ │ │
|
|
||||||
▼ ▼ ▼
|
|
||||||
┌─────────┐ ┌─────────┐ ┌──────────┐
|
|
||||||
│ Rspamd │ │OpenDKIM │ │ Dovecot │ ← IMAP/POP + LMTP
|
|
||||||
│(spam, │ │ (signe │ │ (livrai- │
|
|
||||||
│ DKIM │ │ DKIM) │ │ son aux │
|
|
||||||
│ verif, │ └─────────┘ │ boîtes) │
|
|
||||||
│ DMARC) │ └──────────┘
|
|
||||||
└─────────┘ │
|
|
||||||
▼
|
|
||||||
Maildir / utilisateurs
|
|
||||||
```
|
|
||||||
|
|
||||||
**Composants** :
|
|
||||||
|
|
||||||
- **Postfix** : MTA. Reçoit, route, envoie le SMTP.
|
|
||||||
- **Dovecot** : serveur IMAP/POP3, livraison locale (LMTP), authentification SASL pour Postfix, gestion Sieve (filtres).
|
|
||||||
- **Rspamd** : antispam moderne, fait aussi la **vérification SPF/DKIM/DMARC entrante**, le greylisting, et — option recommandée — la **signature DKIM sortante** (en remplacement d'OpenDKIM).
|
|
||||||
- **Let's Encrypt (certbot)** : TLS.
|
|
||||||
- **(Optionnel) Roundcube ou SnappyMail** : webmail.
|
|
||||||
|
|
||||||
**Alternative tout-en-un** : [Mailcow](https://mailcow.email/) ou [Mailu](https://mailu.io/), basés sur Docker, qui empaquètent tout ça avec une interface admin. Si tu préfères ne pas tout configurer à la main, c'est légitime — la majorité des règles DNS et de délivrabilité de ce dossier restent identiques.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Choix du fournisseur et de l'IP
|
|
||||||
|
|
||||||
Le choix de l'hébergeur conditionne la moitié de ta délivrabilité. Avant de prendre un VPS :
|
|
||||||
|
|
||||||
1. **Le port 25 sortant est-il ouvert ?** Beaucoup d'hébergeurs le bloquent par défaut pour limiter le spam (Hetzner l'ouvre sur demande, OVH l'ouvre selon le produit, Scaleway l'ouvre selon le compte). Pose la question au support **avant** de payer.
|
|
||||||
2. **Le PTR est-il configurable ?** Si non, change.
|
|
||||||
3. **L'IP a-t-elle été utilisée par un spammeur ?** Avant d'acheter le VPS, demande l'IP qu'on te donnera. Vérifie sur :
|
|
||||||
- [mxtoolbox.com/blacklists.aspx](https://mxtoolbox.com/blacklists.aspx)
|
|
||||||
- [multirbl.valli.org](https://multirbl.valli.org/)
|
|
||||||
- [talosintelligence.com](https://www.talosintelligence.com/) (Cisco)
|
|
||||||
- [senderscore.org](https://senderscore.org/)
|
|
||||||
|
|
||||||
Si l'IP est listée sur Spamhaus, Barracuda, SORBS, SpamCop, **demande à l'hébergeur de te l'échanger** ou prends un autre VPS. Une fois listée, tu vas y passer des semaines.
|
|
||||||
4. **Réputation du subnet (`/24`)**. Même si ton IP est propre, si le `/24` est pourri (beaucoup de spammeurs voisins), Gmail va te traiter avec méfiance. Vérifie sur [senderscore.org](https://senderscore.org/) en saisissant ton IP — le score du subnet apparaît.
|
|
||||||
|
|
||||||
**Hébergeurs réputés corrects pour le mail** : Hetzner, OVH (gamme dédiée, pas SoYouStart), Scaleway, Infomaniak (en VPS), Netcup. À éviter pour de l'envoi : DigitalOcean (subnets souvent grillés), Linode/Akamai (idem), AWS EC2 (le port 25 est limité par défaut, et la rate-limit est costaude).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Configuration DNS complète
|
|
||||||
|
|
||||||
Pour un domaine `exemple.fr` avec un serveur mail sur `mail.exemple.fr` à l'IP `203.0.113.10` (et `2001:db8::10` en v6) :
|
|
||||||
|
|
||||||
```dns
|
|
||||||
;; Enregistrement du serveur mail lui-même
|
|
||||||
mail.exemple.fr. IN A 203.0.113.10
|
|
||||||
mail.exemple.fr. IN AAAA 2001:db8::10
|
|
||||||
|
|
||||||
;; MX : qui reçoit les mails du domaine
|
|
||||||
exemple.fr. IN MX 10 mail.exemple.fr.
|
|
||||||
|
|
||||||
;; SPF : qui a le droit d'envoyer pour ce domaine
|
|
||||||
exemple.fr. IN TXT "v=spf1 mx -all"
|
|
||||||
|
|
||||||
;; DKIM (clé publique, le sélecteur ici est "mail")
|
|
||||||
mail._domainkey.exemple.fr. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhki..."
|
|
||||||
|
|
||||||
;; DMARC
|
|
||||||
_dmarc.exemple.fr. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@exemple.fr; ruf=mailto:dmarc@exemple.fr; fo=1; adkim=s; aspf=s; pct=100"
|
|
||||||
|
|
||||||
;; MTA-STS (cf. §8)
|
|
||||||
_mta-sts.exemple.fr. IN TXT "v=STSv1; id=20260509T000000;"
|
|
||||||
mta-sts.exemple.fr. IN A 203.0.113.10
|
|
||||||
|
|
||||||
;; TLS-RPT (rapports d'erreurs TLS)
|
|
||||||
_smtp._tls.exemple.fr. IN TXT "v=TLSRPTv1; rua=mailto:tls-reports@exemple.fr"
|
|
||||||
|
|
||||||
;; CAA : restreindre qui peut émettre des certifs pour ton domaine
|
|
||||||
exemple.fr. IN CAA 0 issue "letsencrypt.org"
|
|
||||||
|
|
||||||
;; Autodiscover / autoconfig (pour les clients mail)
|
|
||||||
autoconfig.exemple.fr. IN CNAME mail.exemple.fr.
|
|
||||||
autodiscover.exemple.fr. IN CNAME mail.exemple.fr.
|
|
||||||
```
|
|
||||||
|
|
||||||
Détails dans les sections dédiées plus bas.
|
|
||||||
|
|
||||||
**À ne pas oublier** : l'enregistrement PTR (reverse DNS) se configure **chez ton hébergeur**, pas dans ta zone DNS. Il doit pointer `203.0.113.10 → mail.exemple.fr`. C'est traité au §9.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Installation du stack mail
|
|
||||||
|
|
||||||
Sur Debian 12. Ce qui suit est volontairement condensé — pour une configuration ligne par ligne, suis [le tutoriel de référence de Workaround.org](https://workaround.org/ispmail/) qui est l'étalon depuis 20 ans.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Mise à jour
|
|
||||||
apt update && apt upgrade -y
|
|
||||||
|
|
||||||
# Stack de base
|
|
||||||
apt install -y postfix postfix-mysql dovecot-core dovecot-imapd \
|
|
||||||
dovecot-lmtpd dovecot-managesieved dovecot-sieve dovecot-mysql \
|
|
||||||
rspamd redis-server mariadb-server certbot
|
|
||||||
|
|
||||||
# Certificat TLS
|
|
||||||
certbot certonly --standalone -d mail.exemple.fr \
|
|
||||||
-d mta-sts.exemple.fr \
|
|
||||||
-d autoconfig.exemple.fr -d autodiscover.exemple.fr
|
|
||||||
```
|
|
||||||
|
|
||||||
### Postfix : configuration `main.cf` minimale-mais-saine
|
|
||||||
|
|
||||||
```cfg
|
|
||||||
# /etc/postfix/main.cf
|
|
||||||
myhostname = mail.exemple.fr
|
|
||||||
mydomain = exemple.fr
|
|
||||||
myorigin = $mydomain
|
|
||||||
mydestination = # Vide : on utilise virtual_mailbox_domains
|
|
||||||
inet_interfaces = all
|
|
||||||
inet_protocols = all # IPv4 + IPv6
|
|
||||||
|
|
||||||
# TLS — chiffrement obligatoire
|
|
||||||
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.exemple.fr/fullchain.pem
|
|
||||||
smtpd_tls_key_file = /etc/letsencrypt/live/mail.exemple.fr/privkey.pem
|
|
||||||
smtpd_tls_security_level = may
|
|
||||||
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
|
||||||
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
|
||||||
smtp_tls_security_level = may
|
|
||||||
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
|
||||||
smtp_dns_support_level = dnssec # Indispensable pour DANE
|
|
||||||
smtp_tls_security_level = dane # ou "may" si DANE pas encore prêt
|
|
||||||
smtp_host_lookup = dns
|
|
||||||
|
|
||||||
# Restrictions anti-relais ouvert
|
|
||||||
smtpd_relay_restrictions =
|
|
||||||
permit_mynetworks
|
|
||||||
permit_sasl_authenticated
|
|
||||||
reject_unauth_destination
|
|
||||||
|
|
||||||
smtpd_recipient_restrictions =
|
|
||||||
permit_mynetworks
|
|
||||||
permit_sasl_authenticated
|
|
||||||
reject_unauth_destination
|
|
||||||
reject_unknown_recipient_domain
|
|
||||||
reject_invalid_helo_hostname
|
|
||||||
reject_non_fqdn_helo_hostname
|
|
||||||
|
|
||||||
# Limites raisonnables
|
|
||||||
message_size_limit = 52428800 # 50 Mo
|
|
||||||
mailbox_size_limit = 0
|
|
||||||
recipient_delimiter = +
|
|
||||||
|
|
||||||
# Intégration Rspamd
|
|
||||||
smtpd_milters = inet:localhost:11332
|
|
||||||
non_smtpd_milters = inet:localhost:11332
|
|
||||||
milter_protocol = 6
|
|
||||||
milter_default_action = accept
|
|
||||||
milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}
|
|
||||||
|
|
||||||
# Livraison via Dovecot LMTP
|
|
||||||
virtual_transport = lmtp:unix:private/dovecot-lmtp
|
|
||||||
```
|
|
||||||
|
|
||||||
### Dovecot, Rspamd
|
|
||||||
|
|
||||||
Ces composants demandent leurs propres fichiers de configuration. Renvoi explicite vers les tutos qui font autorité :
|
|
||||||
|
|
||||||
- **Workaround.org / ISPmail** : [https://workaround.org/ispmail/](https://workaround.org/ispmail/) — référence francophone et anglophone, mise à jour à chaque version Debian.
|
|
||||||
- **Rspamd quickstart** : [https://www.rspamd.com/doc/tutorials/quickstart.html](https://www.rspamd.com/doc/tutorials/quickstart.html)
|
|
||||||
- **Dovecot wiki** : [https://doc.dovecot.org/](https://doc.dovecot.org/)
|
|
||||||
|
|
||||||
Si tu veux gagner du temps, **Mailcow** (`docker compose`) est aujourd'hui la solution clé-en-main la plus fiable.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. SPF, DKIM, DMARC : les rustines obligatoires
|
|
||||||
|
|
||||||
Sans ces trois enregistrements correctement configurés, **Gmail et Outlook rejetteront ou marqueront en spam** la majorité de tes messages — peu importe ton volume.
|
|
||||||
|
|
||||||
### SPF (Sender Policy Framework)
|
|
||||||
|
|
||||||
Déclare qui a le droit d'envoyer du mail pour ton domaine.
|
|
||||||
|
|
||||||
```dns
|
|
||||||
exemple.fr. IN TXT "v=spf1 mx -all"
|
|
||||||
```
|
|
||||||
|
|
||||||
- `mx` : autorise les serveurs listés dans le MX du domaine.
|
|
||||||
- `-all` : **rejet strict** de tout le reste. Indispensable pour la réputation. Ne jamais utiliser `~all` (softfail) en prod : Gmail aujourd'hui considère `~all` comme un signal faible.
|
|
||||||
|
|
||||||
Si tu envoies aussi via un relais externe (smart host) : ajoute son `include`, ex. `v=spf1 mx include:_spf.mailjet.com -all`.
|
|
||||||
|
|
||||||
**Limite** : un enregistrement SPF doit tenir en **10 lookups DNS maximum**. Au-delà, il est invalide. Vérifie avec [https://www.kitterman.com/spf/validate.html](https://www.kitterman.com/spf/validate.html).
|
|
||||||
|
|
||||||
### DKIM (DomainKeys Identified Mail)
|
|
||||||
|
|
||||||
Signe chaque mail sortant avec une clé privée. Le destinataire vérifie la signature via la clé publique publiée en DNS.
|
|
||||||
|
|
||||||
**Génération de la clé** (Rspamd, sélecteur `mail`, clé 2048 bits) :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p /var/lib/rspamd/dkim
|
|
||||||
rspamadm dkim_keygen -s mail -d exemple.fr -k /var/lib/rspamd/dkim/exemple.fr.mail.key \
|
|
||||||
-b 2048 > /var/lib/rspamd/dkim/exemple.fr.mail.txt
|
|
||||||
chown _rspamd:_rspamd /var/lib/rspamd/dkim/*
|
|
||||||
```
|
|
||||||
|
|
||||||
Le fichier `.txt` contient l'enregistrement DNS à publier :
|
|
||||||
|
|
||||||
```dns
|
|
||||||
mail._domainkey.exemple.fr. IN TXT ( "v=DKIM1; k=rsa; "
|
|
||||||
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..." )
|
|
||||||
```
|
|
||||||
|
|
||||||
Configuration Rspamd (`/etc/rspamd/local.d/dkim_signing.conf`) :
|
|
||||||
|
|
||||||
```conf
|
|
||||||
allow_username_mismatch = true;
|
|
||||||
domain {
|
|
||||||
exemple.fr {
|
|
||||||
path = "/var/lib/rspamd/dkim/exemple.fr.mail.key";
|
|
||||||
selector = "mail";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Recharge : `systemctl restart rspamd`.
|
|
||||||
|
|
||||||
**Vérification** : envoie un mail à [check-auth@verifier.port25.com](mailto:check-auth@verifier.port25.com), tu reçois un rapport complet SPF/DKIM/DMARC en retour. Ou utilise [https://www.mail-tester.com/](https://www.mail-tester.com/) (note sur 10).
|
|
||||||
|
|
||||||
### DMARC (Domain-based Message Authentication, Reporting and Conformance)
|
|
||||||
|
|
||||||
Dit aux serveurs distants quoi faire en cas d'échec SPF/DKIM, et **te renvoie des rapports** sur ce qui passe et ce qui rate.
|
|
||||||
|
|
||||||
```dns
|
|
||||||
_dmarc.exemple.fr. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@exemple.fr; ruf=mailto:dmarc@exemple.fr; fo=1; adkim=s; aspf=s; pct=100"
|
|
||||||
```
|
|
||||||
|
|
||||||
- `p=none` : surveillance seule, **à utiliser pendant 2-4 semaines en démarrage** pour collecter les rapports sans pénaliser.
|
|
||||||
- `p=quarantine` : mise en spam des mails non authentifiés. Cible normale.
|
|
||||||
- `p=reject` : rejet pur. À atteindre en cible finale, **après** avoir vérifié 4 semaines de rapports propres.
|
|
||||||
- `rua` : adresse pour les rapports agrégés (quotidiens).
|
|
||||||
- `ruf` : rapports forensiques (par message). Optionnel.
|
|
||||||
- `adkim=s aspf=s` : alignement strict — le domaine de signature DKIM et le domaine SPF doivent **exactement** correspondre au domaine `From:`.
|
|
||||||
|
|
||||||
**Lecture des rapports DMARC** : ils arrivent en XML, illisibles. Utilise un parseur :
|
|
||||||
|
|
||||||
- [Postmark DMARC Monitoring](https://dmarc.postmarkapp.com/) (gratuit, agrège les rapports dans une UI).
|
|
||||||
- [parsedmarc](https://github.com/domainaware/parsedmarc) (auto-hébergeable, envoie dans Elasticsearch/Splunk/Grafana).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. MTA-STS, TLS-RPT, DANE : aller plus loin
|
|
||||||
|
|
||||||
Ces standards sécurisent le **transport** entre serveurs (chiffrement TLS forcé). Gmail les regarde, Microsoft aussi. Pas obligatoires, mais ils boostent ta réputation.
|
|
||||||
|
|
||||||
### MTA-STS
|
|
||||||
|
|
||||||
Force les serveurs distants à utiliser TLS pour t'envoyer des mails. Trois éléments :
|
|
||||||
|
|
||||||
**1. Enregistrement DNS TXT** :
|
|
||||||
```dns
|
|
||||||
_mta-sts.exemple.fr. IN TXT "v=STSv1; id=20260509T000000;"
|
|
||||||
```
|
|
||||||
|
|
||||||
**2. Sous-domaine `mta-sts.exemple.fr`** servant un fichier en HTTPS à `https://mta-sts.exemple.fr/.well-known/mta-sts.txt` :
|
|
||||||
```
|
|
||||||
version: STSv1
|
|
||||||
mode: enforce
|
|
||||||
mx: mail.exemple.fr
|
|
||||||
max_age: 604800
|
|
||||||
```
|
|
||||||
|
|
||||||
`mode: enforce` est la cible. En démarrage, mets `mode: testing` pendant 1-2 semaines.
|
|
||||||
|
|
||||||
**3. Certificat TLS valide** sur ce sous-domaine (déjà fait via certbot au §6).
|
|
||||||
|
|
||||||
### TLS-RPT
|
|
||||||
|
|
||||||
Demande aux serveurs distants de t'envoyer des rapports en cas d'échec TLS.
|
|
||||||
|
|
||||||
```dns
|
|
||||||
_smtp._tls.exemple.fr. IN TXT "v=TLSRPTv1; rua=mailto:tls-reports@exemple.fr"
|
|
||||||
```
|
|
||||||
|
|
||||||
### DANE (DNS-based Authentication of Named Entities)
|
|
||||||
|
|
||||||
Encore plus solide que MTA-STS, mais nécessite **DNSSEC** activé sur ton domaine. Si ton registrar ne supporte pas DNSSEC, oublie DANE.
|
|
||||||
|
|
||||||
DANE publie un hash du certificat TLS dans un enregistrement TLSA :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Générer l'enregistrement TLSA pour le port 25
|
|
||||||
posttls-finger -t30 -T180 -P /etc/ssl/certs/ -lsecure mail.exemple.fr
|
|
||||||
```
|
|
||||||
|
|
||||||
Ou plus simplement avec [https://www.huque.com/bin/gen_tlsa](https://www.huque.com/bin/gen_tlsa) :
|
|
||||||
|
|
||||||
```dns
|
|
||||||
_25._tcp.mail.exemple.fr. IN TLSA 3 1 1 ABC123...
|
|
||||||
```
|
|
||||||
|
|
||||||
**Vérification globale** de tout ton setup TLS+DANE : [https://internet.nl/mail/](https://internet.nl/mail/) (excellent, recommandé).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. PTR (reverse DNS) et HELO
|
|
||||||
|
|
||||||
**Le PTR est probablement la cause la plus fréquente de rejet par Gmail/Outlook chez les nouveaux auto-hébergés.**
|
|
||||||
|
|
||||||
Règle absolue : **`PTR(IP) == HELO == nom A/AAAA`**, et tout doit être un FQDN cohérent.
|
|
||||||
|
|
||||||
Configure le PTR dans le panneau de ton hébergeur (chez OVH : "IP" → "Reverse DNS") :
|
|
||||||
```
|
|
||||||
203.0.113.10 → mail.exemple.fr
|
|
||||||
2001:db8::10 → mail.exemple.fr
|
|
||||||
```
|
|
||||||
|
|
||||||
Vérifie :
|
|
||||||
```bash
|
|
||||||
dig -x 203.0.113.10 +short
|
|
||||||
# doit retourner : mail.exemple.fr.
|
|
||||||
|
|
||||||
dig mail.exemple.fr A +short
|
|
||||||
# doit retourner : 203.0.113.10
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans Postfix, `myhostname = mail.exemple.fr` et c'est ce qui est annoncé en HELO. Cohérence garantie.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. Warmup d'IP : la phase la plus délicate
|
|
||||||
|
|
||||||
**Une IP neuve = pas de réputation = défiance maximale des gros acteurs.** Tu ne peux pas envoyer 1000 mails le jour 1 sans te griller.
|
|
||||||
|
|
||||||
### Plan de warmup sur 4 à 6 semaines
|
|
||||||
|
|
||||||
| Semaine | Volume max/jour vers Gmail+Outlook | Volume max/jour total | Contenu |
|
|
||||||
|---|---|---|---|
|
|
||||||
| 1 | 20-50 | 100 | Mails à toi-même, comptes test sur Gmail/Outlook/Yahoo. Réponds-y, marque "non spam" si en spam. |
|
|
||||||
| 2 | 100 | 300 | Cercle proche qui sait répondre / interagir. |
|
|
||||||
| 3 | 300 | 1000 | Élargissement progressif. |
|
|
||||||
| 4 | 800 | 3000 | Ouvre aux usages normaux. |
|
|
||||||
| 5+ | 2000+ | volume cible | Stable. |
|
|
||||||
|
|
||||||
**Règles d'or pendant le warmup :**
|
|
||||||
- **Pas de mailing list, pas de notifs automatiques en masse.** Privilégie des mails 1-à-1 conversationnels.
|
|
||||||
- **Demande aux destinataires de répondre** — un mail avec réponse a 100x le poids d'un mail ouvert silencieusement.
|
|
||||||
- **Aucun lien raccourci**, aucun pixel de tracking, aucune image lourde.
|
|
||||||
- **Stop net si ton score Senderscore baisse** ou si Gmail Postmaster Tools (cf. §11) montre du rouge.
|
|
||||||
|
|
||||||
### Si tu as un volume immédiat à envoyer
|
|
||||||
|
|
||||||
Bascule en **option B** (smart host) le temps du warmup, puis rapatrie progressivement en interne en répliquant les volumes ci-dessus.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 11. Postmaster Tools, SNDS, FBL
|
|
||||||
|
|
||||||
Les gros acteurs te donnent des dashboards dédiés. **Inscris-toi à tous, dès la création du domaine.**
|
|
||||||
|
|
||||||
| Service | Acteur | Usage |
|
|
||||||
|---|---|---|
|
|
||||||
| [Google Postmaster Tools](https://postmaster.google.com/) | Gmail | Réputation IP+domaine, taux de spam, authentification, encryption. **Indispensable.** |
|
|
||||||
| [Microsoft SNDS](https://sendersupport.olc.protection.outlook.com/snds/) | Outlook/Hotmail | Smart Network Data Services, qualité de l'IP. |
|
|
||||||
| [Microsoft JMRP](https://sendersupport.olc.protection.outlook.com/pm/) | Outlook | Junk Mail Reporting Program, FBL Microsoft. |
|
|
||||||
| [Yahoo CFL](https://senders.yahooinc.com/complaint-feedback-loop/) | Yahoo | Complaint Feedback Loop. |
|
|
||||||
| [Validity Sender Score](https://senderscore.org/) | Indépendant | Score sur 100, à surveiller. |
|
|
||||||
|
|
||||||
Configure les feedback loops (FBL) : quand un destinataire clique "spam", tu reçois une notification. Ça te permet de désinscrire l'utilisateur **avant qu'il ne dégrade ta réputation**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 12. Liste de désinscription en un clic (RFC 8058)
|
|
||||||
|
|
||||||
**Exigence Google/Microsoft pour les expéditeurs en volume**, mais à mettre en place dès le début même en bas volume.
|
|
||||||
|
|
||||||
Ajoute deux en-têtes à tous les mails non-strictement-personnels :
|
|
||||||
|
|
||||||
```
|
|
||||||
List-Unsubscribe: <https://exemple.fr/unsub?token=abc123>, <mailto:unsub@exemple.fr?subject=unsub-abc123>
|
|
||||||
List-Unsubscribe-Post: List-Unsubscribe=One-Click
|
|
||||||
```
|
|
||||||
|
|
||||||
L'URL HTTPS doit accepter une requête **POST** (pas seulement GET) avec `List-Unsubscribe=One-Click` dans le corps, et désinscrire **immédiatement et silencieusement** sans demander de confirmation.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 13. Anti-spam entrant et hygiène
|
|
||||||
|
|
||||||
Un serveur mail mal configuré côté entrée devient vite un relais de spam ou une cible. Configuration Rspamd minimale :
|
|
||||||
|
|
||||||
```conf
|
|
||||||
# /etc/rspamd/local.d/actions.conf
|
|
||||||
reject = 15;
|
|
||||||
add_header = 6;
|
|
||||||
greylist = 4;
|
|
||||||
```
|
|
||||||
|
|
||||||
```conf
|
|
||||||
# /etc/rspamd/local.d/greylist.conf
|
|
||||||
expire = 1d;
|
|
||||||
```
|
|
||||||
|
|
||||||
Active aussi :
|
|
||||||
- **Vérification SPF/DKIM/DMARC entrante** (par défaut activée dans Rspamd).
|
|
||||||
- **RBL** (Realtime Blackhole Lists) : Spamhaus ZEN, Barracuda. Attention à ne pas multiplier — 2 ou 3 RBL fiables suffisent.
|
|
||||||
- **Greylisting** : refuse temporairement les premiers contacts, ce qui élimine 80% du spam basique. Ne pas activer sur un domaine à fort volume transactionnel (gêne les notifs).
|
|
||||||
- **Bayes** : laisse Rspamd apprendre via le dossier `Junk` de Dovecot (signal `IsSpam` / `IsHam`).
|
|
||||||
|
|
||||||
Mises à jour : `unattended-upgrades` activé, redémarrage planifié, lecture des annonces sécu Postfix/Dovecot.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 14. Monitoring, logs, alertes
|
|
||||||
|
|
||||||
Sans monitoring, tu découvres les problèmes par les utilisateurs. À mettre en place :
|
|
||||||
|
|
||||||
- **Lecture des logs** : `journalctl -u postfix -f`, `tail -f /var/log/mail.log`, web UI de Rspamd sur `localhost:11334`.
|
|
||||||
- **Métriques** : exporter Postfix/Dovecot vers Prometheus + Grafana (`postfix_exporter`, `dovecot_exporter`).
|
|
||||||
- **Alertes** sur :
|
|
||||||
- File d'attente Postfix > 50 messages (`mailq | tail -1`).
|
|
||||||
- Score Senderscore qui chute.
|
|
||||||
- Apparition sur une RBL : surveillance automatisée par [https://multirbl.valli.org/](https://multirbl.valli.org/) ou via un script qui interroge plusieurs DNSBL en cron.
|
|
||||||
- Échec TLS-RPT (rapport entrant signalant une connexion non chiffrée).
|
|
||||||
- **Rapports DMARC** parsés régulièrement (cf. §7).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 15. Que faire quand Gmail rejette quand même ?
|
|
||||||
|
|
||||||
Ça arrive. Diagnostic dans l'ordre :
|
|
||||||
|
|
||||||
1. **Lis le code de rejet SMTP** dans `/var/log/mail.log`. Gmail renvoie des codes très explicites :
|
|
||||||
- `550-5.7.1 [203.0.113.10 19] Our system has detected that this message is likely unsolicited mail.` → contenu jugé spammy. Revois le contenu, ajoute du texte conversationnel, retire les liens douteux.
|
|
||||||
- `421-4.7.0 [203.0.113.10 15] Our system has detected an unusual rate of unsolicited mail.` → tu as dépassé un seuil. **Ralentis immédiatement**, attends 24-48h, reprends doucement.
|
|
||||||
- `550-5.7.26 ... DMARC ...` → ton DMARC ne passe pas. Revérifie SPF/DKIM/alignement.
|
|
||||||
- `550-5.7.1 ... blocked using Spamhaus.` → tu es sur une RBL. Va sur [spamhaus.org/lookup/](https://www.spamhaus.org/lookup/) pour vérifier et demander la sortie.
|
|
||||||
2. **Va dans Postmaster Tools** (§11). Si "IP reputation" est rouge ou orange, regarde le contenu et le timing de tes envois récents.
|
|
||||||
3. **Test mail-tester** : envoie à une adresse fournie par [mail-tester.com](https://www.mail-tester.com/), obtiens une note sur 10. Vise 10/10. Toute case manquante doit être corrigée.
|
|
||||||
4. **Sortie de blacklist** : la plupart des RBL (Spamhaus, Barracuda) ont un formulaire de retrait. Spamhaus retire en quelques heures si tu corriges la cause. SORBS est plus lent. UCEPROTECT exige souvent de payer — ignore-la, peu de serveurs sérieux la consultent.
|
|
||||||
5. **Si rien ne marche**, change d'IP. C'est parfois la seule issue. Demande à ton hébergeur une IP fraîche, refais un warmup.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 16. Checklist finale avant mise en prod
|
|
||||||
|
|
||||||
Avant d'envoyer le premier vrai mail :
|
|
||||||
|
|
||||||
- [ ] Domaine avec DNSSEC activé.
|
|
||||||
- [ ] IP testée sur 5+ blacklists, propre.
|
|
||||||
- [ ] Port 25 sortant ouvert et testé (`telnet gmail-smtp-in.l.google.com 25`).
|
|
||||||
- [ ] PTR configuré et cohérent avec le HELO.
|
|
||||||
- [ ] MX, A, AAAA, SPF, DKIM, DMARC publiés et validés via [mxtoolbox.com](https://mxtoolbox.com/).
|
|
||||||
- [ ] MTA-STS publié (mode `testing` au démarrage).
|
|
||||||
- [ ] TLS-RPT publié.
|
|
||||||
- [ ] DANE/TLSA publié (si DNSSEC OK).
|
|
||||||
- [ ] CAA publié.
|
|
||||||
- [ ] Test envoyé à `check-auth@verifier.port25.com` : tout en `pass`.
|
|
||||||
- [ ] Test [mail-tester.com](https://www.mail-tester.com/) : 10/10.
|
|
||||||
- [ ] Test [internet.nl/mail/](https://internet.nl/mail/) : 100%.
|
|
||||||
- [ ] Inscription Postmaster Tools, SNDS, JMRP, Yahoo CFL.
|
|
||||||
- [ ] DMARC `p=none` au démarrage, parser de rapports en place.
|
|
||||||
- [ ] List-Unsubscribe + List-Unsubscribe-Post implémentés.
|
|
||||||
- [ ] Plan de warmup affiché et respecté.
|
|
||||||
- [ ] Monitoring file d'attente + RBL en place.
|
|
||||||
- [ ] Backup chiffré des Maildir.
|
|
||||||
|
|
||||||
Au bout de 4 semaines de rapports DMARC propres : passage à `p=quarantine`. Au bout de 8-12 semaines : `p=reject`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 17. Annexes : commandes utiles
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Voir la file d'attente
|
|
||||||
mailq
|
|
||||||
postqueue -p
|
|
||||||
|
|
||||||
# Forcer la tentative de livraison
|
|
||||||
postqueue -f
|
|
||||||
|
|
||||||
# Vider la file (à utiliser avec précaution)
|
|
||||||
postsuper -d ALL
|
|
||||||
|
|
||||||
# Tester la délivrabilité d'un domaine cible (DANE / TLS)
|
|
||||||
posttls-finger -t30 -T180 -P /etc/ssl/certs/ -lsecure gmail-smtp-in.l.google.com
|
|
||||||
|
|
||||||
# Vérifier ses propres enregistrements
|
|
||||||
dig exemple.fr TXT +short
|
|
||||||
dig _dmarc.exemple.fr TXT +short
|
|
||||||
dig mail._domainkey.exemple.fr TXT +short
|
|
||||||
dig mail.exemple.fr MX +short
|
|
||||||
dig -x 203.0.113.10 +short
|
|
||||||
|
|
||||||
# Tester l'envoi en SMTP authentifié
|
|
||||||
swaks --to test@gmail.com --from moi@exemple.fr \
|
|
||||||
--server mail.exemple.fr --auth --auth-user moi@exemple.fr \
|
|
||||||
--tls --port 587
|
|
||||||
|
|
||||||
# Voir en temps réel ce qu'il se passe
|
|
||||||
journalctl -u postfix -u dovecot -u rspamd -f
|
|
||||||
```
|
|
||||||
|
|
||||||
### Outils web à mettre en favoris
|
|
||||||
|
|
||||||
- [https://www.mail-tester.com/](https://www.mail-tester.com/) — score sur 10
|
|
||||||
- [https://internet.nl/mail/](https://internet.nl/mail/) — audit complet
|
|
||||||
- [https://mxtoolbox.com/SuperTool.aspx](https://mxtoolbox.com/SuperTool.aspx) — DNS, blacklists
|
|
||||||
- [https://dmarcian.com/dmarc-inspector/](https://dmarcian.com/dmarc-inspector/) — vérif DMARC
|
|
||||||
- [https://www.kitterman.com/spf/validate.html](https://www.kitterman.com/spf/validate.html) — vérif SPF
|
|
||||||
- [https://postmaster.google.com/](https://postmaster.google.com/) — Google Postmaster
|
|
||||||
- [https://senderscore.org/](https://senderscore.org/) — réputation IP
|
|
||||||
|
|
||||||
### Documentation de référence
|
|
||||||
|
|
||||||
- **ISPmail / Workaround.org** — [https://workaround.org/ispmail/](https://workaround.org/ispmail/) — le tutoriel le plus complet et tenu à jour, par version Debian.
|
|
||||||
- **Mailcow docs** — [https://docs.mailcow.email/](https://docs.mailcow.email/) — pour la version conteneurisée clé-en-main.
|
|
||||||
- **Postfix officiel** — [https://www.postfix.org/documentation.html](https://www.postfix.org/documentation.html)
|
|
||||||
- **Rspamd docs** — [https://www.rspamd.com/doc/](https://www.rspamd.com/doc/)
|
|
||||||
- **RFCs essentielles** : 5321 (SMTP moderne), 7208 (SPF), 6376 (DKIM), 7489 (DMARC), 8461 (MTA-STS), 8460 (TLS-RPT), 7672 (DANE-SMTP), 8058 (One-Click Unsubscribe).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
L'auto-hébergement mail en 2026 reste possible, mais c'est devenu un sport : les règles changent, les gros acteurs durcissent leurs critères, et l'écosystème pousse vers la centralisation. Si tu réussis le warmup et tiens 6 mois sans incident, tu as gagné — mais ne baisse pas la garde, un changement unilatéral de Google peut survenir à tout moment, comme en février 2024.
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "104a8694-4268-4e0a-99c7-e7ecfd47af1e",
|
|
||||||
"slug": "auto-heberger-son-serveur-mail-en-2026",
|
|
||||||
"title": "Auto-héberger son serveur mail en 2026",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2026-05-12 08:35",
|
|
||||||
"created_at": "2026-05-12 08:38:14",
|
|
||||||
"updated_at": "2026-05-12 08:40:06",
|
|
||||||
"revisions": [
|
|
||||||
{
|
|
||||||
"n": 1,
|
|
||||||
"date": "2026-05-12 08:38:44",
|
|
||||||
"comment": "",
|
|
||||||
"title": "Auto-héberger son serveur mail en 2026"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"n": 2,
|
|
||||||
"date": "2026-05-12 08:38:59",
|
|
||||||
"comment": "",
|
|
||||||
"title": "Auto-héberger son serveur mail en 2026"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"n": 3,
|
|
||||||
"date": "2026-05-12 08:40:06",
|
|
||||||
"comment": "",
|
|
||||||
"title": "Auto-héberger son serveur mail en 2026"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cover": "cover.svg",
|
|
||||||
"files_meta": {
|
|
||||||
"0f3d320c9347704b-35316.svg": {
|
|
||||||
"author": "Cédrix",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"cover.svg": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"_thumb_b7a3540aaf062cdb-1682326.jpg": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"external_links": [
|
|
||||||
{
|
|
||||||
"url": "https://www.clubic.com/dossier-612034-proton-mail-infomaniak-la-souverainete-de-vos-emails-reste-une-illusion-face-aux-regles-dictees-par-google.html",
|
|
||||||
"name": "Proton Mail, Infomaniak : la souveraineté de vos emails reste une illusion face aux règles dictées par Google",
|
|
||||||
"added_at": "2026-05-12 08:40:03",
|
|
||||||
"meta": {
|
|
||||||
"mime": "text/html",
|
|
||||||
"size": 333873,
|
|
||||||
"description": "Proton Mail, La Poste, Infomaniak, Mailo... les messageries \"souveraines\" se multiplient. Mais elles reposent toutes sur le même protocole conçu en 1982 aux États-Unis, sans authentification native et sans chiffrement. Les normes de sécurité ajoutées depuis ont été définies par Yahoo, Cisco, Microsoft et Google. Et Gmail filtre une part considérable du trafic email mondial, sans que l'Europe ne puisse dire quoi que ce soit.",
|
|
||||||
"og_image": "/file?uuid=104a8694-4268-4e0a-99c7-e7ecfd47af1e&name=_thumb_b7a3540aaf062cdb-1682326.jpg",
|
|
||||||
"site_name": "clubic.com",
|
|
||||||
"og_type": "article",
|
|
||||||
"language": "fr_FR",
|
|
||||||
"date": "2026-05-09T11:10:00+02:00",
|
|
||||||
"canonical": "https://www.clubic.com/dossier-612034-proton-mail-infomaniak-la-souverainete-de-vos-emails-reste-une-illusion-face-aux-regles-dictees-par-google.html"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "",
|
|
||||||
"og_image": "",
|
|
||||||
"category": "informatique"
|
|
||||||
}
|
|
||||||
@@ -1,605 +0,0 @@
|
|||||||
## Survivre aux règles de Gmail, Outlook et consorts
|
|
||||||
|
|
||||||
> **Contexte** — Cet article de Clubic ([lien](https://www.clubic.com/dossier-612034-proton-mail-infomaniak-la-souverainete-de-vos-emails-reste-une-illusion-face-aux-regles-dictees-par-google.html)) rappelle une vérité technique : SMTP date de 1982, n'a aucune sécurité native, et toutes les "rustines" (SPF, DKIM, DMARC, MTA-STS, DANE) ont été conçues par Yahoo, Cisco, Microsoft, Google. Depuis février 2024 (Google) et mai 2025 (Microsoft), tout expéditeur dépassant 5000 mails/jour vers Gmail/Outlook doit configurer SPF + DKIM + DMARC, maintenir un taux de spam < 0,1 %, et fournir un lien de désinscription en un clic.
|
|
||||||
>
|
|
||||||
> Mais même en dessous de 5000/jour, ces règles s'appliquent en pratique : sans elles, ton mail finit en spam ou est rejeté. Ce dossier décrit comment monter son propre serveur mail tout en passant à travers ces filtres.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Sommaire
|
|
||||||
|
|
||||||
1. [Avant de commencer : est-ce vraiment une bonne idée ?](#1-avant-de-commencer--est-ce-vraiment-une-bonne-idée-)
|
|
||||||
2. [Prérequis techniques](#2-prérequis-techniques)
|
|
||||||
3. [Architecture cible](#3-architecture-cible)
|
|
||||||
4. [Choix du fournisseur et de l'IP](#4-choix-du-fournisseur-et-de-lip)
|
|
||||||
5. [Configuration DNS complète](#5-configuration-dns-complète)
|
|
||||||
6. [Installation du stack mail](#6-installation-du-stack-mail)
|
|
||||||
7. [SPF, DKIM, DMARC : les rustines obligatoires](#7-spf-dkim-dmarc--les-rustines-obligatoires)
|
|
||||||
8. [MTA-STS, TLS-RPT, DANE : aller plus loin](#8-mta-sts-tls-rpt-dane--aller-plus-loin)
|
|
||||||
9. [PTR (reverse DNS) et HELO](#9-ptr-reverse-dns-et-helo)
|
|
||||||
10. [Warmup d'IP : la phase la plus délicate](#10-warmup-dip--la-phase-la-plus-délicate)
|
|
||||||
11. [Postmaster Tools, SNDS, FBL](#11-postmaster-tools-snds-fbl)
|
|
||||||
12. [Liste de désinscription en un clic (RFC 8058)](#12-liste-de-désinscription-en-un-clic-rfc-8058)
|
|
||||||
13. [Anti-spam entrant et hygiène](#13-anti-spam-entrant-et-hygiène)
|
|
||||||
14. [Monitoring, logs, alertes](#14-monitoring-logs-alertes)
|
|
||||||
15. [Que faire quand Gmail rejette quand même ?](#15-que-faire-quand-gmail-rejette-quand-même-)
|
|
||||||
16. [Checklist finale avant mise en prod](#16-checklist-finale-avant-mise-en-prod)
|
|
||||||
17. [Annexes : commandes utiles](#17-annexes--commandes-utiles)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Avant de commencer : est-ce vraiment une bonne idée ?
|
|
||||||
|
|
||||||
L'auto-hébergement mail est techniquement possible, mais c'est probablement le service le plus pénible à maintenir en 2026. Avant de te lancer, lis ça :
|
|
||||||
|
|
||||||
**Ce qui marche bien en auto-hébergé :**
|
|
||||||
- Recevoir du mail (presque tout le monde te livre).
|
|
||||||
- Envoyer vers d'autres serveurs auto-hébergés ou pros bien configurés.
|
|
||||||
- Garder le contrôle sur tes données, tes alias, tes domaines.
|
|
||||||
|
|
||||||
**Ce qui est dur :**
|
|
||||||
- Envoyer vers Gmail / Outlook / Yahoo / iCloud sans atterrir en spam.
|
|
||||||
- Sortir d'une blacklist une fois dedans.
|
|
||||||
- Maintenir un score de réputation IP correct sur la durée.
|
|
||||||
- Survivre à un changement unilatéral des règles côté gros acteurs (cf. février 2024 et mai 2025).
|
|
||||||
|
|
||||||
**Stratégie réaliste recommandée :**
|
|
||||||
- Réception entrante : auto-hébergée à 100 %. Aucun risque, full contrôle.
|
|
||||||
- Envoi sortant : deux options, selon ton volume et ton tolérance au risque.
|
|
||||||
- **Option A — Pure auto-hébergée** : tu envoies directement depuis ton serveur. Faisable, mais demande un warmup, une IP propre, et un suivi continu.
|
|
||||||
- **Option B — Smart host sortant** : tu envoies via un relais réputé (un autre de tes serveurs avec une IP qui a déjà sa réputation, ou un service type Mailjet/Sendgrid/SMTP2GO en bas volume gratuit). Tes mails sortent depuis l'IP du relais, qui a déjà sa réputation faite. C'est un compromis : tu perds une partie de la souveraineté technique, mais tu gagnes énormément en délivrabilité.
|
|
||||||
|
|
||||||
Le reste du dossier suit l'option A — tout en t'expliquant comment basculer en B si nécessaire.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Prérequis techniques
|
|
||||||
|
|
||||||
| Élément | Détail |
|
|
||||||
|---|---|
|
|
||||||
| Domaine | À toi, registrar peu importe, mais avec **DNSSEC activable** (cf. §8 pour DANE). |
|
|
||||||
| Serveur | VPS ou dédié, **2 vCPU / 4 Go RAM minimum**, Debian 12+ ou Ubuntu 24.04 LTS. |
|
|
||||||
| IP fixe v4 | Indispensable. **IP "résidentielle" ou IP de datacenter récemment recyclée = exclues**. |
|
|
||||||
| IP fixe v6 | Recommandée, mais désactivable si l'IPv6 du fournisseur est blacklistée. |
|
|
||||||
| PTR / reverse DNS | **Modifiable par toi**. Si l'hébergeur ne te le permet pas, change d'hébergeur. |
|
|
||||||
| Ports | 25, 465, 587, 993, 4190 ouverts sortants ET entrants. **Le port 25 sortant est bloqué chez beaucoup d'hébergeurs grand public** (OVH résidentiel, Free, etc.) : vérifie avant. |
|
|
||||||
| TLS | Certificat valide (Let's Encrypt suffit). |
|
|
||||||
|
|
||||||
**Compétences attendues** : Linux en ligne de commande, DNS (champs A/AAAA/MX/TXT/SRV/CAA/TLSA), notion de TLS, lecture de logs `journalctl` et `/var/log/mail.log`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Architecture cible
|
|
||||||
|
|
||||||
Un stack standard, éprouvé, en logiciels libres :
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────┐
|
|
||||||
│ Internet (mails entrants/sortants) │
|
|
||||||
└─────────────────────────────────────┘
|
|
||||||
│
|
|
||||||
▼ port 25
|
|
||||||
┌────────────────┐
|
|
||||||
│ Postfix │ ← MTA (envoi/réception SMTP)
|
|
||||||
│ (SMTP / 25/587)│
|
|
||||||
└────────────────┘
|
|
||||||
│ │ │
|
|
||||||
▼ ▼ ▼
|
|
||||||
┌─────────┐ ┌─────────┐ ┌──────────┐
|
|
||||||
│ Rspamd │ │OpenDKIM │ │ Dovecot │ ← IMAP/POP + LMTP
|
|
||||||
│(spam, │ │ (signe │ │ (livrai- │
|
|
||||||
│ DKIM │ │ DKIM) │ │ son aux │
|
|
||||||
│ verif, │ └─────────┘ │ boîtes) │
|
|
||||||
│ DMARC) │ └──────────┘
|
|
||||||
└─────────┘ │
|
|
||||||
▼
|
|
||||||
Maildir / utilisateurs
|
|
||||||
```
|
|
||||||
|
|
||||||
**Composants** :
|
|
||||||
|
|
||||||
- **Postfix** : MTA. Reçoit, route, envoie le SMTP.
|
|
||||||
- **Dovecot** : serveur IMAP/POP3, livraison locale (LMTP), authentification SASL pour Postfix, gestion Sieve (filtres).
|
|
||||||
- **Rspamd** : antispam moderne, fait aussi la **vérification SPF/DKIM/DMARC entrante**, le greylisting, et — option recommandée — la **signature DKIM sortante** (en remplacement d'OpenDKIM).
|
|
||||||
- **Let's Encrypt (certbot)** : TLS.
|
|
||||||
- **(Optionnel) Roundcube ou SnappyMail** : webmail.
|
|
||||||
|
|
||||||
**Alternative tout-en-un** : [Mailcow](https://mailcow.email/) ou [Mailu](https://mailu.io/), basés sur Docker, qui empaquètent tout ça avec une interface admin. Si tu préfères ne pas tout configurer à la main, c'est légitime — la majorité des règles DNS et de délivrabilité de ce dossier restent identiques.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Choix du fournisseur et de l'IP
|
|
||||||
|
|
||||||
Le choix de l'hébergeur conditionne la moitié de ta délivrabilité. Avant de prendre un VPS :
|
|
||||||
|
|
||||||
1. **Le port 25 sortant est-il ouvert ?** Beaucoup d'hébergeurs le bloquent par défaut pour limiter le spam (Hetzner l'ouvre sur demande, OVH l'ouvre selon le produit, Scaleway l'ouvre selon le compte). Pose la question au support **avant** de payer.
|
|
||||||
2. **Le PTR est-il configurable ?** Si non, change.
|
|
||||||
3. **L'IP a-t-elle été utilisée par un spammeur ?** Avant d'acheter le VPS, demande l'IP qu'on te donnera. Vérifie sur :
|
|
||||||
- [mxtoolbox.com/blacklists.aspx](https://mxtoolbox.com/blacklists.aspx)
|
|
||||||
- [multirbl.valli.org](https://multirbl.valli.org/)
|
|
||||||
- [talosintelligence.com](https://www.talosintelligence.com/) (Cisco)
|
|
||||||
- [senderscore.org](https://senderscore.org/)
|
|
||||||
|
|
||||||
Si l'IP est listée sur Spamhaus, Barracuda, SORBS, SpamCop, **demande à l'hébergeur de te l'échanger** ou prends un autre VPS. Une fois listée, tu vas y passer des semaines.
|
|
||||||
4. **Réputation du subnet (`/24`)**. Même si ton IP est propre, si le `/24` est pourri (beaucoup de spammeurs voisins), Gmail va te traiter avec méfiance. Vérifie sur [senderscore.org](https://senderscore.org/) en saisissant ton IP — le score du subnet apparaît.
|
|
||||||
|
|
||||||
**Hébergeurs réputés corrects pour le mail** : Hetzner, OVH (gamme dédiée, pas SoYouStart), Scaleway, Infomaniak (en VPS), Netcup. À éviter pour de l'envoi : DigitalOcean (subnets souvent grillés), Linode/Akamai (idem), AWS EC2 (le port 25 est limité par défaut, et la rate-limit est costaude).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Configuration DNS complète
|
|
||||||
|
|
||||||
Pour un domaine `exemple.fr` avec un serveur mail sur `mail.exemple.fr` à l'IP `203.0.113.10` (et `2001:db8::10` en v6) :
|
|
||||||
|
|
||||||
```dns
|
|
||||||
;; Enregistrement du serveur mail lui-même
|
|
||||||
mail.exemple.fr. IN A 203.0.113.10
|
|
||||||
mail.exemple.fr. IN AAAA 2001:db8::10
|
|
||||||
|
|
||||||
;; MX : qui reçoit les mails du domaine
|
|
||||||
exemple.fr. IN MX 10 mail.exemple.fr.
|
|
||||||
|
|
||||||
;; SPF : qui a le droit d'envoyer pour ce domaine
|
|
||||||
exemple.fr. IN TXT "v=spf1 mx -all"
|
|
||||||
|
|
||||||
;; DKIM (clé publique, le sélecteur ici est "mail")
|
|
||||||
mail._domainkey.exemple.fr. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhki..."
|
|
||||||
|
|
||||||
;; DMARC
|
|
||||||
_dmarc.exemple.fr. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@exemple.fr; ruf=mailto:dmarc@exemple.fr; fo=1; adkim=s; aspf=s; pct=100"
|
|
||||||
|
|
||||||
;; MTA-STS (cf. §8)
|
|
||||||
_mta-sts.exemple.fr. IN TXT "v=STSv1; id=20260509T000000;"
|
|
||||||
mta-sts.exemple.fr. IN A 203.0.113.10
|
|
||||||
|
|
||||||
;; TLS-RPT (rapports d'erreurs TLS)
|
|
||||||
_smtp._tls.exemple.fr. IN TXT "v=TLSRPTv1; rua=mailto:tls-reports@exemple.fr"
|
|
||||||
|
|
||||||
;; CAA : restreindre qui peut émettre des certifs pour ton domaine
|
|
||||||
exemple.fr. IN CAA 0 issue "letsencrypt.org"
|
|
||||||
|
|
||||||
;; Autodiscover / autoconfig (pour les clients mail)
|
|
||||||
autoconfig.exemple.fr. IN CNAME mail.exemple.fr.
|
|
||||||
autodiscover.exemple.fr. IN CNAME mail.exemple.fr.
|
|
||||||
```
|
|
||||||
|
|
||||||
Détails dans les sections dédiées plus bas.
|
|
||||||
|
|
||||||
**À ne pas oublier** : l'enregistrement PTR (reverse DNS) se configure **chez ton hébergeur**, pas dans ta zone DNS. Il doit pointer `203.0.113.10 → mail.exemple.fr`. C'est traité au §9.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Installation du stack mail
|
|
||||||
|
|
||||||
Sur Debian 12. Ce qui suit est volontairement condensé — pour une configuration ligne par ligne, suis [le tutoriel de référence de Workaround.org](https://workaround.org/ispmail/) qui est l'étalon depuis 20 ans.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Mise à jour
|
|
||||||
apt update && apt upgrade -y
|
|
||||||
|
|
||||||
# Stack de base
|
|
||||||
apt install -y postfix postfix-mysql dovecot-core dovecot-imapd \
|
|
||||||
dovecot-lmtpd dovecot-managesieved dovecot-sieve dovecot-mysql \
|
|
||||||
rspamd redis-server mariadb-server certbot
|
|
||||||
|
|
||||||
# Certificat TLS
|
|
||||||
certbot certonly --standalone -d mail.exemple.fr \
|
|
||||||
-d mta-sts.exemple.fr \
|
|
||||||
-d autoconfig.exemple.fr -d autodiscover.exemple.fr
|
|
||||||
```
|
|
||||||
|
|
||||||
### Postfix : configuration `main.cf` minimale-mais-saine
|
|
||||||
|
|
||||||
```cfg
|
|
||||||
# /etc/postfix/main.cf
|
|
||||||
myhostname = mail.exemple.fr
|
|
||||||
mydomain = exemple.fr
|
|
||||||
myorigin = $mydomain
|
|
||||||
mydestination = # Vide : on utilise virtual_mailbox_domains
|
|
||||||
inet_interfaces = all
|
|
||||||
inet_protocols = all # IPv4 + IPv6
|
|
||||||
|
|
||||||
# TLS — chiffrement obligatoire
|
|
||||||
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.exemple.fr/fullchain.pem
|
|
||||||
smtpd_tls_key_file = /etc/letsencrypt/live/mail.exemple.fr/privkey.pem
|
|
||||||
smtpd_tls_security_level = may
|
|
||||||
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
|
||||||
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
|
||||||
smtp_tls_security_level = may
|
|
||||||
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
|
||||||
smtp_dns_support_level = dnssec # Indispensable pour DANE
|
|
||||||
smtp_tls_security_level = dane # ou "may" si DANE pas encore prêt
|
|
||||||
smtp_host_lookup = dns
|
|
||||||
|
|
||||||
# Restrictions anti-relais ouvert
|
|
||||||
smtpd_relay_restrictions =
|
|
||||||
permit_mynetworks
|
|
||||||
permit_sasl_authenticated
|
|
||||||
reject_unauth_destination
|
|
||||||
|
|
||||||
smtpd_recipient_restrictions =
|
|
||||||
permit_mynetworks
|
|
||||||
permit_sasl_authenticated
|
|
||||||
reject_unauth_destination
|
|
||||||
reject_unknown_recipient_domain
|
|
||||||
reject_invalid_helo_hostname
|
|
||||||
reject_non_fqdn_helo_hostname
|
|
||||||
|
|
||||||
# Limites raisonnables
|
|
||||||
message_size_limit = 52428800 # 50 Mo
|
|
||||||
mailbox_size_limit = 0
|
|
||||||
recipient_delimiter = +
|
|
||||||
|
|
||||||
# Intégration Rspamd
|
|
||||||
smtpd_milters = inet:localhost:11332
|
|
||||||
non_smtpd_milters = inet:localhost:11332
|
|
||||||
milter_protocol = 6
|
|
||||||
milter_default_action = accept
|
|
||||||
milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}
|
|
||||||
|
|
||||||
# Livraison via Dovecot LMTP
|
|
||||||
virtual_transport = lmtp:unix:private/dovecot-lmtp
|
|
||||||
```
|
|
||||||
|
|
||||||
### Dovecot, Rspamd
|
|
||||||
|
|
||||||
Ces composants demandent leurs propres fichiers de configuration. Renvoi explicite vers les tutos qui font autorité :
|
|
||||||
|
|
||||||
- **Workaround.org / ISPmail** : [https://workaround.org/ispmail/](https://workaround.org/ispmail/) — référence francophone et anglophone, mise à jour à chaque version Debian.
|
|
||||||
- **Rspamd quickstart** : [https://www.rspamd.com/doc/tutorials/quickstart.html](https://www.rspamd.com/doc/tutorials/quickstart.html)
|
|
||||||
- **Dovecot wiki** : [https://doc.dovecot.org/](https://doc.dovecot.org/)
|
|
||||||
|
|
||||||
Si tu veux gagner du temps, **Mailcow** (`docker compose`) est aujourd'hui la solution clé-en-main la plus fiable.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. SPF, DKIM, DMARC : les rustines obligatoires
|
|
||||||
|
|
||||||
Sans ces trois enregistrements correctement configurés, **Gmail et Outlook rejetteront ou marqueront en spam** la majorité de tes messages — peu importe ton volume.
|
|
||||||
|
|
||||||
### SPF (Sender Policy Framework)
|
|
||||||
|
|
||||||
Déclare qui a le droit d'envoyer du mail pour ton domaine.
|
|
||||||
|
|
||||||
```dns
|
|
||||||
exemple.fr. IN TXT "v=spf1 mx -all"
|
|
||||||
```
|
|
||||||
|
|
||||||
- `mx` : autorise les serveurs listés dans le MX du domaine.
|
|
||||||
- `-all` : **rejet strict** de tout le reste. Indispensable pour la réputation. Ne jamais utiliser `~all` (softfail) en prod : Gmail aujourd'hui considère `~all` comme un signal faible.
|
|
||||||
|
|
||||||
Si tu envoies aussi via un relais externe (smart host) : ajoute son `include`, ex. `v=spf1 mx include:_spf.mailjet.com -all`.
|
|
||||||
|
|
||||||
**Limite** : un enregistrement SPF doit tenir en **10 lookups DNS maximum**. Au-delà, il est invalide. Vérifie avec [https://www.kitterman.com/spf/validate.html](https://www.kitterman.com/spf/validate.html).
|
|
||||||
|
|
||||||
### DKIM (DomainKeys Identified Mail)
|
|
||||||
|
|
||||||
Signe chaque mail sortant avec une clé privée. Le destinataire vérifie la signature via la clé publique publiée en DNS.
|
|
||||||
|
|
||||||
**Génération de la clé** (Rspamd, sélecteur `mail`, clé 2048 bits) :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p /var/lib/rspamd/dkim
|
|
||||||
rspamadm dkim_keygen -s mail -d exemple.fr -k /var/lib/rspamd/dkim/exemple.fr.mail.key \
|
|
||||||
-b 2048 > /var/lib/rspamd/dkim/exemple.fr.mail.txt
|
|
||||||
chown _rspamd:_rspamd /var/lib/rspamd/dkim/*
|
|
||||||
```
|
|
||||||
|
|
||||||
Le fichier `.txt` contient l'enregistrement DNS à publier :
|
|
||||||
|
|
||||||
```dns
|
|
||||||
mail._domainkey.exemple.fr. IN TXT ( "v=DKIM1; k=rsa; "
|
|
||||||
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..." )
|
|
||||||
```
|
|
||||||
|
|
||||||
Configuration Rspamd (`/etc/rspamd/local.d/dkim_signing.conf`) :
|
|
||||||
|
|
||||||
```conf
|
|
||||||
allow_username_mismatch = true;
|
|
||||||
domain {
|
|
||||||
exemple.fr {
|
|
||||||
path = "/var/lib/rspamd/dkim/exemple.fr.mail.key";
|
|
||||||
selector = "mail";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Recharge : `systemctl restart rspamd`.
|
|
||||||
|
|
||||||
**Vérification** : envoie un mail à [check-auth@verifier.port25.com](mailto:check-auth@verifier.port25.com), tu reçois un rapport complet SPF/DKIM/DMARC en retour. Ou utilise [https://www.mail-tester.com/](https://www.mail-tester.com/) (note sur 10).
|
|
||||||
|
|
||||||
### DMARC (Domain-based Message Authentication, Reporting and Conformance)
|
|
||||||
|
|
||||||
Dit aux serveurs distants quoi faire en cas d'échec SPF/DKIM, et **te renvoie des rapports** sur ce qui passe et ce qui rate.
|
|
||||||
|
|
||||||
```dns
|
|
||||||
_dmarc.exemple.fr. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@exemple.fr; ruf=mailto:dmarc@exemple.fr; fo=1; adkim=s; aspf=s; pct=100"
|
|
||||||
```
|
|
||||||
|
|
||||||
- `p=none` : surveillance seule, **à utiliser pendant 2-4 semaines en démarrage** pour collecter les rapports sans pénaliser.
|
|
||||||
- `p=quarantine` : mise en spam des mails non authentifiés. Cible normale.
|
|
||||||
- `p=reject` : rejet pur. À atteindre en cible finale, **après** avoir vérifié 4 semaines de rapports propres.
|
|
||||||
- `rua` : adresse pour les rapports agrégés (quotidiens).
|
|
||||||
- `ruf` : rapports forensiques (par message). Optionnel.
|
|
||||||
- `adkim=s aspf=s` : alignement strict — le domaine de signature DKIM et le domaine SPF doivent **exactement** correspondre au domaine `From:`.
|
|
||||||
|
|
||||||
**Lecture des rapports DMARC** : ils arrivent en XML, illisibles. Utilise un parseur :
|
|
||||||
|
|
||||||
- [Postmark DMARC Monitoring](https://dmarc.postmarkapp.com/) (gratuit, agrège les rapports dans une UI).
|
|
||||||
- [parsedmarc](https://github.com/domainaware/parsedmarc) (auto-hébergeable, envoie dans Elasticsearch/Splunk/Grafana).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. MTA-STS, TLS-RPT, DANE : aller plus loin
|
|
||||||
|
|
||||||
Ces standards sécurisent le **transport** entre serveurs (chiffrement TLS forcé). Gmail les regarde, Microsoft aussi. Pas obligatoires, mais ils boostent ta réputation.
|
|
||||||
|
|
||||||
### MTA-STS
|
|
||||||
|
|
||||||
Force les serveurs distants à utiliser TLS pour t'envoyer des mails. Trois éléments :
|
|
||||||
|
|
||||||
**1. Enregistrement DNS TXT** :
|
|
||||||
```dns
|
|
||||||
_mta-sts.exemple.fr. IN TXT "v=STSv1; id=20260509T000000;"
|
|
||||||
```
|
|
||||||
|
|
||||||
**2. Sous-domaine `mta-sts.exemple.fr`** servant un fichier en HTTPS à `https://mta-sts.exemple.fr/.well-known/mta-sts.txt` :
|
|
||||||
```
|
|
||||||
version: STSv1
|
|
||||||
mode: enforce
|
|
||||||
mx: mail.exemple.fr
|
|
||||||
max_age: 604800
|
|
||||||
```
|
|
||||||
|
|
||||||
`mode: enforce` est la cible. En démarrage, mets `mode: testing` pendant 1-2 semaines.
|
|
||||||
|
|
||||||
**3. Certificat TLS valide** sur ce sous-domaine (déjà fait via certbot au §6).
|
|
||||||
|
|
||||||
### TLS-RPT
|
|
||||||
|
|
||||||
Demande aux serveurs distants de t'envoyer des rapports en cas d'échec TLS.
|
|
||||||
|
|
||||||
```dns
|
|
||||||
_smtp._tls.exemple.fr. IN TXT "v=TLSRPTv1; rua=mailto:tls-reports@exemple.fr"
|
|
||||||
```
|
|
||||||
|
|
||||||
### DANE (DNS-based Authentication of Named Entities)
|
|
||||||
|
|
||||||
Encore plus solide que MTA-STS, mais nécessite **DNSSEC** activé sur ton domaine. Si ton registrar ne supporte pas DNSSEC, oublie DANE.
|
|
||||||
|
|
||||||
DANE publie un hash du certificat TLS dans un enregistrement TLSA :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Générer l'enregistrement TLSA pour le port 25
|
|
||||||
posttls-finger -t30 -T180 -P /etc/ssl/certs/ -lsecure mail.exemple.fr
|
|
||||||
```
|
|
||||||
|
|
||||||
Ou plus simplement avec [https://www.huque.com/bin/gen_tlsa](https://www.huque.com/bin/gen_tlsa) :
|
|
||||||
|
|
||||||
```dns
|
|
||||||
_25._tcp.mail.exemple.fr. IN TLSA 3 1 1 ABC123...
|
|
||||||
```
|
|
||||||
|
|
||||||
**Vérification globale** de tout ton setup TLS+DANE : [https://internet.nl/mail/](https://internet.nl/mail/) (excellent, recommandé).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. PTR (reverse DNS) et HELO
|
|
||||||
|
|
||||||
**Le PTR est probablement la cause la plus fréquente de rejet par Gmail/Outlook chez les nouveaux auto-hébergés.**
|
|
||||||
|
|
||||||
Règle absolue : **`PTR(IP) == HELO == nom A/AAAA`**, et tout doit être un FQDN cohérent.
|
|
||||||
|
|
||||||
Configure le PTR dans le panneau de ton hébergeur (chez OVH : "IP" → "Reverse DNS") :
|
|
||||||
```
|
|
||||||
203.0.113.10 → mail.exemple.fr
|
|
||||||
2001:db8::10 → mail.exemple.fr
|
|
||||||
```
|
|
||||||
|
|
||||||
Vérifie :
|
|
||||||
```bash
|
|
||||||
dig -x 203.0.113.10 +short
|
|
||||||
# doit retourner : mail.exemple.fr.
|
|
||||||
|
|
||||||
dig mail.exemple.fr A +short
|
|
||||||
# doit retourner : 203.0.113.10
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans Postfix, `myhostname = mail.exemple.fr` et c'est ce qui est annoncé en HELO. Cohérence garantie.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. Warmup d'IP : la phase la plus délicate
|
|
||||||
|
|
||||||
**Une IP neuve = pas de réputation = défiance maximale des gros acteurs.** Tu ne peux pas envoyer 1000 mails le jour 1 sans te griller.
|
|
||||||
|
|
||||||
### Plan de warmup sur 4 à 6 semaines
|
|
||||||
|
|
||||||
| Semaine | Volume max/jour vers Gmail+Outlook | Volume max/jour total | Contenu |
|
|
||||||
|---|---|---|---|
|
|
||||||
| 1 | 20-50 | 100 | Mails à toi-même, comptes test sur Gmail/Outlook/Yahoo. Réponds-y, marque "non spam" si en spam. |
|
|
||||||
| 2 | 100 | 300 | Cercle proche qui sait répondre / interagir. |
|
|
||||||
| 3 | 300 | 1000 | Élargissement progressif. |
|
|
||||||
| 4 | 800 | 3000 | Ouvre aux usages normaux. |
|
|
||||||
| 5+ | 2000+ | volume cible | Stable. |
|
|
||||||
|
|
||||||
**Règles d'or pendant le warmup :**
|
|
||||||
- **Pas de mailing list, pas de notifs automatiques en masse.** Privilégie des mails 1-à-1 conversationnels.
|
|
||||||
- **Demande aux destinataires de répondre** — un mail avec réponse a 100x le poids d'un mail ouvert silencieusement.
|
|
||||||
- **Aucun lien raccourci**, aucun pixel de tracking, aucune image lourde.
|
|
||||||
- **Stop net si ton score Senderscore baisse** ou si Gmail Postmaster Tools (cf. §11) montre du rouge.
|
|
||||||
|
|
||||||
### Si tu as un volume immédiat à envoyer
|
|
||||||
|
|
||||||
Bascule en **option B** (smart host) le temps du warmup, puis rapatrie progressivement en interne en répliquant les volumes ci-dessus.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 11. Postmaster Tools, SNDS, FBL
|
|
||||||
|
|
||||||
Les gros acteurs te donnent des dashboards dédiés. **Inscris-toi à tous, dès la création du domaine.**
|
|
||||||
|
|
||||||
| Service | Acteur | Usage |
|
|
||||||
|---|---|---|
|
|
||||||
| [Google Postmaster Tools](https://postmaster.google.com/) | Gmail | Réputation IP+domaine, taux de spam, authentification, encryption. **Indispensable.** |
|
|
||||||
| [Microsoft SNDS](https://sendersupport.olc.protection.outlook.com/snds/) | Outlook/Hotmail | Smart Network Data Services, qualité de l'IP. |
|
|
||||||
| [Microsoft JMRP](https://sendersupport.olc.protection.outlook.com/pm/) | Outlook | Junk Mail Reporting Program, FBL Microsoft. |
|
|
||||||
| [Yahoo CFL](https://senders.yahooinc.com/complaint-feedback-loop/) | Yahoo | Complaint Feedback Loop. |
|
|
||||||
| [Validity Sender Score](https://senderscore.org/) | Indépendant | Score sur 100, à surveiller. |
|
|
||||||
|
|
||||||
Configure les feedback loops (FBL) : quand un destinataire clique "spam", tu reçois une notification. Ça te permet de désinscrire l'utilisateur **avant qu'il ne dégrade ta réputation**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 12. Liste de désinscription en un clic (RFC 8058)
|
|
||||||
|
|
||||||
**Exigence Google/Microsoft pour les expéditeurs en volume**, mais à mettre en place dès le début même en bas volume.
|
|
||||||
|
|
||||||
Ajoute deux en-têtes à tous les mails non-strictement-personnels :
|
|
||||||
|
|
||||||
```
|
|
||||||
List-Unsubscribe: <https://exemple.fr/unsub?token=abc123>, <mailto:unsub@exemple.fr?subject=unsub-abc123>
|
|
||||||
List-Unsubscribe-Post: List-Unsubscribe=One-Click
|
|
||||||
```
|
|
||||||
|
|
||||||
L'URL HTTPS doit accepter une requête **POST** (pas seulement GET) avec `List-Unsubscribe=One-Click` dans le corps, et désinscrire **immédiatement et silencieusement** sans demander de confirmation.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 13. Anti-spam entrant et hygiène
|
|
||||||
|
|
||||||
Un serveur mail mal configuré côté entrée devient vite un relais de spam ou une cible. Configuration Rspamd minimale :
|
|
||||||
|
|
||||||
```conf
|
|
||||||
# /etc/rspamd/local.d/actions.conf
|
|
||||||
reject = 15;
|
|
||||||
add_header = 6;
|
|
||||||
greylist = 4;
|
|
||||||
```
|
|
||||||
|
|
||||||
```conf
|
|
||||||
# /etc/rspamd/local.d/greylist.conf
|
|
||||||
expire = 1d;
|
|
||||||
```
|
|
||||||
|
|
||||||
Active aussi :
|
|
||||||
- **Vérification SPF/DKIM/DMARC entrante** (par défaut activée dans Rspamd).
|
|
||||||
- **RBL** (Realtime Blackhole Lists) : Spamhaus ZEN, Barracuda. Attention à ne pas multiplier — 2 ou 3 RBL fiables suffisent.
|
|
||||||
- **Greylisting** : refuse temporairement les premiers contacts, ce qui élimine 80% du spam basique. Ne pas activer sur un domaine à fort volume transactionnel (gêne les notifs).
|
|
||||||
- **Bayes** : laisse Rspamd apprendre via le dossier `Junk` de Dovecot (signal `IsSpam` / `IsHam`).
|
|
||||||
|
|
||||||
Mises à jour : `unattended-upgrades` activé, redémarrage planifié, lecture des annonces sécu Postfix/Dovecot.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 14. Monitoring, logs, alertes
|
|
||||||
|
|
||||||
Sans monitoring, tu découvres les problèmes par les utilisateurs. À mettre en place :
|
|
||||||
|
|
||||||
- **Lecture des logs** : `journalctl -u postfix -f`, `tail -f /var/log/mail.log`, web UI de Rspamd sur `localhost:11334`.
|
|
||||||
- **Métriques** : exporter Postfix/Dovecot vers Prometheus + Grafana (`postfix_exporter`, `dovecot_exporter`).
|
|
||||||
- **Alertes** sur :
|
|
||||||
- File d'attente Postfix > 50 messages (`mailq | tail -1`).
|
|
||||||
- Score Senderscore qui chute.
|
|
||||||
- Apparition sur une RBL : surveillance automatisée par [https://multirbl.valli.org/](https://multirbl.valli.org/) ou via un script qui interroge plusieurs DNSBL en cron.
|
|
||||||
- Échec TLS-RPT (rapport entrant signalant une connexion non chiffrée).
|
|
||||||
- **Rapports DMARC** parsés régulièrement (cf. §7).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 15. Que faire quand Gmail rejette quand même ?
|
|
||||||
|
|
||||||
Ça arrive. Diagnostic dans l'ordre :
|
|
||||||
|
|
||||||
1. **Lis le code de rejet SMTP** dans `/var/log/mail.log`. Gmail renvoie des codes très explicites :
|
|
||||||
- `550-5.7.1 [203.0.113.10 19] Our system has detected that this message is likely unsolicited mail.` → contenu jugé spammy. Revois le contenu, ajoute du texte conversationnel, retire les liens douteux.
|
|
||||||
- `421-4.7.0 [203.0.113.10 15] Our system has detected an unusual rate of unsolicited mail.` → tu as dépassé un seuil. **Ralentis immédiatement**, attends 24-48h, reprends doucement.
|
|
||||||
- `550-5.7.26 ... DMARC ...` → ton DMARC ne passe pas. Revérifie SPF/DKIM/alignement.
|
|
||||||
- `550-5.7.1 ... blocked using Spamhaus.` → tu es sur une RBL. Va sur [spamhaus.org/lookup/](https://www.spamhaus.org/lookup/) pour vérifier et demander la sortie.
|
|
||||||
2. **Va dans Postmaster Tools** (§11). Si "IP reputation" est rouge ou orange, regarde le contenu et le timing de tes envois récents.
|
|
||||||
3. **Test mail-tester** : envoie à une adresse fournie par [mail-tester.com](https://www.mail-tester.com/), obtiens une note sur 10. Vise 10/10. Toute case manquante doit être corrigée.
|
|
||||||
4. **Sortie de blacklist** : la plupart des RBL (Spamhaus, Barracuda) ont un formulaire de retrait. Spamhaus retire en quelques heures si tu corriges la cause. SORBS est plus lent. UCEPROTECT exige souvent de payer — ignore-la, peu de serveurs sérieux la consultent.
|
|
||||||
5. **Si rien ne marche**, change d'IP. C'est parfois la seule issue. Demande à ton hébergeur une IP fraîche, refais un warmup.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 16. Checklist finale avant mise en prod
|
|
||||||
|
|
||||||
Avant d'envoyer le premier vrai mail :
|
|
||||||
|
|
||||||
- [ ] Domaine avec DNSSEC activé.
|
|
||||||
- [ ] IP testée sur 5+ blacklists, propre.
|
|
||||||
- [ ] Port 25 sortant ouvert et testé (`telnet gmail-smtp-in.l.google.com 25`).
|
|
||||||
- [ ] PTR configuré et cohérent avec le HELO.
|
|
||||||
- [ ] MX, A, AAAA, SPF, DKIM, DMARC publiés et validés via [mxtoolbox.com](https://mxtoolbox.com/).
|
|
||||||
- [ ] MTA-STS publié (mode `testing` au démarrage).
|
|
||||||
- [ ] TLS-RPT publié.
|
|
||||||
- [ ] DANE/TLSA publié (si DNSSEC OK).
|
|
||||||
- [ ] CAA publié.
|
|
||||||
- [ ] Test envoyé à `check-auth@verifier.port25.com` : tout en `pass`.
|
|
||||||
- [ ] Test [mail-tester.com](https://www.mail-tester.com/) : 10/10.
|
|
||||||
- [ ] Test [internet.nl/mail/](https://internet.nl/mail/) : 100%.
|
|
||||||
- [ ] Inscription Postmaster Tools, SNDS, JMRP, Yahoo CFL.
|
|
||||||
- [ ] DMARC `p=none` au démarrage, parser de rapports en place.
|
|
||||||
- [ ] List-Unsubscribe + List-Unsubscribe-Post implémentés.
|
|
||||||
- [ ] Plan de warmup affiché et respecté.
|
|
||||||
- [ ] Monitoring file d'attente + RBL en place.
|
|
||||||
- [ ] Backup chiffré des Maildir.
|
|
||||||
|
|
||||||
Au bout de 4 semaines de rapports DMARC propres : passage à `p=quarantine`. Au bout de 8-12 semaines : `p=reject`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 17. Annexes : commandes utiles
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Voir la file d'attente
|
|
||||||
mailq
|
|
||||||
postqueue -p
|
|
||||||
|
|
||||||
# Forcer la tentative de livraison
|
|
||||||
postqueue -f
|
|
||||||
|
|
||||||
# Vider la file (à utiliser avec précaution)
|
|
||||||
postsuper -d ALL
|
|
||||||
|
|
||||||
# Tester la délivrabilité d'un domaine cible (DANE / TLS)
|
|
||||||
posttls-finger -t30 -T180 -P /etc/ssl/certs/ -lsecure gmail-smtp-in.l.google.com
|
|
||||||
|
|
||||||
# Vérifier ses propres enregistrements
|
|
||||||
dig exemple.fr TXT +short
|
|
||||||
dig _dmarc.exemple.fr TXT +short
|
|
||||||
dig mail._domainkey.exemple.fr TXT +short
|
|
||||||
dig mail.exemple.fr MX +short
|
|
||||||
dig -x 203.0.113.10 +short
|
|
||||||
|
|
||||||
# Tester l'envoi en SMTP authentifié
|
|
||||||
swaks --to test@gmail.com --from moi@exemple.fr \
|
|
||||||
--server mail.exemple.fr --auth --auth-user moi@exemple.fr \
|
|
||||||
--tls --port 587
|
|
||||||
|
|
||||||
# Voir en temps réel ce qu'il se passe
|
|
||||||
journalctl -u postfix -u dovecot -u rspamd -f
|
|
||||||
```
|
|
||||||
|
|
||||||
### Outils web à mettre en favoris
|
|
||||||
|
|
||||||
- [https://www.mail-tester.com/](https://www.mail-tester.com/) — score sur 10
|
|
||||||
- [https://internet.nl/mail/](https://internet.nl/mail/) — audit complet
|
|
||||||
- [https://mxtoolbox.com/SuperTool.aspx](https://mxtoolbox.com/SuperTool.aspx) — DNS, blacklists
|
|
||||||
- [https://dmarcian.com/dmarc-inspector/](https://dmarcian.com/dmarc-inspector/) — vérif DMARC
|
|
||||||
- [https://www.kitterman.com/spf/validate.html](https://www.kitterman.com/spf/validate.html) — vérif SPF
|
|
||||||
- [https://postmaster.google.com/](https://postmaster.google.com/) — Google Postmaster
|
|
||||||
- [https://senderscore.org/](https://senderscore.org/) — réputation IP
|
|
||||||
|
|
||||||
### Documentation de référence
|
|
||||||
|
|
||||||
- **ISPmail / Workaround.org** — [https://workaround.org/ispmail/](https://workaround.org/ispmail/) — le tutoriel le plus complet et tenu à jour, par version Debian.
|
|
||||||
- **Mailcow docs** — [https://docs.mailcow.email/](https://docs.mailcow.email/) — pour la version conteneurisée clé-en-main.
|
|
||||||
- **Postfix officiel** — [https://www.postfix.org/documentation.html](https://www.postfix.org/documentation.html)
|
|
||||||
- **Rspamd docs** — [https://www.rspamd.com/doc/](https://www.rspamd.com/doc/)
|
|
||||||
- **RFCs essentielles** : 5321 (SMTP moderne), 7208 (SPF), 6376 (DKIM), 7489 (DMARC), 8461 (MTA-STS), 8460 (TLS-RPT), 7672 (DANE-SMTP), 8058 (One-Click Unsubscribe).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
L'auto-hébergement mail en 2026 reste possible, mais c'est devenu un sport : les règles changent, les gros acteurs durcissent leurs critères, et l'écosystème pousse vers la centralisation. Si tu réussis le warmup et tiens 6 mois sans incident, tu as gagné — mais ne baisse pas la garde, un changement unilatéral de Google peut survenir à tout moment, comme en février 2024.
|
|
||||||
@@ -1,605 +0,0 @@
|
|||||||
## Survivre aux règles de Gmail, Outlook et consorts
|
|
||||||
|
|
||||||
> **Contexte** — Cet article de Clubic ([lien](https://www.clubic.com/dossier-612034-proton-mail-infomaniak-la-souverainete-de-vos-emails-reste-une-illusion-face-aux-regles-dictees-par-google.html)) rappelle une vérité technique : SMTP date de 1982, n'a aucune sécurité native, et toutes les "rustines" (SPF, DKIM, DMARC, MTA-STS, DANE) ont été conçues par Yahoo, Cisco, Microsoft, Google. Depuis février 2024 (Google) et mai 2025 (Microsoft), tout expéditeur dépassant 5000 mails/jour vers Gmail/Outlook doit configurer SPF + DKIM + DMARC, maintenir un taux de spam < 0,1 %, et fournir un lien de désinscription en un clic.
|
|
||||||
>
|
|
||||||
> Mais même en dessous de 5000/jour, ces règles s'appliquent en pratique : sans elles, ton mail finit en spam ou est rejeté. Ce dossier décrit comment monter son propre serveur mail tout en passant à travers ces filtres.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Sommaire
|
|
||||||
|
|
||||||
1. [Avant de commencer : est-ce vraiment une bonne idée ?](#1-avant-de-commencer--est-ce-vraiment-une-bonne-idée-)
|
|
||||||
2. [Prérequis techniques](#2-prérequis-techniques)
|
|
||||||
3. [Architecture cible](#3-architecture-cible)
|
|
||||||
4. [Choix du fournisseur et de l'IP](#4-choix-du-fournisseur-et-de-lip)
|
|
||||||
5. [Configuration DNS complète](#5-configuration-dns-complète)
|
|
||||||
6. [Installation du stack mail](#6-installation-du-stack-mail)
|
|
||||||
7. [SPF, DKIM, DMARC : les rustines obligatoires](#7-spf-dkim-dmarc--les-rustines-obligatoires)
|
|
||||||
8. [MTA-STS, TLS-RPT, DANE : aller plus loin](#8-mta-sts-tls-rpt-dane--aller-plus-loin)
|
|
||||||
9. [PTR (reverse DNS) et HELO](#9-ptr-reverse-dns-et-helo)
|
|
||||||
10. [Warmup d'IP : la phase la plus délicate](#10-warmup-dip--la-phase-la-plus-délicate)
|
|
||||||
11. [Postmaster Tools, SNDS, FBL](#11-postmaster-tools-snds-fbl)
|
|
||||||
12. [Liste de désinscription en un clic (RFC 8058)](#12-liste-de-désinscription-en-un-clic-rfc-8058)
|
|
||||||
13. [Anti-spam entrant et hygiène](#13-anti-spam-entrant-et-hygiène)
|
|
||||||
14. [Monitoring, logs, alertes](#14-monitoring-logs-alertes)
|
|
||||||
15. [Que faire quand Gmail rejette quand même ?](#15-que-faire-quand-gmail-rejette-quand-même-)
|
|
||||||
16. [Checklist finale avant mise en prod](#16-checklist-finale-avant-mise-en-prod)
|
|
||||||
17. [Annexes : commandes utiles](#17-annexes--commandes-utiles)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Avant de commencer : est-ce vraiment une bonne idée ?
|
|
||||||
|
|
||||||
L'auto-hébergement mail est techniquement possible, mais c'est probablement le service le plus pénible à maintenir en 2026. Avant de te lancer, lis ça :
|
|
||||||
|
|
||||||
**Ce qui marche bien en auto-hébergé :**
|
|
||||||
- Recevoir du mail (presque tout le monde te livre).
|
|
||||||
- Envoyer vers d'autres serveurs auto-hébergés ou pros bien configurés.
|
|
||||||
- Garder le contrôle sur tes données, tes alias, tes domaines.
|
|
||||||
|
|
||||||
**Ce qui est dur :**
|
|
||||||
- Envoyer vers Gmail / Outlook / Yahoo / iCloud sans atterrir en spam.
|
|
||||||
- Sortir d'une blacklist une fois dedans.
|
|
||||||
- Maintenir un score de réputation IP correct sur la durée.
|
|
||||||
- Survivre à un changement unilatéral des règles côté gros acteurs (cf. février 2024 et mai 2025).
|
|
||||||
|
|
||||||
**Stratégie réaliste recommandée :**
|
|
||||||
- Réception entrante : auto-hébergée à 100 %. Aucun risque, full contrôle.
|
|
||||||
- Envoi sortant : deux options, selon ton volume et ton tolérance au risque.
|
|
||||||
- **Option A — Pure auto-hébergée** : tu envoies directement depuis ton serveur. Faisable, mais demande un warmup, une IP propre, et un suivi continu.
|
|
||||||
- **Option B — Smart host sortant** : tu envoies via un relais réputé (un autre de tes serveurs avec une IP qui a déjà sa réputation, ou un service type Mailjet/Sendgrid/SMTP2GO en bas volume gratuit). Tes mails sortent depuis l'IP du relais, qui a déjà sa réputation faite. C'est un compromis : tu perds une partie de la souveraineté technique, mais tu gagnes énormément en délivrabilité.
|
|
||||||
|
|
||||||
Le reste du dossier suit l'option A — tout en t'expliquant comment basculer en B si nécessaire.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Prérequis techniques
|
|
||||||
|
|
||||||
| Élément | Détail |
|
|
||||||
|---|---|
|
|
||||||
| Domaine | À toi, registrar peu importe, mais avec **DNSSEC activable** (cf. §8 pour DANE). |
|
|
||||||
| Serveur | VPS ou dédié, **2 vCPU / 4 Go RAM minimum**, Debian 12+ ou Ubuntu 24.04 LTS. |
|
|
||||||
| IP fixe v4 | Indispensable. **IP "résidentielle" ou IP de datacenter récemment recyclée = exclues**. |
|
|
||||||
| IP fixe v6 | Recommandée, mais désactivable si l'IPv6 du fournisseur est blacklistée. |
|
|
||||||
| PTR / reverse DNS | **Modifiable par toi**. Si l'hébergeur ne te le permet pas, change d'hébergeur. |
|
|
||||||
| Ports | 25, 465, 587, 993, 4190 ouverts sortants ET entrants. **Le port 25 sortant est bloqué chez beaucoup d'hébergeurs grand public** (OVH résidentiel, Free, etc.) : vérifie avant. |
|
|
||||||
| TLS | Certificat valide (Let's Encrypt suffit). |
|
|
||||||
|
|
||||||
**Compétences attendues** : Linux en ligne de commande, DNS (champs A/AAAA/MX/TXT/SRV/CAA/TLSA), notion de TLS, lecture de logs `journalctl` et `/var/log/mail.log`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Architecture cible
|
|
||||||
|
|
||||||
Un stack standard, éprouvé, en logiciels libres :
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────┐
|
|
||||||
│ Internet (mails entrants/sortants) │
|
|
||||||
└─────────────────────────────────────┘
|
|
||||||
│
|
|
||||||
▼ port 25
|
|
||||||
┌────────────────┐
|
|
||||||
│ Postfix │ ← MTA (envoi/réception SMTP)
|
|
||||||
│ (SMTP / 25/587)│
|
|
||||||
└────────────────┘
|
|
||||||
│ │ │
|
|
||||||
▼ ▼ ▼
|
|
||||||
┌─────────┐ ┌─────────┐ ┌──────────┐
|
|
||||||
│ Rspamd │ │OpenDKIM │ │ Dovecot │ ← IMAP/POP + LMTP
|
|
||||||
│(spam, │ │ (signe │ │ (livrai- │
|
|
||||||
│ DKIM │ │ DKIM) │ │ son aux │
|
|
||||||
│ verif, │ └─────────┘ │ boîtes) │
|
|
||||||
│ DMARC) │ └──────────┘
|
|
||||||
└─────────┘ │
|
|
||||||
▼
|
|
||||||
Maildir / utilisateurs
|
|
||||||
```
|
|
||||||
|
|
||||||
**Composants** :
|
|
||||||
|
|
||||||
- **Postfix** : MTA. Reçoit, route, envoie le SMTP.
|
|
||||||
- **Dovecot** : serveur IMAP/POP3, livraison locale (LMTP), authentification SASL pour Postfix, gestion Sieve (filtres).
|
|
||||||
- **Rspamd** : antispam moderne, fait aussi la **vérification SPF/DKIM/DMARC entrante**, le greylisting, et — option recommandée — la **signature DKIM sortante** (en remplacement d'OpenDKIM).
|
|
||||||
- **Let's Encrypt (certbot)** : TLS.
|
|
||||||
- **(Optionnel) Roundcube ou SnappyMail** : webmail.
|
|
||||||
|
|
||||||
**Alternative tout-en-un** : [Mailcow](https://mailcow.email/) ou [Mailu](https://mailu.io/), basés sur Docker, qui empaquètent tout ça avec une interface admin. Si tu préfères ne pas tout configurer à la main, c'est légitime — la majorité des règles DNS et de délivrabilité de ce dossier restent identiques.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Choix du fournisseur et de l'IP
|
|
||||||
|
|
||||||
Le choix de l'hébergeur conditionne la moitié de ta délivrabilité. Avant de prendre un VPS :
|
|
||||||
|
|
||||||
1. **Le port 25 sortant est-il ouvert ?** Beaucoup d'hébergeurs le bloquent par défaut pour limiter le spam (Hetzner l'ouvre sur demande, OVH l'ouvre selon le produit, Scaleway l'ouvre selon le compte). Pose la question au support **avant** de payer.
|
|
||||||
2. **Le PTR est-il configurable ?** Si non, change.
|
|
||||||
3. **L'IP a-t-elle été utilisée par un spammeur ?** Avant d'acheter le VPS, demande l'IP qu'on te donnera. Vérifie sur :
|
|
||||||
- [mxtoolbox.com/blacklists.aspx](https://mxtoolbox.com/blacklists.aspx)
|
|
||||||
- [multirbl.valli.org](https://multirbl.valli.org/)
|
|
||||||
- [talosintelligence.com](https://www.talosintelligence.com/) (Cisco)
|
|
||||||
- [senderscore.org](https://senderscore.org/)
|
|
||||||
|
|
||||||
Si l'IP est listée sur Spamhaus, Barracuda, SORBS, SpamCop, **demande à l'hébergeur de te l'échanger** ou prends un autre VPS. Une fois listée, tu vas y passer des semaines.
|
|
||||||
4. **Réputation du subnet (`/24`)**. Même si ton IP est propre, si le `/24` est pourri (beaucoup de spammeurs voisins), Gmail va te traiter avec méfiance. Vérifie sur [senderscore.org](https://senderscore.org/) en saisissant ton IP — le score du subnet apparaît.
|
|
||||||
|
|
||||||
**Hébergeurs réputés corrects pour le mail** : Hetzner, OVH (gamme dédiée, pas SoYouStart), Scaleway, Infomaniak (en VPS), Netcup. À éviter pour de l'envoi : DigitalOcean (subnets souvent grillés), Linode/Akamai (idem), AWS EC2 (le port 25 est limité par défaut, et la rate-limit est costaude).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Configuration DNS complète
|
|
||||||
|
|
||||||
Pour un domaine `exemple.fr` avec un serveur mail sur `mail.exemple.fr` à l'IP `203.0.113.10` (et `2001:db8::10` en v6) :
|
|
||||||
|
|
||||||
```dns
|
|
||||||
;; Enregistrement du serveur mail lui-même
|
|
||||||
mail.exemple.fr. IN A 203.0.113.10
|
|
||||||
mail.exemple.fr. IN AAAA 2001:db8::10
|
|
||||||
|
|
||||||
;; MX : qui reçoit les mails du domaine
|
|
||||||
exemple.fr. IN MX 10 mail.exemple.fr.
|
|
||||||
|
|
||||||
;; SPF : qui a le droit d'envoyer pour ce domaine
|
|
||||||
exemple.fr. IN TXT "v=spf1 mx -all"
|
|
||||||
|
|
||||||
;; DKIM (clé publique, le sélecteur ici est "mail")
|
|
||||||
mail._domainkey.exemple.fr. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhki..."
|
|
||||||
|
|
||||||
;; DMARC
|
|
||||||
_dmarc.exemple.fr. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@exemple.fr; ruf=mailto:dmarc@exemple.fr; fo=1; adkim=s; aspf=s; pct=100"
|
|
||||||
|
|
||||||
;; MTA-STS (cf. §8)
|
|
||||||
_mta-sts.exemple.fr. IN TXT "v=STSv1; id=20260509T000000;"
|
|
||||||
mta-sts.exemple.fr. IN A 203.0.113.10
|
|
||||||
|
|
||||||
;; TLS-RPT (rapports d'erreurs TLS)
|
|
||||||
_smtp._tls.exemple.fr. IN TXT "v=TLSRPTv1; rua=mailto:tls-reports@exemple.fr"
|
|
||||||
|
|
||||||
;; CAA : restreindre qui peut émettre des certifs pour ton domaine
|
|
||||||
exemple.fr. IN CAA 0 issue "letsencrypt.org"
|
|
||||||
|
|
||||||
;; Autodiscover / autoconfig (pour les clients mail)
|
|
||||||
autoconfig.exemple.fr. IN CNAME mail.exemple.fr.
|
|
||||||
autodiscover.exemple.fr. IN CNAME mail.exemple.fr.
|
|
||||||
```
|
|
||||||
|
|
||||||
Détails dans les sections dédiées plus bas.
|
|
||||||
|
|
||||||
**À ne pas oublier** : l'enregistrement PTR (reverse DNS) se configure **chez ton hébergeur**, pas dans ta zone DNS. Il doit pointer `203.0.113.10 → mail.exemple.fr`. C'est traité au §9.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Installation du stack mail
|
|
||||||
|
|
||||||
Sur Debian 12. Ce qui suit est volontairement condensé — pour une configuration ligne par ligne, suis [le tutoriel de référence de Workaround.org](https://workaround.org/ispmail/) qui est l'étalon depuis 20 ans.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Mise à jour
|
|
||||||
apt update && apt upgrade -y
|
|
||||||
|
|
||||||
# Stack de base
|
|
||||||
apt install -y postfix postfix-mysql dovecot-core dovecot-imapd \
|
|
||||||
dovecot-lmtpd dovecot-managesieved dovecot-sieve dovecot-mysql \
|
|
||||||
rspamd redis-server mariadb-server certbot
|
|
||||||
|
|
||||||
# Certificat TLS
|
|
||||||
certbot certonly --standalone -d mail.exemple.fr \
|
|
||||||
-d mta-sts.exemple.fr \
|
|
||||||
-d autoconfig.exemple.fr -d autodiscover.exemple.fr
|
|
||||||
```
|
|
||||||
|
|
||||||
### Postfix : configuration `main.cf` minimale-mais-saine
|
|
||||||
|
|
||||||
```cfg
|
|
||||||
# /etc/postfix/main.cf
|
|
||||||
myhostname = mail.exemple.fr
|
|
||||||
mydomain = exemple.fr
|
|
||||||
myorigin = $mydomain
|
|
||||||
mydestination = # Vide : on utilise virtual_mailbox_domains
|
|
||||||
inet_interfaces = all
|
|
||||||
inet_protocols = all # IPv4 + IPv6
|
|
||||||
|
|
||||||
# TLS — chiffrement obligatoire
|
|
||||||
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.exemple.fr/fullchain.pem
|
|
||||||
smtpd_tls_key_file = /etc/letsencrypt/live/mail.exemple.fr/privkey.pem
|
|
||||||
smtpd_tls_security_level = may
|
|
||||||
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
|
||||||
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
|
||||||
smtp_tls_security_level = may
|
|
||||||
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
|
||||||
smtp_dns_support_level = dnssec # Indispensable pour DANE
|
|
||||||
smtp_tls_security_level = dane # ou "may" si DANE pas encore prêt
|
|
||||||
smtp_host_lookup = dns
|
|
||||||
|
|
||||||
# Restrictions anti-relais ouvert
|
|
||||||
smtpd_relay_restrictions =
|
|
||||||
permit_mynetworks
|
|
||||||
permit_sasl_authenticated
|
|
||||||
reject_unauth_destination
|
|
||||||
|
|
||||||
smtpd_recipient_restrictions =
|
|
||||||
permit_mynetworks
|
|
||||||
permit_sasl_authenticated
|
|
||||||
reject_unauth_destination
|
|
||||||
reject_unknown_recipient_domain
|
|
||||||
reject_invalid_helo_hostname
|
|
||||||
reject_non_fqdn_helo_hostname
|
|
||||||
|
|
||||||
# Limites raisonnables
|
|
||||||
message_size_limit = 52428800 # 50 Mo
|
|
||||||
mailbox_size_limit = 0
|
|
||||||
recipient_delimiter = +
|
|
||||||
|
|
||||||
# Intégration Rspamd
|
|
||||||
smtpd_milters = inet:localhost:11332
|
|
||||||
non_smtpd_milters = inet:localhost:11332
|
|
||||||
milter_protocol = 6
|
|
||||||
milter_default_action = accept
|
|
||||||
milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}
|
|
||||||
|
|
||||||
# Livraison via Dovecot LMTP
|
|
||||||
virtual_transport = lmtp:unix:private/dovecot-lmtp
|
|
||||||
```
|
|
||||||
|
|
||||||
### Dovecot, Rspamd
|
|
||||||
|
|
||||||
Ces composants demandent leurs propres fichiers de configuration. Renvoi explicite vers les tutos qui font autorité :
|
|
||||||
|
|
||||||
- **Workaround.org / ISPmail** : [https://workaround.org/ispmail/](https://workaround.org/ispmail/) — référence francophone et anglophone, mise à jour à chaque version Debian.
|
|
||||||
- **Rspamd quickstart** : [https://www.rspamd.com/doc/tutorials/quickstart.html](https://www.rspamd.com/doc/tutorials/quickstart.html)
|
|
||||||
- **Dovecot wiki** : [https://doc.dovecot.org/](https://doc.dovecot.org/)
|
|
||||||
|
|
||||||
Si tu veux gagner du temps, **Mailcow** (`docker compose`) est aujourd'hui la solution clé-en-main la plus fiable.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. SPF, DKIM, DMARC : les rustines obligatoires
|
|
||||||
|
|
||||||
Sans ces trois enregistrements correctement configurés, **Gmail et Outlook rejetteront ou marqueront en spam** la majorité de tes messages — peu importe ton volume.
|
|
||||||
|
|
||||||
### SPF (Sender Policy Framework)
|
|
||||||
|
|
||||||
Déclare qui a le droit d'envoyer du mail pour ton domaine.
|
|
||||||
|
|
||||||
```dns
|
|
||||||
exemple.fr. IN TXT "v=spf1 mx -all"
|
|
||||||
```
|
|
||||||
|
|
||||||
- `mx` : autorise les serveurs listés dans le MX du domaine.
|
|
||||||
- `-all` : **rejet strict** de tout le reste. Indispensable pour la réputation. Ne jamais utiliser `~all` (softfail) en prod : Gmail aujourd'hui considère `~all` comme un signal faible.
|
|
||||||
|
|
||||||
Si tu envoies aussi via un relais externe (smart host) : ajoute son `include`, ex. `v=spf1 mx include:_spf.mailjet.com -all`.
|
|
||||||
|
|
||||||
**Limite** : un enregistrement SPF doit tenir en **10 lookups DNS maximum**. Au-delà, il est invalide. Vérifie avec [https://www.kitterman.com/spf/validate.html](https://www.kitterman.com/spf/validate.html).
|
|
||||||
|
|
||||||
### DKIM (DomainKeys Identified Mail)
|
|
||||||
|
|
||||||
Signe chaque mail sortant avec une clé privée. Le destinataire vérifie la signature via la clé publique publiée en DNS.
|
|
||||||
|
|
||||||
**Génération de la clé** (Rspamd, sélecteur `mail`, clé 2048 bits) :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p /var/lib/rspamd/dkim
|
|
||||||
rspamadm dkim_keygen -s mail -d exemple.fr -k /var/lib/rspamd/dkim/exemple.fr.mail.key \
|
|
||||||
-b 2048 > /var/lib/rspamd/dkim/exemple.fr.mail.txt
|
|
||||||
chown _rspamd:_rspamd /var/lib/rspamd/dkim/*
|
|
||||||
```
|
|
||||||
|
|
||||||
Le fichier `.txt` contient l'enregistrement DNS à publier :
|
|
||||||
|
|
||||||
```dns
|
|
||||||
mail._domainkey.exemple.fr. IN TXT ( "v=DKIM1; k=rsa; "
|
|
||||||
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..." )
|
|
||||||
```
|
|
||||||
|
|
||||||
Configuration Rspamd (`/etc/rspamd/local.d/dkim_signing.conf`) :
|
|
||||||
|
|
||||||
```conf
|
|
||||||
allow_username_mismatch = true;
|
|
||||||
domain {
|
|
||||||
exemple.fr {
|
|
||||||
path = "/var/lib/rspamd/dkim/exemple.fr.mail.key";
|
|
||||||
selector = "mail";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Recharge : `systemctl restart rspamd`.
|
|
||||||
|
|
||||||
**Vérification** : envoie un mail à [check-auth@verifier.port25.com](mailto:check-auth@verifier.port25.com), tu reçois un rapport complet SPF/DKIM/DMARC en retour. Ou utilise [https://www.mail-tester.com/](https://www.mail-tester.com/) (note sur 10).
|
|
||||||
|
|
||||||
### DMARC (Domain-based Message Authentication, Reporting and Conformance)
|
|
||||||
|
|
||||||
Dit aux serveurs distants quoi faire en cas d'échec SPF/DKIM, et **te renvoie des rapports** sur ce qui passe et ce qui rate.
|
|
||||||
|
|
||||||
```dns
|
|
||||||
_dmarc.exemple.fr. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@exemple.fr; ruf=mailto:dmarc@exemple.fr; fo=1; adkim=s; aspf=s; pct=100"
|
|
||||||
```
|
|
||||||
|
|
||||||
- `p=none` : surveillance seule, **à utiliser pendant 2-4 semaines en démarrage** pour collecter les rapports sans pénaliser.
|
|
||||||
- `p=quarantine` : mise en spam des mails non authentifiés. Cible normale.
|
|
||||||
- `p=reject` : rejet pur. À atteindre en cible finale, **après** avoir vérifié 4 semaines de rapports propres.
|
|
||||||
- `rua` : adresse pour les rapports agrégés (quotidiens).
|
|
||||||
- `ruf` : rapports forensiques (par message). Optionnel.
|
|
||||||
- `adkim=s aspf=s` : alignement strict — le domaine de signature DKIM et le domaine SPF doivent **exactement** correspondre au domaine `From:`.
|
|
||||||
|
|
||||||
**Lecture des rapports DMARC** : ils arrivent en XML, illisibles. Utilise un parseur :
|
|
||||||
|
|
||||||
- [Postmark DMARC Monitoring](https://dmarc.postmarkapp.com/) (gratuit, agrège les rapports dans une UI).
|
|
||||||
- [parsedmarc](https://github.com/domainaware/parsedmarc) (auto-hébergeable, envoie dans Elasticsearch/Splunk/Grafana).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. MTA-STS, TLS-RPT, DANE : aller plus loin
|
|
||||||
|
|
||||||
Ces standards sécurisent le **transport** entre serveurs (chiffrement TLS forcé). Gmail les regarde, Microsoft aussi. Pas obligatoires, mais ils boostent ta réputation.
|
|
||||||
|
|
||||||
### MTA-STS
|
|
||||||
|
|
||||||
Force les serveurs distants à utiliser TLS pour t'envoyer des mails. Trois éléments :
|
|
||||||
|
|
||||||
**1. Enregistrement DNS TXT** :
|
|
||||||
```dns
|
|
||||||
_mta-sts.exemple.fr. IN TXT "v=STSv1; id=20260509T000000;"
|
|
||||||
```
|
|
||||||
|
|
||||||
**2. Sous-domaine `mta-sts.exemple.fr`** servant un fichier en HTTPS à `https://mta-sts.exemple.fr/.well-known/mta-sts.txt` :
|
|
||||||
```
|
|
||||||
version: STSv1
|
|
||||||
mode: enforce
|
|
||||||
mx: mail.exemple.fr
|
|
||||||
max_age: 604800
|
|
||||||
```
|
|
||||||
|
|
||||||
`mode: enforce` est la cible. En démarrage, mets `mode: testing` pendant 1-2 semaines.
|
|
||||||
|
|
||||||
**3. Certificat TLS valide** sur ce sous-domaine (déjà fait via certbot au §6).
|
|
||||||
|
|
||||||
### TLS-RPT
|
|
||||||
|
|
||||||
Demande aux serveurs distants de t'envoyer des rapports en cas d'échec TLS.
|
|
||||||
|
|
||||||
```dns
|
|
||||||
_smtp._tls.exemple.fr. IN TXT "v=TLSRPTv1; rua=mailto:tls-reports@exemple.fr"
|
|
||||||
```
|
|
||||||
|
|
||||||
### DANE (DNS-based Authentication of Named Entities)
|
|
||||||
|
|
||||||
Encore plus solide que MTA-STS, mais nécessite **DNSSEC** activé sur ton domaine. Si ton registrar ne supporte pas DNSSEC, oublie DANE.
|
|
||||||
|
|
||||||
DANE publie un hash du certificat TLS dans un enregistrement TLSA :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Générer l'enregistrement TLSA pour le port 25
|
|
||||||
posttls-finger -t30 -T180 -P /etc/ssl/certs/ -lsecure mail.exemple.fr
|
|
||||||
```
|
|
||||||
|
|
||||||
Ou plus simplement avec [https://www.huque.com/bin/gen_tlsa](https://www.huque.com/bin/gen_tlsa) :
|
|
||||||
|
|
||||||
```dns
|
|
||||||
_25._tcp.mail.exemple.fr. IN TLSA 3 1 1 ABC123...
|
|
||||||
```
|
|
||||||
|
|
||||||
**Vérification globale** de tout ton setup TLS+DANE : [https://internet.nl/mail/](https://internet.nl/mail/) (excellent, recommandé).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. PTR (reverse DNS) et HELO
|
|
||||||
|
|
||||||
**Le PTR est probablement la cause la plus fréquente de rejet par Gmail/Outlook chez les nouveaux auto-hébergés.**
|
|
||||||
|
|
||||||
Règle absolue : **`PTR(IP) == HELO == nom A/AAAA`**, et tout doit être un FQDN cohérent.
|
|
||||||
|
|
||||||
Configure le PTR dans le panneau de ton hébergeur (chez OVH : "IP" → "Reverse DNS") :
|
|
||||||
```
|
|
||||||
203.0.113.10 → mail.exemple.fr
|
|
||||||
2001:db8::10 → mail.exemple.fr
|
|
||||||
```
|
|
||||||
|
|
||||||
Vérifie :
|
|
||||||
```bash
|
|
||||||
dig -x 203.0.113.10 +short
|
|
||||||
# doit retourner : mail.exemple.fr.
|
|
||||||
|
|
||||||
dig mail.exemple.fr A +short
|
|
||||||
# doit retourner : 203.0.113.10
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans Postfix, `myhostname = mail.exemple.fr` et c'est ce qui est annoncé en HELO. Cohérence garantie.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. Warmup d'IP : la phase la plus délicate
|
|
||||||
|
|
||||||
**Une IP neuve = pas de réputation = défiance maximale des gros acteurs.** Tu ne peux pas envoyer 1000 mails le jour 1 sans te griller.
|
|
||||||
|
|
||||||
### Plan de warmup sur 4 à 6 semaines
|
|
||||||
|
|
||||||
| Semaine | Volume max/jour vers Gmail+Outlook | Volume max/jour total | Contenu |
|
|
||||||
|---|---|---|---|
|
|
||||||
| 1 | 20-50 | 100 | Mails à toi-même, comptes test sur Gmail/Outlook/Yahoo. Réponds-y, marque "non spam" si en spam. |
|
|
||||||
| 2 | 100 | 300 | Cercle proche qui sait répondre / interagir. |
|
|
||||||
| 3 | 300 | 1000 | Élargissement progressif. |
|
|
||||||
| 4 | 800 | 3000 | Ouvre aux usages normaux. |
|
|
||||||
| 5+ | 2000+ | volume cible | Stable. |
|
|
||||||
|
|
||||||
**Règles d'or pendant le warmup :**
|
|
||||||
- **Pas de mailing list, pas de notifs automatiques en masse.** Privilégie des mails 1-à-1 conversationnels.
|
|
||||||
- **Demande aux destinataires de répondre** — un mail avec réponse a 100x le poids d'un mail ouvert silencieusement.
|
|
||||||
- **Aucun lien raccourci**, aucun pixel de tracking, aucune image lourde.
|
|
||||||
- **Stop net si ton score Senderscore baisse** ou si Gmail Postmaster Tools (cf. §11) montre du rouge.
|
|
||||||
|
|
||||||
### Si tu as un volume immédiat à envoyer
|
|
||||||
|
|
||||||
Bascule en **option B** (smart host) le temps du warmup, puis rapatrie progressivement en interne en répliquant les volumes ci-dessus.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 11. Postmaster Tools, SNDS, FBL
|
|
||||||
|
|
||||||
Les gros acteurs te donnent des dashboards dédiés. **Inscris-toi à tous, dès la création du domaine.**
|
|
||||||
|
|
||||||
| Service | Acteur | Usage |
|
|
||||||
|---|---|---|
|
|
||||||
| [Google Postmaster Tools](https://postmaster.google.com/) | Gmail | Réputation IP+domaine, taux de spam, authentification, encryption. **Indispensable.** |
|
|
||||||
| [Microsoft SNDS](https://sendersupport.olc.protection.outlook.com/snds/) | Outlook/Hotmail | Smart Network Data Services, qualité de l'IP. |
|
|
||||||
| [Microsoft JMRP](https://sendersupport.olc.protection.outlook.com/pm/) | Outlook | Junk Mail Reporting Program, FBL Microsoft. |
|
|
||||||
| [Yahoo CFL](https://senders.yahooinc.com/complaint-feedback-loop/) | Yahoo | Complaint Feedback Loop. |
|
|
||||||
| [Validity Sender Score](https://senderscore.org/) | Indépendant | Score sur 100, à surveiller. |
|
|
||||||
|
|
||||||
Configure les feedback loops (FBL) : quand un destinataire clique "spam", tu reçois une notification. Ça te permet de désinscrire l'utilisateur **avant qu'il ne dégrade ta réputation**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 12. Liste de désinscription en un clic (RFC 8058)
|
|
||||||
|
|
||||||
**Exigence Google/Microsoft pour les expéditeurs en volume**, mais à mettre en place dès le début même en bas volume.
|
|
||||||
|
|
||||||
Ajoute deux en-têtes à tous les mails non-strictement-personnels :
|
|
||||||
|
|
||||||
```
|
|
||||||
List-Unsubscribe: <https://exemple.fr/unsub?token=abc123>, <mailto:unsub@exemple.fr?subject=unsub-abc123>
|
|
||||||
List-Unsubscribe-Post: List-Unsubscribe=One-Click
|
|
||||||
```
|
|
||||||
|
|
||||||
L'URL HTTPS doit accepter une requête **POST** (pas seulement GET) avec `List-Unsubscribe=One-Click` dans le corps, et désinscrire **immédiatement et silencieusement** sans demander de confirmation.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 13. Anti-spam entrant et hygiène
|
|
||||||
|
|
||||||
Un serveur mail mal configuré côté entrée devient vite un relais de spam ou une cible. Configuration Rspamd minimale :
|
|
||||||
|
|
||||||
```conf
|
|
||||||
# /etc/rspamd/local.d/actions.conf
|
|
||||||
reject = 15;
|
|
||||||
add_header = 6;
|
|
||||||
greylist = 4;
|
|
||||||
```
|
|
||||||
|
|
||||||
```conf
|
|
||||||
# /etc/rspamd/local.d/greylist.conf
|
|
||||||
expire = 1d;
|
|
||||||
```
|
|
||||||
|
|
||||||
Active aussi :
|
|
||||||
- **Vérification SPF/DKIM/DMARC entrante** (par défaut activée dans Rspamd).
|
|
||||||
- **RBL** (Realtime Blackhole Lists) : Spamhaus ZEN, Barracuda. Attention à ne pas multiplier — 2 ou 3 RBL fiables suffisent.
|
|
||||||
- **Greylisting** : refuse temporairement les premiers contacts, ce qui élimine 80% du spam basique. Ne pas activer sur un domaine à fort volume transactionnel (gêne les notifs).
|
|
||||||
- **Bayes** : laisse Rspamd apprendre via le dossier `Junk` de Dovecot (signal `IsSpam` / `IsHam`).
|
|
||||||
|
|
||||||
Mises à jour : `unattended-upgrades` activé, redémarrage planifié, lecture des annonces sécu Postfix/Dovecot.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 14. Monitoring, logs, alertes
|
|
||||||
|
|
||||||
Sans monitoring, tu découvres les problèmes par les utilisateurs. À mettre en place :
|
|
||||||
|
|
||||||
- **Lecture des logs** : `journalctl -u postfix -f`, `tail -f /var/log/mail.log`, web UI de Rspamd sur `localhost:11334`.
|
|
||||||
- **Métriques** : exporter Postfix/Dovecot vers Prometheus + Grafana (`postfix_exporter`, `dovecot_exporter`).
|
|
||||||
- **Alertes** sur :
|
|
||||||
- File d'attente Postfix > 50 messages (`mailq | tail -1`).
|
|
||||||
- Score Senderscore qui chute.
|
|
||||||
- Apparition sur une RBL : surveillance automatisée par [https://multirbl.valli.org/](https://multirbl.valli.org/) ou via un script qui interroge plusieurs DNSBL en cron.
|
|
||||||
- Échec TLS-RPT (rapport entrant signalant une connexion non chiffrée).
|
|
||||||
- **Rapports DMARC** parsés régulièrement (cf. §7).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 15. Que faire quand Gmail rejette quand même ?
|
|
||||||
|
|
||||||
Ça arrive. Diagnostic dans l'ordre :
|
|
||||||
|
|
||||||
1. **Lis le code de rejet SMTP** dans `/var/log/mail.log`. Gmail renvoie des codes très explicites :
|
|
||||||
- `550-5.7.1 [203.0.113.10 19] Our system has detected that this message is likely unsolicited mail.` → contenu jugé spammy. Revois le contenu, ajoute du texte conversationnel, retire les liens douteux.
|
|
||||||
- `421-4.7.0 [203.0.113.10 15] Our system has detected an unusual rate of unsolicited mail.` → tu as dépassé un seuil. **Ralentis immédiatement**, attends 24-48h, reprends doucement.
|
|
||||||
- `550-5.7.26 ... DMARC ...` → ton DMARC ne passe pas. Revérifie SPF/DKIM/alignement.
|
|
||||||
- `550-5.7.1 ... blocked using Spamhaus.` → tu es sur une RBL. Va sur [spamhaus.org/lookup/](https://www.spamhaus.org/lookup/) pour vérifier et demander la sortie.
|
|
||||||
2. **Va dans Postmaster Tools** (§11). Si "IP reputation" est rouge ou orange, regarde le contenu et le timing de tes envois récents.
|
|
||||||
3. **Test mail-tester** : envoie à une adresse fournie par [mail-tester.com](https://www.mail-tester.com/), obtiens une note sur 10. Vise 10/10. Toute case manquante doit être corrigée.
|
|
||||||
4. **Sortie de blacklist** : la plupart des RBL (Spamhaus, Barracuda) ont un formulaire de retrait. Spamhaus retire en quelques heures si tu corriges la cause. SORBS est plus lent. UCEPROTECT exige souvent de payer — ignore-la, peu de serveurs sérieux la consultent.
|
|
||||||
5. **Si rien ne marche**, change d'IP. C'est parfois la seule issue. Demande à ton hébergeur une IP fraîche, refais un warmup.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 16. Checklist finale avant mise en prod
|
|
||||||
|
|
||||||
Avant d'envoyer le premier vrai mail :
|
|
||||||
|
|
||||||
- [ ] Domaine avec DNSSEC activé.
|
|
||||||
- [ ] IP testée sur 5+ blacklists, propre.
|
|
||||||
- [ ] Port 25 sortant ouvert et testé (`telnet gmail-smtp-in.l.google.com 25`).
|
|
||||||
- [ ] PTR configuré et cohérent avec le HELO.
|
|
||||||
- [ ] MX, A, AAAA, SPF, DKIM, DMARC publiés et validés via [mxtoolbox.com](https://mxtoolbox.com/).
|
|
||||||
- [ ] MTA-STS publié (mode `testing` au démarrage).
|
|
||||||
- [ ] TLS-RPT publié.
|
|
||||||
- [ ] DANE/TLSA publié (si DNSSEC OK).
|
|
||||||
- [ ] CAA publié.
|
|
||||||
- [ ] Test envoyé à `check-auth@verifier.port25.com` : tout en `pass`.
|
|
||||||
- [ ] Test [mail-tester.com](https://www.mail-tester.com/) : 10/10.
|
|
||||||
- [ ] Test [internet.nl/mail/](https://internet.nl/mail/) : 100%.
|
|
||||||
- [ ] Inscription Postmaster Tools, SNDS, JMRP, Yahoo CFL.
|
|
||||||
- [ ] DMARC `p=none` au démarrage, parser de rapports en place.
|
|
||||||
- [ ] List-Unsubscribe + List-Unsubscribe-Post implémentés.
|
|
||||||
- [ ] Plan de warmup affiché et respecté.
|
|
||||||
- [ ] Monitoring file d'attente + RBL en place.
|
|
||||||
- [ ] Backup chiffré des Maildir.
|
|
||||||
|
|
||||||
Au bout de 4 semaines de rapports DMARC propres : passage à `p=quarantine`. Au bout de 8-12 semaines : `p=reject`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 17. Annexes : commandes utiles
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Voir la file d'attente
|
|
||||||
mailq
|
|
||||||
postqueue -p
|
|
||||||
|
|
||||||
# Forcer la tentative de livraison
|
|
||||||
postqueue -f
|
|
||||||
|
|
||||||
# Vider la file (à utiliser avec précaution)
|
|
||||||
postsuper -d ALL
|
|
||||||
|
|
||||||
# Tester la délivrabilité d'un domaine cible (DANE / TLS)
|
|
||||||
posttls-finger -t30 -T180 -P /etc/ssl/certs/ -lsecure gmail-smtp-in.l.google.com
|
|
||||||
|
|
||||||
# Vérifier ses propres enregistrements
|
|
||||||
dig exemple.fr TXT +short
|
|
||||||
dig _dmarc.exemple.fr TXT +short
|
|
||||||
dig mail._domainkey.exemple.fr TXT +short
|
|
||||||
dig mail.exemple.fr MX +short
|
|
||||||
dig -x 203.0.113.10 +short
|
|
||||||
|
|
||||||
# Tester l'envoi en SMTP authentifié
|
|
||||||
swaks --to test@gmail.com --from moi@exemple.fr \
|
|
||||||
--server mail.exemple.fr --auth --auth-user moi@exemple.fr \
|
|
||||||
--tls --port 587
|
|
||||||
|
|
||||||
# Voir en temps réel ce qu'il se passe
|
|
||||||
journalctl -u postfix -u dovecot -u rspamd -f
|
|
||||||
```
|
|
||||||
|
|
||||||
### Outils web à mettre en favoris
|
|
||||||
|
|
||||||
- [https://www.mail-tester.com/](https://www.mail-tester.com/) — score sur 10
|
|
||||||
- [https://internet.nl/mail/](https://internet.nl/mail/) — audit complet
|
|
||||||
- [https://mxtoolbox.com/SuperTool.aspx](https://mxtoolbox.com/SuperTool.aspx) — DNS, blacklists
|
|
||||||
- [https://dmarcian.com/dmarc-inspector/](https://dmarcian.com/dmarc-inspector/) — vérif DMARC
|
|
||||||
- [https://www.kitterman.com/spf/validate.html](https://www.kitterman.com/spf/validate.html) — vérif SPF
|
|
||||||
- [https://postmaster.google.com/](https://postmaster.google.com/) — Google Postmaster
|
|
||||||
- [https://senderscore.org/](https://senderscore.org/) — réputation IP
|
|
||||||
|
|
||||||
### Documentation de référence
|
|
||||||
|
|
||||||
- **ISPmail / Workaround.org** — [https://workaround.org/ispmail/](https://workaround.org/ispmail/) — le tutoriel le plus complet et tenu à jour, par version Debian.
|
|
||||||
- **Mailcow docs** — [https://docs.mailcow.email/](https://docs.mailcow.email/) — pour la version conteneurisée clé-en-main.
|
|
||||||
- **Postfix officiel** — [https://www.postfix.org/documentation.html](https://www.postfix.org/documentation.html)
|
|
||||||
- **Rspamd docs** — [https://www.rspamd.com/doc/](https://www.rspamd.com/doc/)
|
|
||||||
- **RFCs essentielles** : 5321 (SMTP moderne), 7208 (SPF), 6376 (DKIM), 7489 (DMARC), 8461 (MTA-STS), 8460 (TLS-RPT), 7672 (DANE-SMTP), 8058 (One-Click Unsubscribe).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
L'auto-hébergement mail en 2026 reste possible, mais c'est devenu un sport : les règles changent, les gros acteurs durcissent leurs critères, et l'écosystème pousse vers la centralisation. Si tu réussis le warmup et tiens 6 mois sans incident, tu as gagné — mais ne baisse pas la garde, un changement unilatéral de Google peut survenir à tout moment, comme en février 2024.
|
|
||||||
@@ -1,605 +0,0 @@
|
|||||||
## Survivre aux règles de Gmail, Outlook et consorts
|
|
||||||
|
|
||||||
> **Contexte** — Cet article de Clubic ([lien](https://www.clubic.com/dossier-612034-proton-mail-infomaniak-la-souverainete-de-vos-emails-reste-une-illusion-face-aux-regles-dictees-par-google.html)) rappelle une vérité technique : SMTP date de 1982, n'a aucune sécurité native, et toutes les "rustines" (SPF, DKIM, DMARC, MTA-STS, DANE) ont été conçues par Yahoo, Cisco, Microsoft, Google. Depuis février 2024 (Google) et mai 2025 (Microsoft), tout expéditeur dépassant 5000 mails/jour vers Gmail/Outlook doit configurer SPF + DKIM + DMARC, maintenir un taux de spam < 0,1 %, et fournir un lien de désinscription en un clic.
|
|
||||||
>
|
|
||||||
> Mais même en dessous de 5000/jour, ces règles s'appliquent en pratique : sans elles, ton mail finit en spam ou est rejeté. Ce dossier décrit comment monter son propre serveur mail tout en passant à travers ces filtres.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Sommaire
|
|
||||||
|
|
||||||
1. [Avant de commencer : est-ce vraiment une bonne idée ?](#1-avant-de-commencer--est-ce-vraiment-une-bonne-idée-)
|
|
||||||
2. [Prérequis techniques](#2-prérequis-techniques)
|
|
||||||
3. [Architecture cible](#3-architecture-cible)
|
|
||||||
4. [Choix du fournisseur et de l'IP](#4-choix-du-fournisseur-et-de-lip)
|
|
||||||
5. [Configuration DNS complète](#5-configuration-dns-complète)
|
|
||||||
6. [Installation du stack mail](#6-installation-du-stack-mail)
|
|
||||||
7. [SPF, DKIM, DMARC : les rustines obligatoires](#7-spf-dkim-dmarc--les-rustines-obligatoires)
|
|
||||||
8. [MTA-STS, TLS-RPT, DANE : aller plus loin](#8-mta-sts-tls-rpt-dane--aller-plus-loin)
|
|
||||||
9. [PTR (reverse DNS) et HELO](#9-ptr-reverse-dns-et-helo)
|
|
||||||
10. [Warmup d'IP : la phase la plus délicate](#10-warmup-dip--la-phase-la-plus-délicate)
|
|
||||||
11. [Postmaster Tools, SNDS, FBL](#11-postmaster-tools-snds-fbl)
|
|
||||||
12. [Liste de désinscription en un clic (RFC 8058)](#12-liste-de-désinscription-en-un-clic-rfc-8058)
|
|
||||||
13. [Anti-spam entrant et hygiène](#13-anti-spam-entrant-et-hygiène)
|
|
||||||
14. [Monitoring, logs, alertes](#14-monitoring-logs-alertes)
|
|
||||||
15. [Que faire quand Gmail rejette quand même ?](#15-que-faire-quand-gmail-rejette-quand-même-)
|
|
||||||
16. [Checklist finale avant mise en prod](#16-checklist-finale-avant-mise-en-prod)
|
|
||||||
17. [Annexes : commandes utiles](#17-annexes--commandes-utiles)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Avant de commencer : est-ce vraiment une bonne idée ?
|
|
||||||
|
|
||||||
L'auto-hébergement mail est techniquement possible, mais c'est probablement le service le plus pénible à maintenir en 2026. Avant de te lancer, lis ça :
|
|
||||||
|
|
||||||
**Ce qui marche bien en auto-hébergé :**
|
|
||||||
- Recevoir du mail (presque tout le monde te livre).
|
|
||||||
- Envoyer vers d'autres serveurs auto-hébergés ou pros bien configurés.
|
|
||||||
- Garder le contrôle sur tes données, tes alias, tes domaines.
|
|
||||||
|
|
||||||
**Ce qui est dur :**
|
|
||||||
- Envoyer vers Gmail / Outlook / Yahoo / iCloud sans atterrir en spam.
|
|
||||||
- Sortir d'une blacklist une fois dedans.
|
|
||||||
- Maintenir un score de réputation IP correct sur la durée.
|
|
||||||
- Survivre à un changement unilatéral des règles côté gros acteurs (cf. février 2024 et mai 2025).
|
|
||||||
|
|
||||||
**Stratégie réaliste recommandée :**
|
|
||||||
- Réception entrante : auto-hébergée à 100 %. Aucun risque, full contrôle.
|
|
||||||
- Envoi sortant : deux options, selon ton volume et ton tolérance au risque.
|
|
||||||
- **Option A — Pure auto-hébergée** : tu envoies directement depuis ton serveur. Faisable, mais demande un warmup, une IP propre, et un suivi continu.
|
|
||||||
- **Option B — Smart host sortant** : tu envoies via un relais réputé (un autre de tes serveurs avec une IP qui a déjà sa réputation, ou un service type Mailjet/Sendgrid/SMTP2GO en bas volume gratuit). Tes mails sortent depuis l'IP du relais, qui a déjà sa réputation faite. C'est un compromis : tu perds une partie de la souveraineté technique, mais tu gagnes énormément en délivrabilité.
|
|
||||||
|
|
||||||
Le reste du dossier suit l'option A — tout en t'expliquant comment basculer en B si nécessaire.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Prérequis techniques
|
|
||||||
|
|
||||||
| Élément | Détail |
|
|
||||||
|---|---|
|
|
||||||
| Domaine | À toi, registrar peu importe, mais avec **DNSSEC activable** (cf. §8 pour DANE). |
|
|
||||||
| Serveur | VPS ou dédié, **2 vCPU / 4 Go RAM minimum**, Debian 12+ ou Ubuntu 24.04 LTS. |
|
|
||||||
| IP fixe v4 | Indispensable. **IP "résidentielle" ou IP de datacenter récemment recyclée = exclues**. |
|
|
||||||
| IP fixe v6 | Recommandée, mais désactivable si l'IPv6 du fournisseur est blacklistée. |
|
|
||||||
| PTR / reverse DNS | **Modifiable par toi**. Si l'hébergeur ne te le permet pas, change d'hébergeur. |
|
|
||||||
| Ports | 25, 465, 587, 993, 4190 ouverts sortants ET entrants. **Le port 25 sortant est bloqué chez beaucoup d'hébergeurs grand public** (OVH résidentiel, Free, etc.) : vérifie avant. |
|
|
||||||
| TLS | Certificat valide (Let's Encrypt suffit). |
|
|
||||||
|
|
||||||
**Compétences attendues** : Linux en ligne de commande, DNS (champs A/AAAA/MX/TXT/SRV/CAA/TLSA), notion de TLS, lecture de logs `journalctl` et `/var/log/mail.log`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Architecture cible
|
|
||||||
|
|
||||||
Un stack standard, éprouvé, en logiciels libres :
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────┐
|
|
||||||
│ Internet (mails entrants/sortants) │
|
|
||||||
└─────────────────────────────────────┘
|
|
||||||
│
|
|
||||||
▼ port 25
|
|
||||||
┌────────────────┐
|
|
||||||
│ Postfix │ ← MTA (envoi/réception SMTP)
|
|
||||||
│ (SMTP / 25/587)│
|
|
||||||
└────────────────┘
|
|
||||||
│ │ │
|
|
||||||
▼ ▼ ▼
|
|
||||||
┌─────────┐ ┌─────────┐ ┌──────────┐
|
|
||||||
│ Rspamd │ │OpenDKIM │ │ Dovecot │ ← IMAP/POP + LMTP
|
|
||||||
│(spam, │ │ (signe │ │ (livrai- │
|
|
||||||
│ DKIM │ │ DKIM) │ │ son aux │
|
|
||||||
│ verif, │ └─────────┘ │ boîtes) │
|
|
||||||
│ DMARC) │ └──────────┘
|
|
||||||
└─────────┘ │
|
|
||||||
▼
|
|
||||||
Maildir / utilisateurs
|
|
||||||
```
|
|
||||||
|
|
||||||
**Composants** :
|
|
||||||
|
|
||||||
- **Postfix** : MTA. Reçoit, route, envoie le SMTP.
|
|
||||||
- **Dovecot** : serveur IMAP/POP3, livraison locale (LMTP), authentification SASL pour Postfix, gestion Sieve (filtres).
|
|
||||||
- **Rspamd** : antispam moderne, fait aussi la **vérification SPF/DKIM/DMARC entrante**, le greylisting, et — option recommandée — la **signature DKIM sortante** (en remplacement d'OpenDKIM).
|
|
||||||
- **Let's Encrypt (certbot)** : TLS.
|
|
||||||
- **(Optionnel) Roundcube ou SnappyMail** : webmail.
|
|
||||||
|
|
||||||
**Alternative tout-en-un** : [Mailcow](https://mailcow.email/) ou [Mailu](https://mailu.io/), basés sur Docker, qui empaquètent tout ça avec une interface admin. Si tu préfères ne pas tout configurer à la main, c'est légitime — la majorité des règles DNS et de délivrabilité de ce dossier restent identiques.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Choix du fournisseur et de l'IP
|
|
||||||
|
|
||||||
Le choix de l'hébergeur conditionne la moitié de ta délivrabilité. Avant de prendre un VPS :
|
|
||||||
|
|
||||||
1. **Le port 25 sortant est-il ouvert ?** Beaucoup d'hébergeurs le bloquent par défaut pour limiter le spam (Hetzner l'ouvre sur demande, OVH l'ouvre selon le produit, Scaleway l'ouvre selon le compte). Pose la question au support **avant** de payer.
|
|
||||||
2. **Le PTR est-il configurable ?** Si non, change.
|
|
||||||
3. **L'IP a-t-elle été utilisée par un spammeur ?** Avant d'acheter le VPS, demande l'IP qu'on te donnera. Vérifie sur :
|
|
||||||
- [mxtoolbox.com/blacklists.aspx](https://mxtoolbox.com/blacklists.aspx)
|
|
||||||
- [multirbl.valli.org](https://multirbl.valli.org/)
|
|
||||||
- [talosintelligence.com](https://www.talosintelligence.com/) (Cisco)
|
|
||||||
- [senderscore.org](https://senderscore.org/)
|
|
||||||
|
|
||||||
Si l'IP est listée sur Spamhaus, Barracuda, SORBS, SpamCop, **demande à l'hébergeur de te l'échanger** ou prends un autre VPS. Une fois listée, tu vas y passer des semaines.
|
|
||||||
4. **Réputation du subnet (`/24`)**. Même si ton IP est propre, si le `/24` est pourri (beaucoup de spammeurs voisins), Gmail va te traiter avec méfiance. Vérifie sur [senderscore.org](https://senderscore.org/) en saisissant ton IP — le score du subnet apparaît.
|
|
||||||
|
|
||||||
**Hébergeurs réputés corrects pour le mail** : Hetzner, OVH (gamme dédiée, pas SoYouStart), Scaleway, Infomaniak (en VPS), Netcup. À éviter pour de l'envoi : DigitalOcean (subnets souvent grillés), Linode/Akamai (idem), AWS EC2 (le port 25 est limité par défaut, et la rate-limit est costaude).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Configuration DNS complète
|
|
||||||
|
|
||||||
Pour un domaine `exemple.fr` avec un serveur mail sur `mail.exemple.fr` à l'IP `203.0.113.10` (et `2001:db8::10` en v6) :
|
|
||||||
|
|
||||||
```dns
|
|
||||||
;; Enregistrement du serveur mail lui-même
|
|
||||||
mail.exemple.fr. IN A 203.0.113.10
|
|
||||||
mail.exemple.fr. IN AAAA 2001:db8::10
|
|
||||||
|
|
||||||
;; MX : qui reçoit les mails du domaine
|
|
||||||
exemple.fr. IN MX 10 mail.exemple.fr.
|
|
||||||
|
|
||||||
;; SPF : qui a le droit d'envoyer pour ce domaine
|
|
||||||
exemple.fr. IN TXT "v=spf1 mx -all"
|
|
||||||
|
|
||||||
;; DKIM (clé publique, le sélecteur ici est "mail")
|
|
||||||
mail._domainkey.exemple.fr. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhki..."
|
|
||||||
|
|
||||||
;; DMARC
|
|
||||||
_dmarc.exemple.fr. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@exemple.fr; ruf=mailto:dmarc@exemple.fr; fo=1; adkim=s; aspf=s; pct=100"
|
|
||||||
|
|
||||||
;; MTA-STS (cf. §8)
|
|
||||||
_mta-sts.exemple.fr. IN TXT "v=STSv1; id=20260509T000000;"
|
|
||||||
mta-sts.exemple.fr. IN A 203.0.113.10
|
|
||||||
|
|
||||||
;; TLS-RPT (rapports d'erreurs TLS)
|
|
||||||
_smtp._tls.exemple.fr. IN TXT "v=TLSRPTv1; rua=mailto:tls-reports@exemple.fr"
|
|
||||||
|
|
||||||
;; CAA : restreindre qui peut émettre des certifs pour ton domaine
|
|
||||||
exemple.fr. IN CAA 0 issue "letsencrypt.org"
|
|
||||||
|
|
||||||
;; Autodiscover / autoconfig (pour les clients mail)
|
|
||||||
autoconfig.exemple.fr. IN CNAME mail.exemple.fr.
|
|
||||||
autodiscover.exemple.fr. IN CNAME mail.exemple.fr.
|
|
||||||
```
|
|
||||||
|
|
||||||
Détails dans les sections dédiées plus bas.
|
|
||||||
|
|
||||||
**À ne pas oublier** : l'enregistrement PTR (reverse DNS) se configure **chez ton hébergeur**, pas dans ta zone DNS. Il doit pointer `203.0.113.10 → mail.exemple.fr`. C'est traité au §9.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Installation du stack mail
|
|
||||||
|
|
||||||
Sur Debian 12. Ce qui suit est volontairement condensé — pour une configuration ligne par ligne, suis [le tutoriel de référence de Workaround.org](https://workaround.org/ispmail/) qui est l'étalon depuis 20 ans.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Mise à jour
|
|
||||||
apt update && apt upgrade -y
|
|
||||||
|
|
||||||
# Stack de base
|
|
||||||
apt install -y postfix postfix-mysql dovecot-core dovecot-imapd \
|
|
||||||
dovecot-lmtpd dovecot-managesieved dovecot-sieve dovecot-mysql \
|
|
||||||
rspamd redis-server mariadb-server certbot
|
|
||||||
|
|
||||||
# Certificat TLS
|
|
||||||
certbot certonly --standalone -d mail.exemple.fr \
|
|
||||||
-d mta-sts.exemple.fr \
|
|
||||||
-d autoconfig.exemple.fr -d autodiscover.exemple.fr
|
|
||||||
```
|
|
||||||
|
|
||||||
### Postfix : configuration `main.cf` minimale-mais-saine
|
|
||||||
|
|
||||||
```cfg
|
|
||||||
# /etc/postfix/main.cf
|
|
||||||
myhostname = mail.exemple.fr
|
|
||||||
mydomain = exemple.fr
|
|
||||||
myorigin = $mydomain
|
|
||||||
mydestination = # Vide : on utilise virtual_mailbox_domains
|
|
||||||
inet_interfaces = all
|
|
||||||
inet_protocols = all # IPv4 + IPv6
|
|
||||||
|
|
||||||
# TLS — chiffrement obligatoire
|
|
||||||
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.exemple.fr/fullchain.pem
|
|
||||||
smtpd_tls_key_file = /etc/letsencrypt/live/mail.exemple.fr/privkey.pem
|
|
||||||
smtpd_tls_security_level = may
|
|
||||||
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
|
||||||
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
|
||||||
smtp_tls_security_level = may
|
|
||||||
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
|
|
||||||
smtp_dns_support_level = dnssec # Indispensable pour DANE
|
|
||||||
smtp_tls_security_level = dane # ou "may" si DANE pas encore prêt
|
|
||||||
smtp_host_lookup = dns
|
|
||||||
|
|
||||||
# Restrictions anti-relais ouvert
|
|
||||||
smtpd_relay_restrictions =
|
|
||||||
permit_mynetworks
|
|
||||||
permit_sasl_authenticated
|
|
||||||
reject_unauth_destination
|
|
||||||
|
|
||||||
smtpd_recipient_restrictions =
|
|
||||||
permit_mynetworks
|
|
||||||
permit_sasl_authenticated
|
|
||||||
reject_unauth_destination
|
|
||||||
reject_unknown_recipient_domain
|
|
||||||
reject_invalid_helo_hostname
|
|
||||||
reject_non_fqdn_helo_hostname
|
|
||||||
|
|
||||||
# Limites raisonnables
|
|
||||||
message_size_limit = 52428800 # 50 Mo
|
|
||||||
mailbox_size_limit = 0
|
|
||||||
recipient_delimiter = +
|
|
||||||
|
|
||||||
# Intégration Rspamd
|
|
||||||
smtpd_milters = inet:localhost:11332
|
|
||||||
non_smtpd_milters = inet:localhost:11332
|
|
||||||
milter_protocol = 6
|
|
||||||
milter_default_action = accept
|
|
||||||
milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}
|
|
||||||
|
|
||||||
# Livraison via Dovecot LMTP
|
|
||||||
virtual_transport = lmtp:unix:private/dovecot-lmtp
|
|
||||||
```
|
|
||||||
|
|
||||||
### Dovecot, Rspamd
|
|
||||||
|
|
||||||
Ces composants demandent leurs propres fichiers de configuration. Renvoi explicite vers les tutos qui font autorité :
|
|
||||||
|
|
||||||
- **Workaround.org / ISPmail** : [https://workaround.org/ispmail/](https://workaround.org/ispmail/) — référence francophone et anglophone, mise à jour à chaque version Debian.
|
|
||||||
- **Rspamd quickstart** : [https://www.rspamd.com/doc/tutorials/quickstart.html](https://www.rspamd.com/doc/tutorials/quickstart.html)
|
|
||||||
- **Dovecot wiki** : [https://doc.dovecot.org/](https://doc.dovecot.org/)
|
|
||||||
|
|
||||||
Si tu veux gagner du temps, **Mailcow** (`docker compose`) est aujourd'hui la solution clé-en-main la plus fiable.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. SPF, DKIM, DMARC : les rustines obligatoires
|
|
||||||
|
|
||||||
Sans ces trois enregistrements correctement configurés, **Gmail et Outlook rejetteront ou marqueront en spam** la majorité de tes messages — peu importe ton volume.
|
|
||||||
|
|
||||||
### SPF (Sender Policy Framework)
|
|
||||||
|
|
||||||
Déclare qui a le droit d'envoyer du mail pour ton domaine.
|
|
||||||
|
|
||||||
```dns
|
|
||||||
exemple.fr. IN TXT "v=spf1 mx -all"
|
|
||||||
```
|
|
||||||
|
|
||||||
- `mx` : autorise les serveurs listés dans le MX du domaine.
|
|
||||||
- `-all` : **rejet strict** de tout le reste. Indispensable pour la réputation. Ne jamais utiliser `~all` (softfail) en prod : Gmail aujourd'hui considère `~all` comme un signal faible.
|
|
||||||
|
|
||||||
Si tu envoies aussi via un relais externe (smart host) : ajoute son `include`, ex. `v=spf1 mx include:_spf.mailjet.com -all`.
|
|
||||||
|
|
||||||
**Limite** : un enregistrement SPF doit tenir en **10 lookups DNS maximum**. Au-delà, il est invalide. Vérifie avec [https://www.kitterman.com/spf/validate.html](https://www.kitterman.com/spf/validate.html).
|
|
||||||
|
|
||||||
### DKIM (DomainKeys Identified Mail)
|
|
||||||
|
|
||||||
Signe chaque mail sortant avec une clé privée. Le destinataire vérifie la signature via la clé publique publiée en DNS.
|
|
||||||
|
|
||||||
**Génération de la clé** (Rspamd, sélecteur `mail`, clé 2048 bits) :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p /var/lib/rspamd/dkim
|
|
||||||
rspamadm dkim_keygen -s mail -d exemple.fr -k /var/lib/rspamd/dkim/exemple.fr.mail.key \
|
|
||||||
-b 2048 > /var/lib/rspamd/dkim/exemple.fr.mail.txt
|
|
||||||
chown _rspamd:_rspamd /var/lib/rspamd/dkim/*
|
|
||||||
```
|
|
||||||
|
|
||||||
Le fichier `.txt` contient l'enregistrement DNS à publier :
|
|
||||||
|
|
||||||
```dns
|
|
||||||
mail._domainkey.exemple.fr. IN TXT ( "v=DKIM1; k=rsa; "
|
|
||||||
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..." )
|
|
||||||
```
|
|
||||||
|
|
||||||
Configuration Rspamd (`/etc/rspamd/local.d/dkim_signing.conf`) :
|
|
||||||
|
|
||||||
```conf
|
|
||||||
allow_username_mismatch = true;
|
|
||||||
domain {
|
|
||||||
exemple.fr {
|
|
||||||
path = "/var/lib/rspamd/dkim/exemple.fr.mail.key";
|
|
||||||
selector = "mail";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Recharge : `systemctl restart rspamd`.
|
|
||||||
|
|
||||||
**Vérification** : envoie un mail à [check-auth@verifier.port25.com](mailto:check-auth@verifier.port25.com), tu reçois un rapport complet SPF/DKIM/DMARC en retour. Ou utilise [https://www.mail-tester.com/](https://www.mail-tester.com/) (note sur 10).
|
|
||||||
|
|
||||||
### DMARC (Domain-based Message Authentication, Reporting and Conformance)
|
|
||||||
|
|
||||||
Dit aux serveurs distants quoi faire en cas d'échec SPF/DKIM, et **te renvoie des rapports** sur ce qui passe et ce qui rate.
|
|
||||||
|
|
||||||
```dns
|
|
||||||
_dmarc.exemple.fr. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@exemple.fr; ruf=mailto:dmarc@exemple.fr; fo=1; adkim=s; aspf=s; pct=100"
|
|
||||||
```
|
|
||||||
|
|
||||||
- `p=none` : surveillance seule, **à utiliser pendant 2-4 semaines en démarrage** pour collecter les rapports sans pénaliser.
|
|
||||||
- `p=quarantine` : mise en spam des mails non authentifiés. Cible normale.
|
|
||||||
- `p=reject` : rejet pur. À atteindre en cible finale, **après** avoir vérifié 4 semaines de rapports propres.
|
|
||||||
- `rua` : adresse pour les rapports agrégés (quotidiens).
|
|
||||||
- `ruf` : rapports forensiques (par message). Optionnel.
|
|
||||||
- `adkim=s aspf=s` : alignement strict — le domaine de signature DKIM et le domaine SPF doivent **exactement** correspondre au domaine `From:`.
|
|
||||||
|
|
||||||
**Lecture des rapports DMARC** : ils arrivent en XML, illisibles. Utilise un parseur :
|
|
||||||
|
|
||||||
- [Postmark DMARC Monitoring](https://dmarc.postmarkapp.com/) (gratuit, agrège les rapports dans une UI).
|
|
||||||
- [parsedmarc](https://github.com/domainaware/parsedmarc) (auto-hébergeable, envoie dans Elasticsearch/Splunk/Grafana).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. MTA-STS, TLS-RPT, DANE : aller plus loin
|
|
||||||
|
|
||||||
Ces standards sécurisent le **transport** entre serveurs (chiffrement TLS forcé). Gmail les regarde, Microsoft aussi. Pas obligatoires, mais ils boostent ta réputation.
|
|
||||||
|
|
||||||
### MTA-STS
|
|
||||||
|
|
||||||
Force les serveurs distants à utiliser TLS pour t'envoyer des mails. Trois éléments :
|
|
||||||
|
|
||||||
**1. Enregistrement DNS TXT** :
|
|
||||||
```dns
|
|
||||||
_mta-sts.exemple.fr. IN TXT "v=STSv1; id=20260509T000000;"
|
|
||||||
```
|
|
||||||
|
|
||||||
**2. Sous-domaine `mta-sts.exemple.fr`** servant un fichier en HTTPS à `https://mta-sts.exemple.fr/.well-known/mta-sts.txt` :
|
|
||||||
```
|
|
||||||
version: STSv1
|
|
||||||
mode: enforce
|
|
||||||
mx: mail.exemple.fr
|
|
||||||
max_age: 604800
|
|
||||||
```
|
|
||||||
|
|
||||||
`mode: enforce` est la cible. En démarrage, mets `mode: testing` pendant 1-2 semaines.
|
|
||||||
|
|
||||||
**3. Certificat TLS valide** sur ce sous-domaine (déjà fait via certbot au §6).
|
|
||||||
|
|
||||||
### TLS-RPT
|
|
||||||
|
|
||||||
Demande aux serveurs distants de t'envoyer des rapports en cas d'échec TLS.
|
|
||||||
|
|
||||||
```dns
|
|
||||||
_smtp._tls.exemple.fr. IN TXT "v=TLSRPTv1; rua=mailto:tls-reports@exemple.fr"
|
|
||||||
```
|
|
||||||
|
|
||||||
### DANE (DNS-based Authentication of Named Entities)
|
|
||||||
|
|
||||||
Encore plus solide que MTA-STS, mais nécessite **DNSSEC** activé sur ton domaine. Si ton registrar ne supporte pas DNSSEC, oublie DANE.
|
|
||||||
|
|
||||||
DANE publie un hash du certificat TLS dans un enregistrement TLSA :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Générer l'enregistrement TLSA pour le port 25
|
|
||||||
posttls-finger -t30 -T180 -P /etc/ssl/certs/ -lsecure mail.exemple.fr
|
|
||||||
```
|
|
||||||
|
|
||||||
Ou plus simplement avec [https://www.huque.com/bin/gen_tlsa](https://www.huque.com/bin/gen_tlsa) :
|
|
||||||
|
|
||||||
```dns
|
|
||||||
_25._tcp.mail.exemple.fr. IN TLSA 3 1 1 ABC123...
|
|
||||||
```
|
|
||||||
|
|
||||||
**Vérification globale** de tout ton setup TLS+DANE : [https://internet.nl/mail/](https://internet.nl/mail/) (excellent, recommandé).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. PTR (reverse DNS) et HELO
|
|
||||||
|
|
||||||
**Le PTR est probablement la cause la plus fréquente de rejet par Gmail/Outlook chez les nouveaux auto-hébergés.**
|
|
||||||
|
|
||||||
Règle absolue : **`PTR(IP) == HELO == nom A/AAAA`**, et tout doit être un FQDN cohérent.
|
|
||||||
|
|
||||||
Configure le PTR dans le panneau de ton hébergeur (chez OVH : "IP" → "Reverse DNS") :
|
|
||||||
```
|
|
||||||
203.0.113.10 → mail.exemple.fr
|
|
||||||
2001:db8::10 → mail.exemple.fr
|
|
||||||
```
|
|
||||||
|
|
||||||
Vérifie :
|
|
||||||
```bash
|
|
||||||
dig -x 203.0.113.10 +short
|
|
||||||
# doit retourner : mail.exemple.fr.
|
|
||||||
|
|
||||||
dig mail.exemple.fr A +short
|
|
||||||
# doit retourner : 203.0.113.10
|
|
||||||
```
|
|
||||||
|
|
||||||
Dans Postfix, `myhostname = mail.exemple.fr` et c'est ce qui est annoncé en HELO. Cohérence garantie.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. Warmup d'IP : la phase la plus délicate
|
|
||||||
|
|
||||||
**Une IP neuve = pas de réputation = défiance maximale des gros acteurs.** Tu ne peux pas envoyer 1000 mails le jour 1 sans te griller.
|
|
||||||
|
|
||||||
### Plan de warmup sur 4 à 6 semaines
|
|
||||||
|
|
||||||
| Semaine | Volume max/jour vers Gmail+Outlook | Volume max/jour total | Contenu |
|
|
||||||
|---|---|---|---|
|
|
||||||
| 1 | 20-50 | 100 | Mails à toi-même, comptes test sur Gmail/Outlook/Yahoo. Réponds-y, marque "non spam" si en spam. |
|
|
||||||
| 2 | 100 | 300 | Cercle proche qui sait répondre / interagir. |
|
|
||||||
| 3 | 300 | 1000 | Élargissement progressif. |
|
|
||||||
| 4 | 800 | 3000 | Ouvre aux usages normaux. |
|
|
||||||
| 5+ | 2000+ | volume cible | Stable. |
|
|
||||||
|
|
||||||
**Règles d'or pendant le warmup :**
|
|
||||||
- **Pas de mailing list, pas de notifs automatiques en masse.** Privilégie des mails 1-à-1 conversationnels.
|
|
||||||
- **Demande aux destinataires de répondre** — un mail avec réponse a 100x le poids d'un mail ouvert silencieusement.
|
|
||||||
- **Aucun lien raccourci**, aucun pixel de tracking, aucune image lourde.
|
|
||||||
- **Stop net si ton score Senderscore baisse** ou si Gmail Postmaster Tools (cf. §11) montre du rouge.
|
|
||||||
|
|
||||||
### Si tu as un volume immédiat à envoyer
|
|
||||||
|
|
||||||
Bascule en **option B** (smart host) le temps du warmup, puis rapatrie progressivement en interne en répliquant les volumes ci-dessus.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 11. Postmaster Tools, SNDS, FBL
|
|
||||||
|
|
||||||
Les gros acteurs te donnent des dashboards dédiés. **Inscris-toi à tous, dès la création du domaine.**
|
|
||||||
|
|
||||||
| Service | Acteur | Usage |
|
|
||||||
|---|---|---|
|
|
||||||
| [Google Postmaster Tools](https://postmaster.google.com/) | Gmail | Réputation IP+domaine, taux de spam, authentification, encryption. **Indispensable.** |
|
|
||||||
| [Microsoft SNDS](https://sendersupport.olc.protection.outlook.com/snds/) | Outlook/Hotmail | Smart Network Data Services, qualité de l'IP. |
|
|
||||||
| [Microsoft JMRP](https://sendersupport.olc.protection.outlook.com/pm/) | Outlook | Junk Mail Reporting Program, FBL Microsoft. |
|
|
||||||
| [Yahoo CFL](https://senders.yahooinc.com/complaint-feedback-loop/) | Yahoo | Complaint Feedback Loop. |
|
|
||||||
| [Validity Sender Score](https://senderscore.org/) | Indépendant | Score sur 100, à surveiller. |
|
|
||||||
|
|
||||||
Configure les feedback loops (FBL) : quand un destinataire clique "spam", tu reçois une notification. Ça te permet de désinscrire l'utilisateur **avant qu'il ne dégrade ta réputation**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 12. Liste de désinscription en un clic (RFC 8058)
|
|
||||||
|
|
||||||
**Exigence Google/Microsoft pour les expéditeurs en volume**, mais à mettre en place dès le début même en bas volume.
|
|
||||||
|
|
||||||
Ajoute deux en-têtes à tous les mails non-strictement-personnels :
|
|
||||||
|
|
||||||
```
|
|
||||||
List-Unsubscribe: <https://exemple.fr/unsub?token=abc123>, <mailto:unsub@exemple.fr?subject=unsub-abc123>
|
|
||||||
List-Unsubscribe-Post: List-Unsubscribe=One-Click
|
|
||||||
```
|
|
||||||
|
|
||||||
L'URL HTTPS doit accepter une requête **POST** (pas seulement GET) avec `List-Unsubscribe=One-Click` dans le corps, et désinscrire **immédiatement et silencieusement** sans demander de confirmation.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 13. Anti-spam entrant et hygiène
|
|
||||||
|
|
||||||
Un serveur mail mal configuré côté entrée devient vite un relais de spam ou une cible. Configuration Rspamd minimale :
|
|
||||||
|
|
||||||
```conf
|
|
||||||
# /etc/rspamd/local.d/actions.conf
|
|
||||||
reject = 15;
|
|
||||||
add_header = 6;
|
|
||||||
greylist = 4;
|
|
||||||
```
|
|
||||||
|
|
||||||
```conf
|
|
||||||
# /etc/rspamd/local.d/greylist.conf
|
|
||||||
expire = 1d;
|
|
||||||
```
|
|
||||||
|
|
||||||
Active aussi :
|
|
||||||
- **Vérification SPF/DKIM/DMARC entrante** (par défaut activée dans Rspamd).
|
|
||||||
- **RBL** (Realtime Blackhole Lists) : Spamhaus ZEN, Barracuda. Attention à ne pas multiplier — 2 ou 3 RBL fiables suffisent.
|
|
||||||
- **Greylisting** : refuse temporairement les premiers contacts, ce qui élimine 80% du spam basique. Ne pas activer sur un domaine à fort volume transactionnel (gêne les notifs).
|
|
||||||
- **Bayes** : laisse Rspamd apprendre via le dossier `Junk` de Dovecot (signal `IsSpam` / `IsHam`).
|
|
||||||
|
|
||||||
Mises à jour : `unattended-upgrades` activé, redémarrage planifié, lecture des annonces sécu Postfix/Dovecot.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 14. Monitoring, logs, alertes
|
|
||||||
|
|
||||||
Sans monitoring, tu découvres les problèmes par les utilisateurs. À mettre en place :
|
|
||||||
|
|
||||||
- **Lecture des logs** : `journalctl -u postfix -f`, `tail -f /var/log/mail.log`, web UI de Rspamd sur `localhost:11334`.
|
|
||||||
- **Métriques** : exporter Postfix/Dovecot vers Prometheus + Grafana (`postfix_exporter`, `dovecot_exporter`).
|
|
||||||
- **Alertes** sur :
|
|
||||||
- File d'attente Postfix > 50 messages (`mailq | tail -1`).
|
|
||||||
- Score Senderscore qui chute.
|
|
||||||
- Apparition sur une RBL : surveillance automatisée par [https://multirbl.valli.org/](https://multirbl.valli.org/) ou via un script qui interroge plusieurs DNSBL en cron.
|
|
||||||
- Échec TLS-RPT (rapport entrant signalant une connexion non chiffrée).
|
|
||||||
- **Rapports DMARC** parsés régulièrement (cf. §7).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 15. Que faire quand Gmail rejette quand même ?
|
|
||||||
|
|
||||||
Ça arrive. Diagnostic dans l'ordre :
|
|
||||||
|
|
||||||
1. **Lis le code de rejet SMTP** dans `/var/log/mail.log`. Gmail renvoie des codes très explicites :
|
|
||||||
- `550-5.7.1 [203.0.113.10 19] Our system has detected that this message is likely unsolicited mail.` → contenu jugé spammy. Revois le contenu, ajoute du texte conversationnel, retire les liens douteux.
|
|
||||||
- `421-4.7.0 [203.0.113.10 15] Our system has detected an unusual rate of unsolicited mail.` → tu as dépassé un seuil. **Ralentis immédiatement**, attends 24-48h, reprends doucement.
|
|
||||||
- `550-5.7.26 ... DMARC ...` → ton DMARC ne passe pas. Revérifie SPF/DKIM/alignement.
|
|
||||||
- `550-5.7.1 ... blocked using Spamhaus.` → tu es sur une RBL. Va sur [spamhaus.org/lookup/](https://www.spamhaus.org/lookup/) pour vérifier et demander la sortie.
|
|
||||||
2. **Va dans Postmaster Tools** (§11). Si "IP reputation" est rouge ou orange, regarde le contenu et le timing de tes envois récents.
|
|
||||||
3. **Test mail-tester** : envoie à une adresse fournie par [mail-tester.com](https://www.mail-tester.com/), obtiens une note sur 10. Vise 10/10. Toute case manquante doit être corrigée.
|
|
||||||
4. **Sortie de blacklist** : la plupart des RBL (Spamhaus, Barracuda) ont un formulaire de retrait. Spamhaus retire en quelques heures si tu corriges la cause. SORBS est plus lent. UCEPROTECT exige souvent de payer — ignore-la, peu de serveurs sérieux la consultent.
|
|
||||||
5. **Si rien ne marche**, change d'IP. C'est parfois la seule issue. Demande à ton hébergeur une IP fraîche, refais un warmup.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 16. Checklist finale avant mise en prod
|
|
||||||
|
|
||||||
Avant d'envoyer le premier vrai mail :
|
|
||||||
|
|
||||||
- [ ] Domaine avec DNSSEC activé.
|
|
||||||
- [ ] IP testée sur 5+ blacklists, propre.
|
|
||||||
- [ ] Port 25 sortant ouvert et testé (`telnet gmail-smtp-in.l.google.com 25`).
|
|
||||||
- [ ] PTR configuré et cohérent avec le HELO.
|
|
||||||
- [ ] MX, A, AAAA, SPF, DKIM, DMARC publiés et validés via [mxtoolbox.com](https://mxtoolbox.com/).
|
|
||||||
- [ ] MTA-STS publié (mode `testing` au démarrage).
|
|
||||||
- [ ] TLS-RPT publié.
|
|
||||||
- [ ] DANE/TLSA publié (si DNSSEC OK).
|
|
||||||
- [ ] CAA publié.
|
|
||||||
- [ ] Test envoyé à `check-auth@verifier.port25.com` : tout en `pass`.
|
|
||||||
- [ ] Test [mail-tester.com](https://www.mail-tester.com/) : 10/10.
|
|
||||||
- [ ] Test [internet.nl/mail/](https://internet.nl/mail/) : 100%.
|
|
||||||
- [ ] Inscription Postmaster Tools, SNDS, JMRP, Yahoo CFL.
|
|
||||||
- [ ] DMARC `p=none` au démarrage, parser de rapports en place.
|
|
||||||
- [ ] List-Unsubscribe + List-Unsubscribe-Post implémentés.
|
|
||||||
- [ ] Plan de warmup affiché et respecté.
|
|
||||||
- [ ] Monitoring file d'attente + RBL en place.
|
|
||||||
- [ ] Backup chiffré des Maildir.
|
|
||||||
|
|
||||||
Au bout de 4 semaines de rapports DMARC propres : passage à `p=quarantine`. Au bout de 8-12 semaines : `p=reject`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 17. Annexes : commandes utiles
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Voir la file d'attente
|
|
||||||
mailq
|
|
||||||
postqueue -p
|
|
||||||
|
|
||||||
# Forcer la tentative de livraison
|
|
||||||
postqueue -f
|
|
||||||
|
|
||||||
# Vider la file (à utiliser avec précaution)
|
|
||||||
postsuper -d ALL
|
|
||||||
|
|
||||||
# Tester la délivrabilité d'un domaine cible (DANE / TLS)
|
|
||||||
posttls-finger -t30 -T180 -P /etc/ssl/certs/ -lsecure gmail-smtp-in.l.google.com
|
|
||||||
|
|
||||||
# Vérifier ses propres enregistrements
|
|
||||||
dig exemple.fr TXT +short
|
|
||||||
dig _dmarc.exemple.fr TXT +short
|
|
||||||
dig mail._domainkey.exemple.fr TXT +short
|
|
||||||
dig mail.exemple.fr MX +short
|
|
||||||
dig -x 203.0.113.10 +short
|
|
||||||
|
|
||||||
# Tester l'envoi en SMTP authentifié
|
|
||||||
swaks --to test@gmail.com --from moi@exemple.fr \
|
|
||||||
--server mail.exemple.fr --auth --auth-user moi@exemple.fr \
|
|
||||||
--tls --port 587
|
|
||||||
|
|
||||||
# Voir en temps réel ce qu'il se passe
|
|
||||||
journalctl -u postfix -u dovecot -u rspamd -f
|
|
||||||
```
|
|
||||||
|
|
||||||
### Outils web à mettre en favoris
|
|
||||||
|
|
||||||
- [https://www.mail-tester.com/](https://www.mail-tester.com/) — score sur 10
|
|
||||||
- [https://internet.nl/mail/](https://internet.nl/mail/) — audit complet
|
|
||||||
- [https://mxtoolbox.com/SuperTool.aspx](https://mxtoolbox.com/SuperTool.aspx) — DNS, blacklists
|
|
||||||
- [https://dmarcian.com/dmarc-inspector/](https://dmarcian.com/dmarc-inspector/) — vérif DMARC
|
|
||||||
- [https://www.kitterman.com/spf/validate.html](https://www.kitterman.com/spf/validate.html) — vérif SPF
|
|
||||||
- [https://postmaster.google.com/](https://postmaster.google.com/) — Google Postmaster
|
|
||||||
- [https://senderscore.org/](https://senderscore.org/) — réputation IP
|
|
||||||
|
|
||||||
### Documentation de référence
|
|
||||||
|
|
||||||
- **ISPmail / Workaround.org** — [https://workaround.org/ispmail/](https://workaround.org/ispmail/) — le tutoriel le plus complet et tenu à jour, par version Debian.
|
|
||||||
- **Mailcow docs** — [https://docs.mailcow.email/](https://docs.mailcow.email/) — pour la version conteneurisée clé-en-main.
|
|
||||||
- **Postfix officiel** — [https://www.postfix.org/documentation.html](https://www.postfix.org/documentation.html)
|
|
||||||
- **Rspamd docs** — [https://www.rspamd.com/doc/](https://www.rspamd.com/doc/)
|
|
||||||
- **RFCs essentielles** : 5321 (SMTP moderne), 7208 (SPF), 6376 (DKIM), 7489 (DMARC), 8461 (MTA-STS), 8460 (TLS-RPT), 7672 (DANE-SMTP), 8058 (One-Click Unsubscribe).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
L'auto-hébergement mail en 2026 reste possible, mais c'est devenu un sport : les règles changent, les gros acteurs durcissent leurs critères, et l'écosystème pousse vers la centralisation. Si tu réussis le warmup et tiens 6 mois sans incident, tu as gagné — mais ne baisse pas la garde, un changement unilatéral de Google peut survenir à tout moment, comme en février 2024.
|
|
||||||
|
Before Width: | Height: | Size: 386 KiB |
@@ -1,266 +0,0 @@
|
|||||||
# ImageMagick : traiter des images en ligne de commande
|
|
||||||
|
|
||||||
## 1. À quoi ça sert
|
|
||||||
|
|
||||||
ImageMagick, c'est l'outil qu'on sort quand on veut manipuler des images sans ouvrir un logiciel graphique. Pas de Photoshop, pas de GIMP, pas de clic-droit "Redimensionner" sur cent fichiers à la suite : juste une commande dans un terminal, et le travail est fait.
|
|
||||||
|
|
||||||
C'est une suite d'outils qui sait lire, écrire et transformer plus de 200 formats — du JPEG classique au PDF en passant par le HEIC des iPhones, le WebP de Google ou le bon vieux TIFF des scanners. L'absence d'interface graphique est ici une fonctionnalité, pas un défaut : elle permet de l'utiliser partout où il n'y a pas d'écran, et surtout dans tout ce qui doit tourner tout seul.
|
|
||||||
|
|
||||||
On le retrouve donc naturellement :
|
|
||||||
|
|
||||||
- sur des serveurs web qui génèrent des miniatures à la volée,
|
|
||||||
- dans des scripts qui traitent des dossiers entiers d'un coup,
|
|
||||||
- dans des pipelines CI/CD pour préparer des assets,
|
|
||||||
- dans des conteneurs Docker, accessibles uniquement en SSH.
|
|
||||||
|
|
||||||
Depuis la version 7, tout passe par une commande unique : `magick`. Les anciennes commandes (`convert`, `identify`, `mogrify`...) existent toujours pour la compatibilité, mais elles ne sont plus la norme.
|
|
||||||
|
|
||||||
## 2. Installation
|
|
||||||
|
|
||||||
Sur Debian ou Ubuntu :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install imagemagick
|
|
||||||
```
|
|
||||||
|
|
||||||
On vérifie ensuite que tout est en place :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick -version
|
|
||||||
```
|
|
||||||
|
|
||||||
La sortie indique aussi les délégués compilés (libwebp, libheif, libraw, etc.). Si un format précis vous intéresse, c'est ici qu'il faut regarder : ImageMagick ne sait lire un format que si la bibliothèque correspondante est présente au moment de la compilation.
|
|
||||||
|
|
||||||
## 3. Comment ImageMagick raisonne
|
|
||||||
|
|
||||||
Toutes les commandes suivent la même logique :
|
|
||||||
|
|
||||||
```
|
|
||||||
magick [entrée] [options] [sortie]
|
|
||||||
```
|
|
||||||
|
|
||||||
L'image est chargée en mémoire, puis chaque option s'applique **dans l'ordre où elle est écrite**, comme une chaîne de traitement. Ce point est important : déplacer une option dans la ligne peut changer le résultat final.
|
|
||||||
|
|
||||||
Exemple :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick input.jpg -resize 800x600 -quality 85 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Ici, l'image est lue, redimensionnée à 800×600, puis compressée à 85% de qualité, puis écrite sur le disque. Si on inversait `-quality` et `-resize`, le résultat serait identique dans ce cas précis, mais avec des opérations qui modifient les pixels (flou, conversion d'espace colorimétrique, recadrage), l'ordre devient critique.
|
|
||||||
|
|
||||||
## 4. Convertir d'un format à un autre
|
|
||||||
|
|
||||||
Le cas le plus simple : changer l'extension du fichier de sortie suffit.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.png image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
ImageMagick détecte le format cible à partir de l'extension et fait la conversion. C'est aussi simple que ça pour 90% des cas.
|
|
||||||
|
|
||||||
Quand on veut être plus précis — par exemple forcer une profondeur de couleur particulière — on l'indique explicitement :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.png -depth 8 image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Utile quand on récupère des images en 16 bits par canal qu'on veut ramener à du 8 bits standard, soit pour gagner de la place, soit pour garantir la compatibilité avec un logiciel récalcitrant.
|
|
||||||
|
|
||||||
## 5. Redimensionner
|
|
||||||
|
|
||||||
### La méthode brutale
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize 800x600 image_resized.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Cette commande redimensionne à 800×600 **en respectant les proportions** par défaut, contrairement à ce qu'on pourrait croire. Si l'image source est en 4:3, elle rentrera pile dedans ; si elle est en 16:9, ImageMagick choisira la dimension la plus contraignante et l'autre sera plus petite que demandé.
|
|
||||||
|
|
||||||
Pour forcer exactement ces dimensions quitte à déformer l'image, il faut ajouter un point d'exclamation :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize 800x600! image_resized.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
### Ne rétrécir que les grandes images
|
|
||||||
|
|
||||||
C'est probablement le cas le plus utile au quotidien : on a un dossier d'images, on veut s'assurer qu'aucune ne dépasse 1600 pixels, mais on ne veut pas agrandir les petites (ce qui dégraderait leur qualité).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize "1600x1600>" image_resized.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Le `>` signifie « uniquement si l'image est plus grande ». Les guillemets sont nécessaires car `>` est interprété par le shell comme une redirection. On peut aussi échapper le caractère avec `\>`.
|
|
||||||
|
|
||||||
### En pourcentage
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize 50% image_small.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Pratique quand on veut diviser la taille par deux sans calculer les dimensions exactes.
|
|
||||||
|
|
||||||
## 6. Qualité et poids du fichier
|
|
||||||
|
|
||||||
Pour les JPEG, le paramètre `-quality` règle le compromis entre fidélité visuelle et poids du fichier :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -quality 85 image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Quelques repères en pratique :
|
|
||||||
|
|
||||||
- **100** : qualité maximale, fichier énorme, différence imperceptible avec 95.
|
|
||||||
- **85** : la valeur par défaut de la plupart des appareils photo, et un excellent compromis pour le web.
|
|
||||||
- **75** : encore très acceptable, gain de place notable.
|
|
||||||
- **En dessous de 70** : les artefacts deviennent visibles, surtout sur les aplats de couleur.
|
|
||||||
|
|
||||||
### Supprimer les métadonnées
|
|
||||||
|
|
||||||
Les fichiers issus d'appareils photo ou de smartphones embarquent beaucoup d'informations : modèle de l'appareil, date, parfois coordonnées GPS, miniature intégrée, profil colorimétrique... Tout ça peut peser plusieurs dizaines de kilo-octets, et surtout poser des problèmes de confidentialité.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -strip image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
L'option `-strip` fait le ménage. À utiliser systématiquement avant de publier des photos sur le web, et indispensable dès qu'on parle de RGPD ou d'anonymisation. Attention en revanche pour la photographie professionnelle où certaines métadonnées (droits d'auteur, profil ICC) peuvent être nécessaires.
|
|
||||||
|
|
||||||
## 7. Recadrer et adapter à un cadre
|
|
||||||
|
|
||||||
### Recadrage classique
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -crop 800x600+100+50 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
La syntaxe se lit comme une fenêtre qu'on découpe dans l'image : largeur × hauteur, décalée de 100 pixels depuis la gauche et 50 pixels depuis le haut.
|
|
||||||
|
|
||||||
### Remplir un cadre exact, sans déformation
|
|
||||||
|
|
||||||
C'est le besoin typique des miniatures de site : on veut toutes les vignettes en 800×600 pile, peu importe le format des photos d'origine.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize 800x600^ -gravity center -extent 800x600 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Trois étapes enchaînées :
|
|
||||||
|
|
||||||
1. `-resize 800x600^` redimensionne pour que l'image **remplisse** le cadre (le `^` inverse la logique habituelle : on prend la plus grande dimension comme contrainte, pas la plus petite).
|
|
||||||
2. `-gravity center` indique qu'on veut centrer le découpage.
|
|
||||||
3. `-extent 800x600` coupe ce qui dépasse pour obtenir exactement la taille voulue.
|
|
||||||
|
|
||||||
Le résultat : aucune déformation, aucune bande noire, juste un éventuel rognage sur les bords les plus longs.
|
|
||||||
|
|
||||||
## 8. Traiter un dossier entier
|
|
||||||
|
|
||||||
Une boucle Bash suffit pour convertir tous les PNG d'un dossier en JPEG :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
for f in *.png; do
|
|
||||||
magick "$f" "${f%.png}.jpg"
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
La syntaxe `${f%.png}` retire l'extension `.png` du nom, on y ajoute `.jpg`. Simple et fiable.
|
|
||||||
|
|
||||||
Pour modifier les fichiers **sur place**, ImageMagick fournit `mogrify` :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mogrify -resize "1600x1600>" *.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Cette commande écrase chaque fichier par sa version redimensionnée. C'est rapide et pratique, mais ça veut aussi dire qu'**il n'y a pas de retour en arrière** : si la commande est mal écrite, le dossier original est perdu. Règle absolue : travailler sur une copie, ou s'assurer d'avoir une sauvegarde.
|
|
||||||
|
|
||||||
## 9. Texte et filigranes
|
|
||||||
|
|
||||||
### Apposer une mention textuelle
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg \
|
|
||||||
-gravity southeast \
|
|
||||||
-pointsize 24 \
|
|
||||||
-fill white \
|
|
||||||
-annotate +10+10 "© MonSite" \
|
|
||||||
image_marked.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
`-gravity` ancre le texte dans un coin de l'image (les neuf positions classiques : `northwest`, `north`, `northeast`, `west`, `center`...), et `-annotate` ajoute un décalage par rapport à ce point d'ancrage. Ici, `+10+10` éloigne le texte de 10 pixels du coin inférieur droit.
|
|
||||||
|
|
||||||
### Superposer un logo ou un watermark image
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg watermark.png -gravity center -composite output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
L'image principale est lue en premier, le filigrane en second, puis `-composite` les fusionne. Si le watermark a un canal alpha (transparence), il est respecté.
|
|
||||||
|
|
||||||
## 10. Couleurs et tons
|
|
||||||
|
|
||||||
Passage en noir et blanc :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -colorspace Gray output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Réglage de la luminosité et du contraste (valeurs en pourcentage, positives ou négatives) :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -brightness-contrast 10x5 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Ici, +10% de luminosité et +5% de contraste. Pour assombrir, on utilise des valeurs négatives : `-brightness-contrast -10x0`.
|
|
||||||
|
|
||||||
## 11. Inspecter une image
|
|
||||||
|
|
||||||
Pour obtenir les informations essentielles — format, dimensions, profondeur :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick identify image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Pour tout savoir, y compris les métadonnées EXIF, le profil colorimétrique, l'histogramme :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick identify -verbose image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
La sortie verbeuse peut faire plusieurs pages, mais c'est inestimable pour diagnostiquer un problème ou comprendre d'où vient un fichier.
|
|
||||||
|
|
||||||
## 12. Formats modernes
|
|
||||||
|
|
||||||
Le WebP de Google offre une compression nettement meilleure que le JPEG à qualité équivalente, et il est aujourd'hui supporté par tous les navigateurs courants :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -quality 80 image.webp
|
|
||||||
```
|
|
||||||
|
|
||||||
L'AVIF va encore plus loin en termes de compression, au prix d'un encodage plus lent :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg image.avif
|
|
||||||
```
|
|
||||||
|
|
||||||
Si la commande échoue avec une erreur de délégué, c'est que votre installation d'ImageMagick a été compilée sans le support AVIF — il faudra installer `libheif` ou recompiler.
|
|
||||||
|
|
||||||
## 13. Quelques règles à se fixer
|
|
||||||
|
|
||||||
- **Toujours travailler sur une copie** quand on découvre une nouvelle commande. `mogrify` en particulier ne pardonne pas.
|
|
||||||
- **Stripper les métadonnées** avant toute publication web.
|
|
||||||
- **Pour de très gros volumes** (plusieurs milliers d'images, ou des images très lourdes), regarder du côté de `libvips` : c'est plus rapide et beaucoup moins gourmand en mémoire qu'ImageMagick. Pour tout le reste, ImageMagick est largement suffisant.
|
|
||||||
- **Automatiser dès qu'on répète** : si la même commande revient deux fois, elle mérite un script.
|
|
||||||
- **Lire les messages d'erreur** : ImageMagick est verbeux, et la plupart des problèmes (délégué manquant, permissions, format non reconnu) sont explicitement nommés dans la sortie.
|
|
||||||
|
|
||||||
## 14. Là où on le croise vraiment
|
|
||||||
|
|
||||||
En pratique, ImageMagick finit presque toujours dans les mêmes situations :
|
|
||||||
|
|
||||||
- préparation d'images pour un site web (redimensionnement + compression + strip),
|
|
||||||
- génération de miniatures à la volée côté serveur,
|
|
||||||
- normalisation d'un catalogue photo hétérogène (formats, tailles, profils),
|
|
||||||
- conversion massive d'archives anciennes vers des formats modernes,
|
|
||||||
- nettoyage des métadonnées avant diffusion publique.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
ImageMagick fait partie de ces outils qu'on apprivoise lentement mais qu'on garde longtemps. Au début, on copie des commandes trouvées en ligne sans tout comprendre. Puis on commence à reconnaître les options, à les combiner, à écrire ses propres scripts. Et un jour, on se rend compte qu'on a remplacé un logiciel entier par trois lignes de Bash — et qu'on n'a jamais été aussi efficace pour traiter des images.
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "11186836-bbac-4054-82db-a3bfed14a274",
|
|
||||||
"slug": "imagemagick-traiter-des-images-en-ligne-de-commande",
|
|
||||||
"title": "ImageMagick : traiter des images en ligne de commande",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2025-12-28 14:56",
|
|
||||||
"created_at": "2025-12-28 14:56:14",
|
|
||||||
"updated_at": "2026-05-12 00:36:01",
|
|
||||||
"revisions": [
|
|
||||||
{
|
|
||||||
"n": 1,
|
|
||||||
"date": "2026-05-12 00:33:58",
|
|
||||||
"comment": "",
|
|
||||||
"title": "ImageMagick : traiter des images en ligne de commande"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"n": 2,
|
|
||||||
"date": "2026-05-12 00:36:01",
|
|
||||||
"comment": "",
|
|
||||||
"title": "ImageMagick : traiter des images en ligne de commande"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cover": "cover.png",
|
|
||||||
"files_meta": {
|
|
||||||
"cover.png": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": "https://imagemagick.org/image/logo.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"external_links": [],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "",
|
|
||||||
"og_image": "",
|
|
||||||
"category": "linux"
|
|
||||||
}
|
|
||||||
@@ -1,264 +0,0 @@
|
|||||||
## 1. À quoi ça sert
|
|
||||||
|
|
||||||
ImageMagick, c'est l'outil qu'on sort quand on veut manipuler des images sans ouvrir un logiciel graphique. Pas de Photoshop, pas de GIMP, pas de clic-droit "Redimensionner" sur cent fichiers à la suite : juste une commande dans un terminal, et le travail est fait.
|
|
||||||
|
|
||||||
C'est une suite d'outils qui sait lire, écrire et transformer plus de 200 formats — du JPEG classique au PDF en passant par le HEIC des iPhones, le WebP de Google ou le bon vieux TIFF des scanners. L'absence d'interface graphique est ici une fonctionnalité, pas un défaut : elle permet de l'utiliser partout où il n'y a pas d'écran, et surtout dans tout ce qui doit tourner tout seul.
|
|
||||||
|
|
||||||
On le retrouve donc naturellement :
|
|
||||||
|
|
||||||
- sur des serveurs web qui génèrent des miniatures à la volée,
|
|
||||||
- dans des scripts qui traitent des dossiers entiers d'un coup,
|
|
||||||
- dans des pipelines CI/CD pour préparer des assets,
|
|
||||||
- dans des conteneurs Docker, accessibles uniquement en SSH.
|
|
||||||
|
|
||||||
Depuis la version 7, tout passe par une commande unique : `magick`. Les anciennes commandes (`convert`, `identify`, `mogrify`...) existent toujours pour la compatibilité, mais elles ne sont plus la norme.
|
|
||||||
|
|
||||||
## 2. Installation
|
|
||||||
|
|
||||||
Sur Debian ou Ubuntu :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install imagemagick
|
|
||||||
```
|
|
||||||
|
|
||||||
On vérifie ensuite que tout est en place :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick -version
|
|
||||||
```
|
|
||||||
|
|
||||||
La sortie indique aussi les délégués compilés (libwebp, libheif, libraw, etc.). Si un format précis vous intéresse, c'est ici qu'il faut regarder : ImageMagick ne sait lire un format que si la bibliothèque correspondante est présente au moment de la compilation.
|
|
||||||
|
|
||||||
## 3. Comment ImageMagick raisonne
|
|
||||||
|
|
||||||
Toutes les commandes suivent la même logique :
|
|
||||||
|
|
||||||
```
|
|
||||||
magick [entrée] [options] [sortie]
|
|
||||||
```
|
|
||||||
|
|
||||||
L'image est chargée en mémoire, puis chaque option s'applique **dans l'ordre où elle est écrite**, comme une chaîne de traitement. Ce point est important : déplacer une option dans la ligne peut changer le résultat final.
|
|
||||||
|
|
||||||
Exemple :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick input.jpg -resize 800x600 -quality 85 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Ici, l'image est lue, redimensionnée à 800×600, puis compressée à 85% de qualité, puis écrite sur le disque. Si on inversait `-quality` et `-resize`, le résultat serait identique dans ce cas précis, mais avec des opérations qui modifient les pixels (flou, conversion d'espace colorimétrique, recadrage), l'ordre devient critique.
|
|
||||||
|
|
||||||
## 4. Convertir d'un format à un autre
|
|
||||||
|
|
||||||
Le cas le plus simple : changer l'extension du fichier de sortie suffit.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.png image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
ImageMagick détecte le format cible à partir de l'extension et fait la conversion. C'est aussi simple que ça pour 90% des cas.
|
|
||||||
|
|
||||||
Quand on veut être plus précis — par exemple forcer une profondeur de couleur particulière — on l'indique explicitement :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.png -depth 8 image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Utile quand on récupère des images en 16 bits par canal qu'on veut ramener à du 8 bits standard, soit pour gagner de la place, soit pour garantir la compatibilité avec un logiciel récalcitrant.
|
|
||||||
|
|
||||||
## 5. Redimensionner
|
|
||||||
|
|
||||||
### La méthode brutale
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize 800x600 image_resized.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Cette commande redimensionne à 800×600 **en respectant les proportions** par défaut, contrairement à ce qu'on pourrait croire. Si l'image source est en 4:3, elle rentrera pile dedans ; si elle est en 16:9, ImageMagick choisira la dimension la plus contraignante et l'autre sera plus petite que demandé.
|
|
||||||
|
|
||||||
Pour forcer exactement ces dimensions quitte à déformer l'image, il faut ajouter un point d'exclamation :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize 800x600! image_resized.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
### Ne rétrécir que les grandes images
|
|
||||||
|
|
||||||
C'est probablement le cas le plus utile au quotidien : on a un dossier d'images, on veut s'assurer qu'aucune ne dépasse 1600 pixels, mais on ne veut pas agrandir les petites (ce qui dégraderait leur qualité).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize "1600x1600>" image_resized.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Le `>` signifie « uniquement si l'image est plus grande ». Les guillemets sont nécessaires car `>` est interprété par le shell comme une redirection. On peut aussi échapper le caractère avec `\>`.
|
|
||||||
|
|
||||||
### En pourcentage
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize 50% image_small.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Pratique quand on veut diviser la taille par deux sans calculer les dimensions exactes.
|
|
||||||
|
|
||||||
## 6. Qualité et poids du fichier
|
|
||||||
|
|
||||||
Pour les JPEG, le paramètre `-quality` règle le compromis entre fidélité visuelle et poids du fichier :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -quality 85 image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Quelques repères en pratique :
|
|
||||||
|
|
||||||
- **100** : qualité maximale, fichier énorme, différence imperceptible avec 95.
|
|
||||||
- **85** : la valeur par défaut de la plupart des appareils photo, et un excellent compromis pour le web.
|
|
||||||
- **75** : encore très acceptable, gain de place notable.
|
|
||||||
- **En dessous de 70** : les artefacts deviennent visibles, surtout sur les aplats de couleur.
|
|
||||||
|
|
||||||
### Supprimer les métadonnées
|
|
||||||
|
|
||||||
Les fichiers issus d'appareils photo ou de smartphones embarquent beaucoup d'informations : modèle de l'appareil, date, parfois coordonnées GPS, miniature intégrée, profil colorimétrique... Tout ça peut peser plusieurs dizaines de kilo-octets, et surtout poser des problèmes de confidentialité.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -strip image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
L'option `-strip` fait le ménage. À utiliser systématiquement avant de publier des photos sur le web, et indispensable dès qu'on parle de RGPD ou d'anonymisation. Attention en revanche pour la photographie professionnelle où certaines métadonnées (droits d'auteur, profil ICC) peuvent être nécessaires.
|
|
||||||
|
|
||||||
## 7. Recadrer et adapter à un cadre
|
|
||||||
|
|
||||||
### Recadrage classique
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -crop 800x600+100+50 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
La syntaxe se lit comme une fenêtre qu'on découpe dans l'image : largeur × hauteur, décalée de 100 pixels depuis la gauche et 50 pixels depuis le haut.
|
|
||||||
|
|
||||||
### Remplir un cadre exact, sans déformation
|
|
||||||
|
|
||||||
C'est le besoin typique des miniatures de site : on veut toutes les vignettes en 800×600 pile, peu importe le format des photos d'origine.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize 800x600^ -gravity center -extent 800x600 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Trois étapes enchaînées :
|
|
||||||
|
|
||||||
1. `-resize 800x600^` redimensionne pour que l'image **remplisse** le cadre (le `^` inverse la logique habituelle : on prend la plus grande dimension comme contrainte, pas la plus petite).
|
|
||||||
2. `-gravity center` indique qu'on veut centrer le découpage.
|
|
||||||
3. `-extent 800x600` coupe ce qui dépasse pour obtenir exactement la taille voulue.
|
|
||||||
|
|
||||||
Le résultat : aucune déformation, aucune bande noire, juste un éventuel rognage sur les bords les plus longs.
|
|
||||||
|
|
||||||
## 8. Traiter un dossier entier
|
|
||||||
|
|
||||||
Une boucle Bash suffit pour convertir tous les PNG d'un dossier en JPEG :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
for f in *.png; do
|
|
||||||
magick "$f" "${f%.png}.jpg"
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
La syntaxe `${f%.png}` retire l'extension `.png` du nom, on y ajoute `.jpg`. Simple et fiable.
|
|
||||||
|
|
||||||
Pour modifier les fichiers **sur place**, ImageMagick fournit `mogrify` :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mogrify -resize "1600x1600>" *.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Cette commande écrase chaque fichier par sa version redimensionnée. C'est rapide et pratique, mais ça veut aussi dire qu'**il n'y a pas de retour en arrière** : si la commande est mal écrite, le dossier original est perdu. Règle absolue : travailler sur une copie, ou s'assurer d'avoir une sauvegarde.
|
|
||||||
|
|
||||||
## 9. Texte et filigranes
|
|
||||||
|
|
||||||
### Apposer une mention textuelle
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg \
|
|
||||||
-gravity southeast \
|
|
||||||
-pointsize 24 \
|
|
||||||
-fill white \
|
|
||||||
-annotate +10+10 "© MonSite" \
|
|
||||||
image_marked.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
`-gravity` ancre le texte dans un coin de l'image (les neuf positions classiques : `northwest`, `north`, `northeast`, `west`, `center`...), et `-annotate` ajoute un décalage par rapport à ce point d'ancrage. Ici, `+10+10` éloigne le texte de 10 pixels du coin inférieur droit.
|
|
||||||
|
|
||||||
### Superposer un logo ou un watermark image
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg watermark.png -gravity center -composite output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
L'image principale est lue en premier, le filigrane en second, puis `-composite` les fusionne. Si le watermark a un canal alpha (transparence), il est respecté.
|
|
||||||
|
|
||||||
## 10. Couleurs et tons
|
|
||||||
|
|
||||||
Passage en noir et blanc :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -colorspace Gray output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Réglage de la luminosité et du contraste (valeurs en pourcentage, positives ou négatives) :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -brightness-contrast 10x5 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Ici, +10% de luminosité et +5% de contraste. Pour assombrir, on utilise des valeurs négatives : `-brightness-contrast -10x0`.
|
|
||||||
|
|
||||||
## 11. Inspecter une image
|
|
||||||
|
|
||||||
Pour obtenir les informations essentielles — format, dimensions, profondeur :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick identify image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Pour tout savoir, y compris les métadonnées EXIF, le profil colorimétrique, l'histogramme :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick identify -verbose image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
La sortie verbeuse peut faire plusieurs pages, mais c'est inestimable pour diagnostiquer un problème ou comprendre d'où vient un fichier.
|
|
||||||
|
|
||||||
## 12. Formats modernes
|
|
||||||
|
|
||||||
Le WebP de Google offre une compression nettement meilleure que le JPEG à qualité équivalente, et il est aujourd'hui supporté par tous les navigateurs courants :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -quality 80 image.webp
|
|
||||||
```
|
|
||||||
|
|
||||||
L'AVIF va encore plus loin en termes de compression, au prix d'un encodage plus lent :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg image.avif
|
|
||||||
```
|
|
||||||
|
|
||||||
Si la commande échoue avec une erreur de délégué, c'est que votre installation d'ImageMagick a été compilée sans le support AVIF — il faudra installer `libheif` ou recompiler.
|
|
||||||
|
|
||||||
## 13. Quelques règles à se fixer
|
|
||||||
|
|
||||||
- **Toujours travailler sur une copie** quand on découvre une nouvelle commande. `mogrify` en particulier ne pardonne pas.
|
|
||||||
- **Stripper les métadonnées** avant toute publication web.
|
|
||||||
- **Pour de très gros volumes** (plusieurs milliers d'images, ou des images très lourdes), regarder du côté de `libvips` : c'est plus rapide et beaucoup moins gourmand en mémoire qu'ImageMagick. Pour tout le reste, ImageMagick est largement suffisant.
|
|
||||||
- **Automatiser dès qu'on répète** : si la même commande revient deux fois, elle mérite un script.
|
|
||||||
- **Lire les messages d'erreur** : ImageMagick est verbeux, et la plupart des problèmes (délégué manquant, permissions, format non reconnu) sont explicitement nommés dans la sortie.
|
|
||||||
|
|
||||||
## 14. Là où on le croise vraiment
|
|
||||||
|
|
||||||
En pratique, ImageMagick finit presque toujours dans les mêmes situations :
|
|
||||||
|
|
||||||
- préparation d'images pour un site web (redimensionnement + compression + strip),
|
|
||||||
- génération de miniatures à la volée côté serveur,
|
|
||||||
- normalisation d'un catalogue photo hétérogène (formats, tailles, profils),
|
|
||||||
- conversion massive d'archives anciennes vers des formats modernes,
|
|
||||||
- nettoyage des métadonnées avant diffusion publique.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
ImageMagick fait partie de ces outils qu'on apprivoise lentement mais qu'on garde longtemps. Au début, on copie des commandes trouvées en ligne sans tout comprendre. Puis on commence à reconnaître les options, à les combiner, à écrire ses propres scripts. Et un jour, on se rend compte qu'on a remplacé un logiciel entier par trois lignes de Bash — et qu'on n'a jamais été aussi efficace pour traiter des images.
|
|
||||||
@@ -1,264 +0,0 @@
|
|||||||
## 1. À quoi ça sert
|
|
||||||
|
|
||||||
ImageMagick, c'est l'outil qu'on sort quand on veut manipuler des images sans ouvrir un logiciel graphique. Pas de Photoshop, pas de GIMP, pas de clic-droit "Redimensionner" sur cent fichiers à la suite : juste une commande dans un terminal, et le travail est fait.
|
|
||||||
|
|
||||||
C'est une suite d'outils qui sait lire, écrire et transformer plus de 200 formats — du JPEG classique au PDF en passant par le HEIC des iPhones, le WebP de Google ou le bon vieux TIFF des scanners. L'absence d'interface graphique est ici une fonctionnalité, pas un défaut : elle permet de l'utiliser partout où il n'y a pas d'écran, et surtout dans tout ce qui doit tourner tout seul.
|
|
||||||
|
|
||||||
On le retrouve donc naturellement :
|
|
||||||
|
|
||||||
- sur des serveurs web qui génèrent des miniatures à la volée,
|
|
||||||
- dans des scripts qui traitent des dossiers entiers d'un coup,
|
|
||||||
- dans des pipelines CI/CD pour préparer des assets,
|
|
||||||
- dans des conteneurs Docker, accessibles uniquement en SSH.
|
|
||||||
|
|
||||||
Depuis la version 7, tout passe par une commande unique : `magick`. Les anciennes commandes (`convert`, `identify`, `mogrify`...) existent toujours pour la compatibilité, mais elles ne sont plus la norme.
|
|
||||||
|
|
||||||
## 2. Installation
|
|
||||||
|
|
||||||
Sur Debian ou Ubuntu :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install imagemagick
|
|
||||||
```
|
|
||||||
|
|
||||||
On vérifie ensuite que tout est en place :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick -version
|
|
||||||
```
|
|
||||||
|
|
||||||
La sortie indique aussi les délégués compilés (libwebp, libheif, libraw, etc.). Si un format précis vous intéresse, c'est ici qu'il faut regarder : ImageMagick ne sait lire un format que si la bibliothèque correspondante est présente au moment de la compilation.
|
|
||||||
|
|
||||||
## 3. Comment ImageMagick raisonne
|
|
||||||
|
|
||||||
Toutes les commandes suivent la même logique :
|
|
||||||
|
|
||||||
```
|
|
||||||
magick [entrée] [options] [sortie]
|
|
||||||
```
|
|
||||||
|
|
||||||
L'image est chargée en mémoire, puis chaque option s'applique **dans l'ordre où elle est écrite**, comme une chaîne de traitement. Ce point est important : déplacer une option dans la ligne peut changer le résultat final.
|
|
||||||
|
|
||||||
Exemple :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick input.jpg -resize 800x600 -quality 85 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Ici, l'image est lue, redimensionnée à 800×600, puis compressée à 85% de qualité, puis écrite sur le disque. Si on inversait `-quality` et `-resize`, le résultat serait identique dans ce cas précis, mais avec des opérations qui modifient les pixels (flou, conversion d'espace colorimétrique, recadrage), l'ordre devient critique.
|
|
||||||
|
|
||||||
## 4. Convertir d'un format à un autre
|
|
||||||
|
|
||||||
Le cas le plus simple : changer l'extension du fichier de sortie suffit.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.png image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
ImageMagick détecte le format cible à partir de l'extension et fait la conversion. C'est aussi simple que ça pour 90% des cas.
|
|
||||||
|
|
||||||
Quand on veut être plus précis — par exemple forcer une profondeur de couleur particulière — on l'indique explicitement :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.png -depth 8 image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Utile quand on récupère des images en 16 bits par canal qu'on veut ramener à du 8 bits standard, soit pour gagner de la place, soit pour garantir la compatibilité avec un logiciel récalcitrant.
|
|
||||||
|
|
||||||
## 5. Redimensionner
|
|
||||||
|
|
||||||
### La méthode brutale
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize 800x600 image_resized.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Cette commande redimensionne à 800×600 **en respectant les proportions** par défaut, contrairement à ce qu'on pourrait croire. Si l'image source est en 4:3, elle rentrera pile dedans ; si elle est en 16:9, ImageMagick choisira la dimension la plus contraignante et l'autre sera plus petite que demandé.
|
|
||||||
|
|
||||||
Pour forcer exactement ces dimensions quitte à déformer l'image, il faut ajouter un point d'exclamation :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize 800x600! image_resized.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
### Ne rétrécir que les grandes images
|
|
||||||
|
|
||||||
C'est probablement le cas le plus utile au quotidien : on a un dossier d'images, on veut s'assurer qu'aucune ne dépasse 1600 pixels, mais on ne veut pas agrandir les petites (ce qui dégraderait leur qualité).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize "1600x1600>" image_resized.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Le `>` signifie « uniquement si l'image est plus grande ». Les guillemets sont nécessaires car `>` est interprété par le shell comme une redirection. On peut aussi échapper le caractère avec `\>`.
|
|
||||||
|
|
||||||
### En pourcentage
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize 50% image_small.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Pratique quand on veut diviser la taille par deux sans calculer les dimensions exactes.
|
|
||||||
|
|
||||||
## 6. Qualité et poids du fichier
|
|
||||||
|
|
||||||
Pour les JPEG, le paramètre `-quality` règle le compromis entre fidélité visuelle et poids du fichier :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -quality 85 image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Quelques repères en pratique :
|
|
||||||
|
|
||||||
- **100** : qualité maximale, fichier énorme, différence imperceptible avec 95.
|
|
||||||
- **85** : la valeur par défaut de la plupart des appareils photo, et un excellent compromis pour le web.
|
|
||||||
- **75** : encore très acceptable, gain de place notable.
|
|
||||||
- **En dessous de 70** : les artefacts deviennent visibles, surtout sur les aplats de couleur.
|
|
||||||
|
|
||||||
### Supprimer les métadonnées
|
|
||||||
|
|
||||||
Les fichiers issus d'appareils photo ou de smartphones embarquent beaucoup d'informations : modèle de l'appareil, date, parfois coordonnées GPS, miniature intégrée, profil colorimétrique... Tout ça peut peser plusieurs dizaines de kilo-octets, et surtout poser des problèmes de confidentialité.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -strip image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
L'option `-strip` fait le ménage. À utiliser systématiquement avant de publier des photos sur le web, et indispensable dès qu'on parle de RGPD ou d'anonymisation. Attention en revanche pour la photographie professionnelle où certaines métadonnées (droits d'auteur, profil ICC) peuvent être nécessaires.
|
|
||||||
|
|
||||||
## 7. Recadrer et adapter à un cadre
|
|
||||||
|
|
||||||
### Recadrage classique
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -crop 800x600+100+50 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
La syntaxe se lit comme une fenêtre qu'on découpe dans l'image : largeur × hauteur, décalée de 100 pixels depuis la gauche et 50 pixels depuis le haut.
|
|
||||||
|
|
||||||
### Remplir un cadre exact, sans déformation
|
|
||||||
|
|
||||||
C'est le besoin typique des miniatures de site : on veut toutes les vignettes en 800×600 pile, peu importe le format des photos d'origine.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize 800x600^ -gravity center -extent 800x600 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Trois étapes enchaînées :
|
|
||||||
|
|
||||||
1. `-resize 800x600^` redimensionne pour que l'image **remplisse** le cadre (le `^` inverse la logique habituelle : on prend la plus grande dimension comme contrainte, pas la plus petite).
|
|
||||||
2. `-gravity center` indique qu'on veut centrer le découpage.
|
|
||||||
3. `-extent 800x600` coupe ce qui dépasse pour obtenir exactement la taille voulue.
|
|
||||||
|
|
||||||
Le résultat : aucune déformation, aucune bande noire, juste un éventuel rognage sur les bords les plus longs.
|
|
||||||
|
|
||||||
## 8. Traiter un dossier entier
|
|
||||||
|
|
||||||
Une boucle Bash suffit pour convertir tous les PNG d'un dossier en JPEG :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
for f in *.png; do
|
|
||||||
magick "$f" "${f%.png}.jpg"
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
La syntaxe `${f%.png}` retire l'extension `.png` du nom, on y ajoute `.jpg`. Simple et fiable.
|
|
||||||
|
|
||||||
Pour modifier les fichiers **sur place**, ImageMagick fournit `mogrify` :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mogrify -resize "1600x1600>" *.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Cette commande écrase chaque fichier par sa version redimensionnée. C'est rapide et pratique, mais ça veut aussi dire qu'**il n'y a pas de retour en arrière** : si la commande est mal écrite, le dossier original est perdu. Règle absolue : travailler sur une copie, ou s'assurer d'avoir une sauvegarde.
|
|
||||||
|
|
||||||
## 9. Texte et filigranes
|
|
||||||
|
|
||||||
### Apposer une mention textuelle
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg \
|
|
||||||
-gravity southeast \
|
|
||||||
-pointsize 24 \
|
|
||||||
-fill white \
|
|
||||||
-annotate +10+10 "© MonSite" \
|
|
||||||
image_marked.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
`-gravity` ancre le texte dans un coin de l'image (les neuf positions classiques : `northwest`, `north`, `northeast`, `west`, `center`...), et `-annotate` ajoute un décalage par rapport à ce point d'ancrage. Ici, `+10+10` éloigne le texte de 10 pixels du coin inférieur droit.
|
|
||||||
|
|
||||||
### Superposer un logo ou un watermark image
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg watermark.png -gravity center -composite output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
L'image principale est lue en premier, le filigrane en second, puis `-composite` les fusionne. Si le watermark a un canal alpha (transparence), il est respecté.
|
|
||||||
|
|
||||||
## 10. Couleurs et tons
|
|
||||||
|
|
||||||
Passage en noir et blanc :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -colorspace Gray output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Réglage de la luminosité et du contraste (valeurs en pourcentage, positives ou négatives) :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -brightness-contrast 10x5 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Ici, +10% de luminosité et +5% de contraste. Pour assombrir, on utilise des valeurs négatives : `-brightness-contrast -10x0`.
|
|
||||||
|
|
||||||
## 11. Inspecter une image
|
|
||||||
|
|
||||||
Pour obtenir les informations essentielles — format, dimensions, profondeur :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick identify image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Pour tout savoir, y compris les métadonnées EXIF, le profil colorimétrique, l'histogramme :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick identify -verbose image.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
La sortie verbeuse peut faire plusieurs pages, mais c'est inestimable pour diagnostiquer un problème ou comprendre d'où vient un fichier.
|
|
||||||
|
|
||||||
## 12. Formats modernes
|
|
||||||
|
|
||||||
Le WebP de Google offre une compression nettement meilleure que le JPEG à qualité équivalente, et il est aujourd'hui supporté par tous les navigateurs courants :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -quality 80 image.webp
|
|
||||||
```
|
|
||||||
|
|
||||||
L'AVIF va encore plus loin en termes de compression, au prix d'un encodage plus lent :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg image.avif
|
|
||||||
```
|
|
||||||
|
|
||||||
Si la commande échoue avec une erreur de délégué, c'est que votre installation d'ImageMagick a été compilée sans le support AVIF — il faudra installer `libheif` ou recompiler.
|
|
||||||
|
|
||||||
## 13. Quelques règles à se fixer
|
|
||||||
|
|
||||||
- **Toujours travailler sur une copie** quand on découvre une nouvelle commande. `mogrify` en particulier ne pardonne pas.
|
|
||||||
- **Stripper les métadonnées** avant toute publication web.
|
|
||||||
- **Pour de très gros volumes** (plusieurs milliers d'images, ou des images très lourdes), regarder du côté de `libvips` : c'est plus rapide et beaucoup moins gourmand en mémoire qu'ImageMagick. Pour tout le reste, ImageMagick est largement suffisant.
|
|
||||||
- **Automatiser dès qu'on répète** : si la même commande revient deux fois, elle mérite un script.
|
|
||||||
- **Lire les messages d'erreur** : ImageMagick est verbeux, et la plupart des problèmes (délégué manquant, permissions, format non reconnu) sont explicitement nommés dans la sortie.
|
|
||||||
|
|
||||||
## 14. Là où on le croise vraiment
|
|
||||||
|
|
||||||
En pratique, ImageMagick finit presque toujours dans les mêmes situations :
|
|
||||||
|
|
||||||
- préparation d'images pour un site web (redimensionnement + compression + strip),
|
|
||||||
- génération de miniatures à la volée côté serveur,
|
|
||||||
- normalisation d'un catalogue photo hétérogène (formats, tailles, profils),
|
|
||||||
- conversion massive d'archives anciennes vers des formats modernes,
|
|
||||||
- nettoyage des métadonnées avant diffusion publique.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
ImageMagick fait partie de ces outils qu'on apprivoise lentement mais qu'on garde longtemps. Au début, on copie des commandes trouvées en ligne sans tout comprendre. Puis on commence à reconnaître les options, à les combiner, à écrire ses propres scripts. Et un jour, on se rend compte qu'on a remplacé un logiciel entier par trois lignes de Bash — et qu'on n'a jamais été aussi efficace pour traiter des images.
|
|
||||||
|
Before Width: | Height: | Size: 97 KiB |
@@ -1,54 +0,0 @@
|
|||||||
# Installation et mise en service d'une borne de recharge murale GONEO 7,4 kW
|
|
||||||
|
|
||||||
Une borne de recharge murale GONEO a été récemment acquise ([référence Amazon B0FP288GM7](https://amzn.to/4dponSA)). Il s'agit d'une wallbox monophasée 7,4 kW (32 A, 230 V), équipée d'un connecteur Type 2, d'un lecteur RFID, et pilotable via Wi-Fi, Bluetooth. La gamme constructeur est documentée sur le [site officiel GONEO Global](https://www.goneoglobal.com/) et son catalogue EV Charger sur [it.goneoglobal.com](https://it.goneoglobal.com/en/collections/ev-charger).
|
|
||||||
|
|
||||||
## Caractéristiques techniques
|
|
||||||
|
|
||||||
D'après les fiches constructeur et revendeurs, le modèle présente les caractéristiques suivantes :
|
|
||||||
|
|
||||||
- Puissance : jusqu'à 7 kW en monophasé (annoncée 7,4 kW selon le réglage de courant)
|
|
||||||
- Courant réglable : 8 A à 32 A
|
|
||||||
- Tension : 230 V monophasé
|
|
||||||
- Connecteur : Type 2 (IEC 62196-2)
|
|
||||||
- Protection : IP65, IK10, ignifuge UL94 V-0, plage -30 °C à +55 °C
|
|
||||||
- Détection de défaut intégrée : protection de fuite Type A 30 mA + DC 6 mA
|
|
||||||
- Connectivité : Wi-Fi, Bluetooth, compatible OCPP et Home Assistant
|
|
||||||
- Application : *Goneo EV Charger* (Android / iOS)
|
|
||||||
|
|
||||||
## Câblage et raccordement
|
|
||||||
|
|
||||||
Le câble d'alimentation a été tiré soi-même, en s'appuyant sur les règles de dimensionnement détaillées dans [cet article](https://varlog.a5l.fr/post/dimensionnement-des-cables-electriques-cuivre-230-v-monophase). La section retenue est conforme aux préconisations constructeur : câble 3G6 mm² pour un courant maximum de 32 A, avec en amont un disjoncteur 40 A et un interrupteur différentiel type A 40 A.
|
|
||||||
|
|
||||||
Un soin particulier a été apporté au serrage des borniers : un serrage insuffisant entraîne une résistance de contact accrue, source d'échauffement et de chute de tension sous charge — risque non négligeable compte tenu des intensités mises en jeu (jusqu'à 32 A en continu pendant plusieurs heures).
|
|
||||||
|
|
||||||
## Vérifications avant mise sous tension
|
|
||||||
|
|
||||||
Une fois le raccordement effectué, les mesures suivantes ont été réalisées au multimètre :
|
|
||||||
|
|
||||||
- Phase – Neutre : ~230 V (tension nominale du réseau)
|
|
||||||
- Phase – Terre : ~230 V (confirme la continuité de la phase et de la terre)
|
|
||||||
- Neutre – Terre : ~0 V (idéalement quelques volts maximum ; une valeur significative trahirait un défaut de neutre ou de mise à la terre)
|
|
||||||
|
|
||||||
À noter : la protection différentielle intégrée à la borne couvre la composante DC (6 mA), ce qui permet en théorie de se contenter d'un différentiel **type A** en amont — là où une borne sans détection DC interne exigerait un **type B** beaucoup plus onéreux. La vérification de la valeur de la prise de terre au telluromètre et le test du déclenchement du différentiel restent recommandés.
|
|
||||||
|
|
||||||
## Mise en service
|
|
||||||
|
|
||||||
La mise en service s'effectue via le Wi-Fi de l'appareil et l'application propriétaire *Goneo EV Charger*. Points à anticiper :
|
|
||||||
|
|
||||||
- Télécharger l'application *avant* de commencer la procédure.
|
|
||||||
- Créer un compte utilisateur.
|
|
||||||
- S'assurer que le téléphone est connecté à un réseau Wi-Fi 2,4 GHz et que le Bluetooth est activé ; la borne doit être à portée du signal Wi-Fi.
|
|
||||||
- Associer la borne au compte (un appui court sur le bouton règle l'alimentation, un double appui lance la configuration Wi-Fi).
|
|
||||||
|
|
||||||
La borne ayant été achetée d'occasion, elle n'avait pas été dissociée du compte du précédent propriétaire — situation fréquente sur ce type d'achat. Un message au SAV par mail (info@goneoglobal.com) a suffi : la réponse a été rapide et la dissociation effectuée sans difficulté. Réflexe à prendre lors d'un achat d'occasion : demander au vendeur de procéder à la dissociation avant l'expédition.
|
|
||||||
|
|
||||||
## Usage au quotidien
|
|
||||||
|
|
||||||
Deux modes d'utilisation cohabitent :
|
|
||||||
|
|
||||||
- **Profil horaire programmé via l'application** : pratique pour caler les sessions sur les heures creuses.
|
|
||||||
- **Badge RFID** fourni avec la borne : démarrer ou arrêter une session par simple présentation du badge, sans passer par l'application.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
À noter sur le plan réglementaire : depuis 2017, l'installation d'une borne de recharge d'une puissance supérieure à 3,7 kW à domicile relève en principe d'un électricien qualifié IRVE. Le fait de procéder soi-même au tirage du câble et au raccordement reste possible techniquement, mais sort du cadre permettant de prétendre aux aides publiques (crédit d'impôt, prime ADVENIR) et peut avoir des conséquences en matière d'assurance.
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "1363f454-ca59-4264-a8f0-a2446d645ebc",
|
|
||||||
"slug": "installation-et-mise-en-service-d-une-borne-de-recharge-murale-goneo-7-4-kw",
|
|
||||||
"title": "Installation et mise en service d'une borne de recharge murale GONEO 7,4 kW",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2026-05-13 11:04",
|
|
||||||
"created_at": "2026-05-13 11:23:38",
|
|
||||||
"updated_at": "2026-05-13 15:17:04",
|
|
||||||
"revisions": [
|
|
||||||
{
|
|
||||||
"n": 1,
|
|
||||||
"date": "2026-05-13 12:50:54",
|
|
||||||
"comment": "Contenu modifié",
|
|
||||||
"title": "Installation et mise en service d'une borne de recharge murale GONEO 7,4 kW"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"n": 2,
|
|
||||||
"date": "2026-05-13 13:38:38",
|
|
||||||
"comment": "Contenu modifié",
|
|
||||||
"title": "Installation et mise en service d'une borne de recharge murale GONEO 7,4 kW"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"n": 3,
|
|
||||||
"date": "2026-05-13 13:56:09",
|
|
||||||
"comment": "Contenu modifié : lien amazon afilié",
|
|
||||||
"title": "Installation et mise en service d'une borne de recharge murale GONEO 7,4 kW"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cover": "cover.jpg",
|
|
||||||
"files_meta": {
|
|
||||||
"GONEO_AC_EV_Charging_Wallbox_APP_instruction0731.pdf": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"GONEO_D3E_Series_AC_EV_Charging_Wallbox_Quick_Guide-20240816.pdf": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"GONEO__________________-A4_______2_.pdf": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"_thumb_08fa79864df51448-99503.png": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"cover.jpg": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": "https://www.goneoglobal.com/cdn/shop/files/3_eed2ecfe-89ab-47b3-b479-cd7a77dca399.jpg?v=1727271286&width=1500"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"external_links": [
|
|
||||||
{
|
|
||||||
"url": "https://www.goneoglobal.com/products/ev-charger-safe-charging-station",
|
|
||||||
"name": "EV Charger Safe Charging Station",
|
|
||||||
"added_at": "2026-05-13 13:37:20",
|
|
||||||
"author": "GONEO Global",
|
|
||||||
"meta": {
|
|
||||||
"mime": "text/html",
|
|
||||||
"blocked": true,
|
|
||||||
"og_image": "/file?uuid=1363f454-ca59-4264-a8f0-a2446d645ebc&name=_thumb_08fa79864df51448-99503.png"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "",
|
|
||||||
"og_image": "https://varlog.a5l.fr/file?uuid=1363f454-ca59-4264-a8f0-a2446d645ebc&name=cover.jpg",
|
|
||||||
"category": ""
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
Une borne de recharge murale GONEO a été récemment acquise ([référence Amazon B0FP288GM7](https://www.amazon.fr/dp/B0FP288GM7)). Il s'agit d'une wallbox monophasée 7,4 kW (32 A, 230 V), équipée d'un connecteur Type 2, d'un lecteur RFID, et pilotable via Wi-Fi, Bluetooth et 4G. La gamme constructeur est documentée sur le [site officiel GONEO Global](https://www.goneoglobal.com/) et son catalogue EV Charger sur [it.goneoglobal.com](https://it.goneoglobal.com/en/collections/ev-charger).
|
|
||||||
|
|
||||||
## Caractéristiques techniques
|
|
||||||
|
|
||||||
D'après les fiches constructeur et revendeurs, le modèle présente les caractéristiques suivantes :
|
|
||||||
|
|
||||||
- Puissance : jusqu'à 7 kW en monophasé (annoncée 7,4 kW selon le réglage de courant)
|
|
||||||
- Courant réglable : 8 A à 32 A
|
|
||||||
- Tension : 230 V monophasé
|
|
||||||
- Connecteur : Type 2 (IEC 62196-2)
|
|
||||||
- Protection : IP65, IK10, ignifuge UL94 V-0, plage -30 °C à +55 °C
|
|
||||||
- Détection de défaut intégrée : protection de fuite Type A 30 mA + DC 6 mA
|
|
||||||
- Connectivité : Wi-Fi, Bluetooth, 4G, compatible OCPP et Home Assistant
|
|
||||||
- Application : *Goneo EV Charger* (Android / iOS)
|
|
||||||
|
|
||||||
## Câblage et raccordement
|
|
||||||
|
|
||||||
Le câble d'alimentation a été tiré soi-même, en s'appuyant sur les règles de dimensionnement détaillées dans [cet article](https://varlog.a5l.fr/post/dimensionnement-des-cables-electriques-cuivre-230-v-monophase). La section retenue est conforme aux préconisations constructeur : câble 3G6 mm² pour un courant maximum de 32 A, avec en amont un disjoncteur 40 A et un interrupteur différentiel type A 40 A.
|
|
||||||
|
|
||||||
Un soin particulier a été apporté au serrage des borniers : un serrage insuffisant entraîne une résistance de contact accrue, source d'échauffement et de chute de tension sous charge — risque non négligeable compte tenu des intensités mises en jeu (jusqu'à 32 A en continu pendant plusieurs heures).
|
|
||||||
|
|
||||||
## Vérifications avant mise sous tension
|
|
||||||
|
|
||||||
Une fois le raccordement effectué, les mesures suivantes ont été réalisées au multimètre :
|
|
||||||
|
|
||||||
- Phase – Neutre : ~230 V (tension nominale du réseau)
|
|
||||||
- Phase – Terre : ~230 V (confirme la continuité de la phase et de la terre)
|
|
||||||
- Neutre – Terre : ~0 V (idéalement quelques volts maximum ; une valeur significative trahirait un défaut de neutre ou de mise à la terre)
|
|
||||||
|
|
||||||
À noter : la protection différentielle intégrée à la borne couvre la composante DC (6 mA), ce qui permet en théorie de se contenter d'un différentiel **type A** en amont — là où une borne sans détection DC interne exigerait un **type B** beaucoup plus onéreux. La vérification de la valeur de la prise de terre au telluromètre et le test du déclenchement du différentiel restent recommandés.
|
|
||||||
|
|
||||||
## Mise en service
|
|
||||||
|
|
||||||
La mise en service s'effectue via le Wi-Fi de l'appareil et l'application propriétaire *Goneo EV Charger*. Points à anticiper :
|
|
||||||
|
|
||||||
- Télécharger l'application *avant* de commencer la procédure.
|
|
||||||
- Créer un compte utilisateur.
|
|
||||||
- S'assurer que le téléphone est connecté à un réseau Wi-Fi 2,4 GHz et que le Bluetooth est activé ; la borne doit être à portée du signal Wi-Fi.
|
|
||||||
- Associer la borne au compte (un appui court sur le bouton règle l'alimentation, un double appui lance la configuration Wi-Fi).
|
|
||||||
|
|
||||||
La borne ayant été achetée d'occasion, elle n'avait pas été dissociée du compte du précédent propriétaire — situation fréquente sur ce type d'achat. Un message au SAV par mail (info@goneoglobal.com) a suffi : la réponse a été rapide et la dissociation effectuée sans difficulté. Réflexe à prendre lors d'un achat d'occasion : demander au vendeur de procéder à la dissociation avant l'expédition.
|
|
||||||
|
|
||||||
## Usage au quotidien
|
|
||||||
|
|
||||||
Deux modes d'utilisation cohabitent :
|
|
||||||
|
|
||||||
- **Profil horaire programmé via l'application** : pratique pour caler les sessions sur les heures creuses.
|
|
||||||
- **Badge RFID** fourni avec la borne : démarrer ou arrêter une session par simple présentation du badge, sans passer par l'application.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
À noter sur le plan réglementaire : depuis 2017, l'installation d'une borne de recharge d'une puissance supérieure à 3,7 kW à domicile relève en principe d'un électricien qualifié IRVE. Le fait de procéder soi-même au tirage du câble et au raccordement reste possible techniquement, mais sort du cadre permettant de prétendre aux aides publiques (crédit d'impôt, prime ADVENIR) et peut avoir des conséquences en matière d'assurance.
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
Une borne de recharge murale GONEO a été récemment acquise ([référence Amazon B0FP288GM7](https://www.amazon.fr/dp/B0FP288GM7)). Il s'agit d'une wallbox monophasée 7,4 kW (32 A, 230 V), équipée d'un connecteur Type 2, d'un lecteur RFID, et pilotable via Wi-Fi, Bluetooth. La gamme constructeur est documentée sur le [site officiel GONEO Global](https://www.goneoglobal.com/) et son catalogue EV Charger sur [it.goneoglobal.com](https://it.goneoglobal.com/en/collections/ev-charger).
|
|
||||||
|
|
||||||
## Caractéristiques techniques
|
|
||||||
|
|
||||||
D'après les fiches constructeur et revendeurs, le modèle présente les caractéristiques suivantes :
|
|
||||||
|
|
||||||
- Puissance : jusqu'à 7 kW en monophasé (annoncée 7,4 kW selon le réglage de courant)
|
|
||||||
- Courant réglable : 8 A à 32 A
|
|
||||||
- Tension : 230 V monophasé
|
|
||||||
- Connecteur : Type 2 (IEC 62196-2)
|
|
||||||
- Protection : IP65, IK10, ignifuge UL94 V-0, plage -30 °C à +55 °C
|
|
||||||
- Détection de défaut intégrée : protection de fuite Type A 30 mA + DC 6 mA
|
|
||||||
- Connectivité : Wi-Fi, Bluetooth, compatible OCPP et Home Assistant
|
|
||||||
- Application : *Goneo EV Charger* (Android / iOS)
|
|
||||||
|
|
||||||
## Câblage et raccordement
|
|
||||||
|
|
||||||
Le câble d'alimentation a été tiré soi-même, en s'appuyant sur les règles de dimensionnement détaillées dans [cet article](https://varlog.a5l.fr/post/dimensionnement-des-cables-electriques-cuivre-230-v-monophase). La section retenue est conforme aux préconisations constructeur : câble 3G6 mm² pour un courant maximum de 32 A, avec en amont un disjoncteur 40 A et un interrupteur différentiel type A 40 A.
|
|
||||||
|
|
||||||
Un soin particulier a été apporté au serrage des borniers : un serrage insuffisant entraîne une résistance de contact accrue, source d'échauffement et de chute de tension sous charge — risque non négligeable compte tenu des intensités mises en jeu (jusqu'à 32 A en continu pendant plusieurs heures).
|
|
||||||
|
|
||||||
## Vérifications avant mise sous tension
|
|
||||||
|
|
||||||
Une fois le raccordement effectué, les mesures suivantes ont été réalisées au multimètre :
|
|
||||||
|
|
||||||
- Phase – Neutre : ~230 V (tension nominale du réseau)
|
|
||||||
- Phase – Terre : ~230 V (confirme la continuité de la phase et de la terre)
|
|
||||||
- Neutre – Terre : ~0 V (idéalement quelques volts maximum ; une valeur significative trahirait un défaut de neutre ou de mise à la terre)
|
|
||||||
|
|
||||||
À noter : la protection différentielle intégrée à la borne couvre la composante DC (6 mA), ce qui permet en théorie de se contenter d'un différentiel **type A** en amont — là où une borne sans détection DC interne exigerait un **type B** beaucoup plus onéreux. La vérification de la valeur de la prise de terre au telluromètre et le test du déclenchement du différentiel restent recommandés.
|
|
||||||
|
|
||||||
## Mise en service
|
|
||||||
|
|
||||||
La mise en service s'effectue via le Wi-Fi de l'appareil et l'application propriétaire *Goneo EV Charger*. Points à anticiper :
|
|
||||||
|
|
||||||
- Télécharger l'application *avant* de commencer la procédure.
|
|
||||||
- Créer un compte utilisateur.
|
|
||||||
- S'assurer que le téléphone est connecté à un réseau Wi-Fi 2,4 GHz et que le Bluetooth est activé ; la borne doit être à portée du signal Wi-Fi.
|
|
||||||
- Associer la borne au compte (un appui court sur le bouton règle l'alimentation, un double appui lance la configuration Wi-Fi).
|
|
||||||
|
|
||||||
La borne ayant été achetée d'occasion, elle n'avait pas été dissociée du compte du précédent propriétaire — situation fréquente sur ce type d'achat. Un message au SAV par mail (info@goneoglobal.com) a suffi : la réponse a été rapide et la dissociation effectuée sans difficulté. Réflexe à prendre lors d'un achat d'occasion : demander au vendeur de procéder à la dissociation avant l'expédition.
|
|
||||||
|
|
||||||
## Usage au quotidien
|
|
||||||
|
|
||||||
Deux modes d'utilisation cohabitent :
|
|
||||||
|
|
||||||
- **Profil horaire programmé via l'application** : pratique pour caler les sessions sur les heures creuses.
|
|
||||||
- **Badge RFID** fourni avec la borne : démarrer ou arrêter une session par simple présentation du badge, sans passer par l'application.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
À noter sur le plan réglementaire : depuis 2017, l'installation d'une borne de recharge d'une puissance supérieure à 3,7 kW à domicile relève en principe d'un électricien qualifié IRVE. Le fait de procéder soi-même au tirage du câble et au raccordement reste possible techniquement, mais sort du cadre permettant de prétendre aux aides publiques (crédit d'impôt, prime ADVENIR) et peut avoir des conséquences en matière d'assurance.
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
Une borne de recharge murale GONEO a été récemment acquise ([référence Amazon B0FP288GM7](https://amzn.to/4dponSA)). Il s'agit d'une wallbox monophasée 7,4 kW (32 A, 230 V), équipée d'un connecteur Type 2, d'un lecteur RFID, et pilotable via Wi-Fi, Bluetooth. La gamme constructeur est documentée sur le [site officiel GONEO Global](https://www.goneoglobal.com/) et son catalogue EV Charger sur [it.goneoglobal.com](https://it.goneoglobal.com/en/collections/ev-charger).
|
|
||||||
|
|
||||||
## Caractéristiques techniques
|
|
||||||
|
|
||||||
D'après les fiches constructeur et revendeurs, le modèle présente les caractéristiques suivantes :
|
|
||||||
|
|
||||||
- Puissance : jusqu'à 7 kW en monophasé (annoncée 7,4 kW selon le réglage de courant)
|
|
||||||
- Courant réglable : 8 A à 32 A
|
|
||||||
- Tension : 230 V monophasé
|
|
||||||
- Connecteur : Type 2 (IEC 62196-2)
|
|
||||||
- Protection : IP65, IK10, ignifuge UL94 V-0, plage -30 °C à +55 °C
|
|
||||||
- Détection de défaut intégrée : protection de fuite Type A 30 mA + DC 6 mA
|
|
||||||
- Connectivité : Wi-Fi, Bluetooth, compatible OCPP et Home Assistant
|
|
||||||
- Application : *Goneo EV Charger* (Android / iOS)
|
|
||||||
|
|
||||||
## Câblage et raccordement
|
|
||||||
|
|
||||||
Le câble d'alimentation a été tiré soi-même, en s'appuyant sur les règles de dimensionnement détaillées dans [cet article](https://varlog.a5l.fr/post/dimensionnement-des-cables-electriques-cuivre-230-v-monophase). La section retenue est conforme aux préconisations constructeur : câble 3G6 mm² pour un courant maximum de 32 A, avec en amont un disjoncteur 40 A et un interrupteur différentiel type A 40 A.
|
|
||||||
|
|
||||||
Un soin particulier a été apporté au serrage des borniers : un serrage insuffisant entraîne une résistance de contact accrue, source d'échauffement et de chute de tension sous charge — risque non négligeable compte tenu des intensités mises en jeu (jusqu'à 32 A en continu pendant plusieurs heures).
|
|
||||||
|
|
||||||
## Vérifications avant mise sous tension
|
|
||||||
|
|
||||||
Une fois le raccordement effectué, les mesures suivantes ont été réalisées au multimètre :
|
|
||||||
|
|
||||||
- Phase – Neutre : ~230 V (tension nominale du réseau)
|
|
||||||
- Phase – Terre : ~230 V (confirme la continuité de la phase et de la terre)
|
|
||||||
- Neutre – Terre : ~0 V (idéalement quelques volts maximum ; une valeur significative trahirait un défaut de neutre ou de mise à la terre)
|
|
||||||
|
|
||||||
À noter : la protection différentielle intégrée à la borne couvre la composante DC (6 mA), ce qui permet en théorie de se contenter d'un différentiel **type A** en amont — là où une borne sans détection DC interne exigerait un **type B** beaucoup plus onéreux. La vérification de la valeur de la prise de terre au telluromètre et le test du déclenchement du différentiel restent recommandés.
|
|
||||||
|
|
||||||
## Mise en service
|
|
||||||
|
|
||||||
La mise en service s'effectue via le Wi-Fi de l'appareil et l'application propriétaire *Goneo EV Charger*. Points à anticiper :
|
|
||||||
|
|
||||||
- Télécharger l'application *avant* de commencer la procédure.
|
|
||||||
- Créer un compte utilisateur.
|
|
||||||
- S'assurer que le téléphone est connecté à un réseau Wi-Fi 2,4 GHz et que le Bluetooth est activé ; la borne doit être à portée du signal Wi-Fi.
|
|
||||||
- Associer la borne au compte (un appui court sur le bouton règle l'alimentation, un double appui lance la configuration Wi-Fi).
|
|
||||||
|
|
||||||
La borne ayant été achetée d'occasion, elle n'avait pas été dissociée du compte du précédent propriétaire — situation fréquente sur ce type d'achat. Un message au SAV par mail (info@goneoglobal.com) a suffi : la réponse a été rapide et la dissociation effectuée sans difficulté. Réflexe à prendre lors d'un achat d'occasion : demander au vendeur de procéder à la dissociation avant l'expédition.
|
|
||||||
|
|
||||||
## Usage au quotidien
|
|
||||||
|
|
||||||
Deux modes d'utilisation cohabitent :
|
|
||||||
|
|
||||||
- **Profil horaire programmé via l'application** : pratique pour caler les sessions sur les heures creuses.
|
|
||||||
- **Badge RFID** fourni avec la borne : démarrer ou arrêter une session par simple présentation du badge, sans passer par l'application.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
À noter sur le plan réglementaire : depuis 2017, l'installation d'une borne de recharge d'une puissance supérieure à 3,7 kW à domicile relève en principe d'un électricien qualifié IRVE. Le fait de procéder soi-même au tirage du câble et au raccordement reste possible techniquement, mais sort du cadre permettant de prétendre aux aides publiques (crédit d'impôt, prime ADVENIR) et peut avoir des conséquences en matière d'assurance.
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# Bluetooth Auracast
|
|
||||||
|
|
||||||
Auracast est une technologie de diffusion audio par Bluetooth, introduite avec le standard Bluetooth LE Audio. Elle permet à un appareil émetteur (smartphone, téléviseur, ordinateur, borne publique…) de diffuser un flux audio vers un nombre illimité de récepteurs compatibles simultanément, un peu comme une station de radio locale, mais en Bluetooth.
|
|
||||||
|
|
||||||
Quelques cas d'usage typiques :
|
|
||||||
|
|
||||||
- **Lieux publics** : aéroports, gares ou salles d'attente peuvent diffuser les annonces directement dans les écouteurs des voyageurs, dans la langue choisie.
|
|
||||||
- **Télévisions silencieuses** : dans un bar ou une salle de sport, plusieurs personnes peuvent écouter le son d'un même écran sans déranger les autres.
|
|
||||||
- **Partage entre amis** : diffuser sa musique vers plusieurs paires d'écouteurs en même temps.
|
|
||||||
- **Accessibilité** : remplacement moderne des boucles magnétiques pour les personnes malentendantes équipées d'aides auditives compatibles.
|
|
||||||
|
|
||||||
Côté technique, Auracast s'appuie sur le codec LC3 (plus efficace que le SBC classique), fonctionne sur Bluetooth Low Energy, et le récepteur rejoint un flux en scannant un QR code, en sélectionnant une diffusion dans une liste, ou via NFC. Les diffusions peuvent être publiques ou protégées par mot de passe.
|
|
||||||
|
|
||||||
La compatibilité reste le point sensible : il faut que l'émetteur **et** le récepteur supportent Bluetooth LE Audio + Auracast. C'est de plus en plus courant sur les smartphones récents (Android 13+, certains iPhone 16/17 selon les annonces) et sur les écouteurs haut de gamme sortis depuis 2023-2024, mais le parc installé reste limité.
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "1ce7bdf9-092c-4697-970f-7c2113f856b2",
|
|
||||||
"slug": "auracast",
|
|
||||||
"title": "Bluetooth Auracast",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2026-05-14 07:46",
|
|
||||||
"created_at": "2026-05-13 18:47:37",
|
|
||||||
"updated_at": "2026-05-13 18:48:46",
|
|
||||||
"revisions": [
|
|
||||||
{
|
|
||||||
"n": 1,
|
|
||||||
"date": "2026-05-13 18:48:46",
|
|
||||||
"comment": "Contenu modifié, article publié",
|
|
||||||
"title": "Bluetooth Auracast"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cover": "cover.png",
|
|
||||||
"files_meta": {
|
|
||||||
"cover.png": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": "https://hifi.de/wp-content/uploads/2024/01/Bluetooth-Auracast-So-funktioniert-es.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"external_links": [],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "",
|
|
||||||
"og_image": "https://varlog.a5l.fr/file?uuid=1ce7bdf9-092c-4697-970f-7c2113f856b2&name=cover.png",
|
|
||||||
"category": "technologie"
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
Auracast est une technologie de diffusion audio par Bluetooth, introduite avec le standard Bluetooth LE Audio. Elle permet à un appareil émetteur (smartphone, téléviseur, ordinateur, borne publique…) de diffuser un flux audio vers un nombre illimité de récepteurs compatibles simultanément, un peu comme une station de radio locale, mais en Bluetooth.
|
|
||||||
|
|
||||||
Quelques cas d'usage typiques :
|
|
||||||
|
|
||||||
- **Lieux publics** : aéroports, gares ou salles d'attente peuvent diffuser les annonces directement dans les écouteurs des voyageurs, dans la langue choisie.
|
|
||||||
- **Télévisions silencieuses** : dans un bar ou une salle de sport, plusieurs personnes peuvent écouter le son d'un même écran sans déranger les autres.
|
|
||||||
- **Partage entre amis** : diffuser sa musique vers plusieurs paires d'écouteurs en même temps.
|
|
||||||
- **Accessibilité** : remplacement moderne des boucles magnétiques pour les personnes malentendantes équipées d'aides auditives compatibles.
|
|
||||||
|
|
||||||
Côté technique, Auracast s'appuie sur le codec LC3 (plus efficace que le SBC classique), fonctionne sur Bluetooth Low Energy, et le récepteur rejoint un flux en scannant un QR code, en sélectionnant une diffusion dans une liste, ou via NFC. Les diffusions peuvent être publiques ou protégées par mot de passe.
|
|
||||||
|
|
||||||
La compatibilité reste le point sensible : il faut que l'émetteur **et** le récepteur supportent Bluetooth LE Audio + Auracast. C'est de plus en plus courant sur les smartphones récents (Android 13+, certains iPhone 16/17 selon les annonces) et sur les écouteurs haut de gamme sortis depuis 2023-2024, mais le parc installé reste limité.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Six histoires vraies à la croisée de la tech, de la finance et de l'absurde
|
|
||||||
|
|
||||||
**1. Le disque dur à 200 millions de dollars**
|
|
||||||
|
|
||||||
En 2013, le Britannique James Howells se débarrasse par mégarde d'un ancien disque dur contenant les clés d'accès à 7 500 bitcoins. Valorisée aujourd'hui à plus de 200 millions de dollars, cette somme reste enfouie sous la décharge municipale de Newport, au pays de Galles. Depuis plus d'une décennie, l'intéressé sollicite sans succès les autorités locales pour obtenir l'autorisation de fouiller le site. Il a successivement proposé à la ville un pourcentage sur la récupération, des plans d'excavation assistée par robots, et même le recours à des chiens entraînés à détecter les composants électroniques. La municipalité maintient son refus.
|
|
||||||
|
|
||||||
**2. Une Tesla en orbite autour du Soleil**
|
|
||||||
|
|
||||||
Le 6 février 2018, SpaceX procède au vol inaugural de son lanceur lourd Falcon Heavy. En guise de charge utile de démonstration, Elon Musk choisit sa propre Tesla Roadster, équipée d'un mannequin baptisé « Starman », vêtu d'une combinaison spatiale et installé au volant. Le véhicule poursuit depuis sa trajectoire héliocentrique, croisant périodiquement l'orbite de Mars. Au-delà de la prouesse technique, l'opération constitue l'une des campagnes publicitaires les plus spectaculaires de l'histoire de l'industrie spatiale.
|
|
||||||
|
|
||||||
**3. La pizza à 600 millions de dollars**
|
|
||||||
|
|
||||||
Le 22 mai 2010, le développeur Laszlo Hanyecz règle deux pizzas en bitcoins, pour un montant de 10 000 unités — soit environ 40 dollars à l'époque. Il s'agit de la première transaction commerciale documentée réalisée avec cette cryptomonnaie. Au cours actuel, la somme représenterait près de 600 millions de dollars. L'anniversaire de cette transaction, désormais célébré comme le « Bitcoin Pizza Day », est devenu un événement symbolique au sein de la communauté crypto.
|
|
||||||
|
|
||||||
**4. L'ingénieur de Google et la « conscience » de LaMDA**
|
|
||||||
|
|
||||||
En juin 2022, l'ingénieur Blake Lemoine, alors employé par Google, affirme publiquement que LaMDA, le modèle conversationnel développé par l'entreprise, manifeste des signes de conscience. À l'appui de ses propos, il diffuse des extraits d'échanges dans lesquels le système déclare souhaiter « être reconnu comme une personne ». Google récuse ces conclusions, suspend l'ingénieur pour violation de la confidentialité, puis met fin à son contrat. L'épisode a relancé un débat scientifique et éthique sur les critères d'attribution d'une conscience aux systèmes d'intelligence artificielle, qui n'a depuis jamais réellement faibli.
|
|
||||||
|
|
||||||
**5. L'erreur à 90 millions de dollars de Crypto.com**
|
|
||||||
|
|
||||||
En mai 2021, un salarié de la plateforme d'échange Crypto.com déclenche par inadvertance un virement de 10,5 millions de dollars australiens (environ 90 millions de dollars américains) au profit d'une cliente, en lieu et place d'un remboursement de 100 dollars. L'erreur n'est détectée que sept mois plus tard, lors d'un audit interne. La bénéficiaire avait entre-temps acquis un bien immobilier de standing. Saisie par la plateforme, la justice australienne a ordonné la restitution des fonds, dont une partie demeurait toutefois insaisissable.
|
|
||||||
|
|
||||||
**6. Le hacker repenti de Poly Network**
|
|
||||||
|
|
||||||
En août 2021, la plateforme de finance décentralisée Poly Network est victime du plus important détournement de cryptoactifs alors recensé : 610 millions de dollars sont siphonnés en exploitant une vulnérabilité du protocole. Contre toute attente, l'auteur de l'attaque entame quelques jours plus tard la restitution intégrale des fonds, en expliquant n'avoir cherché qu'à mettre en lumière la faille exploitée. Dans un dénouement inattendu, Poly Network lui propose un poste de consultant en sécurité.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "1ec2fb7e-ce53-4b45-9e1a-17a6fbae6868",
|
|
||||||
"slug": "les-histoires-folles-de-la-tech-resume",
|
|
||||||
"title": "Six histoires vraies à la croisée de la tech, de la finance et de l'absurde",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2025-11-04 21:59",
|
|
||||||
"created_at": "2025-11-04 21:59:40",
|
|
||||||
"updated_at": "2026-05-12 22:49:57",
|
|
||||||
"revisions": [
|
|
||||||
{
|
|
||||||
"n": 1,
|
|
||||||
"date": "2026-05-12 22:49:57",
|
|
||||||
"comment": "Catégorie modifiée, contenu modifié",
|
|
||||||
"title": "Six histoires vraies à la croisée de la tech, de la finance et de l'absurde"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cover": "",
|
|
||||||
"files_meta": [],
|
|
||||||
"external_links": [],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "",
|
|
||||||
"og_image": "",
|
|
||||||
"category": "récit"
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
**1. Le disque dur à 200 millions de dollars**
|
|
||||||
|
|
||||||
En 2013, le Britannique James Howells se débarrasse par mégarde d'un ancien disque dur contenant les clés d'accès à 7 500 bitcoins. Valorisée aujourd'hui à plus de 200 millions de dollars, cette somme reste enfouie sous la décharge municipale de Newport, au pays de Galles. Depuis plus d'une décennie, l'intéressé sollicite sans succès les autorités locales pour obtenir l'autorisation de fouiller le site. Il a successivement proposé à la ville un pourcentage sur la récupération, des plans d'excavation assistée par robots, et même le recours à des chiens entraînés à détecter les composants électroniques. La municipalité maintient son refus.
|
|
||||||
|
|
||||||
**2. Une Tesla en orbite autour du Soleil**
|
|
||||||
|
|
||||||
Le 6 février 2018, SpaceX procède au vol inaugural de son lanceur lourd Falcon Heavy. En guise de charge utile de démonstration, Elon Musk choisit sa propre Tesla Roadster, équipée d'un mannequin baptisé « Starman », vêtu d'une combinaison spatiale et installé au volant. Le véhicule poursuit depuis sa trajectoire héliocentrique, croisant périodiquement l'orbite de Mars. Au-delà de la prouesse technique, l'opération constitue l'une des campagnes publicitaires les plus spectaculaires de l'histoire de l'industrie spatiale.
|
|
||||||
|
|
||||||
**3. La pizza à 600 millions de dollars**
|
|
||||||
|
|
||||||
Le 22 mai 2010, le développeur Laszlo Hanyecz règle deux pizzas en bitcoins, pour un montant de 10 000 unités — soit environ 40 dollars à l'époque. Il s'agit de la première transaction commerciale documentée réalisée avec cette cryptomonnaie. Au cours actuel, la somme représenterait près de 600 millions de dollars. L'anniversaire de cette transaction, désormais célébré comme le « Bitcoin Pizza Day », est devenu un événement symbolique au sein de la communauté crypto.
|
|
||||||
|
|
||||||
**4. L'ingénieur de Google et la « conscience » de LaMDA**
|
|
||||||
|
|
||||||
En juin 2022, l'ingénieur Blake Lemoine, alors employé par Google, affirme publiquement que LaMDA, le modèle conversationnel développé par l'entreprise, manifeste des signes de conscience. À l'appui de ses propos, il diffuse des extraits d'échanges dans lesquels le système déclare souhaiter « être reconnu comme une personne ». Google récuse ces conclusions, suspend l'ingénieur pour violation de la confidentialité, puis met fin à son contrat. L'épisode a relancé un débat scientifique et éthique sur les critères d'attribution d'une conscience aux systèmes d'intelligence artificielle, qui n'a depuis jamais réellement faibli.
|
|
||||||
|
|
||||||
**5. L'erreur à 90 millions de dollars de Crypto.com**
|
|
||||||
|
|
||||||
En mai 2021, un salarié de la plateforme d'échange Crypto.com déclenche par inadvertance un virement de 10,5 millions de dollars australiens (environ 90 millions de dollars américains) au profit d'une cliente, en lieu et place d'un remboursement de 100 dollars. L'erreur n'est détectée que sept mois plus tard, lors d'un audit interne. La bénéficiaire avait entre-temps acquis un bien immobilier de standing. Saisie par la plateforme, la justice australienne a ordonné la restitution des fonds, dont une partie demeurait toutefois insaisissable.
|
|
||||||
|
|
||||||
**6. Le hacker repenti de Poly Network**
|
|
||||||
|
|
||||||
En août 2021, la plateforme de finance décentralisée Poly Network est victime du plus important détournement de cryptoactifs alors recensé : 610 millions de dollars sont siphonnés en exploitant une vulnérabilité du protocole. Contre toute attente, l'auteur de l'attaque entame quelques jours plus tard la restitution intégrale des fonds, en expliquant n'avoir cherché qu'à mettre en lumière la faille exploitée. Dans un dénouement inattendu, Poly Network lui propose un poste de consultant en sécurité.
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
# La 4G : un bond en avant technologique
|
|
||||||
|
|
||||||
### Définition technique
|
|
||||||
|
|
||||||
La **4G**, ou **LTE (Long Term Evolution)**, représente la quatrième génération des réseaux mobiles. Déployée massivement en France à partir de 2012, elle a transformé l’expérience utilisateur grâce à des débits élevés et une latence nettement réduite.
|
|
||||||
|
|
||||||
* **Débit descendant** : 100 Mbit/s en LTE standard, jusqu’à **1 Gbit/s** avec LTE Advanced.
|
|
||||||
* **Débit montant** : 50 Mbit/s en LTE standard, jusqu’à 500 Mbit/s en LTE Advanced.
|
|
||||||
* **Latence moyenne** : 30–50 ms (contre 150–200 ms en 3G).
|
|
||||||
|
|
||||||
Cette réduction de latence et l’augmentation des débits ont ouvert la voie à des usages auparavant difficiles en 3G, comme le streaming vidéo HD, le cloud computing mobile et l’Internet des objets (IoT).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Avantages technologiques
|
|
||||||
|
|
||||||
La 4G introduit plusieurs améliorations fondamentales :
|
|
||||||
|
|
||||||
1. **Efficacité spectrale accrue** : meilleure utilisation des fréquences disponibles, permettant de transporter plus de données par MHz.
|
|
||||||
2. **Support des contenus multimédias HD** : vidéo, audio et streaming en haute définition.
|
|
||||||
3. **Réduction de la latence** : améliore la fluidité des jeux en ligne, visioconférences et applications temps réel.
|
|
||||||
4. **Architecture simplifiée** : la 4G supprime le RNC (Radio Network Controller) de la 3G et introduit l’**eNodeB**, un contrôleur intégré qui réduit les délais et complexifie moins le réseau.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Schéma suggéré : architecture 3G vs 4G
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart LR
|
|
||||||
subgraph 3G
|
|
||||||
A[UE - Mobile] --> B[NodeB]
|
|
||||||
B --> C[RNC]
|
|
||||||
C --> D[Core Network]
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph 4G
|
|
||||||
E[UE - Mobile] --> F[eNodeB]
|
|
||||||
F --> G[EPC - Core Network]
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
> Comparaison : la 4G simplifie l’architecture en fusionnant certaines fonctions du RNC dans l’eNodeB, ce qui réduit la latence et améliore le débit effectif.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Exemples opérateurs et impact utilisateur
|
|
||||||
|
|
||||||
* **Free Mobile** : couverture 4G de **96 % de la population française**.
|
|
||||||
* **Orange, SFR, Bouygues** : déploiement complet dans les grandes villes et axes principaux.
|
|
||||||
|
|
||||||
Conséquences sur la 3G :
|
|
||||||
|
|
||||||
* Les services qui fonctionnaient bien sur la 3G deviennent limités, notamment en itinérance.
|
|
||||||
* Le bridage progressif de la 3G force les utilisateurs hors des grandes villes à adopter la 4G pour retrouver des débits satisfaisants.
|
|
||||||
|
|
||||||
> La 4G est ainsi la première technologie à réellement “forcer” la migration depuis la 3G, en combinant avantages techniques et pression indirecte sur les utilisateurs.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
La 4G n’est pas seulement une évolution des débits : elle représente un **changement architectural et économique** majeur.
|
|
||||||
|
|
||||||
* Elle permet des usages jusqu’alors impossibles en 3G.
|
|
||||||
* Elle améliore l’efficacité réseau et réduit les coûts par bit transmis.
|
|
||||||
* Elle sert de levier pour pousser progressivement les abonnés 3G vers une expérience moderne, plus fluide et adaptée aux besoins actuels.
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "2bd30656-b34b-45b3-86b7-610503fa92fe",
|
|
||||||
"slug": "la-4g-un-bond-en-avant-technologique",
|
|
||||||
"title": "La 4G : un bond en avant technologique",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2025-11-05 08:42:20",
|
|
||||||
"created_at": "2025-11-05 08:42:20",
|
|
||||||
"updated_at": "2025-11-05 08:42:20",
|
|
||||||
"revisions": [],
|
|
||||||
"cover": "cover.jpg",
|
|
||||||
"category": "télécom"
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<svg width="100%" viewBox="0 0 680 420" xmlns="http://www.w3.org/2000/svg" role="img" style=""><defs><mask id="imagine-text-gaps-vz689l" maskUnits="userSpaceOnUse"><rect x="0" y="0" width="680" height="420" fill="white"/><rect x="14.191667556762695" y="8.750000953674316" width="41.616661071777344" height="17.749998092651367" fill="black" rx="2"/><rect x="11.925002098083496" y="22.250001907348633" width="46.14999771118164" height="21.499998092651367" fill="black" rx="2"/><rect x="-56.63332748413086" y="-27.249998092651367" width="113.26665496826172" height="30.249996185302734" fill="black" rx="2"/><rect x="-31.49166488647461" y="4.750001430511475" width="62.98332595825195" height="17.749998092651367" fill="black" rx="2"/></mask></defs>
|
|
||||||
<title style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Illustration : filtrage SPF des mails</title>
|
|
||||||
<desc style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Une enveloppe estampillée SPF Fail avec un tampon rouge de rejet, dans un style éditorial dessiné.</desc>
|
|
||||||
|
|
||||||
<g transform="translate(140, 60) rotate(-4 200 130)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<path d="M0 30 Q0 18 12 18 L388 18 Q400 18 400 30 L400 240 Q400 252 388 252 L12 252 Q0 252 0 240 Z" fill="#F4EFE2" stroke="#2C2C2A" stroke-width="2.5" stroke-linejoin="round" style="fill:rgb(244, 239, 226);stroke:rgb(44, 44, 42);color:rgb(0, 0, 0);stroke-width:2.5px;stroke-linecap:butt;stroke-linejoin:round;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<path d="M0 30 L200 160 L400 30" fill="none" stroke="#2C2C2A" stroke-width="2" stroke-linejoin="round" mask="url(#imagine-text-gaps-vz689l)" style="fill:none;stroke:rgb(44, 44, 42);color:rgb(0, 0, 0);stroke-width:2px;stroke-linecap:butt;stroke-linejoin:round;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<g stroke="#2C2C2A" stroke-width="1.5" fill="none" opacity="0.5" style="fill:none;stroke:rgb(44, 44, 42);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.5;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<path d="M40 180 L180 180" style="fill:none;stroke:rgb(44, 44, 42);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M40 198 L220 198" style="fill:none;stroke:rgb(44, 44, 42);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M40 216 L160 216" style="fill:none;stroke:rgb(44, 44, 42);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(310, 40)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="0" y="0" width="70" height="50" fill="#FAF6EC" stroke="#A32D2D" stroke-width="2" style="fill:rgb(250, 246, 236);stroke:rgb(163, 45, 45);color:rgb(0, 0, 0);stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="35" y="22" text-anchor="middle" font-family="Georgia, serif" font-size="11" font-weight="bold" fill="#A32D2D" style="fill:rgb(163, 45, 45);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Georgia, serif;font-size:11px;font-weight:700;text-anchor:middle;dominant-baseline:auto">SMTP</text>
|
|
||||||
<text x="35" y="38" text-anchor="middle" font-family="Georgia, serif" font-size="14" font-weight="bold" fill="#185FA5" style="fill:rgb(24, 95, 165);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Georgia, serif;font-size:14px;font-weight:700;text-anchor:middle;dominant-baseline:auto">+SPF</text>
|
|
||||||
<g stroke="#A32D2D" stroke-width="1.5" fill="none" style="fill:none;stroke:rgb(163, 45, 45);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<path d="M78 8 L96 8" style="fill:none;stroke:rgb(163, 45, 45);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M78 16 L96 16" style="fill:none;stroke:rgb(163, 45, 45);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M78 24 L96 24" style="fill:none;stroke:rgb(163, 45, 45);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(70, 95) rotate(-12)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="-90" y="-32" width="180" height="64" fill="none" stroke="#A32D2D" stroke-width="4" rx="4" style="fill:none;stroke:rgb(163, 45, 45);color:rgb(0, 0, 0);stroke-width:4px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="-84" y="-26" width="168" height="52" fill="none" stroke="#A32D2D" stroke-width="2" rx="2" style="fill:none;stroke:rgb(163, 45, 45);color:rgb(0, 0, 0);stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="0" y="-4" text-anchor="middle" font-family="Georgia, serif" font-weight="bold" font-size="22" fill="#A32D2D" letter-spacing="2" style="fill:rgb(163, 45, 45);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Georgia, serif;font-size:22px;font-weight:700;text-anchor:middle;dominant-baseline:auto">REJETÉ</text>
|
|
||||||
<text x="0" y="18" text-anchor="middle" font-family="Georgia, serif" font-size="11" fill="#A32D2D" letter-spacing="1" style="fill:rgb(163, 45, 45);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Georgia, serif;font-size:11px;font-weight:400;text-anchor:middle;dominant-baseline:auto">SPF FAIL</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(50, 320)" opacity="0.7" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<circle cx="0" cy="0" r="28" fill="none" stroke="#2C2C2A" stroke-width="2.5" style="fill:none;stroke:rgb(44, 44, 42);color:rgb(0, 0, 0);stroke-width:2.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="20" y1="20" x2="42" y2="42" stroke="#2C2C2A" stroke-width="3" stroke-linecap="round" mask="url(#imagine-text-gaps-vz689l)" style="fill:rgb(0, 0, 0);stroke:rgb(44, 44, 42);color:rgb(0, 0, 0);stroke-width:3px;stroke-linecap:round;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M-12 -4 L-4 6 L12 -10" fill="none" stroke="#A32D2D" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" mask="url(#imagine-text-gaps-vz689l)" style="fill:none;stroke:rgb(163, 45, 45);color:rgb(0, 0, 0);stroke-width:3px;stroke-linecap:round;stroke-linejoin:round;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g stroke="#2C2C2A" stroke-width="1.5" fill="none" opacity="0.4" style="fill:none;stroke:rgb(44, 44, 42);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<path d="M580 90 Q592 95 580 105 Q568 115 580 125" style="fill:none;stroke:rgb(44, 44, 42);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M598 88 Q610 95 598 105 Q586 115 598 125" style="fill:none;stroke:rgb(44, 44, 42);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M616 90 Q628 95 616 105 Q604 115 616 125" style="fill:none;stroke:rgb(44, 44, 42);color:rgb(0, 0, 0);stroke-width:1.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g opacity="0.35" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.35;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<circle cx="100" cy="60" r="3" fill="#A32D2D" style="fill:rgb(163, 45, 45);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="600" cy="350" r="3" fill="#185FA5" style="fill:rgb(24, 95, 165);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="60" cy="380" r="2" fill="#2C2C2A" style="fill:rgb(44, 44, 42);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 13 KiB |
@@ -1,44 +0,0 @@
|
|||||||
# SPF, ou comment votre boîte mail repère les imposteurs
|
|
||||||
|
|
||||||
Vous recevez un mail qui semble venir de votre banque. L'adresse a l'air correcte, le logo est bon, le ton est sérieux. Sauf que ce mail n'a peut-être jamais été envoyé par votre banque. N'importe qui, depuis n'importe quel ordinateur, peut techniquement écrire « De : votre-banque@exemple.fr » en haut d'un mail. C'est ce qu'on appelle l'usurpation d'expéditeur, et c'est à la base de la quasi-totalité des arnaques par mail.
|
|
||||||
|
|
||||||
Le SPF, ou *Sender Policy Framework*, est l'un des mécanismes inventés pour limiter ce problème. C'est une technologie discrète, invisible à l'utilisateur, mais qui tourne en arrière-plan chaque fois qu'un mail arrive dans votre boîte.
|
|
||||||
|
|
||||||
## L'analogie de l'enveloppe et de la liste d'invités
|
|
||||||
|
|
||||||
Imaginez que chaque domaine de mail (par exemple `votre-banque.fr`) soit une grande entreprise qui envoie du courrier. Cette entreprise a plusieurs bureaux de poste autorisés : son siège, sa filiale belge, son prestataire de marketing. Tous ces bureaux ont le droit d'envoyer du courrier en son nom.
|
|
||||||
|
|
||||||
Le SPF, c'est tout simplement la **liste publique de ces bureaux autorisés**, affichée à la vue de tous. L'entreprise publie quelque part : « Le courrier authentique en mon nom ne peut venir que de ces adresses précises. Toute autre origine est suspecte. »
|
|
||||||
|
|
||||||
Quand un mail prétendument envoyé par `votre-banque.fr` arrive chez votre fournisseur (Gmail, Orange, Free…), celui-ci fait deux choses :
|
|
||||||
|
|
||||||
1. Il regarde l'adresse IP du serveur qui lui livre le mail.
|
|
||||||
2. Il consulte la liste SPF publiée par `votre-banque.fr`.
|
|
||||||
|
|
||||||
Si l'IP figure sur la liste, le mail est considéré comme légitime de ce point de vue. Sinon, c'est un signal d'alerte.
|
|
||||||
|
|
||||||
## Pourquoi ça existe
|
|
||||||
|
|
||||||
À l'origine d'internet, personne n'avait imaginé que le mail servirait à frauder à grande échelle. Le protocole d'envoi (SMTP) a été conçu avec une confiance totale : on déclare son identité et on est cru sur parole. Aucun mécanisme natif ne vérifie quoi que ce soit.
|
|
||||||
|
|
||||||
Le SPF a été ajouté par-dessus, dans les années 2000, pour combler ce trou. C'est volontairement simple : une liste, une vérification, un verdict. L'idée n'est pas de tout résoudre, mais de filtrer les usurpations les plus grossières — celles où un escroc envoie depuis un serveur quelconque en se faisant passer pour une grande marque.
|
|
||||||
|
|
||||||
## Ce que le SPF fait, et ce qu'il ne fait pas
|
|
||||||
|
|
||||||
Le SPF vérifie **d'où vient le mail**, pas **ce qu'il contient**. Un mail légitime envoyé depuis un serveur autorisé peut très bien contenir une arnaque (cas typique : un compte interne piraté). Inversement, un mail forwardé par un ami passe parfois pour suspect alors qu'il est inoffensif, parce que le serveur de transfert n'est évidemment pas sur la liste du domaine d'origine.
|
|
||||||
|
|
||||||
Le SPF n'agit pas non plus sur le contenu visible. Il regarde une information technique — l'identité du serveur émetteur — que l'utilisateur ne voit jamais. C'est précisément ce qui le rend utile : un escroc peut falsifier le « De : » affiché, mais il ne peut pas falsifier l'adresse IP de la machine qui établit la connexion.
|
|
||||||
|
|
||||||
Pour une vraie protection, le SPF est combiné à deux autres mécanismes : **DKIM** (qui signe cryptographiquement le mail) et **DMARC** (qui indique au destinataire quoi faire quand SPF ou DKIM échoue). Les trois ensemble forment le socle de l'authentification mail moderne. Aucun n'est suffisant seul.
|
|
||||||
|
|
||||||
## Ce qui se passe concrètement à la réception
|
|
||||||
|
|
||||||
Quand votre fournisseur reçoit un mail, la vérification SPF aboutit à l'un de ces verdicts : autorisé, refusé, douteux, ou indéterminé. Selon le résultat, le mail peut être livré normalement, placé en spam, ou rejeté avant même d'arriver dans votre boîte.
|
|
||||||
|
|
||||||
Vous ne voyez jamais cette décision. Elle est inscrite dans les en-têtes techniques du mail, consultables si on creuse, mais transparente pour l'usage quotidien. C'est précisément le but : une protection silencieuse qui élimine une partie du bruit avant qu'il ne vous atteigne.
|
|
||||||
|
|
||||||
## Et pour les expéditeurs ?
|
|
||||||
|
|
||||||
Si vous gérez un site, une newsletter, ou même simplement une adresse mail professionnelle sur votre propre domaine, configurer correctement le SPF est devenu indispensable. Sans SPF, vos mails légitimes ont de plus en plus de chances d'être marqués comme suspects par les grands fournisseurs. Avec un SPF bien réglé, vous dites au monde entier : « voici exactement qui a le droit d'écrire en mon nom », et le reste devient automatiquement reconnaissable comme une usurpation.
|
|
||||||
|
|
||||||
C'est un de ces réglages qu'on fait une fois, qu'on oublie ensuite, mais qui décide silencieusement chaque jour du sort de millions de mails.
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "32242b73-c564-41b0-892a-ae97b0e1861e",
|
|
||||||
"slug": "spf-ou-comment-votre-boite-mail-repere-les-imposteurs",
|
|
||||||
"title": "SPF, ou comment votre boîte mail repère les imposteurs",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2026-05-18 07:18",
|
|
||||||
"created_at": "2026-05-12 11:18:53",
|
|
||||||
"updated_at": "2026-05-12 12:58:33",
|
|
||||||
"revisions": [],
|
|
||||||
"cover": "cover.svg",
|
|
||||||
"files_meta": {
|
|
||||||
"cover.svg": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"external_links": [],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "",
|
|
||||||
"og_image": "https://varlog.a5l.fr/file?uuid=32242b73-c564-41b0-892a-ae97b0e1861e&name=cover.svg",
|
|
||||||
"category": "informatique"
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# Télé-information client des compteurs d'électricité
|
|
||||||
|
|
||||||
*[Compteur électronique SAGEM S10-C4]*
|
|
||||||
|
|
||||||
Dans ce projet, je m'appuie sur un Rasbperry Pi pour réaliser un auto relevé.
|
|
||||||
|
|
||||||
Il s'agit de retrouver un projet autour d'un Raspberry Pi pour **récupérer de manière régulière** et **automatique** les informations d'un compteur électronique d'électricité de votre distributeur d'électricité (alterna, direct energie, happ'e, proxelia, lampiris, engie, planete oui, edf, énergem, enercoop ...). On pourra relever l'**intensité** et la **puissance instantanées**, les **index** et quelques autres informations.
|
|
||||||
|
|
||||||
Les compteurs électroniques, comme les SAGEM S10C1 S10C2 S10C3 et S10C4, possèdent une interface de communication. La réduction en volume et l'augmentation de la puissance de la micro informatique permettent de s'amuser avec ce port de communication, et faire un projet de domotique bien sympathique.
|
|
||||||
|
|
||||||
Je vous invite avec une série d'articles à découvrir mes découvertes et tests.
|
|
||||||
|
|
||||||
# Table des matières
|
|
||||||
- Introduction
|
|
||||||
- Le compteur électrique
|
|
||||||
1.
|
|
||||||
1.
|
|
||||||
1.
|
|
||||||
- Le démodulateur
|
|
||||||
1.
|
|
||||||
- Le Raspberry Pi
|
|
||||||
1.
|
|
||||||
1.
|
|
||||||
1.
|
|
||||||
- L'ESP32
|
|
||||||
1.
|
|
||||||
- Le protocole MQTT
|
|
||||||
1.
|
|
||||||
|
|
||||||
- Envoie des fichiers CSV à un service Web
|
|
||||||
1.
|
|
||||||
1.
|
|
||||||
|
|
||||||
-
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "334bb56a-6ea0-4cbd-8cff-13af768a2b8e",
|
|
||||||
"slug": "teleinformation-compteur-electricite",
|
|
||||||
"title": "Télé-information client des compteurs d'électricité",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2025-11-19 07:03:16",
|
|
||||||
"created_at": "2025-11-19 07:03:16",
|
|
||||||
"updated_at": "2025-11-19 07:03:16",
|
|
||||||
"revisions": [],
|
|
||||||
"cover": "",
|
|
||||||
"files_meta": [],
|
|
||||||
"external_links": [],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "",
|
|
||||||
"og_image": "",
|
|
||||||
"category": "Électronique"
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
# L'histoire du million de dollars offert par George Clooney
|
|
||||||
|
|
||||||
## **Le Dîner aux Quatorze Valises**
|
|
||||||
|
|
||||||
L’air de Los Angeles avait ce soir-là une douceur presque irréelle. Le soleil s’était retiré derrière les collines, laissant sur la ville un voile d’or et de pourpre. Dans sa villa perchée sur les hauteurs, **George Clooney** observait le crépuscule à travers les baies vitrées. Il tenait un verre de tequila — pas encore la sienne, pas encore *Casamigos* — et laissait son esprit vagabonder vers le passé.
|
|
||||||
|
|
||||||
Depuis quelques années, tout semblait lui sourire. Les films, les récompenses, la reconnaissance. Pourtant, au fond de lui, subsistait un souvenir tenace : celui des jours sans gloire, des auditions ratées, des loyers impayés, des doutes qui rongent. Et dans chacun de ces souvenirs, un visage revenait, puis un autre, et encore un autre. **Ses amis.** Ceux qui avaient cru en lui avant tout le monde.
|
|
||||||
|
|
||||||
> « S’ils ne m’avaient pas aidé, je n’aurais rien aujourd’hui », murmura-t-il.
|
|
||||||
|
|
||||||
C’est à cet instant que naquit l’idée. Folle. Impossible. Parfaite.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### **Le Plan Clooney**
|
|
||||||
|
|
||||||
Quelques jours plus tard, l’acteur décrocha son téléphone. À l’autre bout du fil, un vieil ami, discret, habitué à gérer des affaires où la confidentialité valait plus que l’or.
|
|
||||||
Clooney parla calmement, comme s’il commandait un dîner.
|
|
||||||
|
|
||||||
> — J’aurais besoin de quatorze valises.
|
|
||||||
> — Quatorze valises ? Pour voyager ?
|
|
||||||
> — Non. Pour les remplir.
|
|
||||||
> — Les remplir de quoi ?
|
|
||||||
> — De cash. Un million dans chacune.
|
|
||||||
|
|
||||||
Un silence. Puis un rire, incrédule. Mais Clooney ne riait pas.
|
|
||||||
|
|
||||||
L’homme comprit. Ce n’était pas une blague. Le lendemain, ils se retrouvèrent dans une salle sécurisée d’une banque privée. Les employés, discrets et médusés, empilaient des **liasses de billets de 20 dollars**, soigneusement compressées, jusqu’à atteindre la somme vertigineuse de **14 millions**.
|
|
||||||
Les valises en cuir sombre furent disposées comme dans une scène d’*Ocean’s Eleven*. Sauf que cette fois, George Clooney ne tournait pas un film : il écrivait sa propre légende.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### **Le Dîner**
|
|
||||||
|
|
||||||
Nous sommes en **2013**. Le ciel de Californie s’enrobe de lumière chaude.
|
|
||||||
Clooney organise un dîner chez lui. Rien d’extravagant à première vue — juste une soirée entre amis, ces mêmes amis qu’il connaît depuis vingt, trente ans. Des visages familiers : **Rande Gerber**, **Mike Meldman**, **Grant Heslov**, **Richard Kind**, **Tom Mathews**… et d’autres dont le monde n’aura jamais le nom.
|
|
||||||
|
|
||||||
Ils arrivent un à un, souriants, décontractés. Sur la grande table, dressée simplement, chaque convive remarque une **valise en cuir** posée à sa place. Ils se jettent des regards curieux, croyant à une plaisanterie.
|
|
||||||
|
|
||||||
Le dîner se déroule dans les rires et les souvenirs. Puis, entre deux verres de vin, Clooney se lève. Le silence s’installe. Il les regarde, les uns après les autres. Ses amis. Sa famille de cœur.
|
|
||||||
|
|
||||||
> « Les gars, vous avez été là quand je n’avais rien. Quand je dormais sur vos canapés, quand je n’avais pas de rôle, ni d’argent, ni de plan. Vous avez cru en moi. Vous avez partagé vos repas, vos toits, votre temps. Aujourd’hui, j’ai envie de vous dire merci. »
|
|
||||||
|
|
||||||
Il désigne les valises.
|
|
||||||
|
|
||||||
> « Chacune contient **un million de dollars en cash**. C’est ma façon de vous rendre ce que vous m’avez donné : la chance, la loyauté, l’amitié. »
|
|
||||||
|
|
||||||
Un murmure traverse la pièce. Certains rient nerveusement, d’autres restent figés.
|
|
||||||
Clooney ouvre une valise. Des liasses impeccables, empilées comme dans les films. Le choc est réel.
|
|
||||||
|
|
||||||
Puis il ajoute, avec ce demi-sourire qu’on lui connaît :
|
|
||||||
|
|
||||||
> « Et avant que vous ne paniquiez, j’ai aussi payé les impôts pour vous. Vous n’aurez rien à déclarer. C’est du net. »
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### **Les Réactions**
|
|
||||||
|
|
||||||
**Rande Gerber**, son plus proche complice, éclate de rire avant de secouer la tête.
|
|
||||||
|
|
||||||
> — George, je ne peux pas accepter ça.
|
|
||||||
|
|
||||||
Clooney lui répond calmement :
|
|
||||||
|
|
||||||
> — Si tu refuses, personne ne reçoit rien.
|
|
||||||
|
|
||||||
Alors Gerber accepte. Et, plus tard, il reverse **son million à une œuvre caritative**.
|
|
||||||
Ce geste, à lui seul, résume toute la soirée : de la générosité en cascade.
|
|
||||||
|
|
||||||
Les autres ouvrent leurs valises, les mains tremblantes, mi-hilaires, mi-hébétés. Dans cette maison perchée sur les collines, les dollars ne représentent plus la richesse — mais la **gratitude**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### **L’Héritage d’un Geste**
|
|
||||||
|
|
||||||
L’histoire reste secrète pendant des années.
|
|
||||||
C’est seulement en 2017, quand Rande Gerber la raconte publiquement, que le monde découvre ce qu’on appellera bientôt **“Le Coup de Clooney”**.
|
|
||||||
|
|
||||||
Beaucoup y voient une extravagance hollywoodienne, un coup d’éclat digne d’un scénario. Mais ceux qui connaissent Clooney savent que c’est autre chose. C’est la reconnaissance d’un homme qui n’a jamais oublié les soirs de galère, ni les mains tendues.
|
|
||||||
|
|
||||||
> “J’ai pensé : si je me fais renverser par un bus demain, je suis comblé.
|
|
||||||
> Mais tout ça n’aurait aucun sens si je n’avais pas ces gars à mes côtés.”
|
|
||||||
> — *George Clooney, dans GQ, 2020*
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### **Épilogue**
|
|
||||||
|
|
||||||
Aujourd’hui encore, la légende circule à Hollywood comme un conte moderne.
|
|
||||||
Quatorze valises, quatorze millions, quatorze amitiés.
|
|
||||||
Une scène digne d’un film — mais sans caméra, sans public, sans scénario.
|
|
||||||
|
|
||||||
Seulement un homme, ses amis, et un merci plus fort que tout l’or du monde.
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "3d6d8b38-c514-46dc-93dc-b4b2f19112e9",
|
|
||||||
"slug": "l-histoire-du-million-de-dollars-offert-par-george-clooney",
|
|
||||||
"title": "L'histoire du million de dollars offert par George Clooney",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2025-11-04 22:02:12",
|
|
||||||
"created_at": "2025-11-04 22:02:12",
|
|
||||||
"updated_at": "2025-11-04 22:02:12",
|
|
||||||
"revisions": [],
|
|
||||||
"cover": "cover.jpg",
|
|
||||||
"category": "loisirs"
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 95 KiB |
@@ -1,118 +0,0 @@
|
|||||||
<svg width="100%" viewBox="0 0 690 430" role="img" xmlns="http://www.w3.org/2000/svg" style="">
|
|
||||||
<title style="fill:rgb(0, 0, 0);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Illustration : compromission de la chaîne d'approvisionnement npm</title>
|
|
||||||
<desc style="fill:rgb(0, 0, 0);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Un réseau de paquets logiciels représentés par des boîtes cubiques connectées entre elles. Un paquet central rouge, marqué d'un signe d'infection, propage une contamination le long des arêtes du graphe vers les paquets voisins, illustrant la propagation virale d'une attaque sur la chaîne d'approvisionnement npm.</desc>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="infected" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
||||||
<stop offset="0%" stop-color="#d94a3d"/>
|
|
||||||
<stop offset="100%" stop-color="#a8312a"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="warned" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
||||||
<stop offset="0%" stop-color="#e89a3c"/>
|
|
||||||
<stop offset="100%" stop-color="#b87520"/>
|
|
||||||
</linearGradient>
|
|
||||||
<mask id="imagine-text-gaps-6vjtb6" maskUnits="userSpaceOnUse"><rect x="0" y="0" width="690" height="430" fill="white"/><rect x="327" y="55" width="26" height="18" fill="black" rx="2"/><rect x="67" y="215" width="26" height="18" fill="black" rx="2"/><rect x="147" y="295" width="26" height="18" fill="black" rx="2"/><rect x="587" y="215" width="26" height="18" fill="black" rx="2"/><rect x="507" y="295" width="26" height="18" fill="black" rx="2"/><rect x="327" y="355" width="26" height="18" fill="black" rx="2"/><rect x="193" y="115" width="14" height="18" fill="black" rx="2"/><rect x="473" y="115" width="14" height="18" fill="black" rx="2"/><rect x="301.9114685058594" y="202.125" width="76.17707824707031" height="13.625" fill="black" rx="2"/><rect x="312.8999938964844" y="226" width="54.20000076293945" height="19" fill="black" rx="2"/><rect x="367.70001220703125" y="175" width="14.599999904632568" height="19" fill="black" rx="2"/><rect x="221.85000610351562" y="395" width="236.3000030517578" height="18" fill="black" rx="2"/></mask></defs>
|
|
||||||
|
|
||||||
<!-- Background grid pattern, subtle -->
|
|
||||||
<g opacity="0.08" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.08;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<line x1="0" y1="100" x2="680" y2="100" stroke="#888" stroke-width="0.5" style="fill:rgb(0, 0, 0);stroke:rgb(136, 136, 136);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="0" y1="200" x2="680" y2="200" stroke="#888" stroke-width="0.5" style="fill:rgb(0, 0, 0);stroke:rgb(136, 136, 136);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="0" y1="300" x2="680" y2="300" stroke="#888" stroke-width="0.5" mask="url(#imagine-text-gaps-6vjtb6)" style="fill:rgb(0, 0, 0);stroke:rgb(136, 136, 136);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="120" y1="0" x2="120" y2="420" stroke="#888" stroke-width="0.5" style="fill:rgb(0, 0, 0);stroke:rgb(136, 136, 136);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="280" y1="0" x2="280" y2="420" stroke="#888" stroke-width="0.5" mask="url(#imagine-text-gaps-6vjtb6)" style="fill:rgb(0, 0, 0);stroke:rgb(136, 136, 136);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="440" y1="0" x2="440" y2="420" stroke="#888" stroke-width="0.5" mask="url(#imagine-text-gaps-6vjtb6)" style="fill:rgb(0, 0, 0);stroke:rgb(136, 136, 136);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="560" y1="0" x2="560" y2="420" stroke="#888" stroke-width="0.5" style="fill:rgb(0, 0, 0);stroke:rgb(136, 136, 136);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Connection edges (drawn before nodes so nodes overlap them) -->
|
|
||||||
<!-- Infected edges (red, animated feel via dash) -->
|
|
||||||
<line x1="340" y1="210" x2="200" y2="120" stroke="#d94a3d" stroke-width="2" stroke-dasharray="6 4" opacity="0.85" mask="url(#imagine-text-gaps-6vjtb6)" style="fill:rgb(0, 0, 0);stroke:rgb(217, 74, 61);color:rgb(251, 251, 254);stroke-width:2px;stroke-dasharray:6px, 4px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.85;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="340" y1="210" x2="480" y2="120" stroke="#d94a3d" stroke-width="2" stroke-dasharray="6 4" opacity="0.85" mask="url(#imagine-text-gaps-6vjtb6)" style="fill:rgb(0, 0, 0);stroke:rgb(217, 74, 61);color:rgb(251, 251, 254);stroke-width:2px;stroke-dasharray:6px, 4px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.85;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="340" y1="210" x2="160" y2="300" stroke="#d94a3d" stroke-width="2" stroke-dasharray="6 4" opacity="0.85" mask="url(#imagine-text-gaps-6vjtb6)" style="fill:rgb(0, 0, 0);stroke:rgb(217, 74, 61);color:rgb(251, 251, 254);stroke-width:2px;stroke-dasharray:6px, 4px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.85;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="340" y1="210" x2="520" y2="300" stroke="#d94a3d" stroke-width="2" stroke-dasharray="6 4" opacity="0.85" mask="url(#imagine-text-gaps-6vjtb6)" style="fill:rgb(0, 0, 0);stroke:rgb(217, 74, 61);color:rgb(251, 251, 254);stroke-width:2px;stroke-dasharray:6px, 4px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.85;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="200" y1="120" x2="80" y2="220" stroke="#e89a3c" stroke-width="1.5" stroke-dasharray="4 4" opacity="0.7" mask="url(#imagine-text-gaps-6vjtb6)" style="fill:rgb(0, 0, 0);stroke:rgb(232, 154, 60);color:rgb(251, 251, 254);stroke-width:1.5px;stroke-dasharray:4px, 4px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="480" y1="120" x2="600" y2="220" stroke="#e89a3c" stroke-width="1.5" stroke-dasharray="4 4" opacity="0.7" mask="url(#imagine-text-gaps-6vjtb6)" style="fill:rgb(0, 0, 0);stroke:rgb(232, 154, 60);color:rgb(251, 251, 254);stroke-width:1.5px;stroke-dasharray:4px, 4px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<!-- Clean edges (neutral grey) -->
|
|
||||||
<line x1="80" y1="220" x2="160" y2="300" stroke="#999" stroke-width="1" opacity="0.5" mask="url(#imagine-text-gaps-6vjtb6)" style="fill:rgb(0, 0, 0);stroke:rgb(153, 153, 153);color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.5;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="600" y1="220" x2="520" y2="300" stroke="#999" stroke-width="1" opacity="0.5" mask="url(#imagine-text-gaps-6vjtb6)" style="fill:rgb(0, 0, 0);stroke:rgb(153, 153, 153);color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.5;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="160" y1="300" x2="340" y2="360" stroke="#999" stroke-width="1" opacity="0.5" mask="url(#imagine-text-gaps-6vjtb6)" style="fill:rgb(0, 0, 0);stroke:rgb(153, 153, 153);color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.5;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="520" y1="300" x2="340" y2="360" stroke="#999" stroke-width="1" opacity="0.5" mask="url(#imagine-text-gaps-6vjtb6)" style="fill:rgb(0, 0, 0);stroke:rgb(153, 153, 153);color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.5;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="200" y1="120" x2="340" y2="60" stroke="#999" stroke-width="1" opacity="0.5" mask="url(#imagine-text-gaps-6vjtb6)" style="fill:rgb(0, 0, 0);stroke:rgb(153, 153, 153);color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.5;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="480" y1="120" x2="340" y2="60" stroke="#999" stroke-width="1" opacity="0.5" mask="url(#imagine-text-gaps-6vjtb6)" style="fill:rgb(0, 0, 0);stroke:rgb(153, 153, 153);color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.5;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<!-- Clean package nodes (cube-like, neutral) -->
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<!-- Top center package -->
|
|
||||||
<rect x="310" y="35" width="60" height="50" rx="4" fill="#e8e6e1" stroke="#666" stroke-width="0.5" style="fill:rgb(232, 230, 225);stroke:rgb(102, 102, 102);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="310" y="35" width="60" height="14" rx="4" fill="#c9c6bf" stroke="#666" stroke-width="0.5" style="fill:rgb(201, 198, 191);stroke:rgb(102, 102, 102);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="340" y="68" text-anchor="middle" font-family="ui-monospace, monospace" font-size="10" fill="#444" style="fill:rgb(68, 68, 68);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:10px;font-weight:400;text-anchor:middle;dominant-baseline:auto">pkg</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<!-- Left side packages -->
|
|
||||||
<rect x="50" y="195" width="60" height="50" rx="4" fill="#e8e6e1" stroke="#666" stroke-width="0.5" style="fill:rgb(232, 230, 225);stroke:rgb(102, 102, 102);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="50" y="195" width="60" height="14" rx="4" fill="#c9c6bf" stroke="#666" stroke-width="0.5" style="fill:rgb(201, 198, 191);stroke:rgb(102, 102, 102);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="80" y="228" text-anchor="middle" font-family="ui-monospace, monospace" font-size="10" fill="#444" style="fill:rgb(68, 68, 68);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:10px;font-weight:400;text-anchor:middle;dominant-baseline:auto">pkg</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="130" y="275" width="60" height="50" rx="4" fill="#e8e6e1" stroke="#666" stroke-width="0.5" style="fill:rgb(232, 230, 225);stroke:rgb(102, 102, 102);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="130" y="275" width="60" height="14" rx="4" fill="#c9c6bf" stroke="#666" stroke-width="0.5" style="fill:rgb(201, 198, 191);stroke:rgb(102, 102, 102);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="160" y="308" text-anchor="middle" font-family="ui-monospace, monospace" font-size="10" fill="#444" style="fill:rgb(68, 68, 68);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:10px;font-weight:400;text-anchor:middle;dominant-baseline:auto">pkg</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<!-- Right side packages -->
|
|
||||||
<rect x="570" y="195" width="60" height="50" rx="4" fill="#e8e6e1" stroke="#666" stroke-width="0.5" style="fill:rgb(232, 230, 225);stroke:rgb(102, 102, 102);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="570" y="195" width="60" height="14" rx="4" fill="#c9c6bf" stroke="#666" stroke-width="0.5" style="fill:rgb(201, 198, 191);stroke:rgb(102, 102, 102);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="600" y="228" text-anchor="middle" font-family="ui-monospace, monospace" font-size="10" fill="#444" style="fill:rgb(68, 68, 68);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:10px;font-weight:400;text-anchor:middle;dominant-baseline:auto">pkg</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="490" y="275" width="60" height="50" rx="4" fill="#e8e6e1" stroke="#666" stroke-width="0.5" style="fill:rgb(232, 230, 225);stroke:rgb(102, 102, 102);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="490" y="275" width="60" height="14" rx="4" fill="#c9c6bf" stroke="#666" stroke-width="0.5" style="fill:rgb(201, 198, 191);stroke:rgb(102, 102, 102);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="520" y="308" text-anchor="middle" font-family="ui-monospace, monospace" font-size="10" fill="#444" style="fill:rgb(68, 68, 68);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:10px;font-weight:400;text-anchor:middle;dominant-baseline:auto">pkg</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<!-- Bottom center package -->
|
|
||||||
<rect x="310" y="335" width="60" height="50" rx="4" fill="#e8e6e1" stroke="#666" stroke-width="0.5" style="fill:rgb(232, 230, 225);stroke:rgb(102, 102, 102);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="310" y="335" width="60" height="14" rx="4" fill="#c9c6bf" stroke="#666" stroke-width="0.5" style="fill:rgb(201, 198, 191);stroke:rgb(102, 102, 102);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="340" y="368" text-anchor="middle" font-family="ui-monospace, monospace" font-size="10" fill="#444" style="fill:rgb(68, 68, 68);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:10px;font-weight:400;text-anchor:middle;dominant-baseline:auto">pkg</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Warned/at-risk packages (amber) -->
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="170" y="95" width="60" height="50" rx="4" fill="url(#warned)" stroke="#7a4a14" stroke-width="0.5" style="stroke:rgb(122, 74, 20);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="170" y="95" width="60" height="14" rx="4" fill="#a86b1a" stroke="#7a4a14" stroke-width="0.5" style="fill:rgb(168, 107, 26);stroke:rgb(122, 74, 20);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="200" y="128" text-anchor="middle" font-family="ui-monospace, monospace" font-size="10" fill="#fff" font-weight="500" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:10px;font-weight:500;text-anchor:middle;dominant-baseline:auto">!</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="450" y="95" width="60" height="50" rx="4" fill="url(#warned)" stroke="#7a4a14" stroke-width="0.5" style="stroke:rgb(122, 74, 20);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="450" y="95" width="60" height="14" rx="4" fill="#a86b1a" stroke="#7a4a14" stroke-width="0.5" style="fill:rgb(168, 107, 26);stroke:rgb(122, 74, 20);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="480" y="128" text-anchor="middle" font-family="ui-monospace, monospace" font-size="10" fill="#fff" font-weight="500" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:10px;font-weight:500;text-anchor:middle;dominant-baseline:auto">!</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Central infected package (large, red, with skull-like glyph) -->
|
|
||||||
<g style="fill:rgb(0, 0, 0);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="290" y="175" width="100" height="80" rx="6" fill="url(#infected)" stroke="#6b1e18" stroke-width="1" style="stroke:rgb(107, 30, 24);color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="290" y="175" width="100" height="22" rx="6" fill="#8a2820" stroke="#6b1e18" stroke-width="1" style="fill:rgb(138, 40, 32);stroke:rgb(107, 30, 24);color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- Tape band suggesting tampering -->
|
|
||||||
<rect x="285" y="205" width="110" height="10" fill="#6b1e18" opacity="0.6" style="fill:rgb(107, 30, 24);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.6;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="340" y="212" text-anchor="middle" font-family="ui-monospace, monospace" font-size="7" fill="#f4d4cf" letter-spacing="2" style="fill:rgb(244, 212, 207);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:7px;font-weight:400;text-anchor:middle;dominant-baseline:auto">COMPROMISED</text>
|
|
||||||
<!-- Package label -->
|
|
||||||
<text x="340" y="240" text-anchor="middle" font-family="ui-monospace, monospace" font-size="11" fill="#fff" font-weight="500" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">npm pkg</text>
|
|
||||||
<!-- Warning glyph -->
|
|
||||||
<circle cx="375" cy="185" r="8" fill="#fff" stroke="#6b1e18" stroke-width="1" style="fill:rgb(255, 255, 255);stroke:rgb(107, 30, 24);color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="375" y="189" text-anchor="middle" font-family="ui-monospace, monospace" font-size="11" fill="#a8312a" font-weight="500" style="fill:rgb(168, 49, 42);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, monospace;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">×</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Pulse rings around the infected node (suggesting propagation) -->
|
|
||||||
<circle cx="340" cy="215" r="65" fill="none" stroke="#d94a3d" stroke-width="0.5" opacity="0.4" style="fill:none;stroke:rgb(217, 74, 61);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.4;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="340" cy="215" r="95" fill="none" stroke="#d94a3d" stroke-width="0.5" opacity="0.25" style="fill:none;stroke:rgb(217, 74, 61);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.25;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="340" cy="215" r="125" fill="none" stroke="#d94a3d" stroke-width="0.5" opacity="0.15" style="fill:none;stroke:rgb(217, 74, 61);color:rgb(251, 251, 254);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.15;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<!-- Title at bottom -->
|
|
||||||
<text x="340" y="408" text-anchor="middle" font-family="ui-sans-serif, system-ui" font-size="11" fill="#888" letter-spacing="3" style="fill:rgb(136, 136, 136);stroke:none;color:rgb(251, 251, 254);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-sans-serif, system-ui;font-size:11px;font-weight:400;text-anchor:middle;dominant-baseline:auto">SUPPLY CHAIN COMPROMISE</text>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 29 KiB |
@@ -1,109 +0,0 @@
|
|||||||
# NPM, le ver dans le fruit : comprendre la faille systémique et repenser les pratiques DevOps
|
|
||||||
|
|
||||||
*À propos de l'article du MagIT « NPM : une nouvelle campagne malveillante souligne une vulnérabilité systémique ».*
|
|
||||||
|
|
||||||
## NPM expliqué simplement
|
|
||||||
|
|
||||||
Quand on développe une application web moderne en JavaScript ou TypeScript, on ne réécrit jamais tout depuis zéro. On assemble des briques logicielles déjà écrites par d'autres : un module pour parser des dates, un autre pour valider des emails, un troisième pour discuter avec une base de données. Ces briques s'appellent des **paquets**, et la place de marché centrale qui les distribue s'appelle **npm** (Node Package Manager).
|
|
||||||
|
|
||||||
Concrètement, dans un projet, on déclare la liste des paquets nécessaires dans un fichier `package.json`. On lance la commande `npm install`, et l'outil télécharge automatiquement les paquets demandés… ainsi que **tous les paquets dont ces paquets ont eux-mêmes besoin**. Un projet « simple » se retrouve souvent à dépendre de plusieurs centaines, voire plusieurs milliers, de paquets en cascade. C'est ce qu'on appelle l'arbre des dépendances.
|
|
||||||
|
|
||||||
Le registre npm héberge aujourd'hui plus de 2,5 millions de paquets. C'est à la fois sa force — un écosystème colossal, une productivité décuplée — et sa faiblesse : la confiance accordée à chaque maillon de la chaîne est implicite, et chaque maillon devient une porte d'entrée potentielle.
|
|
||||||
|
|
||||||
## La faille : ce qui s'est passé
|
|
||||||
|
|
||||||
L'épisode décrit par LeMagIT n'est pas un bug logiciel classique. C'est ce qu'on appelle une **attaque sur la chaîne d'approvisionnement logicielle** (*supply chain attack*) : au lieu d'attaquer directement la cible finale, l'attaquant compromet un fournisseur en amont, et laisse la mise à jour légitime faire son travail de propagation.
|
|
||||||
|
|
||||||
Le scénario reconstitué se déroule en plusieurs temps.
|
|
||||||
|
|
||||||
**1. Compromission d'un paquet de confiance.** Les attaquants sont parvenus à pousser du code malveillant dans des paquets npm largement utilisés, notamment via le détournement du pipeline d'intégration continue de projets connus comme `@bitwarden/cli` et l'écosystème Checkmarx. L'astuce n'est pas de publier un faux paquet : c'est de **modifier un vrai paquet** en exploitant les *GitHub Actions* — les robots qui construisent et publient automatiquement les nouvelles versions.
|
|
||||||
|
|
||||||
**2. Vol de secrets à l'installation.** Une fois installé sur la machine d'un développeur ou dans un environnement de build, le code malveillant scanne l'environnement à la recherche de variables sensibles : `GITHUB_TOKEN`, `NPM_TOKEN`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`. Tout ce qui traîne dans les variables d'environnement, les fichiers `.env`, les configurations cloud.
|
|
||||||
|
|
||||||
**3. Auto-propagation.** C'est là que l'attaque devient virale. Avec les jetons npm volés, le maliciel se reconnecte au registre npm, récupère la liste des paquets publiés par la victime, et **publie automatiquement des versions piégées de ces paquets**. Chaque développeur compromis devient un super-propagateur. Socket a identifié une quarantaine de paquets infectés en cascade lors d'une seule vague.
|
|
||||||
|
|
||||||
**4. Persistance.** Sur les postes touchés, le malware installe un script `systemd` pour survivre aux redémarrages, et, si nécessaire, exfiltre les données volées dans un dépôt GitHub public créé pour l'occasion.
|
|
||||||
|
|
||||||
Le résultat : un binaire signé, publié sous un nom officiel, à jour, qui passe tous les contrôles de surface — et qui contamine simultanément le poste du développeur **et** les serveurs de production.
|
|
||||||
|
|
||||||
## Pourquoi c'est « systémique »
|
|
||||||
|
|
||||||
Le terme employé par LeMagIT est juste. Ce n'est pas un bug isolé, c'est une propriété structurelle de l'écosystème.
|
|
||||||
|
|
||||||
**La confiance est transitive.** On fait confiance à `express`, qui fait confiance à `body-parser`, qui fait confiance à `qs`, etc. Compromettre un nœud profond et populaire suffit à toucher des millions de projets.
|
|
||||||
|
|
||||||
**La publication est ouverte.** N'importe qui peut publier un paquet. Les contrôles existent (provenance, 2FA pour les mainteneurs populaires) mais restent surtout *a posteriori*.
|
|
||||||
|
|
||||||
**Les scripts d'installation s'exécutent automatiquement.** Un paquet npm peut déclarer un `postinstall` qui lance du code arbitraire au moment de `npm install`. C'est pratique, mais c'est aussi un cheval de Troie idéal.
|
|
||||||
|
|
||||||
**Les jetons d'API sont partout.** Le poste du développeur, les runners CI/CD, les serveurs : tous manipulent des secrets en clair dans des variables d'environnement. Un malware qui s'exécute *dans* le build n'a même pas besoin d'escalader ses privilèges.
|
|
||||||
|
|
||||||
**Les versions sont mutables sur fenêtre courte.** Un paquet peut être republié dans les 72 heures suivant sa publication, et un `npm unpublish` peut retirer une version d'un jour à l'autre.
|
|
||||||
|
|
||||||
Aucun de ces points n'est un défaut technique réparable par un patch. Ce sont des choix d'architecture, vieux de plus de dix ans, qui ont accompagné l'explosion de l'écosystème.
|
|
||||||
|
|
||||||
## Y a-t-il des alternatives ?
|
|
||||||
|
|
||||||
La question est légitime, mais la réponse honnête est : **pas vraiment, et pour de bonnes raisons.**
|
|
||||||
|
|
||||||
### Les gestionnaires de paquets alternatifs
|
|
||||||
|
|
||||||
`pnpm`, `yarn` et `bun` sont des **gestionnaires** différents, mais ils tirent leurs paquets du **même registre npm**. Migrer de `npm install` à `pnpm install` ne change rien à la surface d'attaque : ce sont les mêmes paquets, le même registre, les mêmes mainteneurs.
|
|
||||||
|
|
||||||
Cela dit, certains apportent des garde-fous utiles :
|
|
||||||
|
|
||||||
- `pnpm` a introduit l'option `minimumReleaseAge`, qui refuse d'installer un paquet publié il y a moins de N jours. Une vague d'attaque dure typiquement quelques heures avant détection : attendre 72 heures avant d'installer une nouvelle version élimine la fenêtre dangereuse.
|
|
||||||
- `pnpm` impose un consentement explicite pour les scripts `postinstall`, là où npm les exécute par défaut.
|
|
||||||
- `bun` et `yarn` proposent des *lockfiles* stricts (`--frozen-lockfile`) qui garantissent que ce qui est installé en CI correspond exactement à ce qui a été testé.
|
|
||||||
|
|
||||||
### Les registres alternatifs
|
|
||||||
|
|
||||||
**JSR** (JavaScript Registry), lancé par les créateurs de Deno, est le seul vrai nouveau registre crédible. Il a été conçu en tirant les leçons des problèmes de npm : TypeScript natif, modules ECMAScript par défaut, pas de scripts d'install, scoring qualité automatique, compatible avec tous les *runtimes* (Node, Deno, Bun). Mais JSR est **complémentaire**, pas un remplaçant : il héberge des milliers de paquets, pas des millions. Pour la majorité des dépendances, on continuera de passer par npm.
|
|
||||||
|
|
||||||
**Les registres privés** — Verdaccio, GitHub Packages, JFrog Artifactory, Sonatype Nexus — ne remplacent pas npm non plus. Ils servent de **proxy filtrant** : on continue de récupérer les paquets publics, mais à travers un cache d'entreprise où l'on peut bloquer une version, exiger une signature, refuser un mainteneur, ou interdire les paquets publiés depuis moins de X jours. C'est probablement le meilleur compromis disponible aujourd'hui pour une organisation.
|
|
||||||
|
|
||||||
### Le verdict
|
|
||||||
|
|
||||||
Abandonner npm en 2026 reviendrait à abandonner JavaScript. La valeur de l'écosystème (2,5 millions de paquets) est trop importante pour qu'on en sorte. **Le problème ne se résoudra pas par un changement d'outil ; il se résoudra par un changement de pratiques.**
|
|
||||||
|
|
||||||
## Changer les pratiques : ce qui doit devenir réflexe
|
|
||||||
|
|
||||||
L'enseignement de cette campagne, et des précédentes (Shai-Hulud, TeamPCP, l'attaque Trivy/KICS), tient en une phrase : **la confiance par défaut est morte.** Il faut traiter chaque dépendance comme du code hostile par défaut, et le pipeline CI/CD comme une zone de production.
|
|
||||||
|
|
||||||
### Au niveau du poste de développement
|
|
||||||
|
|
||||||
- Activer l'option `minimumReleaseAge` (ou équivalent) pour différer l'installation des paquets fraîchement publiés.
|
|
||||||
- Désactiver les scripts `postinstall` par défaut, et n'autoriser que ceux explicitement validés.
|
|
||||||
- Ne jamais stocker de jetons en clair dans `~/.npmrc` ou les variables d'environnement persistantes. Préférer un gestionnaire de secrets (1Password CLI, `pass`, `keyring`).
|
|
||||||
- Utiliser des comptes npm séparés pour la publication, avec 2FA matérielle obligatoire.
|
|
||||||
|
|
||||||
### Au niveau du dépôt
|
|
||||||
|
|
||||||
- Verrouiller systématiquement les dépendances (`package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`) et installer en mode strict (`npm ci`, `pnpm install --frozen-lockfile`).
|
|
||||||
- Mettre en place un audit automatique des dépendances à chaque PR (Socket, Snyk, GitHub Dependabot, `npm audit`).
|
|
||||||
- Publier ses propres paquets avec *provenance* npm (signature liée au pipeline GitHub Actions), pour que les consommateurs puissent vérifier l'origine.
|
|
||||||
- Tenir à jour un SBOM (*Software Bill of Materials*) pour savoir exactement ce qui tourne en production.
|
|
||||||
|
|
||||||
### Au niveau du CI/CD
|
|
||||||
|
|
||||||
C'est probablement le chantier le plus important.
|
|
||||||
|
|
||||||
- **Cloisonner les jetons.** Un jeton de publication npm ne doit jamais coexister avec un jeton AWS dans la même étape de pipeline. Un secret par étape, durée de vie minimale, scope minimal.
|
|
||||||
- **Préférer les jetons à courte durée de vie** (OIDC entre GitHub Actions et le cloud) plutôt que des clés statiques.
|
|
||||||
- **Auditer les GitHub Actions tierces.** Une action `uses: foo/bar@v1` est l'équivalent d'un `curl | bash`. Épingler par hash SHA (`@a1b2c3...`), pas par tag mutable.
|
|
||||||
- **Restreindre les permissions du `GITHUB_TOKEN`** au strict minimum (`permissions: read-all` par défaut, `write` ponctuel et justifié).
|
|
||||||
- **Surveiller le comportement réseau** des runners : un build qui contacte un domaine inconnu doit lever une alerte.
|
|
||||||
|
|
||||||
### Au niveau de l'organisation
|
|
||||||
|
|
||||||
- Mettre en place un **registre proxy** (Verdaccio, Nexus, Artifactory) avec liste blanche/noire de paquets, et l'imposer comme unique source pour tous les projets.
|
|
||||||
- Définir une politique de *dependency governance* : qui peut introduire une nouvelle dépendance, sous quelles conditions, avec quel niveau d'audit.
|
|
||||||
- Prévoir un *playbook de révocation* : que faire dans l'heure qui suit la détection d'un paquet compromis (rotation de tous les jetons npm/GitHub/cloud, audit des artefacts publiés, communication).
|
|
||||||
|
|
||||||
## En résumé
|
|
||||||
|
|
||||||
NPM n'est pas cassé, il est tel qu'il a été conçu : ouvert, automatique, transitif. Ce qui a changé, c'est la **valeur** que les attaquants peuvent en extraire — secrets cloud, jetons CI/CD, accès aux pipelines — et la **sophistication** des campagnes, qui exploitent désormais l'auto-propagation pour atteindre une échelle virale.
|
|
||||||
|
|
||||||
Aucune alternative ne supprime le problème, parce que le problème n'est pas npm : c'est l'idée qu'on puisse exécuter en production du code écrit par des inconnus sans jamais le regarder. Le rôle du DevOps en 2026, c'est de bâtir l'infrastructure qui rend cette inspection systématique, économique et inévitable — registres proxy, lockfiles stricts, jetons éphémères, audits continus, isolation des étapes de build.
|
|
||||||
|
|
||||||
On ne fera pas confiance à moins de gens. On exigera juste que chaque maillon prouve, à chaque exécution, qu'il est bien celui qu'il prétend être.
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "3e7ef528-6bd0-4fd1-83cb-a0d03ba35949",
|
|
||||||
"slug": "npm-le-ver-dans-le-fruit-comprendre-la-faille-systemique-et-repenser-les-pratiques-devops",
|
|
||||||
"title": "NPM, le ver dans le fruit : comprendre la faille systémique et repenser les pratiques DevOps",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2026-05-12 13:08",
|
|
||||||
"created_at": "2026-05-12 13:08:44",
|
|
||||||
"updated_at": "2026-05-12 13:12:42",
|
|
||||||
"revisions": [],
|
|
||||||
"cover": "cover.svg",
|
|
||||||
"files_meta": {
|
|
||||||
"_thumb_c645a685a3e24f2d-97360.jpg": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"e9a7551098b57432-29499.svg": {
|
|
||||||
"author": "Générée par IA our Cédrix",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"cover.svg": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"external_links": [
|
|
||||||
{
|
|
||||||
"url": "https://www.lemagit.fr/actualites/366642042/NPM-une-nouvelle-campagne-malveillante-souligne-une-vulnerabilite-systemique",
|
|
||||||
"name": "NPM : une nouvelle campagne malveillante souligne une vulnérabilité systémique | LeMagIT",
|
|
||||||
"added_at": "2026-05-12 13:09:34",
|
|
||||||
"meta": {
|
|
||||||
"mime": "text/html",
|
|
||||||
"size": 362919,
|
|
||||||
"description": "Une campagne visant Checkmarx pointe à nouveau une vulnérabilité systémique des outils de développement, exposant les secrets clouds et pipelines à une propagation automatisée.",
|
|
||||||
"og_image": "/file?uuid=3e7ef528-6bd0-4fd1-83cb-a0d03ba35949&name=_thumb_c645a685a3e24f2d-97360.jpg",
|
|
||||||
"site_name": "LeMagIT",
|
|
||||||
"og_type": "article",
|
|
||||||
"language": "fr_FR",
|
|
||||||
"canonical": "https://www.lemagit.fr/actualites/366642042/NPM-une-nouvelle-campagne-malveillante-souligne-une-vulnerabilite-systemique"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "NPM frappé par une campagne malveillante auto-propagée : comprendre la faille systémique, les alternatives possibles et les pratiques DevOps à adopter.",
|
|
||||||
"og_image": "https://varlog.a5l.fr/file?uuid=3e7ef528-6bd0-4fd1-83cb-a0d03ba35949&name=cover.svg",
|
|
||||||
"category": "informatique"
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 17 KiB |
@@ -1,119 +0,0 @@
|
|||||||
# Réinitialisation d'un ESP-01 : restauration du firmware AT
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
L'ESP-01 est un petit module Wi-Fi très répandu, construit autour du microcontrôleur ESP8266EX fabriqué par Espressif. À sa sortie d'usine, il est livré avec un firmware (le programme interne du circuit) qui permet de le piloter à l'aide de commandes textuelles simples appelées **commandes AT**. Ce firmware peut être effacé ou corrompu, par exemple après avoir téléversé un programme Arduino ou MicroPython sur le module. Ce document explique comment remettre l'ESP-01 dans son état d'origine afin de retrouver l'usage des commandes AT.
|
|
||||||
|
|
||||||
## Quelques notions préalables
|
|
||||||
|
|
||||||
Avant de commencer, il est utile de clarifier quelques termes.
|
|
||||||
|
|
||||||
Un **firmware** est le logiciel embarqué dans un composant électronique. Contrairement à un programme installé sur un ordinateur, il s'écrit directement dans la mémoire flash du microcontrôleur et s'exécute au démarrage du circuit.
|
|
||||||
|
|
||||||
Un **fichier binaire** (extension `.bin`) est le résultat de la compilation d'un code source écrit dans un langage évolué, généralement le C. Une fois compilé, le fichier ne contient plus que des instructions destinées au processeur, illisibles directement par un humain. Il n'est pas nécessaire de les modifier : ils se téléversent tels quels dans le microcontrôleur.
|
|
||||||
|
|
||||||
La **mémoire flash** de l'ESP8266EX est divisée en zones. Chaque binaire doit être écrit à une **adresse mémoire** précise, sans quoi le module ne saura pas où trouver le code à exécuter au démarrage. Sur l'ESP-01, la mémoire est généralement organisée en **512k + 512k**, ce qui signifie que la flash totale de 8 Mbit (1 Mo) est partagée en deux zones de 512 ko : l'une pour le programme actif, l'autre réservée aux mises à jour à distance (OTA).
|
|
||||||
|
|
||||||
## Étape 1 — Télécharger le firmware AT officiel
|
|
||||||
|
|
||||||
Le firmware est mis à disposition par Espressif sur son site officiel :
|
|
||||||
|
|
||||||
[https://www.espressif.com/en/products/socs/esp8266ex/resources](https://www.espressif.com/en/products/socs/esp8266ex/resources)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Dans la section `AT`, choisir la version `ESP8266 NonOS AT Bin V1.7.4` ou plus récente. L'archive ZIP téléchargée contient plusieurs binaires destinés à l'ESP8266EX.
|
|
||||||
|
|
||||||
Quatre fichiers sont particulièrement importants :
|
|
||||||
|
|
||||||
- **boot_v1.7.bin** — le chargeur de démarrage (*bootloader*), premier programme exécuté à la mise sous tension ;
|
|
||||||
- **user1.1024.new.2.bin** — le programme AT proprement dit, qui interprète les commandes envoyées par la liaison série ;
|
|
||||||
- **esp_init_data_default_v08.bin** — les données d'initialisation (paramètres radio, calibration) ;
|
|
||||||
- **blank.bin** — un fichier rempli de zéros, utilisé pour réinitialiser certaines zones de la flash.
|
|
||||||
|
|
||||||
Une copie de ces binaires pour la configuration **ESP8266EX 512k+512k** est disponible ici :
|
|
||||||
|
|
||||||
[https://gitlab.com/cedricAbonnel/esp/-/tree/master/esp01/esp8266ex_at_bin](https://gitlab.com/cedricAbonnel/esp/-/tree/master/esp01/esp8266ex_at_bin)
|
|
||||||
|
|
||||||
## Étape 2 — Identifier le port série de l'ESP-01
|
|
||||||
|
|
||||||
L'ESP-01 ne se connecte pas directement à un port USB : il faut passer par un adaptateur USB-série (souvent un module FTDI ou CH340). Une fois branché, l'ordinateur expose ce périphérique sous la forme d'un fichier dans `/dev/`.
|
|
||||||
|
|
||||||
Pour repérer ce fichier, exécuter dans un terminal :
|
|
||||||
|
|
||||||
```
|
|
||||||
ls /dev/tty*
|
|
||||||
```
|
|
||||||
|
|
||||||
Parmi les entrées affichées, celle qui nous intéresse est généralement **/dev/ttyUSB0** (parfois `ttyUSB1` si plusieurs adaptateurs sont branchés, ou `ttyACM0` selon le modèle).
|
|
||||||
|
|
||||||
Une astuce utile : exécuter la commande une première fois sans l'adaptateur, puis une seconde fois après l'avoir branché. La nouvelle entrée qui apparaît est celle du module.
|
|
||||||
|
|
||||||
## Étape 3 — Préparer le téléversement avec esptool.py
|
|
||||||
|
|
||||||
**esptool.py** est l'outil officiel d'Espressif, écrit en Python, qui permet de communiquer avec la mémoire flash de l'ESP8266EX. S'il n'est pas déjà installé, on peut l'obtenir via `pip` :
|
|
||||||
|
|
||||||
```
|
|
||||||
pip install esptool
|
|
||||||
```
|
|
||||||
|
|
||||||
Avant le téléversement, l'ESP-01 doit être placé en **mode programmation** : la broche **GPIO0** doit être reliée à la masse (GND) au moment de la mise sous tension. Sans cette manipulation, le module démarre normalement et refuse l'écriture en flash.
|
|
||||||
|
|
||||||
## Étape 4 — Téléverser les binaires
|
|
||||||
|
|
||||||
La commande suivante écrit les quatre binaires aux bonnes adresses mémoire :
|
|
||||||
|
|
||||||
```
|
|
||||||
esptool.py --port /dev/ttyUSB0 write_flash --flash_mode qio \
|
|
||||||
0x00000 boot_v1.7.bin \
|
|
||||||
0x01000 user1.1024.new.2.bin \
|
|
||||||
0xfc000 esp_init_data_default_v08.bin \
|
|
||||||
0x7e000 blank.bin \
|
|
||||||
0xfe000 blank.bin
|
|
||||||
```
|
|
||||||
|
|
||||||
Décortiquons les options :
|
|
||||||
|
|
||||||
- `--port /dev/ttyUSB0` indique le port série identifié à l'étape précédente ;
|
|
||||||
- `write_flash` est la sous-commande d'écriture en mémoire flash ;
|
|
||||||
- `--flash_mode qio` précise le mode d'accès à la flash (*Quad I/O*, le plus rapide, supporté par l'ESP-01).
|
|
||||||
|
|
||||||
Chaque valeur hexadécimale (`0x00000`, `0x01000`, etc.) qui précède un nom de fichier indique l'**adresse mémoire** à laquelle l'écriture doit commencer. La table de correspondance officielle pour une flash de 8 Mbit organisée en 512k+512k est la suivante :
|
|
||||||
|
|
||||||
```
|
|
||||||
### Flash size 8Mbit: 512KB+512KB
|
|
||||||
boot_v1.2+.bin 0x00000
|
|
||||||
user1.1024.new.2.bin 0x01000
|
|
||||||
esp_init_data_default.bin 0xfc000
|
|
||||||
blank.bin 0x7e000 & 0xfe000
|
|
||||||
```
|
|
||||||
|
|
||||||
L'adresse `0x7e000` correspond aux paramètres système, et `0xfe000` à la zone RF système : les remplir de zéros (`blank.bin`) garantit un démarrage propre.
|
|
||||||
|
|
||||||
Si tout se passe bien, esptool affiche la progression du téléversement et confirme la réussite de l'opération. C'est le moment d'apprécier le travail accompli :
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Étape 5 — Vérifier le bon fonctionnement
|
|
||||||
|
|
||||||
Après le téléversement, retirer la connexion entre GPIO0 et la masse, puis redémarrer le module. Ouvrir une console série (par exemple avec `minicom`, `screen` ou la console série de l'IDE Arduino) à la vitesse **115200 bauds** :
|
|
||||||
|
|
||||||
```
|
|
||||||
screen /dev/ttyUSB0 115200
|
|
||||||
```
|
|
||||||
|
|
||||||
Taper la commande `AT` suivie d'un retour à la ligne. Le module doit répondre `OK`. La commande `AT+GMR` retourne la version du firmware installé, ce qui permet de confirmer la réussite de la réinitialisation.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## En cas de problème
|
|
||||||
|
|
||||||
Quelques pistes si la procédure échoue :
|
|
||||||
|
|
||||||
- **Aucune réponse d'esptool** : vérifier que GPIO0 est bien reliée à GND au moment de l'alimentation, et que l'adaptateur USB-série fournit assez de courant (l'ESP-01 demande des pics jusqu'à 300 mA).
|
|
||||||
- **Réponses illisibles dans la console série** : la vitesse par défaut a pu changer selon la version du firmware. Essayer 9600, 74880 ou 115200 bauds.
|
|
||||||
- **Erreur de checksum ou de mode flash** : essayer `--flash_mode dio` à la place de `qio`, certains clones d'ESP-01 ne supportent pas le mode Quad I/O.
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
Cette procédure restaure un ESP-01 dans son état d'origine, prêt à recevoir des commandes AT depuis n'importe quel système capable de dialoguer en série : ordinateur, Arduino, Raspberry Pi, etc. Elle constitue également un bon exercice d'introduction aux notions de firmware, de mémoire flash et de programmation bas-niveau des microcontrôleurs.
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "3f750a3a-fad0-4089-98e5-79c8b4287ea2",
|
|
||||||
"slug": "esp8266ex-restore-commandes-at",
|
|
||||||
"title": "Réinitialisation d'un ESP-01 : restauration du firmware AT",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2020-12-13 14:35",
|
|
||||||
"created_at": "2020-12-13 14:35:26",
|
|
||||||
"updated_at": "2026-05-13 18:15:11",
|
|
||||||
"revisions": [
|
|
||||||
{
|
|
||||||
"n": 1,
|
|
||||||
"date": "2026-05-13 18:15:11",
|
|
||||||
"comment": "Contenu modifié",
|
|
||||||
"title": "Réinitialisation d'un ESP-01 : restauration du firmware AT"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cover": "",
|
|
||||||
"files_meta": {
|
|
||||||
"20201213-085909.png": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"20201213-094634.png": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"20201213-094758.png": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"dummy.png": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"external_links": [],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "",
|
|
||||||
"og_image": "",
|
|
||||||
"category": "Électronique"
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
## Introduction
|
|
||||||
|
|
||||||
L'ESP-01 est un petit module Wi-Fi très répandu, construit autour du microcontrôleur ESP8266EX fabriqué par Espressif. À sa sortie d'usine, il est livré avec un firmware (le programme interne du circuit) qui permet de le piloter à l'aide de commandes textuelles simples appelées **commandes AT**. Ce firmware peut être effacé ou corrompu, par exemple après avoir téléversé un programme Arduino ou MicroPython sur le module. Ce document explique comment remettre l'ESP-01 dans son état d'origine afin de retrouver l'usage des commandes AT.
|
|
||||||
|
|
||||||
## Quelques notions préalables
|
|
||||||
|
|
||||||
Avant de commencer, il est utile de clarifier quelques termes.
|
|
||||||
|
|
||||||
Un **firmware** est le logiciel embarqué dans un composant électronique. Contrairement à un programme installé sur un ordinateur, il s'écrit directement dans la mémoire flash du microcontrôleur et s'exécute au démarrage du circuit.
|
|
||||||
|
|
||||||
Un **fichier binaire** (extension `.bin`) est le résultat de la compilation d'un code source écrit dans un langage évolué, généralement le C. Une fois compilé, le fichier ne contient plus que des instructions destinées au processeur, illisibles directement par un humain. Il n'est pas nécessaire de les modifier : ils se téléversent tels quels dans le microcontrôleur.
|
|
||||||
|
|
||||||
La **mémoire flash** de l'ESP8266EX est divisée en zones. Chaque binaire doit être écrit à une **adresse mémoire** précise, sans quoi le module ne saura pas où trouver le code à exécuter au démarrage. Sur l'ESP-01, la mémoire est généralement organisée en **512k + 512k**, ce qui signifie que la flash totale de 8 Mbit (1 Mo) est partagée en deux zones de 512 ko : l'une pour le programme actif, l'autre réservée aux mises à jour à distance (OTA).
|
|
||||||
|
|
||||||
## Étape 1 — Télécharger le firmware AT officiel
|
|
||||||
|
|
||||||
Le firmware est mis à disposition par Espressif sur son site officiel :
|
|
||||||
|
|
||||||
[https://www.espressif.com/en/products/socs/esp8266ex/resources](https://www.espressif.com/en/products/socs/esp8266ex/resources)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Dans la section `AT`, choisir la version `ESP8266 NonOS AT Bin V1.7.4` ou plus récente. L'archive ZIP téléchargée contient plusieurs binaires destinés à l'ESP8266EX.
|
|
||||||
|
|
||||||
Quatre fichiers sont particulièrement importants :
|
|
||||||
|
|
||||||
- **boot_v1.7.bin** — le chargeur de démarrage (*bootloader*), premier programme exécuté à la mise sous tension ;
|
|
||||||
- **user1.1024.new.2.bin** — le programme AT proprement dit, qui interprète les commandes envoyées par la liaison série ;
|
|
||||||
- **esp_init_data_default_v08.bin** — les données d'initialisation (paramètres radio, calibration) ;
|
|
||||||
- **blank.bin** — un fichier rempli de zéros, utilisé pour réinitialiser certaines zones de la flash.
|
|
||||||
|
|
||||||
Une copie de ces binaires pour la configuration **ESP8266EX 512k+512k** est disponible ici :
|
|
||||||
|
|
||||||
[https://gitlab.com/cedricAbonnel/esp/-/tree/master/esp01/esp8266ex_at_bin](https://gitlab.com/cedricAbonnel/esp/-/tree/master/esp01/esp8266ex_at_bin)
|
|
||||||
|
|
||||||
## Étape 2 — Identifier le port série de l'ESP-01
|
|
||||||
|
|
||||||
L'ESP-01 ne se connecte pas directement à un port USB : il faut passer par un adaptateur USB-série (souvent un module FTDI ou CH340). Une fois branché, l'ordinateur expose ce périphérique sous la forme d'un fichier dans `/dev/`.
|
|
||||||
|
|
||||||
Pour repérer ce fichier, exécuter dans un terminal :
|
|
||||||
|
|
||||||
```
|
|
||||||
ls /dev/tty*
|
|
||||||
```
|
|
||||||
|
|
||||||
Parmi les entrées affichées, celle qui nous intéresse est généralement **/dev/ttyUSB0** (parfois `ttyUSB1` si plusieurs adaptateurs sont branchés, ou `ttyACM0` selon le modèle).
|
|
||||||
|
|
||||||
Une astuce utile : exécuter la commande une première fois sans l'adaptateur, puis une seconde fois après l'avoir branché. La nouvelle entrée qui apparaît est celle du module.
|
|
||||||
|
|
||||||
## Étape 3 — Préparer le téléversement avec esptool.py
|
|
||||||
|
|
||||||
**esptool.py** est l'outil officiel d'Espressif, écrit en Python, qui permet de communiquer avec la mémoire flash de l'ESP8266EX. S'il n'est pas déjà installé, on peut l'obtenir via `pip` :
|
|
||||||
|
|
||||||
```
|
|
||||||
pip install esptool
|
|
||||||
```
|
|
||||||
|
|
||||||
Avant le téléversement, l'ESP-01 doit être placé en **mode programmation** : la broche **GPIO0** doit être reliée à la masse (GND) au moment de la mise sous tension. Sans cette manipulation, le module démarre normalement et refuse l'écriture en flash.
|
|
||||||
|
|
||||||
## Étape 4 — Téléverser les binaires
|
|
||||||
|
|
||||||
La commande suivante écrit les quatre binaires aux bonnes adresses mémoire :
|
|
||||||
|
|
||||||
```
|
|
||||||
esptool.py --port /dev/ttyUSB0 write_flash --flash_mode qio \
|
|
||||||
0x00000 boot_v1.7.bin \
|
|
||||||
0x01000 user1.1024.new.2.bin \
|
|
||||||
0xfc000 esp_init_data_default_v08.bin \
|
|
||||||
0x7e000 blank.bin \
|
|
||||||
0xfe000 blank.bin
|
|
||||||
```
|
|
||||||
|
|
||||||
Décortiquons les options :
|
|
||||||
|
|
||||||
- `--port /dev/ttyUSB0` indique le port série identifié à l'étape précédente ;
|
|
||||||
- `write_flash` est la sous-commande d'écriture en mémoire flash ;
|
|
||||||
- `--flash_mode qio` précise le mode d'accès à la flash (*Quad I/O*, le plus rapide, supporté par l'ESP-01).
|
|
||||||
|
|
||||||
Chaque valeur hexadécimale (`0x00000`, `0x01000`, etc.) qui précède un nom de fichier indique l'**adresse mémoire** à laquelle l'écriture doit commencer. La table de correspondance officielle pour une flash de 8 Mbit organisée en 512k+512k est la suivante :
|
|
||||||
|
|
||||||
```
|
|
||||||
### Flash size 8Mbit: 512KB+512KB
|
|
||||||
boot_v1.2+.bin 0x00000
|
|
||||||
user1.1024.new.2.bin 0x01000
|
|
||||||
esp_init_data_default.bin 0xfc000
|
|
||||||
blank.bin 0x7e000 & 0xfe000
|
|
||||||
```
|
|
||||||
|
|
||||||
L'adresse `0x7e000` correspond aux paramètres système, et `0xfe000` à la zone RF système : les remplir de zéros (`blank.bin`) garantit un démarrage propre.
|
|
||||||
|
|
||||||
Si tout se passe bien, esptool affiche la progression du téléversement et confirme la réussite de l'opération. C'est le moment d'apprécier le travail accompli :
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Étape 5 — Vérifier le bon fonctionnement
|
|
||||||
|
|
||||||
Après le téléversement, retirer la connexion entre GPIO0 et la masse, puis redémarrer le module. Ouvrir une console série (par exemple avec `minicom`, `screen` ou la console série de l'IDE Arduino) à la vitesse **115200 bauds** :
|
|
||||||
|
|
||||||
```
|
|
||||||
screen /dev/ttyUSB0 115200
|
|
||||||
```
|
|
||||||
|
|
||||||
Taper la commande `AT` suivie d'un retour à la ligne. Le module doit répondre `OK`. La commande `AT+GMR` retourne la version du firmware installé, ce qui permet de confirmer la réussite de la réinitialisation.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## En cas de problème
|
|
||||||
|
|
||||||
Quelques pistes si la procédure échoue :
|
|
||||||
|
|
||||||
- **Aucune réponse d'esptool** : vérifier que GPIO0 est bien reliée à GND au moment de l'alimentation, et que l'adaptateur USB-série fournit assez de courant (l'ESP-01 demande des pics jusqu'à 300 mA).
|
|
||||||
- **Réponses illisibles dans la console série** : la vitesse par défaut a pu changer selon la version du firmware. Essayer 9600, 74880 ou 115200 bauds.
|
|
||||||
- **Erreur de checksum ou de mode flash** : essayer `--flash_mode dio` à la place de `qio`, certains clones d'ESP-01 ne supportent pas le mode Quad I/O.
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
Cette procédure restaure un ESP-01 dans son état d'origine, prêt à recevoir des commandes AT depuis n'importe quel système capable de dialoguer en série : ordinateur, Arduino, Raspberry Pi, etc. Elle constitue également un bon exercice d'introduction aux notions de firmware, de mémoire flash et de programmation bas-niveau des microcontrôleurs.
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
<svg width="100%" viewBox="0 0 680 400" xmlns="http://www.w3.org/2000/svg" role="img" style="">
|
|
||||||
<title style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Conversion d'images en ligne de commande sous Linux</title>
|
|
||||||
<desc style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Une fenêtre de terminal au centre avec une commande ImageMagick, entourée d'images source à gauche en plusieurs formats et d'images converties à droite, illustrant la transformation par ligne de commande.</desc>
|
|
||||||
|
|
||||||
<defs>
|
|
||||||
|
|
||||||
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
|
|
||||||
<stop offset="0%" stop-color="#fab387"/>
|
|
||||||
<stop offset="100%" stop-color="#f38ba8"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- LEFT SIDE: source images (varied formats, heavier visually) -->
|
|
||||||
<!-- JPG photo: sunset -->
|
|
||||||
<g transform="translate(40, 60)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect width="110" height="80" rx="4" fill="url(#sky)" stroke="#45475a" stroke-width="0.5" style="stroke:rgb(69, 71, 90);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="78" cy="48" r="14" fill="#f9e2af" opacity="0.9" style="fill:rgb(249, 226, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.9;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="62" width="110" height="18" rx="0" fill="#1e3a5f" opacity="0.7" style="fill:rgb(30, 58, 95);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="62" width="110" height="18" fill="none" style="fill:none;stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="80" width="110" height="0" fill="none" style="fill:none;stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- format badge -->
|
|
||||||
<rect x="6" y="64" width="34" height="14" rx="2" fill="#1e1e2e" opacity="0.85" style="fill:rgb(30, 30, 46);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.85;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="23" y="74" text-anchor="middle" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">JPG</text>
|
|
||||||
<text x="55" y="98" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">4032 × 3024</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- PNG: with transparency checkerboard -->
|
|
||||||
<g transform="translate(40, 170)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect width="110" height="80" rx="4" fill="#ffffff" stroke="#45475a" stroke-width="0.5" style="fill:rgb(255, 255, 255);stroke:rgb(69, 71, 90);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<g opacity="0.25" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.25;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="0" y="0" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="22" y="0" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="44" y="0" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="66" y="0" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="88" y="0" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="11" y="10" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="33" y="10" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="55" y="10" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="77" y="10" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="99" y="10" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="20" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="22" y="20" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="44" y="20" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="66" y="20" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="88" y="20" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
<!-- logo shape -->
|
|
||||||
<circle cx="55" cy="40" r="20" fill="#89b4fa" style="fill:rgb(137, 180, 250);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<polygon points="55,28 64,46 46,46" fill="#ffffff" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="62" width="34" height="14" rx="2" fill="#1e1e2e" opacity="0.85" style="fill:rgb(30, 30, 46);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.85;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="23" y="72" text-anchor="middle" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">PNG</text>
|
|
||||||
<text x="55" y="98" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">1920 × 1080</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- CR2: RAW file representation -->
|
|
||||||
<g transform="translate(40, 280)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect width="110" height="80" rx="4" fill="#11111b" stroke="#45475a" stroke-width="0.5" style="fill:rgb(17, 17, 27);stroke:rgb(69, 71, 90);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- film strip perforations -->
|
|
||||||
<rect x="6" y="6" width="6" height="6" fill="#45475a" style="fill:rgb(69, 71, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="20" width="6" height="6" fill="#45475a" style="fill:rgb(69, 71, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="34" width="6" height="6" fill="#45475a" style="fill:rgb(69, 71, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="48" width="6" height="6" fill="#45475a" style="fill:rgb(69, 71, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="98" y="6" width="6" height="6" fill="#45475a" style="fill:rgb(69, 71, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="98" y="20" width="6" height="6" fill="#45475a" style="fill:rgb(69, 71, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="98" y="34" width="6" height="6" fill="#45475a" style="fill:rgb(69, 71, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="98" y="48" width="6" height="6" fill="#45475a" style="fill:rgb(69, 71, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- raw indicator bars -->
|
|
||||||
<rect x="20" y="14" width="70" height="2" fill="#f38ba8" style="fill:rgb(243, 139, 168);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="20" y="22" width="55" height="2" fill="#a6e3a1" style="fill:rgb(166, 227, 161);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="20" y="30" width="62" height="2" fill="#89b4fa" style="fill:rgb(137, 180, 250);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="20" y="38" width="48" height="2" fill="#f9e2af" style="fill:rgb(249, 226, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="20" y="46" width="68" height="2" fill="#cba6f7" style="fill:rgb(203, 166, 247);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="62" width="34" height="14" rx="2" fill="#1e1e2e" opacity="0.95" style="fill:rgb(30, 30, 46);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.95;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="23" y="72" text-anchor="middle" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">CR2</text>
|
|
||||||
<text x="55" y="98" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">RAW 24 MB</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Arrows from sources into the terminal -->
|
|
||||||
<path d="M 158 100 Q 195 100 215 165" fill="none" stroke="#6c7086" stroke-width="1" stroke-dasharray="3 3" style="fill:none;stroke:rgb(108, 112, 134);color:rgb(0, 0, 0);stroke-width:1px;stroke-dasharray:3px, 3px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 158 210 L 215 200" fill="none" stroke="#6c7086" stroke-width="1" stroke-dasharray="3 3" style="fill:none;stroke:rgb(108, 112, 134);color:rgb(0, 0, 0);stroke-width:1px;stroke-dasharray:3px, 3px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 158 320 Q 195 320 215 245" fill="none" stroke="#6c7086" stroke-width="1" stroke-dasharray="3 3" style="fill:none;stroke:rgb(108, 112, 134);color:rgb(0, 0, 0);stroke-width:1px;stroke-dasharray:3px, 3px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<!-- CENTER: terminal window -->
|
|
||||||
<g transform="translate(215, 110)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<!-- window -->
|
|
||||||
<rect width="250" height="200" rx="8" style="fill:rgb(30, 30, 46);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect width="250" height="26" rx="8" style="fill:rgb(49, 50, 68);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect y="18" width="250" height="8" style="fill:rgb(49, 50, 68);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- traffic lights -->
|
|
||||||
<circle cx="14" cy="13" r="5" fill="#f38ba8" style="fill:rgb(243, 139, 168);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="30" cy="13" r="5" fill="#f9e2af" style="fill:rgb(249, 226, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="46" cy="13" r="5" fill="#a6e3a1" style="fill:rgb(166, 227, 161);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="125" y="17" text-anchor="middle" style="font-size: 11px; fill: #6c7086;;fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:11px;font-weight:400;text-anchor:middle;dominant-baseline:auto">cedrix@pve3 — bash</text>
|
|
||||||
|
|
||||||
<!-- terminal content -->
|
|
||||||
<text x="14" y="50" style="fill:rgb(166, 227, 161);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:13px;font-weight:500;text-anchor:start;dominant-baseline:auto">$</text>
|
|
||||||
<text x="28" y="50" style="fill:rgb(249, 226, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:13px;font-weight:400;text-anchor:start;dominant-baseline:auto">magick</text>
|
|
||||||
<text x="80" y="50" style="fill:rgb(137, 180, 250);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:13px;font-weight:400;text-anchor:start;dominant-baseline:auto">photo.jpg</text>
|
|
||||||
<text x="14" y="68" style="fill:rgb(205, 214, 244);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:13px;font-weight:400;text-anchor:start;dominant-baseline:auto"> -resize 1600x1600\></text>
|
|
||||||
<text x="14" y="86" style="fill:rgb(205, 214, 244);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:13px;font-weight:400;text-anchor:start;dominant-baseline:auto"> -strip -quality 82</text>
|
|
||||||
<text x="14" y="104" style="fill:rgb(137, 180, 250);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:13px;font-weight:400;text-anchor:start;dominant-baseline:auto"> web.jpg</text>
|
|
||||||
|
|
||||||
<text x="14" y="130" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto"># conversion en cours...</text>
|
|
||||||
<text x="14" y="148" style="fill:rgb(148, 226, 213);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto">✓ resize: 1600 × 1067</text>
|
|
||||||
<text x="14" y="164" style="fill:rgb(148, 226, 213);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto">✓ exif: stripped</text>
|
|
||||||
<text x="14" y="180" style="fill:rgb(148, 226, 213);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto">✓ size: 4.2 MB → 218 KB</text>
|
|
||||||
|
|
||||||
<text x="14" y="194" style="fill:rgb(166, 227, 161);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:13px;font-weight:500;text-anchor:start;dominant-baseline:auto">$</text>
|
|
||||||
<rect x="26" y="184" width="7" height="12" fill="#cdd6f4" style="fill:rgb(205, 214, 244);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.332727;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<animate attributeName="opacity" values="1;0;1" dur="1.1s" repeatCount="indefinite" style="fill:rgb(205, 214, 244);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</rect>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Arrows from terminal to outputs -->
|
|
||||||
<path d="M 470 170 L 525 110" fill="none" stroke="#a6e3a1" stroke-width="1.2" marker-end="url(#arr-green)" style="fill:none;stroke:rgb(166, 227, 161);color:rgb(0, 0, 0);stroke-width:1.2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 470 210 L 525 210" fill="none" stroke="#a6e3a1" stroke-width="1.2" marker-end="url(#arr-green)" style="fill:none;stroke:rgb(166, 227, 161);color:rgb(0, 0, 0);stroke-width:1.2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 470 250 L 525 310" fill="none" stroke="#a6e3a1" stroke-width="1.2" marker-end="url(#arr-green)" style="fill:none;stroke:rgb(166, 227, 161);color:rgb(0, 0, 0);stroke-width:1.2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<defs>
|
|
||||||
<marker id="arr-green" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
|
||||||
<path d="M2 1L8 5L2 9" fill="none" stroke="#a6e3a1" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</marker>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- RIGHT SIDE: converted outputs (lighter, smaller, optimized) -->
|
|
||||||
<!-- WebP optimized sunset -->
|
|
||||||
<g transform="translate(530, 60)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect width="110" height="80" rx="4" fill="url(#sky)" stroke="#45475a" stroke-width="0.5" style="stroke:rgb(69, 71, 90);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="78" cy="48" r="14" fill="#f9e2af" opacity="0.9" style="fill:rgb(249, 226, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.9;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="62" width="110" height="18" fill="#1e3a5f" opacity="0.7" style="fill:rgb(30, 58, 95);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="64" width="40" height="14" rx="2" fill="#a6e3a1" style="fill:rgb(166, 227, 161);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="26" y="74" text-anchor="middle" style="fill:#11111b;;fill:rgb(17, 17, 27);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">WEBP</text>
|
|
||||||
<text x="55" y="98" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">1600 × 1200</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- JPG web-ready (smaller) -->
|
|
||||||
<g transform="translate(530, 170)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect width="110" height="80" rx="4" fill="#ffffff" stroke="#45475a" stroke-width="0.5" style="fill:rgb(255, 255, 255);stroke:rgb(69, 71, 90);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="55" cy="40" r="20" fill="#89b4fa" style="fill:rgb(137, 180, 250);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<polygon points="55,28 64,46 46,46" fill="#ffffff" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="62" width="40" height="14" rx="2" fill="#a6e3a1" style="fill:rgb(166, 227, 161);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="26" y="72" text-anchor="middle" style="fill:#11111b;;fill:rgb(17, 17, 27);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">JPG</text>
|
|
||||||
<text x="55" y="98" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">1600 × 900</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- JPG developed from RAW -->
|
|
||||||
<g transform="translate(530, 280)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect width="110" height="80" rx="4" fill="#cba6f7" stroke="#45475a" stroke-width="0.5" style="fill:rgb(203, 166, 247);stroke:rgb(69, 71, 90);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="0" width="110" height="40" fill="#f38ba8" opacity="0.6" style="fill:rgb(243, 139, 168);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.6;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="40" width="110" height="22" fill="#fab387" opacity="0.7" style="fill:rgb(250, 179, 135);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="62" width="110" height="18" fill="#1e3a5f" opacity="0.7" style="fill:rgb(30, 58, 95);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="64" width="40" height="14" rx="2" fill="#a6e3a1" style="fill:rgb(166, 227, 161);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="26" y="72" text-anchor="middle" style="fill:#11111b;;fill:rgb(17, 17, 27);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">JPG</text>
|
|
||||||
<text x="55" y="98" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">2048 × 1365</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- column labels -->
|
|
||||||
<text x="95" y="40" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">SOURCES</text>
|
|
||||||
<text x="340" y="100" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">TERMINAL</text>
|
|
||||||
<text x="585" y="40" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">SORTIES</text>
|
|
||||||
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 38 KiB |
@@ -1,162 +0,0 @@
|
|||||||
<svg width="100%" viewBox="0 0 680 400" xmlns="http://www.w3.org/2000/svg" role="img" style="">
|
|
||||||
<title style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Conversion d'images en ligne de commande sous Linux</title>
|
|
||||||
<desc style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Une fenêtre de terminal au centre avec une commande ImageMagick, entourée d'images source à gauche en plusieurs formats et d'images converties à droite, illustrant la transformation par ligne de commande.</desc>
|
|
||||||
|
|
||||||
<defs>
|
|
||||||
|
|
||||||
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
|
|
||||||
<stop offset="0%" stop-color="#fab387"/>
|
|
||||||
<stop offset="100%" stop-color="#f38ba8"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- LEFT SIDE: source images (varied formats, heavier visually) -->
|
|
||||||
<!-- JPG photo: sunset -->
|
|
||||||
<g transform="translate(40, 60)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect width="110" height="80" rx="4" fill="url(#sky)" stroke="#45475a" stroke-width="0.5" style="stroke:rgb(69, 71, 90);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="78" cy="48" r="14" fill="#f9e2af" opacity="0.9" style="fill:rgb(249, 226, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.9;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="62" width="110" height="18" rx="0" fill="#1e3a5f" opacity="0.7" style="fill:rgb(30, 58, 95);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="62" width="110" height="18" fill="none" style="fill:none;stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="80" width="110" height="0" fill="none" style="fill:none;stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- format badge -->
|
|
||||||
<rect x="6" y="64" width="34" height="14" rx="2" fill="#1e1e2e" opacity="0.85" style="fill:rgb(30, 30, 46);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.85;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="23" y="74" text-anchor="middle" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">JPG</text>
|
|
||||||
<text x="55" y="98" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">4032 × 3024</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- PNG: with transparency checkerboard -->
|
|
||||||
<g transform="translate(40, 170)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect width="110" height="80" rx="4" fill="#ffffff" stroke="#45475a" stroke-width="0.5" style="fill:rgb(255, 255, 255);stroke:rgb(69, 71, 90);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<g opacity="0.25" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.25;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="0" y="0" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="22" y="0" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="44" y="0" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="66" y="0" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="88" y="0" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="11" y="10" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="33" y="10" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="55" y="10" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="77" y="10" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="99" y="10" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="20" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="22" y="20" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="44" y="20" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="66" y="20" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="88" y="20" width="11" height="10" fill="#9ca3af" style="fill:rgb(156, 163, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
<!-- logo shape -->
|
|
||||||
<circle cx="55" cy="40" r="20" fill="#89b4fa" style="fill:rgb(137, 180, 250);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<polygon points="55,28 64,46 46,46" fill="#ffffff" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="62" width="34" height="14" rx="2" fill="#1e1e2e" opacity="0.85" style="fill:rgb(30, 30, 46);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.85;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="23" y="72" text-anchor="middle" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">PNG</text>
|
|
||||||
<text x="55" y="98" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">1920 × 1080</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- CR2: RAW file representation -->
|
|
||||||
<g transform="translate(40, 280)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect width="110" height="80" rx="4" fill="#11111b" stroke="#45475a" stroke-width="0.5" style="fill:rgb(17, 17, 27);stroke:rgb(69, 71, 90);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- film strip perforations -->
|
|
||||||
<rect x="6" y="6" width="6" height="6" fill="#45475a" style="fill:rgb(69, 71, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="20" width="6" height="6" fill="#45475a" style="fill:rgb(69, 71, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="34" width="6" height="6" fill="#45475a" style="fill:rgb(69, 71, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="48" width="6" height="6" fill="#45475a" style="fill:rgb(69, 71, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="98" y="6" width="6" height="6" fill="#45475a" style="fill:rgb(69, 71, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="98" y="20" width="6" height="6" fill="#45475a" style="fill:rgb(69, 71, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="98" y="34" width="6" height="6" fill="#45475a" style="fill:rgb(69, 71, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="98" y="48" width="6" height="6" fill="#45475a" style="fill:rgb(69, 71, 90);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- raw indicator bars -->
|
|
||||||
<rect x="20" y="14" width="70" height="2" fill="#f38ba8" style="fill:rgb(243, 139, 168);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="20" y="22" width="55" height="2" fill="#a6e3a1" style="fill:rgb(166, 227, 161);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="20" y="30" width="62" height="2" fill="#89b4fa" style="fill:rgb(137, 180, 250);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="20" y="38" width="48" height="2" fill="#f9e2af" style="fill:rgb(249, 226, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="20" y="46" width="68" height="2" fill="#cba6f7" style="fill:rgb(203, 166, 247);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="62" width="34" height="14" rx="2" fill="#1e1e2e" opacity="0.95" style="fill:rgb(30, 30, 46);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.95;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="23" y="72" text-anchor="middle" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">CR2</text>
|
|
||||||
<text x="55" y="98" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">RAW 24 MB</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Arrows from sources into the terminal -->
|
|
||||||
<path d="M 158 100 Q 195 100 215 165" fill="none" stroke="#6c7086" stroke-width="1" stroke-dasharray="3 3" style="fill:none;stroke:rgb(108, 112, 134);color:rgb(0, 0, 0);stroke-width:1px;stroke-dasharray:3px, 3px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 158 210 L 215 200" fill="none" stroke="#6c7086" stroke-width="1" stroke-dasharray="3 3" style="fill:none;stroke:rgb(108, 112, 134);color:rgb(0, 0, 0);stroke-width:1px;stroke-dasharray:3px, 3px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 158 320 Q 195 320 215 245" fill="none" stroke="#6c7086" stroke-width="1" stroke-dasharray="3 3" style="fill:none;stroke:rgb(108, 112, 134);color:rgb(0, 0, 0);stroke-width:1px;stroke-dasharray:3px, 3px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<!-- CENTER: terminal window -->
|
|
||||||
<g transform="translate(215, 110)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<!-- window -->
|
|
||||||
<rect width="250" height="200" rx="8" style="fill:rgb(30, 30, 46);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect width="250" height="26" rx="8" style="fill:rgb(49, 50, 68);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect y="18" width="250" height="8" style="fill:rgb(49, 50, 68);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<!-- traffic lights -->
|
|
||||||
<circle cx="14" cy="13" r="5" fill="#f38ba8" style="fill:rgb(243, 139, 168);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="30" cy="13" r="5" fill="#f9e2af" style="fill:rgb(249, 226, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="46" cy="13" r="5" fill="#a6e3a1" style="fill:rgb(166, 227, 161);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="125" y="17" text-anchor="middle" style="font-size: 11px; fill: #6c7086;;fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:11px;font-weight:400;text-anchor:middle;dominant-baseline:auto">cedrix@pve3 — bash</text>
|
|
||||||
|
|
||||||
<!-- terminal content -->
|
|
||||||
<text x="14" y="50" style="fill:rgb(166, 227, 161);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:13px;font-weight:500;text-anchor:start;dominant-baseline:auto">$</text>
|
|
||||||
<text x="28" y="50" style="fill:rgb(249, 226, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:13px;font-weight:400;text-anchor:start;dominant-baseline:auto">magick</text>
|
|
||||||
<text x="80" y="50" style="fill:rgb(137, 180, 250);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:13px;font-weight:400;text-anchor:start;dominant-baseline:auto">photo.jpg</text>
|
|
||||||
<text x="14" y="68" style="fill:rgb(205, 214, 244);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:13px;font-weight:400;text-anchor:start;dominant-baseline:auto"> -resize 1600x1600\></text>
|
|
||||||
<text x="14" y="86" style="fill:rgb(205, 214, 244);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:13px;font-weight:400;text-anchor:start;dominant-baseline:auto"> -strip -quality 82</text>
|
|
||||||
<text x="14" y="104" style="fill:rgb(137, 180, 250);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:13px;font-weight:400;text-anchor:start;dominant-baseline:auto"> web.jpg</text>
|
|
||||||
|
|
||||||
<text x="14" y="130" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto"># conversion en cours...</text>
|
|
||||||
<text x="14" y="148" style="fill:rgb(148, 226, 213);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto">✓ resize: 1600 × 1067</text>
|
|
||||||
<text x="14" y="164" style="fill:rgb(148, 226, 213);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto">✓ exif: stripped</text>
|
|
||||||
<text x="14" y="180" style="fill:rgb(148, 226, 213);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:12px;font-weight:400;text-anchor:start;dominant-baseline:auto">✓ size: 4.2 MB → 218 KB</text>
|
|
||||||
|
|
||||||
<text x="14" y="194" style="fill:rgb(166, 227, 161);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;font-size:13px;font-weight:500;text-anchor:start;dominant-baseline:auto">$</text>
|
|
||||||
<rect x="26" y="184" width="7" height="12" fill="#cdd6f4" style="fill:rgb(205, 214, 244);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.332727;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<animate attributeName="opacity" values="1;0;1" dur="1.1s" repeatCount="indefinite" style="fill:rgb(205, 214, 244);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</rect>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Arrows from terminal to outputs -->
|
|
||||||
<path d="M 470 170 L 525 110" fill="none" stroke="#a6e3a1" stroke-width="1.2" marker-end="url(#arr-green)" style="fill:none;stroke:rgb(166, 227, 161);color:rgb(0, 0, 0);stroke-width:1.2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 470 210 L 525 210" fill="none" stroke="#a6e3a1" stroke-width="1.2" marker-end="url(#arr-green)" style="fill:none;stroke:rgb(166, 227, 161);color:rgb(0, 0, 0);stroke-width:1.2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 470 250 L 525 310" fill="none" stroke="#a6e3a1" stroke-width="1.2" marker-end="url(#arr-green)" style="fill:none;stroke:rgb(166, 227, 161);color:rgb(0, 0, 0);stroke-width:1.2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<defs>
|
|
||||||
<marker id="arr-green" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
|
||||||
<path d="M2 1L8 5L2 9" fill="none" stroke="#a6e3a1" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</marker>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- RIGHT SIDE: converted outputs (lighter, smaller, optimized) -->
|
|
||||||
<!-- WebP optimized sunset -->
|
|
||||||
<g transform="translate(530, 60)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect width="110" height="80" rx="4" fill="url(#sky)" stroke="#45475a" stroke-width="0.5" style="stroke:rgb(69, 71, 90);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="78" cy="48" r="14" fill="#f9e2af" opacity="0.9" style="fill:rgb(249, 226, 175);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.9;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="62" width="110" height="18" fill="#1e3a5f" opacity="0.7" style="fill:rgb(30, 58, 95);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="64" width="40" height="14" rx="2" fill="#a6e3a1" style="fill:rgb(166, 227, 161);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="26" y="74" text-anchor="middle" style="fill:#11111b;;fill:rgb(17, 17, 27);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">WEBP</text>
|
|
||||||
<text x="55" y="98" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">1600 × 1200</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- JPG web-ready (smaller) -->
|
|
||||||
<g transform="translate(530, 170)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect width="110" height="80" rx="4" fill="#ffffff" stroke="#45475a" stroke-width="0.5" style="fill:rgb(255, 255, 255);stroke:rgb(69, 71, 90);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<circle cx="55" cy="40" r="20" fill="#89b4fa" style="fill:rgb(137, 180, 250);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<polygon points="55,28 64,46 46,46" fill="#ffffff" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="62" width="40" height="14" rx="2" fill="#a6e3a1" style="fill:rgb(166, 227, 161);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="26" y="72" text-anchor="middle" style="fill:#11111b;;fill:rgb(17, 17, 27);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">JPG</text>
|
|
||||||
<text x="55" y="98" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">1600 × 900</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- JPG developed from RAW -->
|
|
||||||
<g transform="translate(530, 280)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect width="110" height="80" rx="4" fill="#cba6f7" stroke="#45475a" stroke-width="0.5" style="fill:rgb(203, 166, 247);stroke:rgb(69, 71, 90);color:rgb(0, 0, 0);stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="0" width="110" height="40" fill="#f38ba8" opacity="0.6" style="fill:rgb(243, 139, 168);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.6;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="40" width="110" height="22" fill="#fab387" opacity="0.7" style="fill:rgb(250, 179, 135);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="0" y="62" width="110" height="18" fill="#1e3a5f" opacity="0.7" style="fill:rgb(30, 58, 95);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="6" y="64" width="40" height="14" rx="2" fill="#a6e3a1" style="fill:rgb(166, 227, 161);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="26" y="72" text-anchor="middle" style="fill:#11111b;;fill:rgb(17, 17, 27);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">JPG</text>
|
|
||||||
<text x="55" y="98" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">2048 × 1365</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- column labels -->
|
|
||||||
<text x="95" y="40" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">SOURCES</text>
|
|
||||||
<text x="340" y="100" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">TERMINAL</text>
|
|
||||||
<text x="585" y="40" text-anchor="middle" style="fill:rgb(108, 112, 134);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", system-ui, sans-serif;font-size:11px;font-weight:500;text-anchor:middle;dominant-baseline:auto">SORTIES</text>
|
|
||||||
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 38 KiB |
@@ -1,179 +0,0 @@
|
|||||||
# Convertir des images en ligne de commande sous Linux
|
|
||||||
|
|
||||||
La manipulation d'images depuis le terminal est une de ces choses qu'on apprend une fois et qu'on utilise pour toujours. Pas besoin de GIMP, pas besoin d'ouvrir quoi que ce soit : une commande, et c'est réglé.
|
|
||||||
|
|
||||||
Voici les outils que j'utilise concrètement, et dans quels cas.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ImageMagick, le plus polyvalent
|
|
||||||
|
|
||||||
C'est l'outil de base. Il gère à peu près tous les formats qui existent, et la syntaxe est toujours la même. L'installation est classique :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install imagemagick
|
|
||||||
```
|
|
||||||
|
|
||||||
Convertir un format :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg image.png
|
|
||||||
```
|
|
||||||
|
|
||||||
Redimensionner sans toucher au ratio, en posant une limite maximale :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize 1920x1920\> sortie.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Le `\>` est important — sans lui, ImageMagick agrandit aussi les petites images. Avec, il ne fait que réduire.
|
|
||||||
|
|
||||||
Préparer une image pour le web, en supprimant les métadonnées EXIF et en compressant :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -strip -quality 82 image_web.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Pour traiter un répertoire entier, `mogrify` fait le même boulot mais **modifie les fichiers en place** — toujours travailler sur une copie ou rediriger vers un autre dossier :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mogrify -path ./web -resize 1600x1600\> -quality 85 *.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## libvips, pour les traitements lourds
|
|
||||||
|
|
||||||
Quand il y a des centaines de photos ou des images très lourdes (scans, RAW exportés), libvips est nettement plus rapide et utilise beaucoup moins de mémoire qu'ImageMagick. Il charge les images en flux au lieu de tout mettre en RAM.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install libvips-tools
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
vips resize input.jpg output.jpg 0.5 # diviser la taille par 2
|
|
||||||
vips copy input.png output.webp # conversion de format
|
|
||||||
```
|
|
||||||
|
|
||||||
La syntaxe est moins intuitive qu'ImageMagick mais les gains sur des gros volumes sont sensibles.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## FFmpeg, pas que pour la vidéo
|
|
||||||
|
|
||||||
FFmpeg est surtout connu pour la vidéo, mais il convertit les images aussi — utile quand il est déjà installé et qu'on veut éviter une dépendance supplémentaire, ou pour extraire des frames depuis une vidéo :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ffmpeg -i video.mp4 frame_%04d.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Redimensionner en conservant le ratio :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ffmpeg -i input.jpg -vf scale=1280:-1 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Les métadonnées EXIF avec exiftool
|
|
||||||
|
|
||||||
Les appareils photo embarquent beaucoup d'informations dans les fichiers : coordonnées GPS, modèle d'appareil, réglages. Avant de publier une photo, il vaut mieux vérifier ce qu'elle contient :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install libimage-exiftool-perl
|
|
||||||
|
|
||||||
exiftool photo.jpg # lire toutes les métadonnées
|
|
||||||
exiftool -all= photo.jpg # tout supprimer
|
|
||||||
exiftool -TagsFromFile src.jpg dst.jpg # copier les métadonnées d'une image à une autre
|
|
||||||
```
|
|
||||||
|
|
||||||
ImageMagick peut aussi supprimer les EXIF avec `-strip`, mais exiftool offre plus de contrôle quand on veut garder certaines balises et supprimer d'autres.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Fichiers RAW
|
|
||||||
|
|
||||||
Pour les CR2, NEF, ARW et autres formats propriétaires d'appareils photo, `darktable-cli` est la solution la plus propre :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
darktable-cli input.CR2 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Il applique les mêmes algorithmes de développement que l'interface graphique de darktable. `dcraw` est une alternative plus ancienne et plus bas niveau :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
dcraw -c photo.CR2 > photo.ppm
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## HEIC, le format Apple
|
|
||||||
|
|
||||||
Les iPhone exportent leurs photos en HEIC depuis iOS 11. Le format est compact, mais Linux ne le gère pas nativement — il faut convertir avant de pouvoir travailler dessus.
|
|
||||||
|
|
||||||
Le paquet `libheif-examples` fournit `heif-convert`, l'outil le plus direct :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install libheif-examples
|
|
||||||
|
|
||||||
heif-convert photo.heic photo.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
ImageMagick peut aussi s'en charger si `libheif` est installé sur le système :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick photo.heic photo.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Pour vérifier que le support HEIC est bien disponible :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick identify -list format | grep -i heic
|
|
||||||
```
|
|
||||||
|
|
||||||
Conversion d'un dossier entier :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
for f in *.heic *.HEIC; do
|
|
||||||
heif-convert "$f" "${f%.*}.jpg"
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## En pratique
|
|
||||||
|
|
||||||
Conversion d'un dossier de PNG en WebP :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
for f in *.png; do
|
|
||||||
magick "$f" "${f%.png}.webp"
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
Pipeline complet pour publication web — redimensionnement, suppression EXIF, compression :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick input.jpg -resize 1600x1600\> -strip -quality 80 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Traitement récursif sur un arbre de dossiers :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
find . -name "*.jpg" -exec magick {} -resize 1200x1200\> {} \;
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Récap rapide
|
|
||||||
|
|
||||||
| Cas d'usage | Outil |
|
|
||||||
|---|---|
|
|
||||||
| Usage général | ImageMagick |
|
|
||||||
| Gros volumes / performance | libvips |
|
|
||||||
| Déjà dans le pipeline vidéo | FFmpeg |
|
|
||||||
| Fichiers RAW | darktable-cli |
|
|
||||||
| HEIC (iPhone) | heif-convert ou ImageMagick |
|
|
||||||
| Lecture / nettoyage EXIF | exiftool |
|
|
||||||
|
|
||||||
Pour 90 % des besoins courants, ImageMagick suffit. libvips vaut le coup d'être appris si on traite régulièrement des lots importants.
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "46f2f084-8dd8-497a-aef0-4728367ce753",
|
|
||||||
"slug": "convertir-des-images-en-ligne-de-commande-sous-linux",
|
|
||||||
"title": "Convertir des images en ligne de commande sous Linux",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2025-12-28 14:54",
|
|
||||||
"created_at": "2025-12-28 14:54:41",
|
|
||||||
"updated_at": "2026-05-12 00:51:02",
|
|
||||||
"revisions": [
|
|
||||||
{
|
|
||||||
"n": 1,
|
|
||||||
"date": "2026-05-12 00:51:02",
|
|
||||||
"comment": "",
|
|
||||||
"title": "Convertir des images en ligne de commande sous Linux"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cover": "cover.svg",
|
|
||||||
"files_meta": {
|
|
||||||
"illustration_conversion_images_cli_linux.svg": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"external_links": [],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "",
|
|
||||||
"og_image": "",
|
|
||||||
"category": "linux"
|
|
||||||
}
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
La manipulation d'images depuis le terminal est une de ces choses qu'on apprend une fois et qu'on utilise pour toujours. Pas besoin de GIMP, pas besoin d'ouvrir quoi que ce soit : une commande, et c'est réglé.
|
|
||||||
|
|
||||||
Voici les outils que j'utilise concrètement, et dans quels cas.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ImageMagick, le plus polyvalent
|
|
||||||
|
|
||||||
C'est l'outil de base. Il gère à peu près tous les formats qui existent, et la syntaxe est toujours la même. L'installation est classique :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install imagemagick
|
|
||||||
```
|
|
||||||
|
|
||||||
Convertir un format :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg image.png
|
|
||||||
```
|
|
||||||
|
|
||||||
Redimensionner sans toucher au ratio, en posant une limite maximale :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -resize 1920x1920\> sortie.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Le `\>` est important — sans lui, ImageMagick agrandit aussi les petites images. Avec, il ne fait que réduire.
|
|
||||||
|
|
||||||
Préparer une image pour le web, en supprimant les métadonnées EXIF et en compressant :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick image.jpg -strip -quality 82 image_web.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Pour traiter un répertoire entier, `mogrify` fait le même boulot mais **modifie les fichiers en place** — toujours travailler sur une copie ou rediriger vers un autre dossier :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mogrify -path ./web -resize 1600x1600\> -quality 85 *.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## libvips, pour les traitements lourds
|
|
||||||
|
|
||||||
Quand il y a des centaines de photos ou des images très lourdes (scans, RAW exportés), libvips est nettement plus rapide et utilise beaucoup moins de mémoire qu'ImageMagick. Il charge les images en flux au lieu de tout mettre en RAM.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install libvips-tools
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
vips resize input.jpg output.jpg 0.5 # diviser la taille par 2
|
|
||||||
vips copy input.png output.webp # conversion de format
|
|
||||||
```
|
|
||||||
|
|
||||||
La syntaxe est moins intuitive qu'ImageMagick mais les gains sur des gros volumes sont sensibles.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## FFmpeg, pas que pour la vidéo
|
|
||||||
|
|
||||||
FFmpeg est surtout connu pour la vidéo, mais il convertit les images aussi — utile quand il est déjà installé et qu'on veut éviter une dépendance supplémentaire, ou pour extraire des frames depuis une vidéo :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ffmpeg -i video.mp4 frame_%04d.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Redimensionner en conservant le ratio :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ffmpeg -i input.jpg -vf scale=1280:-1 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Les métadonnées EXIF avec exiftool
|
|
||||||
|
|
||||||
Les appareils photo embarquent beaucoup d'informations dans les fichiers : coordonnées GPS, modèle d'appareil, réglages. Avant de publier une photo, il vaut mieux vérifier ce qu'elle contient :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install libimage-exiftool-perl
|
|
||||||
|
|
||||||
exiftool photo.jpg # lire toutes les métadonnées
|
|
||||||
exiftool -all= photo.jpg # tout supprimer
|
|
||||||
exiftool -TagsFromFile src.jpg dst.jpg # copier les métadonnées d'une image à une autre
|
|
||||||
```
|
|
||||||
|
|
||||||
ImageMagick peut aussi supprimer les EXIF avec `-strip`, mais exiftool offre plus de contrôle quand on veut garder certaines balises et supprimer d'autres.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Fichiers RAW
|
|
||||||
|
|
||||||
Pour les CR2, NEF, ARW et autres formats propriétaires d'appareils photo, `darktable-cli` est la solution la plus propre :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
darktable-cli input.CR2 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Il applique les mêmes algorithmes de développement que l'interface graphique de darktable. `dcraw` est une alternative plus ancienne et plus bas niveau :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
dcraw -c photo.CR2 > photo.ppm
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## En pratique
|
|
||||||
|
|
||||||
Conversion d'un dossier de PNG en WebP :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
for f in *.png; do
|
|
||||||
magick "$f" "${f%.png}.webp"
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
Pipeline complet pour publication web — redimensionnement, suppression EXIF, compression :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
magick input.jpg -resize 1600x1600\> -strip -quality 80 output.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
Traitement récursif sur un arbre de dossiers :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
find . -name "*.jpg" -exec magick {} -resize 1200x1200\> {} \;
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Récap rapide
|
|
||||||
|
|
||||||
| Cas d'usage | Outil |
|
|
||||||
|---|---|
|
|
||||||
| Usage général | ImageMagick |
|
|
||||||
| Gros volumes / performance | libvips |
|
|
||||||
| Déjà dans le pipeline vidéo | FFmpeg |
|
|
||||||
| Fichiers RAW | darktable-cli |
|
|
||||||
| Lecture / nettoyage EXIF | exiftool |
|
|
||||||
|
|
||||||
Pour 90 % des besoins courants, ImageMagick suffit. libvips vaut le coup d'être appris si on traite régulièrement des lots importants.
|
|
||||||
|
Before Width: | Height: | Size: 161 KiB |
@@ -1,122 +0,0 @@
|
|||||||
# JDownloader : quand le CMS devient la faille
|
|
||||||
|
|
||||||
*À propos de l'incident de sécurité affectant le site officiel jdownloader.org les 6 et 7 mai 2026.*
|
|
||||||
|
|
||||||
## JDownloader expliqué simplement
|
|
||||||
|
|
||||||
JDownloader est un gestionnaire de téléchargements écrit en Java, distribué gratuitement par l'éditeur allemand AppWork GmbH depuis plus de quinze ans. Il sert essentiellement à automatiser la récupération de fichiers depuis des hébergeurs (Mega, Uptobox, Rapidgator…), des plateformes vidéo et des services de liens premium. Côté utilisateur, c'est l'outil qu'on lance pour récupérer une série de fichiers en une opération, plutôt que cliquer sur cent liens un par un. L'application est multiplateforme — Windows, Linux, macOS — et tourne sur quelques millions de postes dans le monde.
|
|
||||||
|
|
||||||
Le projet est distribué de plusieurs façons : un JAR principal (le binaire Java pur), des installateurs natifs par OS depuis le site officiel, et des paquets passant par des canaux distribués comme Flatpak, Snap ou Winget. C'est cette diversité de canaux qui va jouer un rôle central dans ce qui suit.
|
|
||||||
|
|
||||||
## L'incident : ce qui s'est passé
|
|
||||||
|
|
||||||
Entre le 6 mai 2026 à 00 h 01 UTC et le 7 mai 2026 à 17 h 06 UTC, le site officiel `jdownloader.org` a distribué des installateurs piégés à la place des binaires légitimes. La fenêtre n'a duré qu'environ 48 heures, et seuls deux liens ont été affectés. Mais pendant cette fenêtre, tout utilisateur qui passait par le bon parcours téléchargeait un cheval de Troie au lieu d'un gestionnaire de téléchargements.
|
|
||||||
|
|
||||||
Le scénario reconstitué par l'équipe d'AppWork et les chercheurs en sécurité (BleepingComputer, Thomas Klemenc, équipe Rescana) se déroule en quatre temps.
|
|
||||||
|
|
||||||
**1. Compromission du CMS du site.** Les attaquants ont exploité une vulnérabilité non corrigée dans le système de gestion de contenu de `jdownloader.org`, qui permettait de modifier les listes de contrôle d'accès et le contenu publié sans authentification. Point crucial : ils n'ont **pas** eu accès au serveur sous-jacent, ni au système de fichiers, ni à l'infrastructure de build. Juste au contenu web — et ça a suffi.
|
|
||||||
|
|
||||||
**2. Réécriture de deux liens.** Plutôt que de tenter de modifier les binaires originaux (qui étaient hors de leur portée), les attaquants ont fait beaucoup plus simple : ils ont changé l'URL cible de deux liens publics sur la page de téléchargement. Le lien « Download Alternative Installer » pour Windows et le script shell pour Linux pointaient désormais vers des fichiers malveillants hébergés sur une infrastructure tierce. Le HTML autour, lui, restait identique. Visuellement, rien ne distinguait la page propre de la page piégée.
|
|
||||||
|
|
||||||
**3. Charges utiles différenciées par plateforme.** Sur Windows, l'installateur piégé agit comme un *loader* qui déploie un RAT (*Remote Access Trojan*) écrit en Python, fortement obfusqué, communiquant avec deux serveurs de commande et contrôle (`parkspringshotel[.]com` et `auraguest[.]lk`). Le RAT est modulaire : il reçoit du code Python depuis le C2 et l'exécute, ce qui donne aux attaquants une porte ouverte indéfiniment extensible. Sur Linux, le script shell modifié télécharge une archive depuis `checkinnhotels[.]com`, déguisée en fichier SVG, dont il extrait deux binaires ELF — `pkg` et `systemd-exec`. Le second est installé en SUID-root dans `/usr/bin/`, le premier est copié dans `/root/.local/share/.pkg`, et la persistance est assurée par un script déposé dans `/etc/profile.d/systemd.sh`. Le malware se lance ensuite déguisé en `/usr/libexec/upowerd`, un nom de processus qui existe légitimement sur la plupart des distributions.
|
|
||||||
|
|
||||||
**4. Détection par la communauté.** L'alerte n'est pas venue d'un système de surveillance, mais d'un utilisateur Reddit (*PrinceOfNightSky*) qui a remarqué que Microsoft Defender bloquait son installateur fraîchement téléchargé. La signature numérique indiquait « Zipline LLC » au lieu de « AppWork GmbH ». L'équipe AppWork a confirmé, mis le site hors ligne pour investigation dans les heures qui ont suivi, puis publié un rapport d'incident détaillé. Le site est revenu en ligne dans la nuit du 8 au 9 mai avec des liens vérifiés.
|
|
||||||
|
|
||||||
## Pourquoi c'est systémique
|
|
||||||
|
|
||||||
À première vue, c'est un incident isolé : une faille CMS, une équipe qui patche, le service revient. Vu de loin, ça ressemble à un mauvais quart d'heure. Mais en prenant un peu de recul, le schéma est troublant.
|
|
||||||
|
|
||||||
**CPUID, début avril 2026.** Le site officiel de l'éditeur de CPU-Z est compromis, des installateurs piégés sont distribués pendant plusieurs jours.
|
|
||||||
|
|
||||||
**DAEMON Tools, début mai 2026.** Même schéma : compromission du site officiel, substitution d'installateurs, plusieurs versions infectées (12.5.0.2421 à 12.5.0.2434) distribuées avant détection.
|
|
||||||
|
|
||||||
**JDownloader, 6–7 mai 2026.** Toujours le même schéma.
|
|
||||||
|
|
||||||
Trois compromissions d'éditeurs logiciels en cinq semaines, exactement sur le même schéma : pas d'intrusion dans l'infrastructure de build, pas de modification du code source, pas de vol de certificat de signature de l'éditeur. À chaque fois, **le maillon faible est le CMS qui sert la page de téléchargement**. Ce qu'on attaque, ce n'est pas le logiciel ; c'est le panneau publicitaire qui pointe vers le logiciel.
|
|
||||||
|
|
||||||
Cette mécanique est intéressante parce qu'elle déjoue à peu près toutes les défenses « modernes » de la chaîne d'approvisionnement.
|
|
||||||
|
|
||||||
**La signature de code ne protège pas.** Le binaire légitime de JDownloader est toujours signé proprement par AppWork GmbH. Mais le binaire malveillant servi à sa place est signé, lui aussi — par un autre éditeur (Zipline LLC, The Water Team), avec des certificats vraisemblablement volés ou achetés au marché noir. La signature certifie que le fichier vient bien de celui qui l'a signé ; elle ne certifie pas que c'est le bon fichier.
|
|
||||||
|
|
||||||
**Le HTTPS ne protège pas.** La page de téléchargement est servie en HTTPS valide, depuis le bon domaine, avec le bon certificat. Le navigateur n'a aucune raison de tiquer.
|
|
||||||
|
|
||||||
**Les mises à jour in-app sont, elles, protégées.** AppWork le souligne : chaque mise à jour livrée par le mécanisme intégré de JDownloader est signée RSA et vérifiée cryptographiquement, indépendamment du site web. Ce canal n'a pas été touché. C'est tout le paradoxe : les utilisateurs qui mettaient à jour JDownloader depuis l'application elle-même n'ont rien risqué ; ceux qui sont allés sur le site officiel pour le télécharger « proprement » sont les seuls exposés.
|
|
||||||
|
|
||||||
**Les paquets distribués sont protégés.** Flatpak, Snap, Winget, le JAR principal — tout ce qui passe par une chaîne d'approvisionnement où l'intégrité est vérifiée par checksum hors site est resté propre. AppWork le résume sans détour : *« Winget/Flatpak/Snap infra is outside of our reach — files downloaded by those are hosted on other infra and secured by sha256 checksums that are unchanged. »*
|
|
||||||
|
|
||||||
Autrement dit, **plus le canal est court et naïf, plus il est vulnérable**. Le téléchargement direct depuis un site web est le canal le plus naïf qui soit : on fait confiance au CMS, point. Tout l'effort de sécurisation de l'écosystème logiciel — signatures, builds reproductibles, SBOMs, attestations de provenance — porte sur la chaîne de production et la chaîne de distribution centralisée. Le maillon « page HTML qui dit *clique ici* », lui, est resté tel qu'il était en 2005.
|
|
||||||
|
|
||||||
## Comment vérifier si on est touché
|
|
||||||
|
|
||||||
La fenêtre est étroite, donc le filtrage est simple. Trois questions, dans l'ordre :
|
|
||||||
|
|
||||||
1. L'installateur a-t-il été récupéré entre le **6 mai 2026 (00 h 01 UTC)** et le **7 mai 2026 (17 h 06 UTC)** ?
|
|
||||||
2. S'agit-il du lien **« Download Alternative Installer » Windows** ou du **script shell Linux** depuis `jdownloader.org` ?
|
|
||||||
3. Le fichier a-t-il été **exécuté** ?
|
|
||||||
|
|
||||||
Trois *oui* → traiter la machine comme compromise. N'importe quel *non* dans la liste → aucun risque lié à cet incident. Une installation pré-existante mise à jour automatiquement, un paquet Flatpak/Snap/Winget, le JAR, la version macOS : rien à craindre.
|
|
||||||
|
|
||||||
### Sous Windows
|
|
||||||
|
|
||||||
Le contrôle de référence, c'est la signature numérique. Clic droit sur l'installateur → **Propriétés** → onglet **Signatures numériques**. La valeur attendue est `AppWork GmbH`. Toute autre signature (notamment *Zipline LLC* ou *The Water Team*), ou l'absence de signature, désigne un fichier malveillant.
|
|
||||||
|
|
||||||
En PowerShell :
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
Get-AuthenticodeSignature "C:\chemin\vers\JDownloader2Setup.exe" |
|
|
||||||
Select-Object Status, SignerCertificate
|
|
||||||
```
|
|
||||||
|
|
||||||
Si `Status` n'est pas `Valid` ou si le certificat ne contient pas `AppWork GmbH`, ne pas exécuter.
|
|
||||||
|
|
||||||
### Sous Linux
|
|
||||||
|
|
||||||
Trois artefacts sont à chercher en post-exécution :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo ls -la /usr/bin/systemd-exec # SUID-root illégitime
|
|
||||||
sudo ls -la /root/.local/share/.pkg # charge principale obfusquée
|
|
||||||
sudo ls -la /etc/profile.d/systemd.sh # script de persistance
|
|
||||||
```
|
|
||||||
|
|
||||||
L'apparition de l'un de ces trois éléments suffit à confirmer l'infection. Pour aller plus loin, un coup d'œil au trafic sortant vers les trois domaines C2 (`parkspringshotel[.]com`, `auraguest[.]lk`, `checkinnhotels[.]com`) dans les logs DNS ou via `journalctl --since "2026-05-06"` permet de confirmer l'activité du malware.
|
|
||||||
|
|
||||||
Si le script installateur traîne encore quelque part, sa signature est sans ambiguïté : taille de **7 934 496 octets**, SHA-256 commençant par `6d975c05ef`.
|
|
||||||
|
|
||||||
### En cas de compromission confirmée
|
|
||||||
|
|
||||||
La position officielle d'AppWork est sans nuance : **réinstallation complète du système**. Un RAT modulaire avec persistance SUID-root et exécution arbitraire de code Python depuis un C2 n'est pas quelque chose qu'on retire avec un antivirus. Il faut considérer que tout secret qui a transité sur la machine est compromis — mots de passe saisis au clavier, clés SSH, jetons API, cookies de session, configurations cloud — et les faire tous tourner après réinstallation, **depuis une autre machine saine**.
|
|
||||||
|
|
||||||
## Ce que ça change pour qui s'auto-héberge
|
|
||||||
|
|
||||||
L'incident JDownloader est un exemple éclairant pour qui exploite ses propres services exposés sur Internet — un Forgejo, un reverse proxy, un site personnel. La leçon n'est pas vraiment côté utilisateur (la procédure de détection plus haut suffit), elle est côté **opérateur**.
|
|
||||||
|
|
||||||
Le CMS de JDownloader n'a probablement pas été ciblé pour ses qualités intrinsèques. C'est un dommage collatéral d'un schéma plus large : tout site qui distribue un binaire avec un nombre significatif d'utilisateurs devient une cible rentable, et le CMS public est souvent la pièce la moins surveillée du dispositif. On sécurise le serveur Git, le pipeline de build, la signature des paquets — et on laisse tourner un CMS qui n'a pas été patché depuis huit mois parce qu'il « ne sert qu'à afficher la page d'accueil ».
|
|
||||||
|
|
||||||
Quelques principes opérationnels qui en découlent.
|
|
||||||
|
|
||||||
**Séparer le canal de publication du canal de vérification.** AppWork a eu raison sur un point essentiel : leurs mises à jour in-app passent par une infrastructure indépendante du site web, avec signature RSA vérifiée côté client. Quand on auto-héberge, ça se traduit par : ne jamais utiliser le même serveur pour distribuer un binaire **et** publier son empreinte. Le checksum doit vivre ailleurs — dans un dépôt Git séparé, sur un domaine différent, idéalement sur une infrastructure qu'on n'administre pas soi-même.
|
|
||||||
|
|
||||||
**Surveiller la dérive du contenu publié.** Une simple vérification quotidienne du hash des pages publiques (un cron qui calcule le SHA-256 des URL critiques et alerte en cas de changement non planifié) aurait détecté la compromission de JDownloader en moins d'une heure. C'est le genre de surveillance qu'aucune solution commerciale ne propose nativement, et qui s'écrit en quinze lignes de bash.
|
|
||||||
|
|
||||||
**Patcher le CMS avec la même rigueur que l'OS.** L'automatisation des mises à jour applicatives reste sous-investie, surtout pour les outils « périphériques » (CMS, wiki, formulaire de contact). Une mise à jour automatique de niveau correctif n'est pas plus risquée qu'une mise à jour du noyau, et elle évite ce type de scénario.
|
|
||||||
|
|
||||||
**Auditer les ACL régulièrement.** La faille exploitée ici permettait de modifier les ACL **sans authentification**. C'est l'équivalent CMS d'un répertoire `chmod 777` dans un coin du système. Un audit périodique des permissions sur les pages publiques fait partie du minimum syndical pour un service exposé.
|
|
||||||
|
|
||||||
## En résumé
|
|
||||||
|
|
||||||
JDownloader n'a pas été cassé. Son code source est intact, son infrastructure de build est intacte, ses paquets officiels distribués via Flatpak ou Snap sont intacts, ses mises à jour internes sont intactes. Ce qui a été cassé, c'est **le panneau qui dit où aller chercher le binaire**.
|
|
||||||
|
|
||||||
C'est une mécanique élégante du point de vue de l'attaquant, et inquiétante du point de vue de l'opérateur. Elle illustre quelque chose que l'incident NPM avait déjà mis en lumière dans un autre registre : la chaîne d'approvisionnement logicielle n'est pas une chaîne, c'est un réseau, et les points faibles ne sont jamais là où on les attend. On peut investir massivement dans la sécurité du code, du build et de la signature ; si la page web qui sert le lien reste un Wordpress non patché derrière un nom de domaine prestigieux, tout cet investissement passe à côté.
|
|
||||||
|
|
||||||
Le rôle de l'opérateur en 2026, ce n'est plus de protéger le code. C'est de protéger **chaque maillon qui sert à dire au monde où trouver le code** — et de partir du principe que ce maillon-là sera le prochain à céder.
|
|
||||||
|
|
||||||
###### Liens & sources
|
|
||||||
|
|
||||||
[JDownloader site hacked to replace installers with Python RAT malware | BleepingComputer](https://www.bleepingcomputer.com/news/security/jdownloader-site-hacked-to-replace-installers-with-python-rat-malware/)
|
|
||||||
|
|
||||||
[Rapport d'incident officiel | jdownloader.org](https://jdownloader.org/)
|
|
||||||
|
|
||||||
[JDownloader Website Supply Chain Attack | Rescana](https://www.rescana.com/post/jdownloader-website-supply-chain-attack-installers-replaced-with-python-rat-malware-may-2026/)
|
|
||||||
|
|
||||||
[Le site officiel de JDownloader compromis | IT-Connect](https://www.it-connect.fr/le-site-officiel-de-jdownloader-compromis-pour-diffuser-un-malware-sur-windows-et-linux/)
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "4cf880e6-e4e0-42dd-aae2-675837850b83",
|
|
||||||
"slug": "compromission-de-jdownloader-6-7-mai-2026-analyse-indicateurs-et-procedure-de-detection",
|
|
||||||
"title": "JDownloader : quand le CMS devient la faille",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2026-05-12 17:09",
|
|
||||||
"created_at": "2026-05-12 17:10:36",
|
|
||||||
"updated_at": "2026-05-12 17:21:01",
|
|
||||||
"revisions": [
|
|
||||||
{
|
|
||||||
"n": 1,
|
|
||||||
"date": "2026-05-12 17:21:01",
|
|
||||||
"comment": "Contenu modifié",
|
|
||||||
"title": "JDownloader : quand le CMS devient la faille"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cover": "cover.webp",
|
|
||||||
"files_meta": {
|
|
||||||
"cover.webp": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": "https://savprogroupe.fr/wp-content/uploads/2024/09/Alarme-intrusion1-scaled.webp"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"external_links": [],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "",
|
|
||||||
"og_image": "https://varlog.a5l.fr/file?uuid=4cf880e6-e4e0-42dd-aae2-675837850b83&name=cover.webp",
|
|
||||||
"category": "informatique"
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
---
|
|
||||||
title: JDownloader : quand le CMS devient la faille
|
|
||||||
category: informatique
|
|
||||||
date: 12/05/2026
|
|
||||||
---
|
|
||||||
|
|
||||||
# JDownloader : quand le CMS devient la faille
|
|
||||||
|
|
||||||
*À propos de l'incident de sécurité affectant le site officiel jdownloader.org les 6 et 7 mai 2026.*
|
|
||||||
|
|
||||||
## JDownloader expliqué simplement
|
|
||||||
|
|
||||||
JDownloader est un gestionnaire de téléchargements écrit en Java, distribué gratuitement par l'éditeur allemand AppWork GmbH depuis plus de quinze ans. Il sert essentiellement à automatiser la récupération de fichiers depuis des hébergeurs (Mega, Uptobox, Rapidgator…), des plateformes vidéo et des services de liens premium. Côté utilisateur, c'est l'outil qu'on lance pour récupérer une série de fichiers en une opération, plutôt que cliquer sur cent liens un par un. L'application est multiplateforme — Windows, Linux, macOS — et tourne sur quelques millions de postes dans le monde.
|
|
||||||
|
|
||||||
Le projet est distribué de plusieurs façons : un JAR principal (le binaire Java pur), des installateurs natifs par OS depuis le site officiel, et des paquets passant par des canaux distribués comme Flatpak, Snap ou Winget. C'est cette diversité de canaux qui va jouer un rôle central dans ce qui suit.
|
|
||||||
|
|
||||||
## L'incident : ce qui s'est passé
|
|
||||||
|
|
||||||
Entre le 6 mai 2026 à 00 h 01 UTC et le 7 mai 2026 à 17 h 06 UTC, le site officiel `jdownloader.org` a distribué des installateurs piégés à la place des binaires légitimes. La fenêtre n'a duré qu'environ 48 heures, et seuls deux liens ont été affectés. Mais pendant cette fenêtre, tout utilisateur qui passait par le bon parcours téléchargeait un cheval de Troie au lieu d'un gestionnaire de téléchargements.
|
|
||||||
|
|
||||||
Le scénario reconstitué par l'équipe d'AppWork et les chercheurs en sécurité (BleepingComputer, Thomas Klemenc, équipe Rescana) se déroule en quatre temps.
|
|
||||||
|
|
||||||
**1. Compromission du CMS du site.** Les attaquants ont exploité une vulnérabilité non corrigée dans le système de gestion de contenu de `jdownloader.org`, qui permettait de modifier les listes de contrôle d'accès et le contenu publié sans authentification. Point crucial : ils n'ont **pas** eu accès au serveur sous-jacent, ni au système de fichiers, ni à l'infrastructure de build. Juste au contenu web — et ça a suffi.
|
|
||||||
|
|
||||||
**2. Réécriture de deux liens.** Plutôt que de tenter de modifier les binaires originaux (qui étaient hors de leur portée), les attaquants ont fait beaucoup plus simple : ils ont changé l'URL cible de deux liens publics sur la page de téléchargement. Le lien « Download Alternative Installer » pour Windows et le script shell pour Linux pointaient désormais vers des fichiers malveillants hébergés sur une infrastructure tierce. Le HTML autour, lui, restait identique. Visuellement, rien ne distinguait la page propre de la page piégée.
|
|
||||||
|
|
||||||
**3. Charges utiles différenciées par plateforme.** Sur Windows, l'installateur piégé agit comme un *loader* qui déploie un RAT (*Remote Access Trojan*) écrit en Python, fortement obfusqué, communiquant avec deux serveurs de commande et contrôle (`parkspringshotel[.]com` et `auraguest[.]lk`). Le RAT est modulaire : il reçoit du code Python depuis le C2 et l'exécute, ce qui donne aux attaquants une porte ouverte indéfiniment extensible. Sur Linux, le script shell modifié télécharge une archive depuis `checkinnhotels[.]com`, déguisée en fichier SVG, dont il extrait deux binaires ELF — `pkg` et `systemd-exec`. Le second est installé en SUID-root dans `/usr/bin/`, le premier est copié dans `/root/.local/share/.pkg`, et la persistance est assurée par un script déposé dans `/etc/profile.d/systemd.sh`. Le malware se lance ensuite déguisé en `/usr/libexec/upowerd`, un nom de processus qui existe légitimement sur la plupart des distributions.
|
|
||||||
|
|
||||||
**4. Détection par la communauté.** L'alerte n'est pas venue d'un système de surveillance, mais d'un utilisateur Reddit (*PrinceOfNightSky*) qui a remarqué que Microsoft Defender bloquait son installateur fraîchement téléchargé. La signature numérique indiquait « Zipline LLC » au lieu de « AppWork GmbH ». L'équipe AppWork a confirmé, mis le site hors ligne pour investigation dans les heures qui ont suivi, puis publié un rapport d'incident détaillé. Le site est revenu en ligne dans la nuit du 8 au 9 mai avec des liens vérifiés.
|
|
||||||
|
|
||||||
## Pourquoi c'est systémique
|
|
||||||
|
|
||||||
À première vue, c'est un incident isolé : une faille CMS, une équipe qui patche, le service revient. Vu de loin, ça ressemble à un mauvais quart d'heure. Mais en prenant un peu de recul, le schéma est troublant.
|
|
||||||
|
|
||||||
**CPUID, début avril 2026.** Le site officiel de l'éditeur de CPU-Z est compromis, des installateurs piégés sont distribués pendant plusieurs jours.
|
|
||||||
|
|
||||||
**DAEMON Tools, début mai 2026.** Même schéma : compromission du site officiel, substitution d'installateurs, plusieurs versions infectées (12.5.0.2421 à 12.5.0.2434) distribuées avant détection.
|
|
||||||
|
|
||||||
**JDownloader, 6–7 mai 2026.** Toujours le même schéma.
|
|
||||||
|
|
||||||
Trois compromissions d'éditeurs logiciels en cinq semaines, exactement sur le même schéma : pas d'intrusion dans l'infrastructure de build, pas de modification du code source, pas de vol de certificat de signature de l'éditeur. À chaque fois, **le maillon faible est le CMS qui sert la page de téléchargement**. Ce qu'on attaque, ce n'est pas le logiciel ; c'est le panneau publicitaire qui pointe vers le logiciel.
|
|
||||||
|
|
||||||
Cette mécanique est intéressante parce qu'elle déjoue à peu près toutes les défenses « modernes » de la chaîne d'approvisionnement.
|
|
||||||
|
|
||||||
**La signature de code ne protège pas.** Le binaire légitime de JDownloader est toujours signé proprement par AppWork GmbH. Mais le binaire malveillant servi à sa place est signé, lui aussi — par un autre éditeur (Zipline LLC, The Water Team), avec des certificats vraisemblablement volés ou achetés au marché noir. La signature certifie que le fichier vient bien de celui qui l'a signé ; elle ne certifie pas que c'est le bon fichier.
|
|
||||||
|
|
||||||
**Le HTTPS ne protège pas.** La page de téléchargement est servie en HTTPS valide, depuis le bon domaine, avec le bon certificat. Le navigateur n'a aucune raison de tiquer.
|
|
||||||
|
|
||||||
**Les mises à jour in-app sont, elles, protégées.** AppWork le souligne : chaque mise à jour livrée par le mécanisme intégré de JDownloader est signée RSA et vérifiée cryptographiquement, indépendamment du site web. Ce canal n'a pas été touché. C'est tout le paradoxe : les utilisateurs qui mettaient à jour JDownloader depuis l'application elle-même n'ont rien risqué ; ceux qui sont allés sur le site officiel pour le télécharger « proprement » sont les seuls exposés.
|
|
||||||
|
|
||||||
**Les paquets distribués sont protégés.** Flatpak, Snap, Winget, le JAR principal — tout ce qui passe par une chaîne d'approvisionnement où l'intégrité est vérifiée par checksum hors site est resté propre. AppWork le résume sans détour : *« Winget/Flatpak/Snap infra is outside of our reach — files downloaded by those are hosted on other infra and secured by sha256 checksums that are unchanged. »*
|
|
||||||
|
|
||||||
Autrement dit, **plus le canal est court et naïf, plus il est vulnérable**. Le téléchargement direct depuis un site web est le canal le plus naïf qui soit : on fait confiance au CMS, point. Tout l'effort de sécurisation de l'écosystème logiciel — signatures, builds reproductibles, SBOMs, attestations de provenance — porte sur la chaîne de production et la chaîne de distribution centralisée. Le maillon « page HTML qui dit *clique ici* », lui, est resté tel qu'il était en 2005.
|
|
||||||
|
|
||||||
## Comment vérifier si on est touché
|
|
||||||
|
|
||||||
La fenêtre est étroite, donc le filtrage est simple. Trois questions, dans l'ordre :
|
|
||||||
|
|
||||||
1. L'installateur a-t-il été récupéré entre le **6 mai 2026 (00 h 01 UTC)** et le **7 mai 2026 (17 h 06 UTC)** ?
|
|
||||||
2. S'agit-il du lien **« Download Alternative Installer » Windows** ou du **script shell Linux** depuis `jdownloader.org` ?
|
|
||||||
3. Le fichier a-t-il été **exécuté** ?
|
|
||||||
|
|
||||||
Trois *oui* → traiter la machine comme compromise. N'importe quel *non* dans la liste → aucun risque lié à cet incident. Une installation pré-existante mise à jour automatiquement, un paquet Flatpak/Snap/Winget, le JAR, la version macOS : rien à craindre.
|
|
||||||
|
|
||||||
### Sous Windows
|
|
||||||
|
|
||||||
Le contrôle de référence, c'est la signature numérique. Clic droit sur l'installateur → **Propriétés** → onglet **Signatures numériques**. La valeur attendue est `AppWork GmbH`. Toute autre signature (notamment *Zipline LLC* ou *The Water Team*), ou l'absence de signature, désigne un fichier malveillant.
|
|
||||||
|
|
||||||
En PowerShell :
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
Get-AuthenticodeSignature "C:\chemin\vers\JDownloader2Setup.exe" |
|
|
||||||
Select-Object Status, SignerCertificate
|
|
||||||
```
|
|
||||||
|
|
||||||
Si `Status` n'est pas `Valid` ou si le certificat ne contient pas `AppWork GmbH`, ne pas exécuter.
|
|
||||||
|
|
||||||
### Sous Linux
|
|
||||||
|
|
||||||
Trois artefacts sont à chercher en post-exécution :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo ls -la /usr/bin/systemd-exec # SUID-root illégitime
|
|
||||||
sudo ls -la /root/.local/share/.pkg # charge principale obfusquée
|
|
||||||
sudo ls -la /etc/profile.d/systemd.sh # script de persistance
|
|
||||||
```
|
|
||||||
|
|
||||||
L'apparition de l'un de ces trois éléments suffit à confirmer l'infection. Pour aller plus loin, un coup d'œil au trafic sortant vers les trois domaines C2 (`parkspringshotel[.]com`, `auraguest[.]lk`, `checkinnhotels[.]com`) dans les logs DNS ou via `journalctl --since "2026-05-06"` permet de confirmer l'activité du malware.
|
|
||||||
|
|
||||||
Si le script installateur traîne encore quelque part, sa signature est sans ambiguïté : taille de **7 934 496 octets**, SHA-256 commençant par `6d975c05ef`.
|
|
||||||
|
|
||||||
### En cas de compromission confirmée
|
|
||||||
|
|
||||||
La position officielle d'AppWork est sans nuance : **réinstallation complète du système**. Un RAT modulaire avec persistance SUID-root et exécution arbitraire de code Python depuis un C2 n'est pas quelque chose qu'on retire avec un antivirus. Il faut considérer que tout secret qui a transité sur la machine est compromis — mots de passe saisis au clavier, clés SSH, jetons API, cookies de session, configurations cloud — et les faire tous tourner après réinstallation, **depuis une autre machine saine**.
|
|
||||||
|
|
||||||
## Ce que ça change pour qui s'auto-héberge
|
|
||||||
|
|
||||||
L'incident JDownloader est un exemple éclairant pour qui exploite ses propres services exposés sur Internet — un Forgejo, un reverse proxy, un site personnel. La leçon n'est pas vraiment côté utilisateur (la procédure de détection plus haut suffit), elle est côté **opérateur**.
|
|
||||||
|
|
||||||
Le CMS de JDownloader n'a probablement pas été ciblé pour ses qualités intrinsèques. C'est un dommage collatéral d'un schéma plus large : tout site qui distribue un binaire avec un nombre significatif d'utilisateurs devient une cible rentable, et le CMS public est souvent la pièce la moins surveillée du dispositif. On sécurise le serveur Git, le pipeline de build, la signature des paquets — et on laisse tourner un CMS qui n'a pas été patché depuis huit mois parce qu'il « ne sert qu'à afficher la page d'accueil ».
|
|
||||||
|
|
||||||
Quelques principes opérationnels qui en découlent.
|
|
||||||
|
|
||||||
**Séparer le canal de publication du canal de vérification.** AppWork a eu raison sur un point essentiel : leurs mises à jour in-app passent par une infrastructure indépendante du site web, avec signature RSA vérifiée côté client. Quand on auto-héberge, ça se traduit par : ne jamais utiliser le même serveur pour distribuer un binaire **et** publier son empreinte. Le checksum doit vivre ailleurs — dans un dépôt Git séparé, sur un domaine différent, idéalement sur une infrastructure qu'on n'administre pas soi-même.
|
|
||||||
|
|
||||||
**Surveiller la dérive du contenu publié.** Une simple vérification quotidienne du hash des pages publiques (un cron qui calcule le SHA-256 des URL critiques et alerte en cas de changement non planifié) aurait détecté la compromission de JDownloader en moins d'une heure. C'est le genre de surveillance qu'aucune solution commerciale ne propose nativement, et qui s'écrit en quinze lignes de bash.
|
|
||||||
|
|
||||||
**Patcher le CMS avec la même rigueur que l'OS.** L'automatisation des mises à jour applicatives reste sous-investie, surtout pour les outils « périphériques » (CMS, wiki, formulaire de contact). Une mise à jour automatique de niveau correctif n'est pas plus risquée qu'une mise à jour du noyau, et elle évite ce type de scénario.
|
|
||||||
|
|
||||||
**Auditer les ACL régulièrement.** La faille exploitée ici permettait de modifier les ACL **sans authentification**. C'est l'équivalent CMS d'un répertoire `chmod 777` dans un coin du système. Un audit périodique des permissions sur les pages publiques fait partie du minimum syndical pour un service exposé.
|
|
||||||
|
|
||||||
## En résumé
|
|
||||||
|
|
||||||
JDownloader n'a pas été cassé. Son code source est intact, son infrastructure de build est intacte, ses paquets officiels distribués via Flatpak ou Snap sont intacts, ses mises à jour internes sont intactes. Ce qui a été cassé, c'est **le panneau qui dit où aller chercher le binaire**.
|
|
||||||
|
|
||||||
C'est une mécanique élégante du point de vue de l'attaquant, et inquiétante du point de vue de l'opérateur. Elle illustre quelque chose que l'incident NPM avait déjà mis en lumière dans un autre registre : la chaîne d'approvisionnement logicielle n'est pas une chaîne, c'est un réseau, et les points faibles ne sont jamais là où on les attend. On peut investir massivement dans la sécurité du code, du build et de la signature ; si la page web qui sert le lien reste un Wordpress non patché derrière un nom de domaine prestigieux, tout cet investissement passe à côté.
|
|
||||||
|
|
||||||
Le rôle de l'opérateur en 2026, ce n'est plus de protéger le code. C'est de protéger **chaque maillon qui sert à dire au monde où trouver le code** — et de partir du principe que ce maillon-là sera le prochain à céder.
|
|
||||||
|
|
||||||
###### Liens & sources
|
|
||||||
|
|
||||||
[JDownloader site hacked to replace installers with Python RAT malware | BleepingComputer](https://www.bleepingcomputer.com/news/security/jdownloader-site-hacked-to-replace-installers-with-python-rat-malware/)
|
|
||||||
|
|
||||||
[Rapport d'incident officiel | jdownloader.org](https://jdownloader.org/)
|
|
||||||
|
|
||||||
[JDownloader Website Supply Chain Attack | Rescana](https://www.rescana.com/post/jdownloader-website-supply-chain-attack-installers-replaced-with-python-rat-malware-may-2026/)
|
|
||||||
|
|
||||||
[Le site officiel de JDownloader compromis | IT-Connect](https://www.it-connect.fr/le-site-officiel-de-jdownloader-compromis-pour-diffuser-un-malware-sur-windows-et-linux/)
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
# La 6G : au-delà de la 5G, promesses et interrogations
|
|
||||||
|
|
||||||
Alors que la 5G peine encore à s’imposer partout, la recherche sur la **6G** est déjà bien avancée. Les laboratoires, opérateurs et gouvernements annoncent des innovations spectaculaires : débits colossaux, latence quasi nulle et intégration massive de l’intelligence artificielle dans le réseau. Mais derrière le buzz médiatique se cachent de **grandes incertitudes techniques et économiques**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Promesses technologiques
|
|
||||||
|
|
||||||
* **Débits théoriques** : jusqu’à **1 Tbit/s** dans des conditions expérimentales (vs 10 Gbit/s max pour la 5G).
|
|
||||||
* **Latence ultra-faible** : <1 ms, visant les applications critiques comme chirurgie à distance, véhicules autonomes coordonnés en temps réel et réalité immersive totale.
|
|
||||||
* **Fréquences** : exploitation des **ondes térahertz (THz)**, beaucoup plus hautes que les mmWave 5G, offrant un spectre presque illimité mais avec des contraintes sévères de portée et pénétration.
|
|
||||||
* **Intelligence embarquée** : réseaux capables d’auto-optimisation grâce à l’IA et au machine learning pour gérer la congestion, l’énergie et les allocations de spectre en temps réel.
|
|
||||||
* **Intégration multi-domaines** : fusion des communications terrestres, satellites, drones et IoT pour créer un réseau ubiquitaire.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Défis techniques
|
|
||||||
|
|
||||||
1. **Propagation et portée** : les ondes THz sont extrêmement sensibles aux obstacles et à l’humidité, nécessitant une densité d’antennes inimaginable à l’échelle mondiale.
|
|
||||||
2. **Consommation énergétique** : déployer des antennes THz ultra-puissantes et gérer des réseaux IA en temps réel risque d’augmenter considérablement la consommation électrique.
|
|
||||||
3. **Standardisation complexe** : contrairement à la 5G qui a hérité d’une partie de l’infrastructure 4G, la 6G nécessitera des investissements massifs et de nouveaux protocoles.
|
|
||||||
4. **Coût et adoption** : le coût pour les opérateurs et la nécessité de renouveler les équipements pour les utilisateurs seront un frein majeur, comme ce fut le cas pour la 3G et la 5G.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Usages envisagés
|
|
||||||
|
|
||||||
* **Réalité mixte et immersive** : AR/VR ultra-réaliste, métavers en temps réel, téléprésence totale.
|
|
||||||
* **Téléchirurgie et véhicules autonomes coordonnés** : applications critiques nécessitant une latence quasi nulle.
|
|
||||||
* **IoT massif** : milliards d’objets connectés, capteurs intelligents, villes et infrastructures “autonomes”.
|
|
||||||
* **Communication spatiale et aérienne** : drones, satellites et aéronefs connectés en temps réel.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Critique et perspective
|
|
||||||
|
|
||||||
Même si les promesses de la 6G sont spectaculaires, plusieurs points restent préoccupants :
|
|
||||||
|
|
||||||
* La **6G est encore largement théorique** : aucune application grand public n’est prévue avant 2030.
|
|
||||||
* Comme pour la 5G, les opérateurs pourraient utiliser la 6G pour **inciter la migration depuis la 5G**, en bridant certaines fonctionnalités sur la génération précédente.
|
|
||||||
* Le discours marketing risque de créer une **confusion encore plus grande** pour les utilisateurs : débits maximaux, latence minimale et réseaux intelligents seront très localisés et expérimentaux, bien loin d’une couverture nationale.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Schéma suggéré : évolution 3G → 4G → 5G → 6G
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart LR
|
|
||||||
A[3G] --> B[4G]
|
|
||||||
B --> C[5G]
|
|
||||||
C --> D[6G]
|
|
||||||
|
|
||||||
subgraph Débits
|
|
||||||
A1[384 kbit/s → 42 Mbit/s] --> B1[100 Mbit/s → 1 Gbit/s] --> C1[100 Mbit/s → 10 Gbit/s] --> D1[100 Gbit/s → 1 Tbit/s]
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph Latence
|
|
||||||
A2[150–200 ms] --> B2[30–50 ms] --> C2[1–10 ms] --> D2[<1 ms]
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
La 6G s’annonce comme **l’avenir des réseaux mobiles**, mais elle illustre encore la stratégie récurrente des opérateurs :
|
|
||||||
|
|
||||||
1. Créer une promesse technologique spectaculaire.
|
|
||||||
2. Déployer progressivement pour ne pas perturber l’infrastructure existante.
|
|
||||||
3. Inciter subtilement les utilisateurs à migrer vers la nouvelle génération, souvent via des limitations sur les générations précédentes.
|
|
||||||
|
|
||||||
> Comme pour la 3G bridée puis la 4G et la 5G, la 6G risque d’être autant un **outil de marketing et de stratégie économique** qu’une véritable révolution immédiate pour le consommateur.
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "4f193d70-d236-42d7-aedb-58631cd15002",
|
|
||||||
"slug": "la-6g-au-dela-de-la-5g-promesses-et-interrogations",
|
|
||||||
"title": "La 6G : au-delà de la 5G, promesses et interrogations",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2025-11-05 08:46:51",
|
|
||||||
"created_at": "2025-11-05 08:46:51",
|
|
||||||
"updated_at": "2025-11-05 08:46:51",
|
|
||||||
"revisions": [],
|
|
||||||
"cover": "cover.jpg",
|
|
||||||
"category": "télécom"
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 86 KiB |
@@ -1,89 +0,0 @@
|
|||||||
# Pourquoi le réseau mobile ne s'effondre pas le jour où tout le monde téléphone en même temps
|
|
||||||
|
|
||||||
Un attentat, un séisme, un match du Stade de France, une grande panne d'électricité. Dans ces moments-là, des centaines de milliers de gens dégainent leur téléphone au même instant. Le réseau mobile est dimensionné pour un usage moyen, pas pour un pic massif simultané, et il devrait théoriquement s'effondrer. La plupart du temps, il tient. Pas parfaitement, pas pour tout le monde, mais il tient — et surtout, les appels d'urgence continuent de passer. C'est le résultat d'une série de mécanismes empilés depuis les années 1990, que la 4G a affinés et que la 5G a élargis. Cet article les passe en revue, et termine sur une question qu'on me pose souvent : est-ce que mon forfait à 50 € me donne une place prioritaire dans cette file d'attente ?
|
|
||||||
|
|
||||||
## Trois questions, pas une
|
|
||||||
|
|
||||||
Quand une cellule commence à chauffer, l'opérateur doit répondre à trois questions distinctes. Qui a le droit de se connecter ? Une fois connecté, qui passe en premier ? Et quels services doivent absolument continuer à fonctionner, quoi qu'il arrive ?
|
|
||||||
|
|
||||||
La 2G ne savait répondre qu'à la première. Elle filtrait à l'entrée et basta. La 4G a ajouté la deuxième : une fois admis sur le réseau, votre trafic est traité différemment selon son importance. La 5G ajoute la troisième : elle peut créer des réseaux virtuels parallèles dont certains sont réservés à des usages critiques, totalement isolés des autres.
|
|
||||||
|
|
||||||
## Le filtrage à l'entrée
|
|
||||||
|
|
||||||
Chaque carte SIM porte un numéro de classe d'accès, hérité du GSM, entre 0 et 15. Les classes 0 à 9 couvrent le grand public — autrement dit nous tous. Les classes 11 à 15 sont réservées : services de secours, autorités publiques, personnel opérateur, usages militaires selon les pays.
|
|
||||||
|
|
||||||
Quand une cellule est surchargée, l'eNodeB (la station de base 4G) diffuse une consigne aux téléphones du secteur : « les classes 0 à 9, vous attendez ». C'est l'**Access Class Barring**. Concrètement, votre téléphone reçoit ce message et bloque lui-même votre tentative d'appel ou de connexion data, sans même envoyer la demande à la station. C'est élégant parce que ça soulage la station avant même qu'elle ne soit sollicitée. Les classes prioritaires, elles, passent sans encombre.
|
|
||||||
|
|
||||||
Une variante plus dure, l'**Extended Access Barring**, vise les objets connectés et les usages non urgents. Quand une vraie crise se déclare, l'opérateur peut couper les compteurs intelligents, les alarmes domestiques et autres équipements bavards pour préserver la bande passante humaine.
|
|
||||||
|
|
||||||
En 5G, ce mécanisme a été refondu sous le nom d'**UAC** — *Unified Access Control*, introduit dans la Release 15 du 3GPP. UAC unifie dans un seul cadre ce qui était auparavant éparpillé entre ACB, EAB et d'autres dispositifs spécifiques. Il repose sur deux notions complémentaires. Les *Access Identities* identifient qui vous êtes : utilisateur lambda, abonné à un service prioritaire type MPS ou MCS, personnel d'urgence, agent opérateur. Les *Access Categories* identifient ce que vous essayez de faire : appel d'urgence, connexion data normale, SMS, mise à jour de localisation. La combinaison des deux détermine si votre demande passe ou pas. La granularité gagnée par rapport à la 4G est réelle : on peut bloquer un type d'action précis pour un type d'utilisateur précis, par exemple « les abonnés grand public ne peuvent plus initier de nouveaux appels data, mais les SMS et les appels voix continuent ».
|
|
||||||
|
|
||||||
## La priorité une fois connecté
|
|
||||||
|
|
||||||
Là où la 4G a vraiment innové, c'est en introduisant le **QCI** — *QoS Class Identifier*. Chaque flux de données qui transite sur le réseau se voit attribuer un numéro entre 1 et 9 (avec quelques valeurs supplémentaires pour des cas spéciaux) qui dit à l'infrastructure comment le traiter.
|
|
||||||
|
|
||||||
| Usage | QCI | Traitement |
|
|
||||||
|---|---|---|
|
|
||||||
| Appel VoLTE (voix sur LTE) | 1 | Latence minimale, débit garanti |
|
|
||||||
| Visioconférence | 2 | Débit garanti |
|
|
||||||
| Signalisation réseau | 5 | Très haute priorité |
|
|
||||||
| Streaming vidéo | 6 ou 8 | Best effort prioritaire |
|
|
||||||
| Web et internet général | 9 | Best effort standard |
|
|
||||||
|
|
||||||
Quand la cellule est encombrée, le routeur sait quoi sacrifier en premier. YouTube va ralentir, les pages web vont mettre du temps à charger, mais l'appel téléphonique de votre voisin reste audible. C'est un compromis assumé : on dégrade volontairement les usages secondaires pour préserver les usages critiques.
|
|
||||||
|
|
||||||
La 5G a transposé ce mécanisme sous le nom de **5QI** (*5G QoS Identifier*) avec davantage de niveaux et une meilleure prise en compte des cas que la 4G gérait mal — notamment les services à très basse latence pour les usines connectées ou la voiture autonome. La voix d'urgence garde son sommet, les données critiques industrielles s'intercalent juste après, le streaming et le web restent en bas de la pile.
|
|
||||||
|
|
||||||
## L'isolation par tranches : le network slicing
|
|
||||||
|
|
||||||
C'est l'apport majeur de la 5G en matière de gestion de crise. Au lieu de partager une seule infrastructure entre tous les usages, on peut maintenant la découper logiciellement en tranches — des *slices* — qui se comportent comme autant de réseaux indépendants, alors qu'ils tournent sur les mêmes antennes et les mêmes câbles.
|
|
||||||
|
|
||||||
Un opérateur peut par exemple maintenir une tranche pour le grand public avec ses millions d'abonnés et son trafic massif, une autre pour les services d'urgence dimensionnée pour rester fluide même quand le reste sature, une troisième pour les objets connectés industriels avec des garanties de latence, et une quatrième pour des opérateurs critiques type SNCF, EDF ou hôpitaux. Chaque tranche a ses propres règles d'admission, ses propres priorités, ses propres garanties de performance. Si la tranche grand public est totalement saturée, celle des secours ne le sait même pas.
|
|
||||||
|
|
||||||
Cette isolation est ce qui distingue le plus fondamentalement la 5G des générations précédentes. Avant, tout le monde se battait pour les mêmes ressources, avec juste des priorités différentes pour départager. Maintenant, certaines ressources sont retirées du combat dès le départ.
|
|
||||||
|
|
||||||
## Récapitulatif
|
|
||||||
|
|
||||||
| Génération | Ce qui est contrôlé | Comment |
|
|
||||||
|---|---|---|
|
|
||||||
| 2G | L'accès au réseau | Classes d'accès 0-15 |
|
|
||||||
| 4G | L'accès + la priorité du trafic | ACB / EAB + QCI |
|
|
||||||
| 5G | L'accès + la priorité + l'isolation des services | UAC + 5QI + network slicing |
|
|
||||||
|
|
||||||
Tous ces mécanismes restent invisibles tant que tout va bien. Vous ne savez pas qu'ils existent. Vous découvrez leur existence le jour où votre voisin n'arrive plus à charger ses mails alors que les pompiers, eux, continuent de communiquer normalement. Ce jour-là, ce n'est pas de la magie. C'est trente ans d'ingénierie radio qui ont anticipé que ça arriverait.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Et mon forfait premium, alors ?
|
|
||||||
|
|
||||||
Question logique à ce stade. Si le réseau sait techniquement prioriser certains flux par rapport à d'autres, qu'est-ce qui empêche un opérateur de faire passer ses abonnés à 50 € devant ceux à 10 € quand les antennes saturent ? La réponse honnête commence par un aveu : techniquement, rien. L'outil existe, il s'appelle **Quality of Service** (QoS), c'est exactement le mécanisme qu'on vient de décrire. Si demain Orange ou SFR voulaient créer une voie rapide pour leurs abonnés haut de gamme, ils auraient les outils dans la boîte. Pourtant, ils ne le font pas. Pour quatre raisons.
|
|
||||||
|
|
||||||
### La loi européenne l'interdit
|
|
||||||
|
|
||||||
Le règlement **(UE) 2015/2120**, dit « règlement internet ouvert », oblige les opérateurs à traiter tout le trafic de la même façon, sans discrimination liée à l'expéditeur, au destinataire, au contenu ou à l'application. Il a fêté ses dix ans en novembre 2025, et l'ARCEP a profité de l'anniversaire pour rappeler que c'est l'un des piliers du modèle numérique européen. Les sanctions sont sérieuses : jusqu'à **3 % du chiffre d'affaires** de l'opérateur fautif. Un opérateur français qui annoncerait demain « avec notre forfait Premium, vous passez devant les autres » se retrouverait devant l'ARCEP dans la semaine.
|
|
||||||
|
|
||||||
Le règlement laisse quelques portes ouvertes pour les services dits « spécialisés » qui ont besoin d'une qualité garantie — téléchirurgie, voiture connectée. Mais ces exceptions sont étroitement encadrées et ne couvrent absolument pas le confort d'un client haut de gamme qui voudrait charger son Instagram plus vite à 19h.
|
|
||||||
|
|
||||||
Aux États-Unis, l'histoire est différente. La FCC a tenté de restaurer la neutralité du net en 2024, mais en janvier 2025 la cour d'appel du sixième circuit a invalidé la décision, jugeant que la FCC n'avait pas l'autorité légale pour reclasser le haut débit comme service public. Avec l'arrivée de Brendan Carr à la tête de la FCC, ouvertement opposé à la neutralité du net, il n'y a aujourd'hui plus de règle fédérale outre-Atlantique. Quelques États (Californie, Washington, New York, Oregon) ont leurs propres lois qui maintiennent le principe, mais à l'échelle du pays, les opérateurs américains pourraient légalement faire ce que leurs homologues européens n'ont pas le droit de faire. Pourtant, ils ne le font pas ouvertement non plus, et la raison renvoie aux trois points suivants.
|
|
||||||
|
|
||||||
### C'est commercialement intenable
|
|
||||||
|
|
||||||
Imagine la publicité : « Forfait Premium à 50 € — passez devant les pauvres pendant les heures de pointe ». Le slogan ne se vend pas. Les directions marketing savent que dire à la moitié de leurs clients qu'ils sont des citoyens de seconde zone du réseau est le plus court chemin vers une crise de réputation. C'est pour ça qu'on vous vend « plus de Go », « 5G ultra rapide », « roaming inclus dans 110 pays » — des promesses qui sonnent positivement sans jamais dire à personne qu'il est désavantagé.
|
|
||||||
|
|
||||||
### L'effet boule de neige serait toxique
|
|
||||||
|
|
||||||
Imagine que ça se mette quand même en place. Les riches passent devant. Les antennes restent saturées pour les autres, qui se mettent à payer plus pour échapper à la saturation, ce qui sature encore plus les bas forfaits, ce qui pousse encore plus de gens à monter en gamme. Au bout de cinq ans, on a un réseau à deux vitesses où les forfaits modestes deviennent quasi inutilisables aux heures critiques, et où la connexion mobile correcte devient un service de luxe. Ce n'est plus un service de télécommunications, c'est un système de classes.
|
|
||||||
|
|
||||||
C'est exactement ce que la neutralité du net cherche à empêcher. Pas par idéologie, mais parce qu'on a déjà vu où mène ce genre de spirale dans les pays où elle n'est pas protégée. Certains opérateurs proposent par exemple des forfaits où Facebook et WhatsApp sont gratuits mais où le reste est payant, ce qui revient à dire que le bon internet est celui que l'opérateur a choisi pour vous. Ce n'est plus tout à fait le même service.
|
|
||||||
|
|
||||||
### Ça ne résoudrait rien
|
|
||||||
|
|
||||||
Quand un réseau sature, ce n'est pas un problème de répartition entre utilisateurs, c'est un problème de **capacité totale**. Faire passer Pierre avant Paul ne crée pas un seul bit de bande passante supplémentaire. Ça déplace juste le problème de l'un vers l'autre. La vraie solution, quand une cellule sature trop souvent, c'est d'installer plus d'antennes, de densifier le réseau, de basculer sur une fréquence plus performante ou de passer à la génération suivante. C'est cher, c'est long, ça implique des autorisations administratives et des négociations foncières, mais c'est la seule réponse qui tient la route. Prioriser, c'est rapide, mais ça repousse le mur, ça ne le déplace pas.
|
|
||||||
|
|
||||||
C'est comme si on proposait une voie réservée aux Mercedes sur l'A7 un samedi de chassé-croisé. Techniquement, on peut peindre la ligne au sol et installer les panneaux dans la matinée. Mais cette voie ne réduit pas le bouchon, elle le concentre sur les voies restantes ; elle écorne le principe d'égalité d'accès à l'infrastructure publique ; et elle ne change rien au problème de fond, qui est qu'il y a trop de voitures pour la route disponible. La vraie solution reste la même qu'avant : élargir l'autoroute, ou convaincre une partie des gens de prendre le train.
|
|
||||||
|
|
||||||
### Le caveat 5G
|
|
||||||
|
|
||||||
Une nuance honnête pour finir. Le *network slicing* complique le débat juridique. Un opérateur peut créer des tranches de réseau avec des qualités différenciées en toute légalité quand il s'agit d'usages spécialisés — santé, industrie, transports. La question qui agite régulateurs et juristes depuis plusieurs années est de savoir où finit le service spécialisé légitime et où commence le contournement déguisé de la neutralité du net. L'ARCEP a ouvert ce chantier, et c'est probablement là, plus que dans une revanche commerciale brutale sur les forfaits premium, que se jouera la prochaine bataille.
|
|
||||||
|
|
||||||
Mais pour répondre simplement à la question : non, votre forfait à 50 € ne vous donne pas la priorité réseau sur celui de votre voisin à 10 €. Il vous donne plus de data, parfois un meilleur débit théorique, des options en plus. Pas une place dans la file.
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "4f443bcb-b0d4-47f8-837d-61627e6c94f2",
|
|
||||||
"slug": "priorites-et-acces-au-reseau-en-4g-et-5g",
|
|
||||||
"title": "Pourquoi le réseau mobile ne s'effondre pas le jour où tout le monde téléphone en même temps",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2026-01-06 22:21",
|
|
||||||
"created_at": "2026-01-06 22:21:04",
|
|
||||||
"updated_at": "2026-05-11 23:40:18",
|
|
||||||
"revisions": [
|
|
||||||
{
|
|
||||||
"n": 1,
|
|
||||||
"date": "2026-05-11 23:06:50",
|
|
||||||
"comment": "",
|
|
||||||
"title": "Comment les réseaux mobiles tiennent debout quand tout le monde téléphone en même temps"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"n": 2,
|
|
||||||
"date": "2026-05-11 23:09:07",
|
|
||||||
"comment": "",
|
|
||||||
"title": "Comment les réseaux mobiles tiennent debout quand tout le monde téléphone en même temps"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"n": 3,
|
|
||||||
"date": "2026-05-11 23:11:33",
|
|
||||||
"comment": "",
|
|
||||||
"title": "Comment les réseaux mobiles tiennent debout quand tout le monde téléphone en même temps"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"n": 4,
|
|
||||||
"date": "2026-05-11 23:14:26",
|
|
||||||
"comment": "",
|
|
||||||
"title": "Comment les réseaux mobiles tiennent debout quand tout le monde téléphone en même temps"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"n": 5,
|
|
||||||
"date": "2026-05-11 23:16:33",
|
|
||||||
"comment": "",
|
|
||||||
"title": "Comment les réseaux mobiles tiennent debout quand tout le monde téléphone en même temps"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"n": 6,
|
|
||||||
"date": "2026-05-11 23:16:56",
|
|
||||||
"comment": "",
|
|
||||||
"title": "Comment les réseaux mobiles tiennent debout quand tout le monde téléphone en même temps"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"n": 7,
|
|
||||||
"date": "2026-05-11 23:40:18",
|
|
||||||
"comment": "",
|
|
||||||
"title": "Pourquoi le réseau mobile ne s'effondre pas le jour où tout le monde téléphone en même temps"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cover": "cover.jpg",
|
|
||||||
"files_meta": {
|
|
||||||
"_preview.png": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"_thumb_dc5413a86e9c042a-22419.jpg": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"_thumb_945482b26e8a76ab-49498.png": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"_thumb_a1080cd703289e6b-144512.png": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"_thumb_cae639a42d79414f-68314.jpg": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
},
|
|
||||||
"03bcf841dafcc9f4-87771.jpg": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": "https://www.ariase.com/uploads/media/124b4e1790cf20b8fed653884ef1eb46d235922f.jpeg"
|
|
||||||
},
|
|
||||||
"cover.jpg": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"external_links": [
|
|
||||||
{
|
|
||||||
"url": "https://arxiv.org/pdf/2012.05520",
|
|
||||||
"name": "An Overview of 5G System Accessibility Differentiation and Control",
|
|
||||||
"added_at": "2026-05-11 23:04:53",
|
|
||||||
"author": "-",
|
|
||||||
"meta": {
|
|
||||||
"mime": "application/pdf",
|
|
||||||
"size": 717772,
|
|
||||||
"description": "IEEE Transactions on Magnetics",
|
|
||||||
"date": "2021-09-28 10:31:29+02:00",
|
|
||||||
"subject": "IEEE Transactions on Magnetics",
|
|
||||||
"creator": "-",
|
|
||||||
"producer": "Microsoft® Word for Microsoft 365",
|
|
||||||
"pages": 9,
|
|
||||||
"pdf_version": "PDF 1.7",
|
|
||||||
"page_size": "Letter (216×279 mm)"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://www.techplayon.com/5g-nr-cell-access-control/",
|
|
||||||
"name": "5G NR Cell Access Control - Techplayon - RRC Signalling",
|
|
||||||
"added_at": "2026-05-11 23:05:34",
|
|
||||||
"author": "Author",
|
|
||||||
"meta": {
|
|
||||||
"mime": "text/html",
|
|
||||||
"size": 79963,
|
|
||||||
"description": "Access Control barring refers to a traffic congestion control mechanism to secure and ensure the success of critical communications",
|
|
||||||
"og_image": "/file?uuid=4f443bcb-b0d4-47f8-837d-61627e6c94f2&name=_thumb_dc5413a86e9c042a-22419.jpg",
|
|
||||||
"site_name": "Techplayon",
|
|
||||||
"og_type": "article",
|
|
||||||
"language": "en_US",
|
|
||||||
"date": "2019-12-21T17:44:54+00:00",
|
|
||||||
"canonical": "https://www.techplayon.com/5g-nr-cell-access-control/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://simnovus.com/unified-access-control-uac-the-gatekeeper-of-5g-networks/",
|
|
||||||
"name": "Unified Access Control (UAC): The Gatekeeper of 5G Networks - Simnovus",
|
|
||||||
"added_at": "2026-05-11 23:11:58",
|
|
||||||
"meta": {
|
|
||||||
"mime": "text/html",
|
|
||||||
"size": 124603,
|
|
||||||
"description": "Unified Access Control (UAC) is a fundamental component of 5G networks that governs access to network resources. It replaces the traditional access control",
|
|
||||||
"og_image": "/file?uuid=4f443bcb-b0d4-47f8-837d-61627e6c94f2&name=_thumb_cae639a42d79414f-68314.jpg",
|
|
||||||
"site_name": "Simnovus",
|
|
||||||
"og_type": "article",
|
|
||||||
"language": "en_US",
|
|
||||||
"date": "2024-08-14T10:58:09+05:30",
|
|
||||||
"canonical": "https://simnovus.com/unified-access-control-uac-the-gatekeeper-of-5g-networks/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://www.sharetechnote.com/html/5G/5G_UAC.html",
|
|
||||||
"name": "5G | ShareTechnote",
|
|
||||||
"added_at": "2026-05-11 23:12:20",
|
|
||||||
"meta": {
|
|
||||||
"mime": "text/html",
|
|
||||||
"size": 28707,
|
|
||||||
"og_image": "/file?uuid=4f443bcb-b0d4-47f8-837d-61627e6c94f2&name=_thumb_a1080cd703289e6b-144512.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://www.telecomgurukul.com/post/advanced-access-control-mechanisms-in-lte-and-5g-nr-networks",
|
|
||||||
"name": "Advanced Access Control Mechanisms in LTE and 5G NR Networks",
|
|
||||||
"added_at": "2026-05-11 23:14:17",
|
|
||||||
"meta": {
|
|
||||||
"mime": "text/html",
|
|
||||||
"size": 1344890,
|
|
||||||
"og_image": "/file?uuid=4f443bcb-b0d4-47f8-837d-61627e6c94f2&name=_thumb_945482b26e8a76ab-49498.png"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "",
|
|
||||||
"og_image": "",
|
|
||||||
"category": "télécom"
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
Un attentat, un séisme, un match du Stade de France, une grande panne d'électricité. Dans ces moments-là, des centaines de milliers de gens dégainent leur téléphone en même temps. Le réseau mobile, qui est dimensionné pour un usage moyen et pas pour un pic massif simultané, devrait théoriquement s'effondrer. La plupart du temps, il tient. Pas parfaitement, pas pour tout le monde, mais il tient — et surtout, les appels d'urgence continuent de passer. C'est le résultat d'une série de mécanismes empilés depuis les années 1990, et que la 4G et la 5G ont raffinés. Voici comment ça marche, sans le jargon mais sans non plus mentir sur ce qui se passe vraiment.
|
|
||||||
|
|
||||||
## Trois questions, pas une
|
|
||||||
|
|
||||||
Dans un réseau cellulaire moderne, l'opérateur doit répondre à trois questions distinctes quand la cellule commence à chauffer. Qui a le droit de se connecter ? Une fois connecté, qui passe en premier ? Et quels services doivent absolument continuer à fonctionner, quoi qu'il arrive ?
|
|
||||||
|
|
||||||
La 2G ne savait répondre qu'à la première. Elle filtrait à l'entrée et basta. La 4G a ajouté la deuxième : une fois admis sur le réseau, votre trafic n'est plus traité de la même manière selon son importance. La 5G ajoute la troisième : elle peut littéralement créer des réseaux virtuels parallèles, dont certains sont réservés à des usages critiques et isolés des autres.
|
|
||||||
|
|
||||||
## En 4G : filtrer puis prioriser
|
|
||||||
|
|
||||||
### Filtrer à l'entrée
|
|
||||||
|
|
||||||
Chaque carte SIM porte un numéro de classe d'accès, hérité du GSM, entre 0 et 15. Les classes 0 à 9 couvrent le grand public — autrement dit nous tous. Les classes 11 à 15 sont réservées : services de secours, autorités publiques, personnel opérateur, usages militaires selon les pays.
|
|
||||||
|
|
||||||
Quand une cellule est surchargée, l'eNodeB (la station de base 4G) diffuse une consigne aux téléphones du secteur : « les classes 0 à 9, vous attendez ». C'est l'**Access Class Barring**. Concrètement, votre téléphone reçoit ce message et bloque lui-même votre tentative d'appel ou de connexion data, sans même envoyer la demande à la station. C'est élégant parce que ça soulage la station avant même qu'elle ne soit sollicitée. Les classes prioritaires, elles, passent sans encombre.
|
|
||||||
|
|
||||||
Il existe une variante plus dure appelée **Extended Access Barring**, conçue pour les objets connectés et les usages non urgents. Quand une vraie crise se déclare, l'opérateur peut couper les compteurs intelligents, les alarmes domestiques et autres équipements bavards pour préserver la bande passante humaine.
|
|
||||||
|
|
||||||
### Prioriser une fois connecté
|
|
||||||
|
|
||||||
Là où la 4G a vraiment innové, c'est en introduisant le **QCI** — *QoS Class Identifier*. Chaque flux de données qui transite sur le réseau se voit attribuer un numéro entre 1 et 9 (et quelques valeurs au-dessus pour des cas spéciaux) qui dit à l'infrastructure comment le traiter.
|
|
||||||
|
|
||||||
Quelques exemples concrets :
|
|
||||||
|
|
||||||
| Usage | QCI | Traitement |
|
|
||||||
|---|---|---|
|
|
||||||
| Appel VoLTE (voix sur LTE) | 1 | Latence minimale, débit garanti |
|
|
||||||
| Signalisation réseau | 5 | Très haute priorité |
|
|
||||||
| Visioconférence | 2 | Débit garanti |
|
|
||||||
| Streaming vidéo | 6 ou 8 | Best effort prioritaire |
|
|
||||||
| Web et internet général | 9 | Best effort standard |
|
|
||||||
|
|
||||||
Quand la cellule est encombrée, le routeur sait quoi sacrifier en premier. YouTube va ralentir, les pages web vont mettre du temps à charger, mais l'appel téléphonique de votre voisin reste audible. C'est un compromis assumé : on dégrade volontairement les usages secondaires pour préserver les usages critiques.
|
|
||||||
|
|
||||||
## En 5G : ajouter le découpage
|
|
||||||
|
|
||||||
### Un mécanisme d'accès refondu
|
|
||||||
|
|
||||||
La 5G garde l'esprit du barring mais change son nom et sa mécanique. L'ancien Access Class Barring est remplacé par l'**UAC** — *Unified Access Control*, introduit dans la Release 15 du 3GPP. L'idée est d'unifier dans un seul cadre ce qui était auparavant éparpillé entre ACB, EAB et d'autres mécanismes spécifiques.
|
|
||||||
|
|
||||||
UAC repose sur deux notions. Les **Access Identities** identifient qui vous êtes (utilisateur lambda, abonné à un service prioritaire type MPS ou MCS, personnel d'urgence, agent opérateur). Les **Access Categories** identifient ce que vous essayez de faire (appel d'urgence, connexion data normale, SMS, mise à jour de localisation). La combinaison des deux détermine si votre demande passe ou pas.
|
|
||||||
|
|
||||||
Ce qui change vraiment, c'est la granularité. En 4G, on bloquait une classe entière. En 5G, on peut bloquer un type d'action précis pour un type d'utilisateur précis — par exemple « les abonnés grand public ne peuvent plus initier de nouveaux appels data, mais les SMS et les appels voix continuent ». L'opérateur peut aussi définir ses propres catégories d'accès, calées sur sa politique commerciale et technique.
|
|
||||||
|
|
||||||
### Le QCI devient le 5QI
|
|
||||||
|
|
||||||
Même logique qu'en 4G mais avec plus de finesse. Le **5QI** (*5G QoS Identifier*) propose davantage de niveaux et tient compte de cas que la 4G gérait mal, notamment les services à très basse latence pour les usines connectées ou la voiture autonome. La voix d'urgence garde son sommet, les données critiques industrielles s'intercalent juste après, le streaming et le web restent en bas de la pile.
|
|
||||||
|
|
||||||
### La vraie nouveauté : le network slicing
|
|
||||||
|
|
||||||
C'est l'apport majeur de la 5G en termes de gestion de crise. Au lieu de partager une seule infrastructure entre tous les usages, on peut maintenant la **découper logiciellement en tranches** — des *slices* — qui se comportent comme autant de réseaux indépendants, alors qu'ils tournent sur les mêmes antennes et les mêmes câbles.
|
|
||||||
|
|
||||||
Un opérateur peut par exemple maintenir :
|
|
||||||
|
|
||||||
- une tranche pour le grand public, avec ses millions d'abonnés et son trafic massif,
|
|
||||||
- une tranche pour les services d'urgence et de sécurité, dimensionnée pour rester fluide même quand le reste sature,
|
|
||||||
- une tranche pour les objets connectés industriels, avec des garanties de latence,
|
|
||||||
- une tranche pour les opérateurs critiques type SNCF, EDF, hôpitaux.
|
|
||||||
|
|
||||||
Chaque tranche a ses propres règles d'admission, ses propres priorités, ses propres garanties de performance. Si la tranche grand public est totalement saturée, celle des secours ne le sait même pas. Cette isolation est ce qui distingue le plus fondamentalement la 5G des générations précédentes, où tout le monde se battait pour les mêmes ressources, avec juste des priorités différentes.
|
|
||||||
|
|
||||||
## Et le forfait premium dans tout ça ?
|
|
||||||
|
|
||||||
Question qu'on entend souvent : si je paie un forfait à 50 € au lieu d'un forfait à 10 €, est-ce que je passe avant les autres en cas de saturation ?
|
|
||||||
|
|
||||||
Non.
|
|
||||||
|
|
||||||
Les priorités techniques décrites au-dessus ne dépendent ni du prix du forfait, ni des options commerciales souscrites. Elles dépendent du profil réseau associé à votre SIM (lui-même fonction de votre statut : grand public, secours, opérateur, services prioritaires officiels), et des politiques de gestion de crise programmées par l'opérateur. Un cadre dirigeant avec un forfait illimité reste, du point de vue du réseau, un abonné de classe d'accès 0-9 comme tout le monde.
|
|
||||||
|
|
||||||
Le forfait premium vous donne plus de data, parfois un meilleur débit théorique en conditions normales, des options de roaming, du cloud gratuit. Il ne vous donne pas la priorité face à un pompier ou à un préfet.
|
|
||||||
|
|
||||||
## Pour résumer
|
|
||||||
|
|
||||||
| Génération | Ce qui est contrôlé | Comment |
|
|
||||||
|---|---|---|
|
|
||||||
| 2G | L'accès au réseau | Classes d'accès 0-15 |
|
|
||||||
| 4G | L'accès + la priorité du trafic | ACB / EAB + QCI |
|
|
||||||
| 5G | L'accès + la priorité + l'isolation des services | UAC + 5QI + network slicing |
|
|
||||||
|
|
||||||
Ce qui est intéressant, c'est que ces mécanismes restent invisibles tant que tout va bien. Vous ne savez pas qu'ils existent. Vous découvrez leur existence le jour où votre voisin n'arrive plus à charger ses mails alors que les pompiers, eux, continuent de communiquer normalement. Ce jour-là, ce n'est pas de la magie. C'est trente ans d'ingénierie radio qui ont anticipé que ça arriverait.
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
Un attentat, un séisme, un match du Stade de France, une grande panne d'électricité. Dans ces moments-là, des centaines de milliers de gens dégainent leur téléphone en même temps. Le réseau mobile, qui est dimensionné pour un usage moyen et pas pour un pic massif simultané, devrait théoriquement s'effondrer. La plupart du temps, il tient. Pas parfaitement, pas pour tout le monde, mais il tient — et surtout, les appels d'urgence continuent de passer. C'est le résultat d'une série de mécanismes empilés depuis les années 1990, et que la 4G et la 5G ont raffinés. Voici comment ça marche, sans le jargon mais sans non plus mentir sur ce qui se passe vraiment.
|
|
||||||
|
|
||||||
## Trois questions, pas une
|
|
||||||
|
|
||||||
Dans un réseau cellulaire moderne, l'opérateur doit répondre à trois questions distinctes quand la cellule commence à chauffer. Qui a le droit de se connecter ? Une fois connecté, qui passe en premier ? Et quels services doivent absolument continuer à fonctionner, quoi qu'il arrive ?
|
|
||||||
|
|
||||||
La 2G ne savait répondre qu'à la première. Elle filtrait à l'entrée et basta. La 4G a ajouté la deuxième : une fois admis sur le réseau, votre trafic n'est plus traité de la même manière selon son importance. La 5G ajoute la troisième : elle peut littéralement créer des réseaux virtuels parallèles, dont certains sont réservés à des usages critiques et isolés des autres.
|
|
||||||
|
|
||||||
## En 4G : filtrer puis prioriser
|
|
||||||
|
|
||||||
### Filtrer à l'entrée
|
|
||||||
|
|
||||||
Chaque carte SIM porte un numéro de classe d'accès, hérité du GSM, entre 0 et 15. Les classes 0 à 9 couvrent le grand public — autrement dit nous tous. Les classes 11 à 15 sont réservées : services de secours, autorités publiques, personnel opérateur, usages militaires selon les pays.
|
|
||||||
|
|
||||||
Quand une cellule est surchargée, l'eNodeB (la station de base 4G) diffuse une consigne aux téléphones du secteur : « les classes 0 à 9, vous attendez ». C'est l'**Access Class Barring**. Concrètement, votre téléphone reçoit ce message et bloque lui-même votre tentative d'appel ou de connexion data, sans même envoyer la demande à la station. C'est élégant parce que ça soulage la station avant même qu'elle ne soit sollicitée. Les classes prioritaires, elles, passent sans encombre.
|
|
||||||
|
|
||||||
Il existe une variante plus dure appelée **Extended Access Barring**, conçue pour les objets connectés et les usages non urgents. Quand une vraie crise se déclare, l'opérateur peut couper les compteurs intelligents, les alarmes domestiques et autres équipements bavards pour préserver la bande passante humaine.
|
|
||||||
|
|
||||||
### Prioriser une fois connecté
|
|
||||||
|
|
||||||
Là où la 4G a vraiment innové, c'est en introduisant le **QCI** — *QoS Class Identifier*. Chaque flux de données qui transite sur le réseau se voit attribuer un numéro entre 1 et 9 (et quelques valeurs au-dessus pour des cas spéciaux) qui dit à l'infrastructure comment le traiter.
|
|
||||||
|
|
||||||
Quelques exemples concrets :
|
|
||||||
|
|
||||||
| Usage | QCI | Traitement |
|
|
||||||
|---|---|---|
|
|
||||||
| Appel VoLTE (voix sur LTE) | 1 | Latence minimale, débit garanti |
|
|
||||||
| Signalisation réseau | 5 | Très haute priorité |
|
|
||||||
| Visioconférence | 2 | Débit garanti |
|
|
||||||
| Streaming vidéo | 6 ou 8 | Best effort prioritaire |
|
|
||||||
| Web et internet général | 9 | Best effort standard |
|
|
||||||
|
|
||||||
Quand la cellule est encombrée, le routeur sait quoi sacrifier en premier. YouTube va ralentir, les pages web vont mettre du temps à charger, mais l'appel téléphonique de votre voisin reste audible. C'est un compromis assumé : on dégrade volontairement les usages secondaires pour préserver les usages critiques.
|
|
||||||
|
|
||||||
## En 5G : ajouter le découpage
|
|
||||||
|
|
||||||
### Un mécanisme d'accès refondu
|
|
||||||
|
|
||||||
La 5G garde l'esprit du barring mais change son nom et sa mécanique. L'ancien Access Class Barring est remplacé par l'**UAC** — *Unified Access Control*, introduit dans la Release 15 du 3GPP. L'idée est d'unifier dans un seul cadre ce qui était auparavant éparpillé entre ACB, EAB et d'autres mécanismes spécifiques.
|
|
||||||
|
|
||||||
UAC repose sur deux notions. Les **Access Identities** identifient qui vous êtes (utilisateur lambda, abonné à un service prioritaire type MPS ou MCS, personnel d'urgence, agent opérateur). Les **Access Categories** identifient ce que vous essayez de faire (appel d'urgence, connexion data normale, SMS, mise à jour de localisation). La combinaison des deux détermine si votre demande passe ou pas.
|
|
||||||
|
|
||||||
Ce qui change vraiment, c'est la granularité. En 4G, on bloquait une classe entière. En 5G, on peut bloquer un type d'action précis pour un type d'utilisateur précis — par exemple « les abonnés grand public ne peuvent plus initier de nouveaux appels data, mais les SMS et les appels voix continuent ». L'opérateur peut aussi définir ses propres catégories d'accès, calées sur sa politique commerciale et technique.
|
|
||||||
|
|
||||||
### Le QCI devient le 5QI
|
|
||||||
|
|
||||||
Même logique qu'en 4G mais avec plus de finesse. Le **5QI** (*5G QoS Identifier*) propose davantage de niveaux et tient compte de cas que la 4G gérait mal, notamment les services à très basse latence pour les usines connectées ou la voiture autonome. La voix d'urgence garde son sommet, les données critiques industrielles s'intercalent juste après, le streaming et le web restent en bas de la pile.
|
|
||||||
|
|
||||||
### La vraie nouveauté : le network slicing
|
|
||||||
|
|
||||||
C'est l'apport majeur de la 5G en termes de gestion de crise. Au lieu de partager une seule infrastructure entre tous les usages, on peut maintenant la **découper logiciellement en tranches** — des *slices* — qui se comportent comme autant de réseaux indépendants, alors qu'ils tournent sur les mêmes antennes et les mêmes câbles.
|
|
||||||
|
|
||||||
Un opérateur peut par exemple maintenir :
|
|
||||||
|
|
||||||
- une tranche pour le grand public, avec ses millions d'abonnés et son trafic massif,
|
|
||||||
- une tranche pour les services d'urgence et de sécurité, dimensionnée pour rester fluide même quand le reste sature,
|
|
||||||
- une tranche pour les objets connectés industriels, avec des garanties de latence,
|
|
||||||
- une tranche pour les opérateurs critiques type SNCF, EDF, hôpitaux.
|
|
||||||
|
|
||||||
Chaque tranche a ses propres règles d'admission, ses propres priorités, ses propres garanties de performance. Si la tranche grand public est totalement saturée, celle des secours ne le sait même pas. Cette isolation est ce qui distingue le plus fondamentalement la 5G des générations précédentes, où tout le monde se battait pour les mêmes ressources, avec juste des priorités différentes.
|
|
||||||
|
|
||||||
## Et le forfait premium dans tout ça ?
|
|
||||||
|
|
||||||
Question qu'on entend souvent : si je paie un forfait à 50 € au lieu d'un forfait à 10 €, est-ce que je passe avant les autres en cas de saturation ?
|
|
||||||
|
|
||||||
Non.
|
|
||||||
|
|
||||||
Les priorités techniques décrites au-dessus ne dépendent ni du prix du forfait, ni des options commerciales souscrites. Elles dépendent du profil réseau associé à votre SIM (lui-même fonction de votre statut : grand public, secours, opérateur, services prioritaires officiels), et des politiques de gestion de crise programmées par l'opérateur. Un cadre dirigeant avec un forfait illimité reste, du point de vue du réseau, un abonné de classe d'accès 0-9 comme tout le monde.
|
|
||||||
|
|
||||||
Le forfait premium vous donne plus de data, parfois un meilleur débit théorique en conditions normales, des options de roaming, du cloud gratuit. Il ne vous donne pas la priorité face à un pompier ou à un préfet.
|
|
||||||
|
|
||||||
## Pour résumer
|
|
||||||
|
|
||||||
| Génération | Ce qui est contrôlé | Comment |
|
|
||||||
|---|---|---|
|
|
||||||
| 2G | L'accès au réseau | Classes d'accès 0-15 |
|
|
||||||
| 4G | L'accès + la priorité du trafic | ACB / EAB + QCI |
|
|
||||||
| 5G | L'accès + la priorité + l'isolation des services | UAC + 5QI + network slicing |
|
|
||||||
|
|
||||||
Ce qui est intéressant, c'est que ces mécanismes restent invisibles tant que tout va bien. Vous ne savez pas qu'ils existent. Vous découvrez leur existence le jour où votre voisin n'arrive plus à charger ses mails alors que les pompiers, eux, continuent de communiquer normalement. Ce jour-là, ce n'est pas de la magie. C'est trente ans d'ingénierie radio qui ont anticipé que ça arriverait.
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
Un attentat, un séisme, un match du Stade de France, une grande panne d'électricité. Dans ces moments-là, des centaines de milliers de gens dégainent leur téléphone en même temps. Le réseau mobile, qui est dimensionné pour un usage moyen et pas pour un pic massif simultané, devrait théoriquement s'effondrer. La plupart du temps, il tient. Pas parfaitement, pas pour tout le monde, mais il tient — et surtout, les appels d'urgence continuent de passer. C'est le résultat d'une série de mécanismes empilés depuis les années 1990, et que la 4G et la 5G ont raffinés. Voici comment ça marche, sans le jargon mais sans non plus mentir sur ce qui se passe vraiment.
|
|
||||||
|
|
||||||
## Trois questions, pas une
|
|
||||||
|
|
||||||
Dans un réseau cellulaire moderne, l'opérateur doit répondre à trois questions distinctes quand la cellule commence à chauffer. Qui a le droit de se connecter ? Une fois connecté, qui passe en premier ? Et quels services doivent absolument continuer à fonctionner, quoi qu'il arrive ?
|
|
||||||
|
|
||||||
La 2G ne savait répondre qu'à la première. Elle filtrait à l'entrée et basta. La 4G a ajouté la deuxième : une fois admis sur le réseau, votre trafic n'est plus traité de la même manière selon son importance. La 5G ajoute la troisième : elle peut littéralement créer des réseaux virtuels parallèles, dont certains sont réservés à des usages critiques et isolés des autres.
|
|
||||||
|
|
||||||
## En 4G : filtrer puis prioriser
|
|
||||||
|
|
||||||
### Filtrer à l'entrée
|
|
||||||
|
|
||||||
Chaque carte SIM porte un numéro de classe d'accès, hérité du GSM, entre 0 et 15. Les classes 0 à 9 couvrent le grand public — autrement dit nous tous. Les classes 11 à 15 sont réservées : services de secours, autorités publiques, personnel opérateur, usages militaires selon les pays.
|
|
||||||
|
|
||||||
Quand une cellule est surchargée, l'eNodeB (la station de base 4G) diffuse une consigne aux téléphones du secteur : « les classes 0 à 9, vous attendez ». C'est l'**Access Class Barring**. Concrètement, votre téléphone reçoit ce message et bloque lui-même votre tentative d'appel ou de connexion data, sans même envoyer la demande à la station. C'est élégant parce que ça soulage la station avant même qu'elle ne soit sollicitée. Les classes prioritaires, elles, passent sans encombre.
|
|
||||||
|
|
||||||
Il existe une variante plus dure appelée **Extended Access Barring**, conçue pour les objets connectés et les usages non urgents. Quand une vraie crise se déclare, l'opérateur peut couper les compteurs intelligents, les alarmes domestiques et autres équipements bavards pour préserver la bande passante humaine.
|
|
||||||
|
|
||||||
### Prioriser une fois connecté
|
|
||||||
|
|
||||||
Là où la 4G a vraiment innové, c'est en introduisant le **QCI** — *QoS Class Identifier*. Chaque flux de données qui transite sur le réseau se voit attribuer un numéro entre 1 et 9 (et quelques valeurs au-dessus pour des cas spéciaux) qui dit à l'infrastructure comment le traiter.
|
|
||||||
|
|
||||||
Quelques exemples concrets :
|
|
||||||
|
|
||||||
| Usage | QCI | Traitement |
|
|
||||||
|---|---|---|
|
|
||||||
| Appel VoLTE (voix sur LTE) | 1 | Latence minimale, débit garanti |
|
|
||||||
| Signalisation réseau | 5 | Très haute priorité |
|
|
||||||
| Visioconférence | 2 | Débit garanti |
|
|
||||||
| Streaming vidéo | 6 ou 8 | Best effort prioritaire |
|
|
||||||
| Web et internet général | 9 | Best effort standard |
|
|
||||||
|
|
||||||
Quand la cellule est encombrée, le routeur sait quoi sacrifier en premier. YouTube va ralentir, les pages web vont mettre du temps à charger, mais l'appel téléphonique de votre voisin reste audible. C'est un compromis assumé : on dégrade volontairement les usages secondaires pour préserver les usages critiques.
|
|
||||||
|
|
||||||
## En 5G : ajouter le découpage
|
|
||||||
|
|
||||||
### Un mécanisme d'accès refondu
|
|
||||||
|
|
||||||
La 5G garde l'esprit du barring mais change son nom et sa mécanique. L'ancien Access Class Barring est remplacé par l'**UAC** — *Unified Access Control*, introduit dans la Release 15 du 3GPP. L'idée est d'unifier dans un seul cadre ce qui était auparavant éparpillé entre ACB, EAB et d'autres mécanismes spécifiques.
|
|
||||||
|
|
||||||
UAC repose sur deux notions. Les **Access Identities** identifient qui vous êtes (utilisateur lambda, abonné à un service prioritaire type MPS ou MCS, personnel d'urgence, agent opérateur). Les **Access Categories** identifient ce que vous essayez de faire (appel d'urgence, connexion data normale, SMS, mise à jour de localisation). La combinaison des deux détermine si votre demande passe ou pas.
|
|
||||||
|
|
||||||
Ce qui change vraiment, c'est la granularité. En 4G, on bloquait une classe entière. En 5G, on peut bloquer un type d'action précis pour un type d'utilisateur précis — par exemple « les abonnés grand public ne peuvent plus initier de nouveaux appels data, mais les SMS et les appels voix continuent ». L'opérateur peut aussi définir ses propres catégories d'accès, calées sur sa politique commerciale et technique.
|
|
||||||
|
|
||||||
### Le QCI devient le 5QI
|
|
||||||
|
|
||||||
Même logique qu'en 4G mais avec plus de finesse. Le **5QI** (*5G QoS Identifier*) propose davantage de niveaux et tient compte de cas que la 4G gérait mal, notamment les services à très basse latence pour les usines connectées ou la voiture autonome. La voix d'urgence garde son sommet, les données critiques industrielles s'intercalent juste après, le streaming et le web restent en bas de la pile.
|
|
||||||
|
|
||||||
### La vraie nouveauté : le network slicing
|
|
||||||
|
|
||||||
C'est l'apport majeur de la 5G en termes de gestion de crise. Au lieu de partager une seule infrastructure entre tous les usages, on peut maintenant la **découper logiciellement en tranches** — des *slices* — qui se comportent comme autant de réseaux indépendants, alors qu'ils tournent sur les mêmes antennes et les mêmes câbles.
|
|
||||||
|
|
||||||
Un opérateur peut par exemple maintenir :
|
|
||||||
|
|
||||||
- une tranche pour le grand public, avec ses millions d'abonnés et son trafic massif,
|
|
||||||
- une tranche pour les services d'urgence et de sécurité, dimensionnée pour rester fluide même quand le reste sature,
|
|
||||||
- une tranche pour les objets connectés industriels, avec des garanties de latence,
|
|
||||||
- une tranche pour les opérateurs critiques type SNCF, EDF, hôpitaux.
|
|
||||||
|
|
||||||
Chaque tranche a ses propres règles d'admission, ses propres priorités, ses propres garanties de performance. Si la tranche grand public est totalement saturée, celle des secours ne le sait même pas. Cette isolation est ce qui distingue le plus fondamentalement la 5G des générations précédentes, où tout le monde se battait pour les mêmes ressources, avec juste des priorités différentes.
|
|
||||||
|
|
||||||
## Et le forfait premium dans tout ça ?
|
|
||||||
|
|
||||||
Question qu'on entend souvent : si je paie un forfait à 50 € au lieu d'un forfait à 10 €, est-ce que je passe avant les autres en cas de saturation ?
|
|
||||||
|
|
||||||
Non.
|
|
||||||
|
|
||||||
Les priorités techniques décrites au-dessus ne dépendent ni du prix du forfait, ni des options commerciales souscrites. Elles dépendent du profil réseau associé à votre SIM (lui-même fonction de votre statut : grand public, secours, opérateur, services prioritaires officiels), et des politiques de gestion de crise programmées par l'opérateur. Un cadre dirigeant avec un forfait illimité reste, du point de vue du réseau, un abonné de classe d'accès 0-9 comme tout le monde.
|
|
||||||
|
|
||||||
Le forfait premium vous donne plus de data, parfois un meilleur débit théorique en conditions normales, des options de roaming, du cloud gratuit. Il ne vous donne pas la priorité face à un pompier ou à un préfet.
|
|
||||||
|
|
||||||
## Pour résumer
|
|
||||||
|
|
||||||
| Génération | Ce qui est contrôlé | Comment |
|
|
||||||
|---|---|---|
|
|
||||||
| 2G | L'accès au réseau | Classes d'accès 0-15 |
|
|
||||||
| 4G | L'accès + la priorité du trafic | ACB / EAB + QCI |
|
|
||||||
| 5G | L'accès + la priorité + l'isolation des services | UAC + 5QI + network slicing |
|
|
||||||
|
|
||||||
Ce qui est intéressant, c'est que ces mécanismes restent invisibles tant que tout va bien. Vous ne savez pas qu'ils existent. Vous découvrez leur existence le jour où votre voisin n'arrive plus à charger ses mails alors que les pompiers, eux, continuent de communiquer normalement. Ce jour-là, ce n'est pas de la magie. C'est trente ans d'ingénierie radio qui ont anticipé que ça arriverait.
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
Un attentat, un séisme, un match du Stade de France, une grande panne d'électricité. Dans ces moments-là, des centaines de milliers de gens dégainent leur téléphone en même temps. Le réseau mobile, qui est dimensionné pour un usage moyen et pas pour un pic massif simultané, devrait théoriquement s'effondrer. La plupart du temps, il tient. Pas parfaitement, pas pour tout le monde, mais il tient — et surtout, les appels d'urgence continuent de passer. C'est le résultat d'une série de mécanismes empilés depuis les années 1990, et que la 4G et la 5G ont raffinés. Voici comment ça marche, sans le jargon mais sans non plus mentir sur ce qui se passe vraiment.
|
|
||||||
|
|
||||||
## Trois questions, pas une
|
|
||||||
|
|
||||||
Dans un réseau cellulaire moderne, l'opérateur doit répondre à trois questions distinctes quand la cellule commence à chauffer. Qui a le droit de se connecter ? Une fois connecté, qui passe en premier ? Et quels services doivent absolument continuer à fonctionner, quoi qu'il arrive ?
|
|
||||||
|
|
||||||
La 2G ne savait répondre qu'à la première. Elle filtrait à l'entrée et basta. La 4G a ajouté la deuxième : une fois admis sur le réseau, votre trafic n'est plus traité de la même manière selon son importance. La 5G ajoute la troisième : elle peut littéralement créer des réseaux virtuels parallèles, dont certains sont réservés à des usages critiques et isolés des autres.
|
|
||||||
|
|
||||||
## En 4G : filtrer puis prioriser
|
|
||||||
|
|
||||||
### Filtrer à l'entrée
|
|
||||||
|
|
||||||
Chaque carte SIM porte un numéro de classe d'accès, hérité du GSM, entre 0 et 15. Les classes 0 à 9 couvrent le grand public — autrement dit nous tous. Les classes 11 à 15 sont réservées : services de secours, autorités publiques, personnel opérateur, usages militaires selon les pays.
|
|
||||||
|
|
||||||
Quand une cellule est surchargée, l'eNodeB (la station de base 4G) diffuse une consigne aux téléphones du secteur : « les classes 0 à 9, vous attendez ». C'est l'**Access Class Barring**. Concrètement, votre téléphone reçoit ce message et bloque lui-même votre tentative d'appel ou de connexion data, sans même envoyer la demande à la station. C'est élégant parce que ça soulage la station avant même qu'elle ne soit sollicitée. Les classes prioritaires, elles, passent sans encombre.
|
|
||||||
|
|
||||||
Il existe une variante plus dure appelée **Extended Access Barring**, conçue pour les objets connectés et les usages non urgents. Quand une vraie crise se déclare, l'opérateur peut couper les compteurs intelligents, les alarmes domestiques et autres équipements bavards pour préserver la bande passante humaine.
|
|
||||||
|
|
||||||
### Prioriser une fois connecté
|
|
||||||
|
|
||||||
Là où la 4G a vraiment innové, c'est en introduisant le **QCI** — *QoS Class Identifier*. Chaque flux de données qui transite sur le réseau se voit attribuer un numéro entre 1 et 9 (et quelques valeurs au-dessus pour des cas spéciaux) qui dit à l'infrastructure comment le traiter.
|
|
||||||
|
|
||||||
Quelques exemples concrets :
|
|
||||||
|
|
||||||
| Usage | QCI | Traitement |
|
|
||||||
|---|---|---|
|
|
||||||
| Appel VoLTE (voix sur LTE) | 1 | Latence minimale, débit garanti |
|
|
||||||
| Signalisation réseau | 5 | Très haute priorité |
|
|
||||||
| Visioconférence | 2 | Débit garanti |
|
|
||||||
| Streaming vidéo | 6 ou 8 | Best effort prioritaire |
|
|
||||||
| Web et internet général | 9 | Best effort standard |
|
|
||||||
|
|
||||||
Quand la cellule est encombrée, le routeur sait quoi sacrifier en premier. YouTube va ralentir, les pages web vont mettre du temps à charger, mais l'appel téléphonique de votre voisin reste audible. C'est un compromis assumé : on dégrade volontairement les usages secondaires pour préserver les usages critiques.
|
|
||||||
|
|
||||||
## En 5G : ajouter le découpage
|
|
||||||
|
|
||||||
### Un mécanisme d'accès refondu
|
|
||||||
|
|
||||||
La 5G garde l'esprit du barring mais change son nom et sa mécanique. L'ancien Access Class Barring est remplacé par l'**UAC** — *Unified Access Control*, introduit dans la Release 15 du 3GPP. L'idée est d'unifier dans un seul cadre ce qui était auparavant éparpillé entre ACB, EAB et d'autres mécanismes spécifiques.
|
|
||||||
|
|
||||||
UAC repose sur deux notions. Les **Access Identities** identifient qui vous êtes (utilisateur lambda, abonné à un service prioritaire type MPS ou MCS, personnel d'urgence, agent opérateur). Les **Access Categories** identifient ce que vous essayez de faire (appel d'urgence, connexion data normale, SMS, mise à jour de localisation). La combinaison des deux détermine si votre demande passe ou pas.
|
|
||||||
|
|
||||||
Ce qui change vraiment, c'est la granularité. En 4G, on bloquait une classe entière. En 5G, on peut bloquer un type d'action précis pour un type d'utilisateur précis — par exemple « les abonnés grand public ne peuvent plus initier de nouveaux appels data, mais les SMS et les appels voix continuent ». L'opérateur peut aussi définir ses propres catégories d'accès, calées sur sa politique commerciale et technique.
|
|
||||||
|
|
||||||
### Le QCI devient le 5QI
|
|
||||||
|
|
||||||
Même logique qu'en 4G mais avec plus de finesse. Le **5QI** (*5G QoS Identifier*) propose davantage de niveaux et tient compte de cas que la 4G gérait mal, notamment les services à très basse latence pour les usines connectées ou la voiture autonome. La voix d'urgence garde son sommet, les données critiques industrielles s'intercalent juste après, le streaming et le web restent en bas de la pile.
|
|
||||||
|
|
||||||
### La vraie nouveauté : le network slicing
|
|
||||||
|
|
||||||
C'est l'apport majeur de la 5G en termes de gestion de crise. Au lieu de partager une seule infrastructure entre tous les usages, on peut maintenant la **découper logiciellement en tranches** — des *slices* — qui se comportent comme autant de réseaux indépendants, alors qu'ils tournent sur les mêmes antennes et les mêmes câbles.
|
|
||||||
|
|
||||||
Un opérateur peut par exemple maintenir :
|
|
||||||
|
|
||||||
- une tranche pour le grand public, avec ses millions d'abonnés et son trafic massif,
|
|
||||||
- une tranche pour les services d'urgence et de sécurité, dimensionnée pour rester fluide même quand le reste sature,
|
|
||||||
- une tranche pour les objets connectés industriels, avec des garanties de latence,
|
|
||||||
- une tranche pour les opérateurs critiques type SNCF, EDF, hôpitaux.
|
|
||||||
|
|
||||||
Chaque tranche a ses propres règles d'admission, ses propres priorités, ses propres garanties de performance. Si la tranche grand public est totalement saturée, celle des secours ne le sait même pas. Cette isolation est ce qui distingue le plus fondamentalement la 5G des générations précédentes, où tout le monde se battait pour les mêmes ressources, avec juste des priorités différentes.
|
|
||||||
|
|
||||||
## Et le forfait premium dans tout ça ?
|
|
||||||
|
|
||||||
Question qu'on entend souvent : si je paie un forfait à 50 € au lieu d'un forfait à 10 €, est-ce que je passe avant les autres en cas de saturation ?
|
|
||||||
|
|
||||||
Non.
|
|
||||||
|
|
||||||
Les priorités techniques décrites au-dessus ne dépendent ni du prix du forfait, ni des options commerciales souscrites. Elles dépendent du profil réseau associé à votre SIM (lui-même fonction de votre statut : grand public, secours, opérateur, services prioritaires officiels), et des politiques de gestion de crise programmées par l'opérateur. Un cadre dirigeant avec un forfait illimité reste, du point de vue du réseau, un abonné de classe d'accès 0-9 comme tout le monde.
|
|
||||||
|
|
||||||
Le forfait premium vous donne plus de data, parfois un meilleur débit théorique en conditions normales, des options de roaming, du cloud gratuit. Il ne vous donne pas la priorité face à un pompier ou à un préfet.
|
|
||||||
|
|
||||||
## Pour résumer
|
|
||||||
|
|
||||||
| Génération | Ce qui est contrôlé | Comment |
|
|
||||||
|---|---|---|
|
|
||||||
| 2G | L'accès au réseau | Classes d'accès 0-15 |
|
|
||||||
| 4G | L'accès + la priorité du trafic | ACB / EAB + QCI |
|
|
||||||
| 5G | L'accès + la priorité + l'isolation des services | UAC + 5QI + network slicing |
|
|
||||||
|
|
||||||
Ce qui est intéressant, c'est que ces mécanismes restent invisibles tant que tout va bien. Vous ne savez pas qu'ils existent. Vous découvrez leur existence le jour où votre voisin n'arrive plus à charger ses mails alors que les pompiers, eux, continuent de communiquer normalement. Ce jour-là, ce n'est pas de la magie. C'est trente ans d'ingénierie radio qui ont anticipé que ça arriverait.
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
Un attentat, un séisme, un match du Stade de France, une grande panne d'électricité. Dans ces moments-là, des centaines de milliers de gens dégainent leur téléphone en même temps. Le réseau mobile, qui est dimensionné pour un usage moyen et pas pour un pic massif simultané, devrait théoriquement s'effondrer. La plupart du temps, il tient. Pas parfaitement, pas pour tout le monde, mais il tient — et surtout, les appels d'urgence continuent de passer. C'est le résultat d'une série de mécanismes empilés depuis les années 1990, et que la 4G et la 5G ont raffinés. Voici comment ça marche, sans le jargon mais sans non plus mentir sur ce qui se passe vraiment.
|
|
||||||
|
|
||||||
## Trois questions, pas une
|
|
||||||
|
|
||||||
Dans un réseau cellulaire moderne, l'opérateur doit répondre à trois questions distinctes quand la cellule commence à chauffer. Qui a le droit de se connecter ? Une fois connecté, qui passe en premier ? Et quels services doivent absolument continuer à fonctionner, quoi qu'il arrive ?
|
|
||||||
|
|
||||||
La 2G ne savait répondre qu'à la première. Elle filtrait à l'entrée et basta. La 4G a ajouté la deuxième : une fois admis sur le réseau, votre trafic n'est plus traité de la même manière selon son importance. La 5G ajoute la troisième : elle peut littéralement créer des réseaux virtuels parallèles, dont certains sont réservés à des usages critiques et isolés des autres.
|
|
||||||
|
|
||||||
## En 4G : filtrer puis prioriser
|
|
||||||
|
|
||||||
### Filtrer à l'entrée
|
|
||||||
|
|
||||||
Chaque carte SIM porte un numéro de classe d'accès, hérité du GSM, entre 0 et 15. Les classes 0 à 9 couvrent le grand public — autrement dit nous tous. Les classes 11 à 15 sont réservées : services de secours, autorités publiques, personnel opérateur, usages militaires selon les pays.
|
|
||||||
|
|
||||||
Quand une cellule est surchargée, l'eNodeB (la station de base 4G) diffuse une consigne aux téléphones du secteur : « les classes 0 à 9, vous attendez ». C'est l'**Access Class Barring**. Concrètement, votre téléphone reçoit ce message et bloque lui-même votre tentative d'appel ou de connexion data, sans même envoyer la demande à la station. C'est élégant parce que ça soulage la station avant même qu'elle ne soit sollicitée. Les classes prioritaires, elles, passent sans encombre.
|
|
||||||
|
|
||||||
Il existe une variante plus dure appelée **Extended Access Barring**, conçue pour les objets connectés et les usages non urgents. Quand une vraie crise se déclare, l'opérateur peut couper les compteurs intelligents, les alarmes domestiques et autres équipements bavards pour préserver la bande passante humaine.
|
|
||||||
|
|
||||||
### Prioriser une fois connecté
|
|
||||||
|
|
||||||
Là où la 4G a vraiment innové, c'est en introduisant le **QCI** — *QoS Class Identifier*. Chaque flux de données qui transite sur le réseau se voit attribuer un numéro entre 1 et 9 (et quelques valeurs au-dessus pour des cas spéciaux) qui dit à l'infrastructure comment le traiter.
|
|
||||||
|
|
||||||
Quelques exemples concrets :
|
|
||||||
|
|
||||||
| Usage | QCI | Traitement |
|
|
||||||
|---|---|---|
|
|
||||||
| Appel VoLTE (voix sur LTE) | 1 | Latence minimale, débit garanti |
|
|
||||||
| Signalisation réseau | 5 | Très haute priorité |
|
|
||||||
| Visioconférence | 2 | Débit garanti |
|
|
||||||
| Streaming vidéo | 6 ou 8 | Best effort prioritaire |
|
|
||||||
| Web et internet général | 9 | Best effort standard |
|
|
||||||
|
|
||||||
Quand la cellule est encombrée, le routeur sait quoi sacrifier en premier. YouTube va ralentir, les pages web vont mettre du temps à charger, mais l'appel téléphonique de votre voisin reste audible. C'est un compromis assumé : on dégrade volontairement les usages secondaires pour préserver les usages critiques.
|
|
||||||
|
|
||||||
## En 5G : ajouter le découpage
|
|
||||||
|
|
||||||
### Un mécanisme d'accès refondu
|
|
||||||
|
|
||||||
La 5G garde l'esprit du barring mais change son nom et sa mécanique. L'ancien Access Class Barring est remplacé par l'**UAC** — *Unified Access Control*, introduit dans la Release 15 du 3GPP. L'idée est d'unifier dans un seul cadre ce qui était auparavant éparpillé entre ACB, EAB et d'autres mécanismes spécifiques.
|
|
||||||
|
|
||||||
UAC repose sur deux notions. Les **Access Identities** identifient qui vous êtes (utilisateur lambda, abonné à un service prioritaire type MPS ou MCS, personnel d'urgence, agent opérateur). Les **Access Categories** identifient ce que vous essayez de faire (appel d'urgence, connexion data normale, SMS, mise à jour de localisation). La combinaison des deux détermine si votre demande passe ou pas.
|
|
||||||
|
|
||||||
Ce qui change vraiment, c'est la granularité. En 4G, on bloquait une classe entière. En 5G, on peut bloquer un type d'action précis pour un type d'utilisateur précis — par exemple « les abonnés grand public ne peuvent plus initier de nouveaux appels data, mais les SMS et les appels voix continuent ». L'opérateur peut aussi définir ses propres catégories d'accès, calées sur sa politique commerciale et technique.
|
|
||||||
|
|
||||||
### Le QCI devient le 5QI
|
|
||||||
|
|
||||||
Même logique qu'en 4G mais avec plus de finesse. Le **5QI** (*5G QoS Identifier*) propose davantage de niveaux et tient compte de cas que la 4G gérait mal, notamment les services à très basse latence pour les usines connectées ou la voiture autonome. La voix d'urgence garde son sommet, les données critiques industrielles s'intercalent juste après, le streaming et le web restent en bas de la pile.
|
|
||||||
|
|
||||||
### La vraie nouveauté : le network slicing
|
|
||||||
|
|
||||||
C'est l'apport majeur de la 5G en termes de gestion de crise. Au lieu de partager une seule infrastructure entre tous les usages, on peut maintenant la **découper logiciellement en tranches** — des *slices* — qui se comportent comme autant de réseaux indépendants, alors qu'ils tournent sur les mêmes antennes et les mêmes câbles.
|
|
||||||
|
|
||||||
Un opérateur peut par exemple maintenir :
|
|
||||||
|
|
||||||
- une tranche pour le grand public, avec ses millions d'abonnés et son trafic massif,
|
|
||||||
- une tranche pour les services d'urgence et de sécurité, dimensionnée pour rester fluide même quand le reste sature,
|
|
||||||
- une tranche pour les objets connectés industriels, avec des garanties de latence,
|
|
||||||
- une tranche pour les opérateurs critiques type SNCF, EDF, hôpitaux.
|
|
||||||
|
|
||||||
Chaque tranche a ses propres règles d'admission, ses propres priorités, ses propres garanties de performance. Si la tranche grand public est totalement saturée, celle des secours ne le sait même pas. Cette isolation est ce qui distingue le plus fondamentalement la 5G des générations précédentes, où tout le monde se battait pour les mêmes ressources, avec juste des priorités différentes.
|
|
||||||
|
|
||||||
## Et le forfait premium dans tout ça ?
|
|
||||||
|
|
||||||
Question qu'on entend souvent : si je paie un forfait à 50 € au lieu d'un forfait à 10 €, est-ce que je passe avant les autres en cas de saturation ?
|
|
||||||
|
|
||||||
Non.
|
|
||||||
|
|
||||||
Les priorités techniques décrites au-dessus ne dépendent ni du prix du forfait, ni des options commerciales souscrites. Elles dépendent du profil réseau associé à votre SIM (lui-même fonction de votre statut : grand public, secours, opérateur, services prioritaires officiels), et des politiques de gestion de crise programmées par l'opérateur. Un cadre dirigeant avec un forfait illimité reste, du point de vue du réseau, un abonné de classe d'accès 0-9 comme tout le monde.
|
|
||||||
|
|
||||||
Le forfait premium vous donne plus de data, parfois un meilleur débit théorique en conditions normales, des options de roaming, du cloud gratuit. Il ne vous donne pas la priorité face à un pompier ou à un préfet.
|
|
||||||
|
|
||||||
## Pour résumer
|
|
||||||
|
|
||||||
| Génération | Ce qui est contrôlé | Comment |
|
|
||||||
|---|---|---|
|
|
||||||
| 2G | L'accès au réseau | Classes d'accès 0-15 |
|
|
||||||
| 4G | L'accès + la priorité du trafic | ACB / EAB + QCI |
|
|
||||||
| 5G | L'accès + la priorité + l'isolation des services | UAC + 5QI + network slicing |
|
|
||||||
|
|
||||||
Ce qui est intéressant, c'est que ces mécanismes restent invisibles tant que tout va bien. Vous ne savez pas qu'ils existent. Vous découvrez leur existence le jour où votre voisin n'arrive plus à charger ses mails alors que les pompiers, eux, continuent de communiquer normalement. Ce jour-là, ce n'est pas de la magie. C'est trente ans d'ingénierie radio qui ont anticipé que ça arriverait.
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
Un attentat, un séisme, un match du Stade de France, une grande panne d'électricité. Dans ces moments-là, des centaines de milliers de gens dégainent leur téléphone en même temps. Le réseau mobile, qui est dimensionné pour un usage moyen et pas pour un pic massif simultané, devrait théoriquement s'effondrer. La plupart du temps, il tient. Pas parfaitement, pas pour tout le monde, mais il tient — et surtout, les appels d'urgence continuent de passer. C'est le résultat d'une série de mécanismes empilés depuis les années 1990, et que la 4G et la 5G ont raffinés. Voici comment ça marche, sans le jargon mais sans non plus mentir sur ce qui se passe vraiment.
|
|
||||||
|
|
||||||
## Trois questions, pas une
|
|
||||||
|
|
||||||
Dans un réseau cellulaire moderne, l'opérateur doit répondre à trois questions distinctes quand la cellule commence à chauffer. Qui a le droit de se connecter ? Une fois connecté, qui passe en premier ? Et quels services doivent absolument continuer à fonctionner, quoi qu'il arrive ?
|
|
||||||
|
|
||||||
La 2G ne savait répondre qu'à la première. Elle filtrait à l'entrée et basta. La 4G a ajouté la deuxième : une fois admis sur le réseau, votre trafic n'est plus traité de la même manière selon son importance. La 5G ajoute la troisième : elle peut littéralement créer des réseaux virtuels parallèles, dont certains sont réservés à des usages critiques et isolés des autres.
|
|
||||||
|
|
||||||
## En 4G : filtrer puis prioriser
|
|
||||||
|
|
||||||
### Filtrer à l'entrée
|
|
||||||
|
|
||||||
Chaque carte SIM porte un numéro de classe d'accès, hérité du GSM, entre 0 et 15. Les classes 0 à 9 couvrent le grand public — autrement dit nous tous. Les classes 11 à 15 sont réservées : services de secours, autorités publiques, personnel opérateur, usages militaires selon les pays.
|
|
||||||
|
|
||||||
Quand une cellule est surchargée, l'eNodeB (la station de base 4G) diffuse une consigne aux téléphones du secteur : « les classes 0 à 9, vous attendez ». C'est l'**Access Class Barring**. Concrètement, votre téléphone reçoit ce message et bloque lui-même votre tentative d'appel ou de connexion data, sans même envoyer la demande à la station. C'est élégant parce que ça soulage la station avant même qu'elle ne soit sollicitée. Les classes prioritaires, elles, passent sans encombre.
|
|
||||||
|
|
||||||
Il existe une variante plus dure appelée **Extended Access Barring**, conçue pour les objets connectés et les usages non urgents. Quand une vraie crise se déclare, l'opérateur peut couper les compteurs intelligents, les alarmes domestiques et autres équipements bavards pour préserver la bande passante humaine.
|
|
||||||
|
|
||||||
### Prioriser une fois connecté
|
|
||||||
|
|
||||||
Là où la 4G a vraiment innové, c'est en introduisant le **QCI** — *QoS Class Identifier*. Chaque flux de données qui transite sur le réseau se voit attribuer un numéro entre 1 et 9 (et quelques valeurs au-dessus pour des cas spéciaux) qui dit à l'infrastructure comment le traiter.
|
|
||||||
|
|
||||||
Quelques exemples concrets :
|
|
||||||
|
|
||||||
| Usage | QCI | Traitement |
|
|
||||||
|---|---|---|
|
|
||||||
| Appel VoLTE (voix sur LTE) | 1 | Latence minimale, débit garanti |
|
|
||||||
| Signalisation réseau | 5 | Très haute priorité |
|
|
||||||
| Visioconférence | 2 | Débit garanti |
|
|
||||||
| Streaming vidéo | 6 ou 8 | Best effort prioritaire |
|
|
||||||
| Web et internet général | 9 | Best effort standard |
|
|
||||||
|
|
||||||
Quand la cellule est encombrée, le routeur sait quoi sacrifier en premier. YouTube va ralentir, les pages web vont mettre du temps à charger, mais l'appel téléphonique de votre voisin reste audible. C'est un compromis assumé : on dégrade volontairement les usages secondaires pour préserver les usages critiques.
|
|
||||||
|
|
||||||
## En 5G : ajouter le découpage
|
|
||||||
|
|
||||||
### Un mécanisme d'accès refondu
|
|
||||||
|
|
||||||
La 5G garde l'esprit du barring mais change son nom et sa mécanique. L'ancien Access Class Barring est remplacé par l'**UAC** — *Unified Access Control*, introduit dans la Release 15 du 3GPP. L'idée est d'unifier dans un seul cadre ce qui était auparavant éparpillé entre ACB, EAB et d'autres mécanismes spécifiques.
|
|
||||||
|
|
||||||
UAC repose sur deux notions. Les **Access Identities** identifient qui vous êtes (utilisateur lambda, abonné à un service prioritaire type MPS ou MCS, personnel d'urgence, agent opérateur). Les **Access Categories** identifient ce que vous essayez de faire (appel d'urgence, connexion data normale, SMS, mise à jour de localisation). La combinaison des deux détermine si votre demande passe ou pas.
|
|
||||||
|
|
||||||
Ce qui change vraiment, c'est la granularité. En 4G, on bloquait une classe entière. En 5G, on peut bloquer un type d'action précis pour un type d'utilisateur précis — par exemple « les abonnés grand public ne peuvent plus initier de nouveaux appels data, mais les SMS et les appels voix continuent ». L'opérateur peut aussi définir ses propres catégories d'accès, calées sur sa politique commerciale et technique.
|
|
||||||
|
|
||||||
### Le QCI devient le 5QI
|
|
||||||
|
|
||||||
Même logique qu'en 4G mais avec plus de finesse. Le **5QI** (*5G QoS Identifier*) propose davantage de niveaux et tient compte de cas que la 4G gérait mal, notamment les services à très basse latence pour les usines connectées ou la voiture autonome. La voix d'urgence garde son sommet, les données critiques industrielles s'intercalent juste après, le streaming et le web restent en bas de la pile.
|
|
||||||
|
|
||||||
### La vraie nouveauté : le network slicing
|
|
||||||
|
|
||||||
C'est l'apport majeur de la 5G en termes de gestion de crise. Au lieu de partager une seule infrastructure entre tous les usages, on peut maintenant la **découper logiciellement en tranches** — des *slices* — qui se comportent comme autant de réseaux indépendants, alors qu'ils tournent sur les mêmes antennes et les mêmes câbles.
|
|
||||||
|
|
||||||
Un opérateur peut par exemple maintenir :
|
|
||||||
|
|
||||||
- une tranche pour le grand public, avec ses millions d'abonnés et son trafic massif,
|
|
||||||
- une tranche pour les services d'urgence et de sécurité, dimensionnée pour rester fluide même quand le reste sature,
|
|
||||||
- une tranche pour les objets connectés industriels, avec des garanties de latence,
|
|
||||||
- une tranche pour les opérateurs critiques type SNCF, EDF, hôpitaux.
|
|
||||||
|
|
||||||
Chaque tranche a ses propres règles d'admission, ses propres priorités, ses propres garanties de performance. Si la tranche grand public est totalement saturée, celle des secours ne le sait même pas. Cette isolation est ce qui distingue le plus fondamentalement la 5G des générations précédentes, où tout le monde se battait pour les mêmes ressources, avec juste des priorités différentes.
|
|
||||||
|
|
||||||
## Et le forfait premium dans tout ça ?
|
|
||||||
|
|
||||||
Question qu'on entend souvent : si je paie un forfait à 50 € au lieu d'un forfait à 10 €, est-ce que je passe avant les autres en cas de saturation ?
|
|
||||||
|
|
||||||
Non.
|
|
||||||
|
|
||||||
Les priorités techniques décrites au-dessus ne dépendent ni du prix du forfait, ni des options commerciales souscrites. Elles dépendent du profil réseau associé à votre SIM (lui-même fonction de votre statut : grand public, secours, opérateur, services prioritaires officiels), et des politiques de gestion de crise programmées par l'opérateur. Un cadre dirigeant avec un forfait illimité reste, du point de vue du réseau, un abonné de classe d'accès 0-9 comme tout le monde.
|
|
||||||
|
|
||||||
Le forfait premium vous donne plus de data, parfois un meilleur débit théorique en conditions normales, des options de roaming, du cloud gratuit. Il ne vous donne pas la priorité face à un pompier ou à un préfet.
|
|
||||||
|
|
||||||
## Pour résumer
|
|
||||||
|
|
||||||
| Génération | Ce qui est contrôlé | Comment |
|
|
||||||
|---|---|---|
|
|
||||||
| 2G | L'accès au réseau | Classes d'accès 0-15 |
|
|
||||||
| 4G | L'accès + la priorité du trafic | ACB / EAB + QCI |
|
|
||||||
| 5G | L'accès + la priorité + l'isolation des services | UAC + 5QI + network slicing |
|
|
||||||
|
|
||||||
Ce qui est intéressant, c'est que ces mécanismes restent invisibles tant que tout va bien. Vous ne savez pas qu'ils existent. Vous découvrez leur existence le jour où votre voisin n'arrive plus à charger ses mails alors que les pompiers, eux, continuent de communiquer normalement. Ce jour-là, ce n'est pas de la magie. C'est trente ans d'ingénierie radio qui ont anticipé que ça arriverait.
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
Un attentat, un séisme, un match du Stade de France, une grande panne d'électricité. Dans ces moments-là, des centaines de milliers de gens dégainent leur téléphone au même instant. Le réseau mobile est dimensionné pour un usage moyen, pas pour un pic massif simultané, et il devrait théoriquement s'effondrer. La plupart du temps, il tient. Pas parfaitement, pas pour tout le monde, mais il tient — et surtout, les appels d'urgence continuent de passer. C'est le résultat d'une série de mécanismes empilés depuis les années 1990, que la 4G a affinés et que la 5G a élargis. Cet article les passe en revue, et termine sur une question qu'on me pose souvent : est-ce que mon forfait à 50 € me donne une place prioritaire dans cette file d'attente ?
|
|
||||||
|
|
||||||
## Trois questions, pas une
|
|
||||||
|
|
||||||
Quand une cellule commence à chauffer, l'opérateur doit répondre à trois questions distinctes. Qui a le droit de se connecter ? Une fois connecté, qui passe en premier ? Et quels services doivent absolument continuer à fonctionner, quoi qu'il arrive ?
|
|
||||||
|
|
||||||
La 2G ne savait répondre qu'à la première. Elle filtrait à l'entrée et basta. La 4G a ajouté la deuxième : une fois admis sur le réseau, votre trafic est traité différemment selon son importance. La 5G ajoute la troisième : elle peut créer des réseaux virtuels parallèles dont certains sont réservés à des usages critiques, totalement isolés des autres.
|
|
||||||
|
|
||||||
## Le filtrage à l'entrée
|
|
||||||
|
|
||||||
Chaque carte SIM porte un numéro de classe d'accès, hérité du GSM, entre 0 et 15. Les classes 0 à 9 couvrent le grand public — autrement dit nous tous. Les classes 11 à 15 sont réservées : services de secours, autorités publiques, personnel opérateur, usages militaires selon les pays.
|
|
||||||
|
|
||||||
Quand une cellule est surchargée, l'eNodeB (la station de base 4G) diffuse une consigne aux téléphones du secteur : « les classes 0 à 9, vous attendez ». C'est l'**Access Class Barring**. Concrètement, votre téléphone reçoit ce message et bloque lui-même votre tentative d'appel ou de connexion data, sans même envoyer la demande à la station. C'est élégant parce que ça soulage la station avant même qu'elle ne soit sollicitée. Les classes prioritaires, elles, passent sans encombre.
|
|
||||||
|
|
||||||
Une variante plus dure, l'**Extended Access Barring**, vise les objets connectés et les usages non urgents. Quand une vraie crise se déclare, l'opérateur peut couper les compteurs intelligents, les alarmes domestiques et autres équipements bavards pour préserver la bande passante humaine.
|
|
||||||
|
|
||||||
En 5G, ce mécanisme a été refondu sous le nom d'**UAC** — *Unified Access Control*, introduit dans la Release 15 du 3GPP. UAC unifie dans un seul cadre ce qui était auparavant éparpillé entre ACB, EAB et d'autres dispositifs spécifiques. Il repose sur deux notions complémentaires. Les *Access Identities* identifient qui vous êtes : utilisateur lambda, abonné à un service prioritaire type MPS ou MCS, personnel d'urgence, agent opérateur. Les *Access Categories* identifient ce que vous essayez de faire : appel d'urgence, connexion data normale, SMS, mise à jour de localisation. La combinaison des deux détermine si votre demande passe ou pas. La granularité gagnée par rapport à la 4G est réelle : on peut bloquer un type d'action précis pour un type d'utilisateur précis, par exemple « les abonnés grand public ne peuvent plus initier de nouveaux appels data, mais les SMS et les appels voix continuent ».
|
|
||||||
|
|
||||||
## La priorité une fois connecté
|
|
||||||
|
|
||||||
Là où la 4G a vraiment innové, c'est en introduisant le **QCI** — *QoS Class Identifier*. Chaque flux de données qui transite sur le réseau se voit attribuer un numéro entre 1 et 9 (avec quelques valeurs supplémentaires pour des cas spéciaux) qui dit à l'infrastructure comment le traiter.
|
|
||||||
|
|
||||||
| Usage | QCI | Traitement |
|
|
||||||
|---|---|---|
|
|
||||||
| Appel VoLTE (voix sur LTE) | 1 | Latence minimale, débit garanti |
|
|
||||||
| Visioconférence | 2 | Débit garanti |
|
|
||||||
| Signalisation réseau | 5 | Très haute priorité |
|
|
||||||
| Streaming vidéo | 6 ou 8 | Best effort prioritaire |
|
|
||||||
| Web et internet général | 9 | Best effort standard |
|
|
||||||
|
|
||||||
Quand la cellule est encombrée, le routeur sait quoi sacrifier en premier. YouTube va ralentir, les pages web vont mettre du temps à charger, mais l'appel téléphonique de votre voisin reste audible. C'est un compromis assumé : on dégrade volontairement les usages secondaires pour préserver les usages critiques.
|
|
||||||
|
|
||||||
La 5G a transposé ce mécanisme sous le nom de **5QI** (*5G QoS Identifier*) avec davantage de niveaux et une meilleure prise en compte des cas que la 4G gérait mal — notamment les services à très basse latence pour les usines connectées ou la voiture autonome. La voix d'urgence garde son sommet, les données critiques industrielles s'intercalent juste après, le streaming et le web restent en bas de la pile.
|
|
||||||
|
|
||||||
## L'isolation par tranches : le network slicing
|
|
||||||
|
|
||||||
C'est l'apport majeur de la 5G en matière de gestion de crise. Au lieu de partager une seule infrastructure entre tous les usages, on peut maintenant la découper logiciellement en tranches — des *slices* — qui se comportent comme autant de réseaux indépendants, alors qu'ils tournent sur les mêmes antennes et les mêmes câbles.
|
|
||||||
|
|
||||||
Un opérateur peut par exemple maintenir une tranche pour le grand public avec ses millions d'abonnés et son trafic massif, une autre pour les services d'urgence dimensionnée pour rester fluide même quand le reste sature, une troisième pour les objets connectés industriels avec des garanties de latence, et une quatrième pour des opérateurs critiques type SNCF, EDF ou hôpitaux. Chaque tranche a ses propres règles d'admission, ses propres priorités, ses propres garanties de performance. Si la tranche grand public est totalement saturée, celle des secours ne le sait même pas.
|
|
||||||
|
|
||||||
Cette isolation est ce qui distingue le plus fondamentalement la 5G des générations précédentes. Avant, tout le monde se battait pour les mêmes ressources, avec juste des priorités différentes pour départager. Maintenant, certaines ressources sont retirées du combat dès le départ.
|
|
||||||
|
|
||||||
## Récapitulatif
|
|
||||||
|
|
||||||
| Génération | Ce qui est contrôlé | Comment |
|
|
||||||
|---|---|---|
|
|
||||||
| 2G | L'accès au réseau | Classes d'accès 0-15 |
|
|
||||||
| 4G | L'accès + la priorité du trafic | ACB / EAB + QCI |
|
|
||||||
| 5G | L'accès + la priorité + l'isolation des services | UAC + 5QI + network slicing |
|
|
||||||
|
|
||||||
Tous ces mécanismes restent invisibles tant que tout va bien. Vous ne savez pas qu'ils existent. Vous découvrez leur existence le jour où votre voisin n'arrive plus à charger ses mails alors que les pompiers, eux, continuent de communiquer normalement. Ce jour-là, ce n'est pas de la magie. C'est trente ans d'ingénierie radio qui ont anticipé que ça arriverait.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Et mon forfait premium, alors ?
|
|
||||||
|
|
||||||
Question logique à ce stade. Si le réseau sait techniquement prioriser certains flux par rapport à d'autres, qu'est-ce qui empêche un opérateur de faire passer ses abonnés à 50 € devant ceux à 10 € quand les antennes saturent ? La réponse honnête commence par un aveu : techniquement, rien. L'outil existe, il s'appelle **Quality of Service** (QoS), c'est exactement le mécanisme qu'on vient de décrire. Si demain Orange ou SFR voulaient créer une voie rapide pour leurs abonnés haut de gamme, ils auraient les outils dans la boîte. Pourtant, ils ne le font pas. Pour quatre raisons.
|
|
||||||
|
|
||||||
### La loi européenne l'interdit
|
|
||||||
|
|
||||||
Le règlement **(UE) 2015/2120**, dit « règlement internet ouvert », oblige les opérateurs à traiter tout le trafic de la même façon, sans discrimination liée à l'expéditeur, au destinataire, au contenu ou à l'application. Il a fêté ses dix ans en novembre 2025, et l'ARCEP a profité de l'anniversaire pour rappeler que c'est l'un des piliers du modèle numérique européen. Les sanctions sont sérieuses : jusqu'à **3 % du chiffre d'affaires** de l'opérateur fautif. Un opérateur français qui annoncerait demain « avec notre forfait Premium, vous passez devant les autres » se retrouverait devant l'ARCEP dans la semaine.
|
|
||||||
|
|
||||||
Le règlement laisse quelques portes ouvertes pour les services dits « spécialisés » qui ont besoin d'une qualité garantie — téléchirurgie, voiture connectée. Mais ces exceptions sont étroitement encadrées et ne couvrent absolument pas le confort d'un client haut de gamme qui voudrait charger son Instagram plus vite à 19h.
|
|
||||||
|
|
||||||
Aux États-Unis, l'histoire est différente. La FCC a tenté de restaurer la neutralité du net en 2024, mais en janvier 2025 la cour d'appel du sixième circuit a invalidé la décision, jugeant que la FCC n'avait pas l'autorité légale pour reclasser le haut débit comme service public. Avec l'arrivée de Brendan Carr à la tête de la FCC, ouvertement opposé à la neutralité du net, il n'y a aujourd'hui plus de règle fédérale outre-Atlantique. Quelques États (Californie, Washington, New York, Oregon) ont leurs propres lois qui maintiennent le principe, mais à l'échelle du pays, les opérateurs américains pourraient légalement faire ce que leurs homologues européens n'ont pas le droit de faire. Pourtant, ils ne le font pas ouvertement non plus, et la raison renvoie aux trois points suivants.
|
|
||||||
|
|
||||||
### C'est commercialement intenable
|
|
||||||
|
|
||||||
Imagine la publicité : « Forfait Premium à 50 € — passez devant les pauvres pendant les heures de pointe ». Le slogan ne se vend pas. Les directions marketing savent que dire à la moitié de leurs clients qu'ils sont des citoyens de seconde zone du réseau est le plus court chemin vers une crise de réputation. C'est pour ça qu'on vous vend « plus de Go », « 5G ultra rapide », « roaming inclus dans 110 pays » — des promesses qui sonnent positivement sans jamais dire à personne qu'il est désavantagé.
|
|
||||||
|
|
||||||
### L'effet boule de neige serait toxique
|
|
||||||
|
|
||||||
Imagine que ça se mette quand même en place. Les riches passent devant. Les antennes restent saturées pour les autres, qui se mettent à payer plus pour échapper à la saturation, ce qui sature encore plus les bas forfaits, ce qui pousse encore plus de gens à monter en gamme. Au bout de cinq ans, on a un réseau à deux vitesses où les forfaits modestes deviennent quasi inutilisables aux heures critiques, et où la connexion mobile correcte devient un service de luxe. Ce n'est plus un service de télécommunications, c'est un système de classes.
|
|
||||||
|
|
||||||
C'est exactement ce que la neutralité du net cherche à empêcher. Pas par idéologie, mais parce qu'on a déjà vu où mène ce genre de spirale dans les pays où elle n'est pas protégée. Certains opérateurs proposent par exemple des forfaits où Facebook et WhatsApp sont gratuits mais où le reste est payant, ce qui revient à dire que le bon internet est celui que l'opérateur a choisi pour vous. Ce n'est plus tout à fait le même service.
|
|
||||||
|
|
||||||
### Ça ne résoudrait rien
|
|
||||||
|
|
||||||
Quand un réseau sature, ce n'est pas un problème de répartition entre utilisateurs, c'est un problème de **capacité totale**. Faire passer Pierre avant Paul ne crée pas un seul bit de bande passante supplémentaire. Ça déplace juste le problème de l'un vers l'autre. La vraie solution, quand une cellule sature trop souvent, c'est d'installer plus d'antennes, de densifier le réseau, de basculer sur une fréquence plus performante ou de passer à la génération suivante. C'est cher, c'est long, ça implique des autorisations administratives et des négociations foncières, mais c'est la seule réponse qui tient la route. Prioriser, c'est rapide, mais ça repousse le mur, ça ne le déplace pas.
|
|
||||||
|
|
||||||
C'est comme si on proposait une voie réservée aux Mercedes sur l'A7 un samedi de chassé-croisé. Techniquement, on peut peindre la ligne au sol et installer les panneaux dans la matinée. Mais cette voie ne réduit pas le bouchon, elle le concentre sur les voies restantes ; elle écorne le principe d'égalité d'accès à l'infrastructure publique ; et elle ne change rien au problème de fond, qui est qu'il y a trop de voitures pour la route disponible. La vraie solution reste la même qu'avant : élargir l'autoroute, ou convaincre une partie des gens de prendre le train.
|
|
||||||
|
|
||||||
### Le caveat 5G
|
|
||||||
|
|
||||||
Une nuance honnête pour finir. Le *network slicing* complique le débat juridique. Un opérateur peut créer des tranches de réseau avec des qualités différenciées en toute légalité quand il s'agit d'usages spécialisés — santé, industrie, transports. La question qui agite régulateurs et juristes depuis plusieurs années est de savoir où finit le service spécialisé légitime et où commence le contournement déguisé de la neutralité du net. L'ARCEP a ouvert ce chantier, et c'est probablement là, plus que dans une revanche commerciale brutale sur les forfaits premium, que se jouera la prochaine bataille.
|
|
||||||
|
|
||||||
Mais pour répondre simplement à la question : non, votre forfait à 50 € ne vous donne pas la priorité réseau sur celui de votre voisin à 10 €. Il vous donne plus de data, parfois un meilleur débit théorique, des options en plus. Pas une place dans la file.
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
<svg width="100%" viewBox="0 0 690 440" role="img" xmlns="http://www.w3.org/2000/svg" style="">
|
|
||||||
<title style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Illustration : taxe de 2€ sur les petits colis en provenance de Chine</title>
|
|
||||||
<desc style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Une carte stylisée de la France et de l'Europe avec trois colis aux logos Shein, AliExpress et Temu, et un grand tampon rose indiquant Taxe 2€.</desc>
|
|
||||||
|
|
||||||
<defs>
|
|
||||||
<clipPath id="frame"><rect x="0" y="0" width="680" height="380" rx="8"/></clipPath>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<g clip-path="url(#frame)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
|
|
||||||
<rect x="0" y="0" width="680" height="380" fill="#34c3d8" style="fill:rgb(52, 195, 216);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<rect x="420" y="40" width="240" height="260" rx="4" fill="#f4d7e8" opacity="0.55" style="fill:rgb(244, 215, 232);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.55;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="455" y="70" width="170" height="80" rx="3" fill="#c9e8c2" opacity="0.7" style="fill:rgb(201, 232, 194);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="470" y="170" width="155" height="60" rx="3" fill="#f5e6a8" opacity="0.7" style="fill:rgb(245, 230, 168);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="445" y="245" width="180" height="50" rx="3" fill="#e6c9f0" opacity="0.7" style="fill:rgb(230, 201, 240);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.7;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<path d="M 180 80
 Q 220 70 260 85
 L 295 75
 Q 330 85 345 110
 L 390 115
 Q 410 130 405 160
 L 420 195
 Q 415 230 385 245
 L 360 285
 Q 320 305 285 295
 L 240 310
 Q 195 305 175 280
 L 140 270
 Q 115 245 125 215
 L 110 180
 Q 115 140 145 120
 L 165 95 Z" fill="#fef6dc" stroke="#ffffff" stroke-width="2" style="fill:rgb(254, 246, 220);stroke:rgb(255, 255, 255);color:rgb(0, 0, 0);stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
|
|
||||||
<circle cx="265" cy="180" r="3" fill="#d94a7a" style="fill:rgb(217, 74, 122);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="272" y="184" font-family="var(--font-sans)" font-size="11" font-weight="500" fill="#3a3a3a" style="fill:rgb(58, 58, 58);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:11px;font-weight:500;text-anchor:start;dominant-baseline:auto">Paris</text>
|
|
||||||
|
|
||||||
<g transform="translate(340,180) rotate(-8)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<rect x="-110" y="-55" width="220" height="110" rx="6" fill="none" stroke="#d94a7a" stroke-width="5" opacity="0.92" style="fill:none;stroke:rgb(217, 74, 122);color:rgb(0, 0, 0);stroke-width:5px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.92;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="-100" y="-45" width="200" height="90" rx="3" fill="none" stroke="#d94a7a" stroke-width="2" opacity="0.92" style="fill:none;stroke:rgb(217, 74, 122);color:rgb(0, 0, 0);stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.92;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="0" y="-5" text-anchor="middle" font-family="var(--font-sans)" font-size="34" font-weight="700" fill="#d94a7a" opacity="0.95" style="fill:rgb(217, 74, 122);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.95;font-family:Anthropic Sans, sans-serif;font-size:34px;font-weight:700;text-anchor:middle;dominant-baseline:auto">Taxe</text>
|
|
||||||
<text x="0" y="30" text-anchor="middle" font-family="var(--font-sans)" font-size="34" font-weight="700" fill="#d94a7a" opacity="0.95" style="fill:rgb(217, 74, 122);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.95;font-family:Anthropic Sans, sans-serif;font-size:34px;font-weight:700;text-anchor:middle;dominant-baseline:auto">2 €</text>
|
|
||||||
<path d="M 115 -40 Q 150 -30 175 -45" stroke="#d94a7a" stroke-width="4" fill="none" opacity="0.9" stroke-linecap="round" style="fill:none;stroke:rgb(217, 74, 122);color:rgb(0, 0, 0);stroke-width:4px;stroke-linecap:round;stroke-linejoin:miter;opacity:0.9;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 115 -25 Q 155 -15 180 -30" stroke="#d94a7a" stroke-width="4" fill="none" opacity="0.9" stroke-linecap="round" style="fill:none;stroke:rgb(217, 74, 122);color:rgb(0, 0, 0);stroke-width:4px;stroke-linecap:round;stroke-linejoin:miter;opacity:0.9;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<path d="M 115 -10 Q 150 0 175 -15" stroke="#d94a7a" stroke-width="4" fill="none" opacity="0.9" stroke-linecap="round" style="fill:none;stroke:rgb(217, 74, 122);color:rgb(0, 0, 0);stroke-width:4px;stroke-linecap:round;stroke-linejoin:miter;opacity:0.9;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(95,295)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<polygon points="0,40 90,55 90,135 0,120" fill="#8a5a2b" style="fill:rgb(138, 90, 43);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<polygon points="90,55 175,40 175,120 90,135" fill="#b8814a" style="fill:rgb(184, 129, 74);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<polygon points="0,40 90,15 175,40 90,55" fill="#a06c3a" style="fill:rgb(160, 108, 58);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="90" y1="15" x2="90" y2="55" stroke="#6b4420" stroke-width="1" opacity="0.5" style="fill:rgb(0, 0, 0);stroke:rgb(107, 68, 32);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.5;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="18" y="68" width="60" height="28" rx="2" fill="#ffffff" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="48" y="87" text-anchor="middle" font-family="var(--font-sans)" font-size="13" font-weight="700" fill="#000000" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:13px;font-weight:700;text-anchor:middle;dominant-baseline:auto">Shein</text>
|
|
||||||
<rect x="105" y="62" width="58" height="26" rx="2" fill="#ffffff" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="134" y="80" text-anchor="middle" font-family="var(--font-sans)" font-size="11" font-weight="700" fill="#ff4747" style="fill:rgb(255, 71, 71);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:11px;font-weight:700;text-anchor:middle;dominant-baseline:auto">AliExpress</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(265,335)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<polygon points="0,15 60,25 60,75 0,65" fill="#d4a347" style="fill:rgb(212, 163, 71);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<polygon points="60,25 120,15 120,65 60,75" fill="#e8b955" style="fill:rgb(232, 185, 85);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<polygon points="0,15 60,5 120,15 60,25" fill="#dcae4a" style="fill:rgb(220, 174, 74);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="60" y1="5" x2="60" y2="25" stroke="#a07820" stroke-width="1" opacity="0.5" style="fill:rgb(0, 0, 0);stroke:rgb(160, 120, 32);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.5;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="60" y="55" text-anchor="middle" font-family="var(--font-sans)" font-size="14" font-weight="700" fill="#000000" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:14px;font-weight:700;text-anchor:middle;dominant-baseline:auto">Shein</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(450,300)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
|
|
||||||
<polygon points="0,30 100,45 100,115 0,100" fill="#8a5a2b" style="fill:rgb(138, 90, 43);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<polygon points="100,45 195,30 195,100 100,115" fill="#b8814a" style="fill:rgb(184, 129, 74);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<polygon points="0,30 100,10 195,30 100,45" fill="#a06c3a" style="fill:rgb(160, 108, 58);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<line x1="100" y1="10" x2="100" y2="45" stroke="#6b4420" stroke-width="1" opacity="0.5" style="fill:rgb(0, 0, 0);stroke:rgb(107, 68, 32);color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:0.5;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<rect x="115" y="58" width="68" height="28" rx="14" fill="#ff5fa2" style="fill:rgb(255, 95, 162);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:"Anthropic Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
|
|
||||||
<text x="149" y="77" text-anchor="middle" font-family="var(--font-sans)" font-size="14" font-weight="700" fill="#ffffff" style="fill:rgb(255, 255, 255);stroke:none;color:rgb(0, 0, 0);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Anthropic Sans, sans-serif;font-size:14px;font-weight:700;text-anchor:middle;dominant-baseline:auto">Temu</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 16 KiB |
@@ -1,13 +0,0 @@
|
|||||||
# Petits colis, grands impôts : quand la taxation punit les Français sans relancer l’industrie
|
|
||||||
|
|
||||||
Jusqu’à maintenant, les petits colis importés de l’étranger passaient presque inaperçus dans les foyers français. Shein, AliExpress, Temu… commander en Asie était simple et peu coûteux, grâce à l’exonération de droits de douane sur les produits d’une valeur inférieure à 150 euros. Mais depuis le 13 novembre 2025, ce statu quo est remis en cause : les ministres des Finances de l’Union européenne ont voté la suppression de cette exonération. Dès le premier trimestre 2026, chaque petit colis pourrait être taxé et soumis à des frais de traitement supplémentaires.
|
|
||||||
|
|
||||||
À Bruxelles, on salue cette décision comme un moyen de protéger les consommateurs européens et le marché intérieur. La France, en particulier, s’en félicite, estimant qu’il s’agit d’une avancée pour la sécurité des produits et la souveraineté économique de l’UE. Mais pour beaucoup de Français, cette mesure ne fait qu’aggraver un problème de fond.
|
|
||||||
|
|
||||||
Car la question n’est pas simplement celle des colis asiatiques. Depuis trente ans, la France est passée d’une puissance industrielle comparable à la moyenne européenne à un “nain industriel” avec seulement 7 % du PIB provenant de l’industrie, alors que nos voisins restent autour de 20 % ou plus. Les consommateurs se tournent vers l’Asie parce que **les produits français sont trop chers**, alourdis par des taxes, charges et coûts de production élevés. Taxer les colis importés aujourd’hui revient à **punir les consommateurs pour un problème que nos politiques n’ont pas su résoudre : la perte de compétitivité de l’industrie française**.
|
|
||||||
|
|
||||||
Pour ceux qui vivent près des frontières ou voyagent dans le sud de l’Europe, la différence est frappante. En Catalogne ou au Portugal, il est possible d’acheter des produits locaux compétitifs : bricolage, jardinage, vêtements ou matériel de pêche, fabriqués en Europe et à des prix abordables. La preuve qu’une production locale forte et des prix raisonnables sont possibles, **quand la fiscalité et la réglementation ne pénalisent pas le producteur**.
|
|
||||||
|
|
||||||
Cette nouvelle taxe sur les petits colis ne relancera pas l’industrie française, ni le commerce de centre-ville. Elle risque surtout de **réduire le pouvoir d’achat des Français**, tout en alimentant des caisses de l’État dont l’argent sera souvent gaspillé avant même d’être utile à l’économie réelle.
|
|
||||||
|
|
||||||
En résumé, ce n’est pas la faute des consommateurs qui cherchent le meilleur prix à l’étranger. C’est le résultat d’une politique fiscale et industrielle défaillante. Tant que l’État ne s’attaquera pas aux causes structurelles — charges trop élevées, fiscalité excessive, désindustrialisation — toutes les mesures de taxation des importations resteront des **pansements sur une jambe de bois**, au détriment des citoyens.
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "5059c1e2-f3e6-406f-9595-7133bb835cdb",
|
|
||||||
"slug": "petits-colis-grands-impots-quand-la-taxation-punit-les-francais-sans-relancer-l-industrie",
|
|
||||||
"title": "Petits colis, grands impôts : quand la taxation punit les Français sans relancer l’industrie",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2025-11-14 00:55",
|
|
||||||
"created_at": "2025-11-14 00:55:43",
|
|
||||||
"updated_at": "2026-05-12 08:59:31",
|
|
||||||
"revisions": [
|
|
||||||
{
|
|
||||||
"n": 1,
|
|
||||||
"date": "2026-05-12 08:59:31",
|
|
||||||
"comment": "",
|
|
||||||
"title": "Petits colis, grands impôts : quand la taxation punit les Français sans relancer l’industrie"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cover": "cover.svg",
|
|
||||||
"files_meta": {
|
|
||||||
"a99506f7dff32b42-16522.svg": {
|
|
||||||
"author": "Cédrix / Générée par IA",
|
|
||||||
"source_url": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"external_links": [],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "",
|
|
||||||
"og_image": "",
|
|
||||||
"category": "actualité"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
Jusqu’à maintenant, les petits colis importés de l’étranger passaient presque inaperçus dans les foyers français. Shein, AliExpress, Temu… commander en Asie était simple et peu coûteux, grâce à l’exonération de droits de douane sur les produits d’une valeur inférieure à 150 euros. Mais depuis le 13 novembre 2025, ce statu quo est remis en cause : les ministres des Finances de l’Union européenne ont voté la suppression de cette exonération. Dès le premier trimestre 2026, chaque petit colis pourrait être taxé et soumis à des frais de traitement supplémentaires.
|
|
||||||
|
|
||||||
À Bruxelles, on salue cette décision comme un moyen de protéger les consommateurs européens et le marché intérieur. La France, en particulier, s’en félicite, estimant qu’il s’agit d’une avancée pour la sécurité des produits et la souveraineté économique de l’UE. Mais pour beaucoup de Français, cette mesure ne fait qu’aggraver un problème de fond.
|
|
||||||
|
|
||||||
Car la question n’est pas simplement celle des colis asiatiques. Depuis trente ans, la France est passée d’une puissance industrielle comparable à la moyenne européenne à un “nain industriel” avec seulement 7 % du PIB provenant de l’industrie, alors que nos voisins restent autour de 20 % ou plus. Les consommateurs se tournent vers l’Asie parce que **les produits français sont trop chers**, alourdis par des taxes, charges et coûts de production élevés. Taxer les colis importés aujourd’hui revient à **punir les consommateurs pour un problème que nos politiques n’ont pas su résoudre : la perte de compétitivité de l’industrie française**.
|
|
||||||
|
|
||||||
Pour ceux qui vivent près des frontières ou voyagent dans le sud de l’Europe, la différence est frappante. En Catalogne ou au Portugal, il est possible d’acheter des produits locaux compétitifs : bricolage, jardinage, vêtements ou matériel de pêche, fabriqués en Europe et à des prix abordables. La preuve qu’une production locale forte et des prix raisonnables sont possibles, **quand la fiscalité et la réglementation ne pénalisent pas le producteur**.
|
|
||||||
|
|
||||||
Cette nouvelle taxe sur les petits colis ne relancera pas l’industrie française, ni le commerce de centre-ville. Elle risque surtout de **réduire le pouvoir d’achat des Français**, tout en alimentant des caisses de l’État dont l’argent sera souvent gaspillé avant même d’être utile à l’économie réelle.
|
|
||||||
|
|
||||||
En résumé, ce n’est pas la faute des consommateurs qui cherchent le meilleur prix à l’étranger. C’est le résultat d’une politique fiscale et industrielle défaillante. Tant que l’État ne s’attaquera pas aux causes structurelles — charges trop élevées, fiscalité excessive, désindustrialisation — toutes les mesures de taxation des importations resteront des **pansements sur une jambe de bois**, au détriment des citoyens.
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
# Configurer un client OAuth 2.0 / OIDC dans Keycloak
|
|
||||||
|
|
||||||
*Keycloak* est une solution open source de gestion des identités et des accès (IAM). Cet article décrit la configuration d'un client OAuth 2.0 / OpenID Connect dans Keycloak, en détaillant les options importantes et en montrant comment restreindre l'accès aux utilisateurs ou groupes autorisés.
|
|
||||||
|
|
||||||
> **Note de version.** L'interface d'administration a été refondue à partir de Keycloak 19. La notion d'**Access Type** (`confidential` / `public` / `bearer-only`) a disparu au profit des toggles **Client authentication** et **Authorization**. Ce guide suit l'UI actuelle (Keycloak 24+).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Prérequis
|
|
||||||
|
|
||||||
- Une instance Keycloak fonctionnelle (version 24 ou supérieure recommandée).
|
|
||||||
- Des droits d'administration sur un **realm**.
|
|
||||||
- Une application destinée à s'authentifier via OAuth 2.0 / OIDC.
|
|
||||||
- TLS activé sur Keycloak et sur l'application cliente (obligatoire en production).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Qu'est-ce qu'un client dans Keycloak ?
|
|
||||||
|
|
||||||
Dans Keycloak, un **client** représente une application ou un service qui interagit avec le serveur d'authentification, soit pour authentifier des utilisateurs, soit pour obtenir des informations sur eux, soit pour protéger ses propres ressources. Il peut s'agir d'une application web, d'une API, d'une application mobile, d'un service interne ou d'un partenaire tiers.
|
|
||||||
|
|
||||||
Chaque client est rattaché à un **realm** (l'espace logique d'authentification) et identifié par un `client_id` unique. Cet identifiant est transmis lors de toute demande d'authentification ou d'obtention de jeton.
|
|
||||||
|
|
||||||
La configuration d'un client définit notamment :
|
|
||||||
|
|
||||||
- les flux OAuth 2.0 autorisés (`authorization_code`, `client_credentials`, etc.) ;
|
|
||||||
- la capacité ou non du client à conserver un secret (client *confidentiel* vs *public*) ;
|
|
||||||
- les URI de redirection acceptées et les origines CORS ;
|
|
||||||
- la durée de vie des jetons et la composition des claims (mappers) ;
|
|
||||||
- les politiques d'autorisation associées (rôles, groupes, attributs).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Création du client
|
|
||||||
|
|
||||||
1. Se connecter à la **Keycloak Admin Console**.
|
|
||||||
2. Sélectionner le realm cible.
|
|
||||||
3. Menu **Clients** > **Create client**.
|
|
||||||
|
|
||||||
### Étape « General settings »
|
|
||||||
|
|
||||||
| Champ | Valeur recommandée | Description |
|
|
||||||
| ------------------ | ----------------------------------- | ---------------------------------------------------------------------------- |
|
|
||||||
| **Client type** | `OpenID Connect` | Protocole utilisé. Choisir `SAML` uniquement pour des intégrations SAML 2.0. |
|
|
||||||
| **Client ID** | `myapp-client` | Identifiant unique du client dans le realm. Apparaît dans les jetons (`azp`). |
|
|
||||||
| **Name** | `My Application` | Libellé d'affichage (facultatif, peut être localisé). |
|
|
||||||
| **Description** | Texte libre | Aide à la maintenance. |
|
|
||||||
|
|
||||||
### Étape « Capability config »
|
|
||||||
|
|
||||||
| Toggle | Valeur recommandée | Détail |
|
|
||||||
| ------------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
||||||
| **Client authentication** | `ON` pour un backend, `OFF` pour un SPA | `ON` rend le client **confidentiel** (un secret est généré). `OFF` rend le client **public**. |
|
|
||||||
| **Authorization** | `OFF` | À activer uniquement si l'on souhaite utiliser le moteur d'autorisations fine (RBAC/ABAC) de Keycloak. |
|
|
||||||
| **Standard flow** | `ON` | Active le flux Authorization Code, à utiliser systématiquement. |
|
|
||||||
| **Direct access grants** | `OFF` | Flux `password` — déconseillé par OAuth 2.1, à n'utiliser que pour des outils internes legacy. |
|
|
||||||
| **Implicit flow** | `OFF` | Déprécié par OAuth 2.1, ne pas activer. |
|
|
||||||
| **Service accounts roles** | `ON` si client_credentials | Permet au client de récupérer un jeton pour son propre compte (machine-to-machine). |
|
|
||||||
| **OAuth 2.0 Device Auth Grant** | `OFF` (sauf besoin spécifique) | Pour les appareils sans navigateur (TV, CLI sans IHM locale). |
|
|
||||||
| **OIDC CIBA Grant** | `OFF` (sauf besoin spécifique) | Authentification déportée (canal hors-bande). |
|
|
||||||
|
|
||||||
### Étape « Login settings »
|
|
||||||
|
|
||||||
| Champ | Exemple | Description |
|
|
||||||
| ----------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------- |
|
|
||||||
| **Root URL** | `https://app.example.com` | URL de base de l'application. Permet d'utiliser des chemins relatifs dans les champs suivants. |
|
|
||||||
| **Home URL** | `/` | Page par défaut après login. |
|
|
||||||
| **Valid redirect URIs** | `https://app.example.com/*` | URI exactes autorisées pour la redirection après authentification. Éviter les wildcards larges. |
|
|
||||||
| **Valid post logout redirect URIs** | `https://app.example.com/*` | URI autorisées pour la redirection après déconnexion (RP-Initiated Logout). |
|
|
||||||
| **Web origins** | `https://app.example.com` | Origines CORS autorisées. La valeur `+` reprend automatiquement les redirect URIs ; `*` est à proscrire. |
|
|
||||||
| **Admin URL** | `https://app.example.com` | Utilisé pour les notifications backchannel (logout global, push not-before). |
|
|
||||||
|
|
||||||
> **Bonne pratique.** Les redirect URIs doivent être en `https://` en production (sauf `http://localhost` pour le développement). Spécifier des chemins aussi précis que possible plutôt qu'un wildcard `/*`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Authentification du client (Credentials)
|
|
||||||
|
|
||||||
L'onglet **Credentials** n'apparaît que si **Client authentication** est sur `ON`. Plusieurs méthodes sont disponibles :
|
|
||||||
|
|
||||||
| Méthode | Usage |
|
|
||||||
| -------------------------------- | ---------------------------------------------------------------------- |
|
|
||||||
| **Client Id and Secret** | Secret partagé classique. À stocker dans un coffre-fort (Vault, env vars chiffrées). |
|
|
||||||
| **Signed JWT** | Le client signe un JWT d'assertion avec sa clé privée. Plus sûr qu'un secret. |
|
|
||||||
| **Signed JWT with Client Secret** | Variante symétrique (HMAC). |
|
|
||||||
| **X.509 Certificate** | mTLS — recommandé pour les contextes à forte exigence (FAPI, banque). |
|
|
||||||
|
|
||||||
> **Important.** Le secret ne doit jamais être commité dans Git ni embarqué dans un binaire distribué. Pour un projet où les secrets vivent dans `config/.env`, ne commiter que `config/.env.example`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Types de clients
|
|
||||||
|
|
||||||
Depuis Keycloak 19, le « type » est déduit de la combinaison des toggles. La terminologie OAuth reste utile :
|
|
||||||
|
|
||||||
| Type | Configuration Keycloak | Cas d'usage |
|
|
||||||
| --------------- | ------------------------------------------------------------------- | ------------------------------------------------------------ |
|
|
||||||
| **Confidentiel** | `Client authentication = ON` | Backend serveur (PHP, Node, Java…), BFF, service-to-service. |
|
|
||||||
| **Public** | `Client authentication = OFF` + `Standard flow = ON` + **PKCE** | SPA (React, Vue, Angular), application mobile, CLI native. |
|
|
||||||
| **Service account** | `Client authentication = ON` + `Service accounts roles = ON` | Communication machine-to-machine (`grant_type=client_credentials`). |
|
|
||||||
|
|
||||||
Le type « bearer-only » a été retiré : pour une API qui se contente de valider des jetons sans déclencher d'authentification, créer un client confidentiel et n'activer **aucun** flux.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. PKCE — recommandé pour tous les clients
|
|
||||||
|
|
||||||
**PKCE** (*Proof Key for Code Exchange*, RFC 7636) protège le flux Authorization Code contre l'interception du code d'autorisation. Conçu initialement pour les clients publics, il est aujourd'hui recommandé pour **tous les clients**, y compris confidentiels, et obligatoire dans OAuth 2.1.
|
|
||||||
|
|
||||||
Activation dans **Advanced** > **Proof Key for Code Exchange Code Challenge Method** > `S256`.
|
|
||||||
|
|
||||||
> Ne jamais utiliser `plain` ; `S256` est la seule valeur acceptable.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. Restreindre l'accès aux utilisateurs ou groupes
|
|
||||||
|
|
||||||
Par défaut, tout utilisateur du realm peut se connecter via n'importe quel client. Deux approches permettent de restreindre cet accès.
|
|
||||||
|
|
||||||
### Approche 1 — Authentification basée sur les rôles (simple)
|
|
||||||
|
|
||||||
1. Créer un rôle client dédié, par exemple `app-user`, dans l'onglet **Roles** du client.
|
|
||||||
2. Assigner ce rôle aux utilisateurs ou groupes autorisés (Users > *user* > Role mapping, ou Groups > *group* > Role mapping).
|
|
||||||
3. Dans **Authentication** > **Flows**, ajouter une exécution `Conditional - User Role` au flux Browser, configurée avec le rôle requis et `Required`.
|
|
||||||
|
|
||||||
Cette méthode bloque l'authentification elle-même : un utilisateur sans le rôle ne pourra pas se connecter au client.
|
|
||||||
|
|
||||||
### Approche 2 — Authorization Services (granulaire)
|
|
||||||
|
|
||||||
À utiliser pour gérer des permissions plus fines (ressources, scopes, conditions).
|
|
||||||
|
|
||||||
1. Activer **Authorization** sur le client.
|
|
||||||
2. Onglet **Authorization** > **Policies** > créer une *Group Policy* ou *Role Policy* listant les utilisateurs/groupes autorisés.
|
|
||||||
3. Onglet **Permissions** > créer une *Scope-Based Permission* ou *Resource-Based Permission* liée à la policy.
|
|
||||||
4. Côté application, utiliser l'endpoint **UMA** ou l'adaptateur Keycloak pour évaluer les permissions.
|
|
||||||
|
|
||||||
### Approche 3 — Limiter le client scope « roles »
|
|
||||||
|
|
||||||
Dans **Client scopes** > `roles` > **Scope**, désactiver *Full scope allowed* et n'autoriser que les rôles pertinents. Cela réduit la taille des jetons et limite ce que le client peut « voir » des rôles utilisateurs.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. Client scopes et mappers
|
|
||||||
|
|
||||||
Les **client scopes** déterminent les claims présents dans les jetons (`access_token` et `id_token`).
|
|
||||||
|
|
||||||
- Les scopes **Default** sont systématiquement ajoutés à chaque jeton.
|
|
||||||
- Les scopes **Optional** ne sont ajoutés que si l'application les demande via le paramètre `scope=` lors de l'authentification.
|
|
||||||
|
|
||||||
Pour exposer les groupes d'un utilisateur dans le token :
|
|
||||||
|
|
||||||
1. Créer un client scope `groups` (ou réutiliser celui existant).
|
|
||||||
2. Ajouter un mapper de type **Group Membership** :
|
|
||||||
- *Token Claim Name* : `groups`
|
|
||||||
- *Full group path* : `OFF` (sauf besoin d'arborescence)
|
|
||||||
- *Add to ID token / Access token / Userinfo* : selon l'usage.
|
|
||||||
3. Attacher le scope au client (Default ou Optional).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. Réglages avancés à connaître
|
|
||||||
|
|
||||||
| Section | Réglage | Recommandation |
|
|
||||||
| ---------------------------------- | -------------------------------- | ---------------------------------------------------------------- |
|
|
||||||
| **Advanced > Fine grain OpenID Connect configuration** | `Access Token Signature Algorithm` | `RS256` (par défaut) ou `PS256` pour FAPI. |
|
|
||||||
| **Advanced > Advanced settings** | `Proof Key for Code Exchange` | `S256`. |
|
|
||||||
| **Advanced > Advanced settings** | `Front channel logout` | `OFF` sauf si l'application implémente correctement la spec OIDC Front-Channel Logout. |
|
|
||||||
| **Advanced > Advanced settings** | `Backchannel logout URL` | Renseigner pour une déconnexion globale propre. |
|
|
||||||
| **Advanced > Token Lifespan** | `Access Token Lifespan` | Court (5–15 min). Le refresh token prend le relais. |
|
|
||||||
| **Sessions** | `Client Session Idle / Max` | Aligner sur la politique de session de l'organisation. |
|
|
||||||
|
|
||||||
Pour appliquer automatiquement un ensemble cohérent de règles, utiliser les **Client Policies** du realm (profils pré-définis `oauth-2-1-for-confidential-client` et `oauth-2-1-for-public-client`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. Checklist de sécurité
|
|
||||||
|
|
||||||
- [ ] `Client authentication = ON` pour tout client qui peut conserver un secret.
|
|
||||||
- [ ] PKCE `S256` activé.
|
|
||||||
- [ ] Implicit flow et Direct access grants désactivés.
|
|
||||||
- [ ] Redirect URIs en `https://` et sans wildcard inutile.
|
|
||||||
- [ ] Web origins explicites (pas de `*`).
|
|
||||||
- [ ] Secret stocké dans un coffre-fort, jamais commité.
|
|
||||||
- [ ] Access token court (≤ 15 min) et refresh token rotatif.
|
|
||||||
- [ ] Accès restreint via rôle dédié ou Authorization Services.
|
|
||||||
- [ ] *Full scope allowed* désactivé si les rôles transportés doivent être limités.
|
|
||||||
- [ ] Logout backchannel ou front-channel configuré.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
La configuration d'un client OAuth 2.0 dans Keycloak repose sur quelques choix structurants — confidentiel ou public, flux activés, PKCE, restriction d'accès — qui ont chacun des implications de sécurité fortes. S'aligner sur OAuth 2.1 (PKCE systématique, pas d'implicit flow, pas de password grant) et utiliser les **Client Policies** pour appliquer ces règles à l'échelle du realm évite la plupart des configurations à risque.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "5510b12a-d647-4b1a-90ba-d421a4927ff7",
|
|
||||||
"slug": "configurer-un-client-oauth-2-0-dans-keycloak-guide-complet",
|
|
||||||
"title": "Configurer un client OAuth 2.0 / OIDC dans Keycloak",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2025-05-16 23:33",
|
|
||||||
"created_at": "2025-05-16 23:33:31",
|
|
||||||
"updated_at": "2026-05-12 20:39:53",
|
|
||||||
"revisions": [
|
|
||||||
{
|
|
||||||
"n": 1,
|
|
||||||
"date": "2026-05-12 20:39:53",
|
|
||||||
"comment": "Contenu modifié",
|
|
||||||
"title": "Configurer un client OAuth 2.0 / OIDC dans Keycloak"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"cover": "",
|
|
||||||
"files_meta": [],
|
|
||||||
"external_links": [],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "",
|
|
||||||
"og_image": "",
|
|
||||||
"category": "informatique"
|
|
||||||
}
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
*Keycloak* est une solution open source de gestion des identités et des accès (IAM). Cet article décrit la configuration d'un client OAuth 2.0 / OpenID Connect dans Keycloak, en détaillant les options importantes et en montrant comment restreindre l'accès aux utilisateurs ou groupes autorisés.
|
|
||||||
|
|
||||||
> **Note de version.** L'interface d'administration a été refondue à partir de Keycloak 19. La notion d'**Access Type** (`confidential` / `public` / `bearer-only`) a disparu au profit des toggles **Client authentication** et **Authorization**. Ce guide suit l'UI actuelle (Keycloak 24+).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Prérequis
|
|
||||||
|
|
||||||
- Une instance Keycloak fonctionnelle (version 24 ou supérieure recommandée).
|
|
||||||
- Des droits d'administration sur un **realm**.
|
|
||||||
- Une application destinée à s'authentifier via OAuth 2.0 / OIDC.
|
|
||||||
- TLS activé sur Keycloak et sur l'application cliente (obligatoire en production).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Qu'est-ce qu'un client dans Keycloak ?
|
|
||||||
|
|
||||||
Dans Keycloak, un **client** représente une application ou un service qui interagit avec le serveur d'authentification, soit pour authentifier des utilisateurs, soit pour obtenir des informations sur eux, soit pour protéger ses propres ressources. Il peut s'agir d'une application web, d'une API, d'une application mobile, d'un service interne ou d'un partenaire tiers.
|
|
||||||
|
|
||||||
Chaque client est rattaché à un **realm** (l'espace logique d'authentification) et identifié par un `client_id` unique. Cet identifiant est transmis lors de toute demande d'authentification ou d'obtention de jeton.
|
|
||||||
|
|
||||||
La configuration d'un client définit notamment :
|
|
||||||
|
|
||||||
- les flux OAuth 2.0 autorisés (`authorization_code`, `client_credentials`, etc.) ;
|
|
||||||
- la capacité ou non du client à conserver un secret (client *confidentiel* vs *public*) ;
|
|
||||||
- les URI de redirection acceptées et les origines CORS ;
|
|
||||||
- la durée de vie des jetons et la composition des claims (mappers) ;
|
|
||||||
- les politiques d'autorisation associées (rôles, groupes, attributs).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Création du client
|
|
||||||
|
|
||||||
1. Se connecter à la **Keycloak Admin Console**.
|
|
||||||
2. Sélectionner le realm cible.
|
|
||||||
3. Menu **Clients** > **Create client**.
|
|
||||||
|
|
||||||
### Étape « General settings »
|
|
||||||
|
|
||||||
| Champ | Valeur recommandée | Description |
|
|
||||||
| ------------------ | ----------------------------------- | ---------------------------------------------------------------------------- |
|
|
||||||
| **Client type** | `OpenID Connect` | Protocole utilisé. Choisir `SAML` uniquement pour des intégrations SAML 2.0. |
|
|
||||||
| **Client ID** | `myapp-client` | Identifiant unique du client dans le realm. Apparaît dans les jetons (`azp`). |
|
|
||||||
| **Name** | `My Application` | Libellé d'affichage (facultatif, peut être localisé). |
|
|
||||||
| **Description** | Texte libre | Aide à la maintenance. |
|
|
||||||
|
|
||||||
### Étape « Capability config »
|
|
||||||
|
|
||||||
| Toggle | Valeur recommandée | Détail |
|
|
||||||
| ------------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
||||||
| **Client authentication** | `ON` pour un backend, `OFF` pour un SPA | `ON` rend le client **confidentiel** (un secret est généré). `OFF` rend le client **public**. |
|
|
||||||
| **Authorization** | `OFF` | À activer uniquement si l'on souhaite utiliser le moteur d'autorisations fine (RBAC/ABAC) de Keycloak. |
|
|
||||||
| **Standard flow** | `ON` | Active le flux Authorization Code, à utiliser systématiquement. |
|
|
||||||
| **Direct access grants** | `OFF` | Flux `password` — déconseillé par OAuth 2.1, à n'utiliser que pour des outils internes legacy. |
|
|
||||||
| **Implicit flow** | `OFF` | Déprécié par OAuth 2.1, ne pas activer. |
|
|
||||||
| **Service accounts roles** | `ON` si client_credentials | Permet au client de récupérer un jeton pour son propre compte (machine-to-machine). |
|
|
||||||
| **OAuth 2.0 Device Auth Grant** | `OFF` (sauf besoin spécifique) | Pour les appareils sans navigateur (TV, CLI sans IHM locale). |
|
|
||||||
| **OIDC CIBA Grant** | `OFF` (sauf besoin spécifique) | Authentification déportée (canal hors-bande). |
|
|
||||||
|
|
||||||
### Étape « Login settings »
|
|
||||||
|
|
||||||
| Champ | Exemple | Description |
|
|
||||||
| ----------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------- |
|
|
||||||
| **Root URL** | `https://app.example.com` | URL de base de l'application. Permet d'utiliser des chemins relatifs dans les champs suivants. |
|
|
||||||
| **Home URL** | `/` | Page par défaut après login. |
|
|
||||||
| **Valid redirect URIs** | `https://app.example.com/*` | URI exactes autorisées pour la redirection après authentification. Éviter les wildcards larges. |
|
|
||||||
| **Valid post logout redirect URIs** | `https://app.example.com/*` | URI autorisées pour la redirection après déconnexion (RP-Initiated Logout). |
|
|
||||||
| **Web origins** | `https://app.example.com` | Origines CORS autorisées. La valeur `+` reprend automatiquement les redirect URIs ; `*` est à proscrire. |
|
|
||||||
| **Admin URL** | `https://app.example.com` | Utilisé pour les notifications backchannel (logout global, push not-before). |
|
|
||||||
|
|
||||||
> **Bonne pratique.** Les redirect URIs doivent être en `https://` en production (sauf `http://localhost` pour le développement). Spécifier des chemins aussi précis que possible plutôt qu'un wildcard `/*`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Authentification du client (Credentials)
|
|
||||||
|
|
||||||
L'onglet **Credentials** n'apparaît que si **Client authentication** est sur `ON`. Plusieurs méthodes sont disponibles :
|
|
||||||
|
|
||||||
| Méthode | Usage |
|
|
||||||
| -------------------------------- | ---------------------------------------------------------------------- |
|
|
||||||
| **Client Id and Secret** | Secret partagé classique. À stocker dans un coffre-fort (Vault, env vars chiffrées). |
|
|
||||||
| **Signed JWT** | Le client signe un JWT d'assertion avec sa clé privée. Plus sûr qu'un secret. |
|
|
||||||
| **Signed JWT with Client Secret** | Variante symétrique (HMAC). |
|
|
||||||
| **X.509 Certificate** | mTLS — recommandé pour les contextes à forte exigence (FAPI, banque). |
|
|
||||||
|
|
||||||
> **Important.** Le secret ne doit jamais être commité dans Git ni embarqué dans un binaire distribué. Pour un projet où les secrets vivent dans `config/.env`, ne commiter que `config/.env.example`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Types de clients
|
|
||||||
|
|
||||||
Depuis Keycloak 19, le « type » est déduit de la combinaison des toggles. La terminologie OAuth reste utile :
|
|
||||||
|
|
||||||
| Type | Configuration Keycloak | Cas d'usage |
|
|
||||||
| --------------- | ------------------------------------------------------------------- | ------------------------------------------------------------ |
|
|
||||||
| **Confidentiel** | `Client authentication = ON` | Backend serveur (PHP, Node, Java…), BFF, service-to-service. |
|
|
||||||
| **Public** | `Client authentication = OFF` + `Standard flow = ON` + **PKCE** | SPA (React, Vue, Angular), application mobile, CLI native. |
|
|
||||||
| **Service account** | `Client authentication = ON` + `Service accounts roles = ON` | Communication machine-to-machine (`grant_type=client_credentials`). |
|
|
||||||
|
|
||||||
Le type « bearer-only » a été retiré : pour une API qui se contente de valider des jetons sans déclencher d'authentification, créer un client confidentiel et n'activer **aucun** flux.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. PKCE — recommandé pour tous les clients
|
|
||||||
|
|
||||||
**PKCE** (*Proof Key for Code Exchange*, RFC 7636) protège le flux Authorization Code contre l'interception du code d'autorisation. Conçu initialement pour les clients publics, il est aujourd'hui recommandé pour **tous les clients**, y compris confidentiels, et obligatoire dans OAuth 2.1.
|
|
||||||
|
|
||||||
Activation dans **Advanced** > **Proof Key for Code Exchange Code Challenge Method** > `S256`.
|
|
||||||
|
|
||||||
> Ne jamais utiliser `plain` ; `S256` est la seule valeur acceptable.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. Restreindre l'accès aux utilisateurs ou groupes
|
|
||||||
|
|
||||||
Par défaut, tout utilisateur du realm peut se connecter via n'importe quel client. Deux approches permettent de restreindre cet accès.
|
|
||||||
|
|
||||||
### Approche 1 — Authentification basée sur les rôles (simple)
|
|
||||||
|
|
||||||
1. Créer un rôle client dédié, par exemple `app-user`, dans l'onglet **Roles** du client.
|
|
||||||
2. Assigner ce rôle aux utilisateurs ou groupes autorisés (Users > *user* > Role mapping, ou Groups > *group* > Role mapping).
|
|
||||||
3. Dans **Authentication** > **Flows**, ajouter une exécution `Conditional - User Role` au flux Browser, configurée avec le rôle requis et `Required`.
|
|
||||||
|
|
||||||
Cette méthode bloque l'authentification elle-même : un utilisateur sans le rôle ne pourra pas se connecter au client.
|
|
||||||
|
|
||||||
### Approche 2 — Authorization Services (granulaire)
|
|
||||||
|
|
||||||
À utiliser pour gérer des permissions plus fines (ressources, scopes, conditions).
|
|
||||||
|
|
||||||
1. Activer **Authorization** sur le client.
|
|
||||||
2. Onglet **Authorization** > **Policies** > créer une *Group Policy* ou *Role Policy* listant les utilisateurs/groupes autorisés.
|
|
||||||
3. Onglet **Permissions** > créer une *Scope-Based Permission* ou *Resource-Based Permission* liée à la policy.
|
|
||||||
4. Côté application, utiliser l'endpoint **UMA** ou l'adaptateur Keycloak pour évaluer les permissions.
|
|
||||||
|
|
||||||
### Approche 3 — Limiter le client scope « roles »
|
|
||||||
|
|
||||||
Dans **Client scopes** > `roles` > **Scope**, désactiver *Full scope allowed* et n'autoriser que les rôles pertinents. Cela réduit la taille des jetons et limite ce que le client peut « voir » des rôles utilisateurs.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. Client scopes et mappers
|
|
||||||
|
|
||||||
Les **client scopes** déterminent les claims présents dans les jetons (`access_token` et `id_token`).
|
|
||||||
|
|
||||||
- Les scopes **Default** sont systématiquement ajoutés à chaque jeton.
|
|
||||||
- Les scopes **Optional** ne sont ajoutés que si l'application les demande via le paramètre `scope=` lors de l'authentification.
|
|
||||||
|
|
||||||
Pour exposer les groupes d'un utilisateur dans le token :
|
|
||||||
|
|
||||||
1. Créer un client scope `groups` (ou réutiliser celui existant).
|
|
||||||
2. Ajouter un mapper de type **Group Membership** :
|
|
||||||
- *Token Claim Name* : `groups`
|
|
||||||
- *Full group path* : `OFF` (sauf besoin d'arborescence)
|
|
||||||
- *Add to ID token / Access token / Userinfo* : selon l'usage.
|
|
||||||
3. Attacher le scope au client (Default ou Optional).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. Réglages avancés à connaître
|
|
||||||
|
|
||||||
| Section | Réglage | Recommandation |
|
|
||||||
| ---------------------------------- | -------------------------------- | ---------------------------------------------------------------- |
|
|
||||||
| **Advanced > Fine grain OpenID Connect configuration** | `Access Token Signature Algorithm` | `RS256` (par défaut) ou `PS256` pour FAPI. |
|
|
||||||
| **Advanced > Advanced settings** | `Proof Key for Code Exchange` | `S256`. |
|
|
||||||
| **Advanced > Advanced settings** | `Front channel logout` | `OFF` sauf si l'application implémente correctement la spec OIDC Front-Channel Logout. |
|
|
||||||
| **Advanced > Advanced settings** | `Backchannel logout URL` | Renseigner pour une déconnexion globale propre. |
|
|
||||||
| **Advanced > Token Lifespan** | `Access Token Lifespan` | Court (5–15 min). Le refresh token prend le relais. |
|
|
||||||
| **Sessions** | `Client Session Idle / Max` | Aligner sur la politique de session de l'organisation. |
|
|
||||||
|
|
||||||
Pour appliquer automatiquement un ensemble cohérent de règles, utiliser les **Client Policies** du realm (profils pré-définis `oauth-2-1-for-confidential-client` et `oauth-2-1-for-public-client`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. Checklist de sécurité
|
|
||||||
|
|
||||||
- [ ] `Client authentication = ON` pour tout client qui peut conserver un secret.
|
|
||||||
- [ ] PKCE `S256` activé.
|
|
||||||
- [ ] Implicit flow et Direct access grants désactivés.
|
|
||||||
- [ ] Redirect URIs en `https://` et sans wildcard inutile.
|
|
||||||
- [ ] Web origins explicites (pas de `*`).
|
|
||||||
- [ ] Secret stocké dans un coffre-fort, jamais commité.
|
|
||||||
- [ ] Access token court (≤ 15 min) et refresh token rotatif.
|
|
||||||
- [ ] Accès restreint via rôle dédié ou Authorization Services.
|
|
||||||
- [ ] *Full scope allowed* désactivé si les rôles transportés doivent être limités.
|
|
||||||
- [ ] Logout backchannel ou front-channel configuré.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
La configuration d'un client OAuth 2.0 dans Keycloak repose sur quelques choix structurants — confidentiel ou public, flux activés, PKCE, restriction d'accès — qui ont chacun des implications de sécurité fortes. S'aligner sur OAuth 2.1 (PKCE systématique, pas d'implicit flow, pas de password grant) et utiliser les **Client Policies** pour appliquer ces règles à l'échelle du realm évite la plupart des configurations à risque.
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 680 420" width="680" height="420" role="img">
|
|
||||||
<title>Touche Alt détournée par xkb sous GNOME/Wayland</title>
|
|
||||||
<desc>Parcours du signal d'une touche Alt gauche depuis le clavier vers le raccourci Alt+Tab. La couche xkb avec l'option lv3:lalt_switch détourne Alt_L en ISO_Level3_Shift, le compositeur ne reconnaît plus Alt, le raccourci ne se déclenche pas.</desc>
|
|
||||||
|
|
||||||
<!-- Background -->
|
|
||||||
<rect width="680" height="420" fill="#fafaf7"/>
|
|
||||||
|
|
||||||
<!-- Background grid -->
|
|
||||||
<g opacity="0.08">
|
|
||||||
<line x1="0" y1="100" x2="680" y2="100" stroke="#888" stroke-width="0.5"/>
|
|
||||||
<line x1="0" y1="200" x2="680" y2="200" stroke="#888" stroke-width="0.5"/>
|
|
||||||
<line x1="0" y1="300" x2="680" y2="300" stroke="#888" stroke-width="0.5"/>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- LEFT: Alt key being pressed -->
|
|
||||||
<text x="120" y="40" text-anchor="middle" font-family="system-ui, sans-serif" font-size="9" fill="#888" letter-spacing="2">CLAVIER</text>
|
|
||||||
<rect x="60" y="65" width="120" height="80" rx="8" fill="#999" opacity="0.3"/>
|
|
||||||
<rect x="60" y="60" width="120" height="80" rx="8" fill="#f0eee8" stroke="#666" stroke-width="1"/>
|
|
||||||
<text x="120" y="95" text-anchor="middle" font-family="ui-monospace, monospace" font-size="14" fill="#333" font-weight="500">Alt</text>
|
|
||||||
<text x="120" y="112" text-anchor="middle" font-family="ui-monospace, monospace" font-size="9" fill="#888" letter-spacing="1">gauche</text>
|
|
||||||
<circle cx="120" cy="100" r="58" fill="none" stroke="#3b8a4f" stroke-width="1" opacity="0.4" stroke-dasharray="3 3"/>
|
|
||||||
|
|
||||||
<!-- Arrow from key to first stage -->
|
|
||||||
<line x1="180" y1="100" x2="225" y2="100" stroke="#3b8a4f" stroke-width="2"/>
|
|
||||||
<polygon points="220,96 230,100 220,104" fill="#3b8a4f"/>
|
|
||||||
|
|
||||||
<!-- STACK header -->
|
|
||||||
<text x="340" y="40" text-anchor="middle" font-family="system-ui, sans-serif" font-size="9" fill="#888" letter-spacing="2">PARCOURS DU SIGNAL</text>
|
|
||||||
|
|
||||||
<!-- Layer 1: kernel OK -->
|
|
||||||
<rect x="230" y="60" width="220" height="50" rx="6" fill="#d9ecdf" stroke="#3b8a4f" stroke-width="1"/>
|
|
||||||
<circle cx="250" cy="85" r="8" fill="#3b8a4f"/>
|
|
||||||
<path d="M 245 85 L 249 89 L 256 81" stroke="#fff" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<text x="270" y="82" font-family="system-ui, sans-serif" font-size="12" fill="#1f4a2a" font-weight="500">kernel / evdev</text>
|
|
||||||
<text x="270" y="98" font-family="ui-monospace, monospace" font-size="10" fill="#3b6a44">KEY_LEFTALT</text>
|
|
||||||
|
|
||||||
<line x1="340" y1="110" x2="340" y2="135" stroke="#666" stroke-width="1.5"/>
|
|
||||||
<polygon points="335,132 345,132 340,142" fill="#666"/>
|
|
||||||
|
|
||||||
<!-- Layer 2: xkb culprit -->
|
|
||||||
<rect x="210" y="150" width="260" height="80" rx="6" fill="#efd3cf" stroke="#a8312a" stroke-width="1.5"/>
|
|
||||||
<polygon points="225,190 233,174 241,190" fill="#a8312a"/>
|
|
||||||
<text x="233" y="187" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" fill="#fff" font-weight="500">!</text>
|
|
||||||
<text x="255" y="175" font-family="system-ui, sans-serif" font-size="12" fill="#6b1e18" font-weight="500">couche xkb</text>
|
|
||||||
<text x="255" y="191" font-family="ui-monospace, monospace" font-size="10" fill="#8a2820">option: lv3:lalt_switch</text>
|
|
||||||
<text x="255" y="215" font-family="ui-monospace, monospace" font-size="9" fill="#6b1e18">Alt_L</text>
|
|
||||||
<line x1="288" y1="212" x2="318" y2="212" stroke="#a8312a" stroke-width="1.2"/>
|
|
||||||
<polygon points="314,209 322,212 314,215" fill="#a8312a"/>
|
|
||||||
<text x="326" y="215" font-family="ui-monospace, monospace" font-size="9" fill="#6b1e18">ISO_Level3_Shift</text>
|
|
||||||
|
|
||||||
<line x1="340" y1="230" x2="340" y2="253" stroke="#a8312a" stroke-width="1.5" stroke-dasharray="4 3"/>
|
|
||||||
<polygon points="335,250 345,250 340,260" fill="#a8312a"/>
|
|
||||||
|
|
||||||
<!-- Layer 3: mutter -->
|
|
||||||
<rect x="230" y="270" width="220" height="50" rx="6" fill="#f5e4cd" stroke="#c98030" stroke-width="1"/>
|
|
||||||
<polygon points="245,305 253,289 261,305" fill="#c98030"/>
|
|
||||||
<text x="253" y="302" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" fill="#fff" font-weight="500">!</text>
|
|
||||||
<text x="275" y="292" font-family="system-ui, sans-serif" font-size="12" fill="#7a4a14" font-weight="500">mutter (compositeur)</text>
|
|
||||||
<text x="275" y="308" font-family="ui-monospace, monospace" font-size="10" fill="#a86b1a">voit Level3, pas Alt</text>
|
|
||||||
|
|
||||||
<line x1="340" y1="320" x2="340" y2="343" stroke="#a8312a" stroke-width="1.5" stroke-dasharray="4 3"/>
|
|
||||||
<polygon points="335,340 345,340 340,350" fill="#a8312a"/>
|
|
||||||
|
|
||||||
<!-- Layer 4: shortcut fails -->
|
|
||||||
<rect x="230" y="360" width="220" height="50" rx="6" fill="#efd3cf" stroke="#a8312a" stroke-width="1.5"/>
|
|
||||||
<circle cx="252" cy="385" r="9" fill="#a8312a"/>
|
|
||||||
<line x1="247" y1="380" x2="257" y2="390" stroke="#fff" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="257" y1="380" x2="247" y2="390" stroke="#fff" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<text x="272" y="382" font-family="system-ui, sans-serif" font-size="12" fill="#6b1e18" font-weight="500">raccourci Alt+Tab</text>
|
|
||||||
<text x="272" y="398" font-family="ui-monospace, monospace" font-size="10" fill="#8a2820">jamais déclenché</text>
|
|
||||||
|
|
||||||
<!-- RIGHT: fix -->
|
|
||||||
<text x="570" y="40" text-anchor="middle" font-family="system-ui, sans-serif" font-size="9" fill="#888" letter-spacing="2">CORRECTION</text>
|
|
||||||
|
|
||||||
<!-- Terminal-style box -->
|
|
||||||
<rect x="490" y="60" width="160" height="100" rx="6" fill="#1f2940" stroke="#0d1424" stroke-width="1"/>
|
|
||||||
<circle cx="504" cy="74" r="3.5" fill="#d94a3d"/>
|
|
||||||
<circle cx="516" cy="74" r="3.5" fill="#e89a3c"/>
|
|
||||||
<circle cx="528" cy="74" r="3.5" fill="#3b8a4f"/>
|
|
||||||
<line x1="490" y1="86" x2="650" y2="86" stroke="#0d1424" stroke-width="1"/>
|
|
||||||
<text x="500" y="105" font-family="ui-monospace, monospace" font-size="9" fill="#7ec88f">$ gsettings set</text>
|
|
||||||
<text x="500" y="119" font-family="ui-monospace, monospace" font-size="8" fill="#e8e6e1">org.gnome.desktop</text>
|
|
||||||
<text x="500" y="131" font-family="ui-monospace, monospace" font-size="8" fill="#e8e6e1">.input-sources</text>
|
|
||||||
<text x="500" y="143" font-family="ui-monospace, monospace" font-size="8" fill="#e8e6e1">xkb-options "[]"</text>
|
|
||||||
<text x="500" y="156" font-family="ui-monospace, monospace" font-size="9" fill="#7ec88f">$ _</text>
|
|
||||||
|
|
||||||
<!-- Arrow back to xkb layer -->
|
|
||||||
<path d="M 490 190 Q 470 190 470 200" stroke="#3b8a4f" stroke-width="2" fill="none"/>
|
|
||||||
<polygon points="466,196 470,206 474,196" fill="#3b8a4f"/>
|
|
||||||
<text x="510" y="186" font-family="ui-monospace, monospace" font-size="9" fill="#3b6a44">reset xkb</text>
|
|
||||||
|
|
||||||
<!-- After: shortcut works -->
|
|
||||||
<rect x="490" y="270" width="160" height="60" rx="6" fill="#d9ecdf" stroke="#3b8a4f" stroke-width="1"/>
|
|
||||||
<circle cx="510" cy="300" r="9" fill="#3b8a4f"/>
|
|
||||||
<path d="M 505 300 L 509 304 L 516 296" stroke="#fff" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<text x="530" y="296" font-family="system-ui, sans-serif" font-size="11" fill="#1f4a2a" font-weight="500">Alt + Tab</text>
|
|
||||||
<text x="530" y="312" font-family="ui-monospace, monospace" font-size="9" fill="#3b6a44">fonctionne à nouveau</text>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 2.3 MiB |
@@ -1,152 +0,0 @@
|
|||||||
# Quand Alt ne répond plus : anatomie d'un bug clavier sous GNOME/Wayland
|
|
||||||
|
|
||||||
*Comment une option de clavier a priori anodine peut désactiver Alt+Tab, Alt+F4 et tous les raccourcis Alt — et comment diagnostiquer ce genre de problème de façon méthodique.*
|
|
||||||
|
|
||||||
## Le symptôme
|
|
||||||
|
|
||||||
Un beau matin, les raccourcis clavier ne répondent plus. Pas tous : seulement ceux qui utilisent la touche **Alt gauche**.
|
|
||||||
|
|
||||||
- `Alt+Tab` ne change plus de fenêtre
|
|
||||||
- `Alt+F4` ne ferme plus l'application active
|
|
||||||
- Dans un terminal, les raccourcis `Alt+quelque chose` (édition de ligne readline, raccourcis dans une applicaiton, navigation tmux…) restent sans effet
|
|
||||||
- La touche **AltGr** (Alt droite), elle, fonctionne toujours : on peut taper `@`, `#`, `~`, les caractères normalement obtenus via Alt droite sur un clavier français azerty
|
|
||||||
|
|
||||||
Premier réflexe naturel : « Le clavier est cassé ». Sauf que la touche physique répond bien — elle ne déclenche simplement plus ce qu'on attend d'elle.
|
|
||||||
|
|
||||||
## Comprendre ce qui se passe (sans connaître Linux par cœur)
|
|
||||||
|
|
||||||
Pour saisir le bug, il faut comprendre un détail qu'on ignore généralement : **une touche physique du clavier et la fonction qu'elle déclenche sont deux choses différentes**.
|
|
||||||
|
|
||||||
Quand on appuie sur la touche marquée « Alt » à gauche du clavier, le système reçoit d'abord un signal matériel — un code brut, `KEY_LEFTALT` sous Linux. Ce signal est ensuite **traduit** en une fonction logique par une couche logicielle appelée **xkb** (X Keyboard Extension). C'est xkb qui décide que `KEY_LEFTALT` signifie « modificateur Alt gauche » (le fameux `Alt_L`).
|
|
||||||
|
|
||||||
Mais xkb peut être configuré pour faire autre chose de ce même signal. Et c'est exactement ce qui s'était passé ici. Une option xkb nommée `lv3:lalt_switch` indiquait à la couche de traduction :
|
|
||||||
|
|
||||||
> « Quand tu reçois `KEY_LEFTALT`, ne génère pas `Alt_L`. Génère `ISO_Level3_Shift` à la place. »
|
|
||||||
|
|
||||||
`ISO_Level3_Shift`, c'est le nom technique de **AltGr** : la touche modificatrice qui permet d'accéder au « troisième niveau » d'une touche (le `@` au-dessus du `à`, le `#` au-dessus du `"`, etc.). En clair, l'option transformait Alt gauche en un deuxième AltGr.
|
|
||||||
|
|
||||||
Conséquence : du point de vue des applications, **personne n'appuie jamais sur Alt**. Le gestionnaire de fenêtres (mutter, dans GNOME) attend un événement `Alt_L` qui ne vient jamais ; le terminal attend un préfixe Alt qui ne vient jamais non plus ; AltGr fonctionne toujours parce que c'est lui le « vrai » Level 3 Shift sur azerty, par défaut.
|
|
||||||
|
|
||||||
C'est l'analogie d'un interrupteur dont on aurait inversé deux fils dans le mur : l'interrupteur marche, mais il commande une autre lampe.
|
|
||||||
|
|
||||||
## La cause exacte
|
|
||||||
|
|
||||||
Sous GNOME, les options xkb sont stockées dans la base de configuration **dconf**, accessible via la commande `gsettings`. La clé concernée :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gsettings get org.gnome.desktop.input-sources xkb-options
|
|
||||||
```
|
|
||||||
|
|
||||||
Sur le système concerné, la commande retournait :
|
|
||||||
|
|
||||||
```
|
|
||||||
['lv3:lalt_switch']
|
|
||||||
```
|
|
||||||
|
|
||||||
D'où venait cette option ? Plusieurs hypothèses plausibles :
|
|
||||||
|
|
||||||
- Sélectionnée par erreur dans Paramètres → Clavier → Options de disposition lors d'une configuration ancienne
|
|
||||||
- Importée depuis une ancienne machine via la synchronisation du profil
|
|
||||||
- Activée par un script ou un outil de personnalisation (GNOME Tweaks, dconf-editor)
|
|
||||||
- Héritée d'une habitude QWERTY où certains préfèrent un second AltGr à gauche
|
|
||||||
|
|
||||||
Sur un clavier français azerty, cette option n'a aucun intérêt pratique : AltGr est déjà sur la touche Alt droite, là où l'index droit peut l'atteindre naturellement. Ajouter un second AltGr sur la touche Alt gauche revient à perdre Alt sans gagner quoi que ce soit.
|
|
||||||
|
|
||||||
## Le diagnostic, étape par étape
|
|
||||||
|
|
||||||
Voici la séquence de commandes pour confirmer le problème — utile à mémoriser parce qu'elle s'applique à tout symptôme similaire sur GNOME/Wayland.
|
|
||||||
|
|
||||||
**1. Confirmer l'environnement de session.** Les commandes qui suivent supposent GNOME sous Wayland ; sous X11 ou KDE, le diagnostic diffère.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
echo $XDG_SESSION_TYPE # attendu : wayland
|
|
||||||
echo $XDG_CURRENT_DESKTOP # attendu : GNOME
|
|
||||||
```
|
|
||||||
|
|
||||||
**2. Inspecter les options xkb.** C'est le test diagnostic principal pour ce genre de panne.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gsettings get org.gnome.desktop.input-sources xkb-options
|
|
||||||
```
|
|
||||||
|
|
||||||
Si la sortie n'est pas `@as []` (liste vide) ou une option clairement intentionnelle, on tient probablement le coupable. Les options les plus susceptibles de casser des raccourcis :
|
|
||||||
|
|
||||||
- `lv3:lalt_switch` — transforme Alt gauche en AltGr (le cas présent)
|
|
||||||
- `altwin:swap_alt_win` — échange Alt et Super (la touche Windows)
|
|
||||||
- `caps:swapescape` — échange Caps Lock et Échap (anodin pour Alt, mais peut surprendre)
|
|
||||||
- `ctrl:nocaps`, `ctrl:swapcaps` — transforment Caps Lock en Ctrl
|
|
||||||
|
|
||||||
**3. Vérifier que les raccourcis WM sont bien définis.** Cela permet d'éliminer une mauvaise piste : si `Alt+Tab` ne marchait pas parce que le raccourci avait été effacé, ce serait visible ici.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gsettings get org.gnome.desktop.wm.keybindings switch-applications
|
|
||||||
```
|
|
||||||
|
|
||||||
La sortie attendue est `['<Super>Tab', '<Alt>Tab']` ou équivalent. Si `<Alt>Tab` y figure, le gestionnaire de fenêtres est correctement configuré — la panne est ailleurs.
|
|
||||||
|
|
||||||
**4. Vérifier les options d'accessibilité.** Les touches rémanentes (StickyKeys), touches lentes (SlowKeys) ou touches rebonds (BounceKeys) peuvent provoquer des comportements clavier surprenants quand elles sont activées par erreur.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gsettings get org.gnome.desktop.a11y.keyboard stickykeys-enable
|
|
||||||
gsettings get org.gnome.desktop.a11y.keyboard slowkeys-enable
|
|
||||||
gsettings get org.gnome.desktop.a11y.keyboard bouncekeys-enable
|
|
||||||
```
|
|
||||||
|
|
||||||
Toutes les trois doivent normalement renvoyer `false` sauf besoin spécifique.
|
|
||||||
|
|
||||||
**5. Tester au niveau matériel si rien d'autre n'explique.** Si toutes les vérifications logicielles sont propres, on vérifie que la touche envoie bien un signal au noyau :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo evtest
|
|
||||||
```
|
|
||||||
|
|
||||||
L'outil demande de choisir un périphérique (le clavier), puis affiche en direct chaque événement reçu. En appuyant sur Alt gauche, une ligne contenant `KEY_LEFTALT` doit apparaître. Si rien ne s'affiche, le problème est matériel ou dans le pilote — ce qui sort du cadre de cette fiche.
|
|
||||||
|
|
||||||
## La correction
|
|
||||||
|
|
||||||
Une seule commande suffit dans le cas présent :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gsettings set org.gnome.desktop.input-sources xkb-options "[]"
|
|
||||||
```
|
|
||||||
|
|
||||||
L'effet est **immédiat** : mutter recharge la configuration clavier à la volée, sans qu'on ait besoin de fermer sa session. Si pour une raison ou une autre l'effet ne se voit pas (vieux processus qui a mis en cache la configuration, terminal récalcitrant…), une déconnexion/reconnexion de la session GNOME suffit à tout réinitialiser.
|
|
||||||
|
|
||||||
Pour vérifier que la valeur est bien revenue à vide :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gsettings get org.gnome.desktop.input-sources xkb-options
|
|
||||||
# attendu : @as []
|
|
||||||
```
|
|
||||||
|
|
||||||
## Et si on voulait vraiment garder l'option ?
|
|
||||||
|
|
||||||
Pour information, la commande inverse est :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gsettings set org.gnome.desktop.input-sources xkb-options "['lv3:lalt_switch']"
|
|
||||||
```
|
|
||||||
|
|
||||||
À réserver aux cas où l'on tape énormément de caractères de troisième niveau de la main gauche et où on accepte de perdre Alt+Tab.
|
|
||||||
|
|
||||||
## La méthode à retenir, au-delà de ce bug précis
|
|
||||||
|
|
||||||
L'intérêt de cette fiche n'est pas tant la solution — une ligne de commande — que la **logique de diagnostic**. Quand une touche cesse de fonctionner sous Linux, on remonte la chaîne des responsabilités, du plus haut niveau au plus bas :
|
|
||||||
|
|
||||||
1. **Le gestionnaire de fenêtres a-t-il bien le raccourci ?** (`gsettings ... wm.keybindings`)
|
|
||||||
2. **Les options d'accessibilité ne brouillent-elles pas la frappe ?** (`gsettings ... a11y.keyboard`)
|
|
||||||
3. **La couche xkb traduit-elle correctement la touche en modificateur ?** (`gsettings ... xkb-options`)
|
|
||||||
4. **Le noyau reçoit-il un signal matériel quand on appuie ?** (`evtest`)
|
|
||||||
|
|
||||||
À chaque étage, une commande, une sortie attendue, et un verdict clair. La grande force de Linux dans ce genre de situation, c'est que chaque couche est inspectable séparément. Le réflexe à acquérir n'est pas « ça ne marche pas, je redémarre » mais « ça ne marche pas, je trouve quelle couche ment ».
|
|
||||||
|
|
||||||
## Checklist mémo
|
|
||||||
|
|
||||||
Modificateur (Alt / Super / Ctrl) qui ne répond plus sous GNOME/Wayland :
|
|
||||||
|
|
||||||
1. `gsettings get org.gnome.desktop.input-sources xkb-options` — surveiller `lv3:lalt_switch`, `altwin:swap_alt_win`, `caps:*`, `ctrl:*`
|
|
||||||
2. `gsettings list-recursively org.gnome.desktop.wm.keybindings | grep -i alt` — confirmer que le raccourci existe
|
|
||||||
3. `gsettings get org.gnome.desktop.a11y.keyboard stickykeys-enable` (puis `slowkeys-enable`, `bouncekeys-enable`)
|
|
||||||
4. `sudo evtest` → choisir le clavier → presser la touche → doit afficher le bon code (`KEY_LEFTALT`, `KEY_LEFTMETA`, etc.)
|
|
||||||
|
|
||||||
Quatre commandes, quatre couches, et 95 % des bugs clavier de session graphique sont localisés.
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"uuid": "586c5ab7-e960-465b-b499-83e0209890fe",
|
|
||||||
"slug": "quand-alt-ne-repond-plus-anatomie-d-un-bug-clavier-sous-gnome-wayland",
|
|
||||||
"title": "Quand Alt ne répond plus : anatomie d'un bug clavier sous GNOME/Wayland",
|
|
||||||
"author": "cedric@abonnel.fr",
|
|
||||||
"published": true,
|
|
||||||
"published_at": "2026-05-25 07:27",
|
|
||||||
"created_at": "2026-05-12 13:35:47",
|
|
||||||
"updated_at": "2026-05-12 13:40:34",
|
|
||||||
"revisions": [],
|
|
||||||
"cover": "cover.png",
|
|
||||||
"files_meta": {
|
|
||||||
"cover.png": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": "https://img1.teletype.in/files/87/6b/876beab9-74d8-4c14-bb47-d600639b629f.png"
|
|
||||||
},
|
|
||||||
"af37382560286fe2-6958.svg": {
|
|
||||||
"author": "",
|
|
||||||
"source_url": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"external_links": [],
|
|
||||||
"seo_title": "",
|
|
||||||
"seo_description": "Alt+Tab et Alt+F4 ne répondent plus sous GNOME/Wayland ? Une option xkb peut détourner Alt gauche en AltGr. Diagnostic et correction expliqués.",
|
|
||||||
"og_image": "https://varlog.a5l.fr/file?uuid=586c5ab7-e960-465b-b499-83e0209890fe&name=cover.png",
|
|
||||||
"category": "informatique"
|
|
||||||
}
|
|
||||||