<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title>Postmortem - Balise - arleo.eu</title>
        <link>https://www.arleo.eu/tags/postmortem/</link>
        <description>Postmortem - Balise - arleo.eu</description>
        <generator>Hugo -- gohugo.io</generator><language>fr</language><lastBuildDate>Mon, 25 May 2026 20:51:17 &#43;0200</lastBuildDate><atom:link href="https://www.arleo.eu/tags/postmortem/" rel="self" type="application/rss+xml" /><item>
    <title>Postmortem — CrowdSec AppSec : faux positif heuristique sur Sonarr/Radarr</title>
    <link>https://www.arleo.eu/posts/postmortem-crowdsec-appsec-false-positive-sonarr/</link>
    <pubDate>Mon, 25 May 2026 20:51:17 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/postmortem-crowdsec-appsec-false-positive-sonarr/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/postmortem-crowdsec-appsec-false-positive-sonarr-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="résumé">Résumé</h2>
<p>Le <strong>25 mai 2026 vers 22h02 (heure locale)</strong>, Sonarr et Radarr sont devenus totalement inaccessibles depuis l&rsquo;IP maison (<code>82.XX.XX.XX</code>), retournant <strong>403</strong> sur toutes les URLs y compris <code>/login</code>. Le service était pourtant opérationnel. Le diagnostic initial suspectait les récents déploiements du refactor <code>crowdsec-cf-sync</code> — la cause réelle est un <strong>faux positif heuristique CrowdSec AppSec</strong>.</p>
<hr>
<h2 id="timeline">Timeline</h2>
<table>
  <thead>
      <tr>
          <th>Heure (locale)</th>
          <th>Événement</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>~22h00</td>
          <td>Cookie de session Sonarr expiré côté navigateur</td>
      </tr>
      <tr>
          <td>22h02:31</td>
          <td>Le navigateur charge la bibliothèque Sonarr → tente de charger 20+ <code>/MediaCover/*.jpg</code> simultanément</td>
      </tr>
      <tr>
          <td>22h02:31</td>
          <td>Sonarr retourne 302 → <code>/login</code> pour chaque image (session invalide)</td>
      </tr>
      <tr>
          <td>22h02:34</td>
          <td>La connexion SignalR WebSocket s&rsquo;établit (101) via <code>access_token</code> dans l&rsquo;URL</td>
      </tr>
      <tr>
          <td>~22h05</td>
          <td>CrowdSec AppSec déclenche la règle heuristique <code>http-probing</code> : rafale de requêtes échouées depuis la même IP</td>
      </tr>
      <tr>
          <td>22h11:37</td>
          <td>Toutes les requêtes de <code>82.XX.XX.XX</code> retournent 403 — <code>cs_reason=heuristic</code> dans les logs nginx</td>
      </tr>
      <tr>
          <td>22h13:34</td>
          <td>Même <code>/login</code> bloqué — l&rsquo;IP ne peut plus s&rsquo;authentifier</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="cause-racine">Cause racine</h2>
<p>CrowdSec AppSec maintient un <strong>état heuristique en mémoire</strong>, distinct des décisions LAPI. Lorsque le navigateur tente de charger simultanément de nombreuses ressources et reçoit des 302/403 de l&rsquo;application upstream (Sonarr), AppSec interprète cette rafale d&rsquo;échecs comme du sondage agressif (<code>http-probing</code>) et bloque l&rsquo;IP source.</p>]]></description>
</item>
<item>
    <title>Postmortem : TypeIt cassé par Mermaid dans le thème LoveIt</title>
    <link>https://www.arleo.eu/posts/postmortem-typeit-mermaid/</link>
    <pubDate>Thu, 14 May 2026 09:44:43 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/postmortem-typeit-mermaid/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/postmortem-typeit-mermaid-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="tldr">TL;DR</h2>
<p>L&rsquo;animation typewriter du thème LoveIt (TypeIt) ne fonctionnait plus sur la home après l&rsquo;ajout de diagrammes Mermaid dans les posts. Cause : un sélecteur DOM <code>#id-1</code> partagé entre les deux librairies. Quand Mermaid trouve un bloc orphelin dans un résumé de home, son initialisation crash, et la chaîne d&rsquo;init JS s&rsquo;arrête avant d&rsquo;atteindre TypeIt. Fix : ajouter `</p>]]></description>
</item>
<item>
    <title>Post-mortem : Cloudflare Bot Management bloquait les webhooks MCP</title>
    <link>https://www.arleo.eu/posts/postmortem-cf-bot-blocking-mcp/</link>
    <pubDate>Sat, 09 May 2026 12:46:28 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/postmortem-cf-bot-blocking-mcp/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/postmortem-cf-bot-blocking-mcp-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="le-symptôme">Le symptôme</h2>
<p>Je viens de finaliser un endpoint webhook dans le <code>hugo-mcp-proxy</code> qui recevra des notifications de GitHub à chaque push sur le repo <code>arleo.eu</code>. Implementation propre : HMAC-SHA256, rate limiting, IPAddressAllow GitHub ranges côté systemd.</p>
<p>Test fonctionnel depuis un client externe :</p>
<div class="code-block code-line-numbers open" data-start="0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ curl -X POST https://mcp-hugo.arleo.eu/webhook/test <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -H <span class="s2">&#34;Content-Type: application/json&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -d <span class="s1">&#39;{&#34;test&#34;: true}&#39;</span></span></span></code></pre></div></div>
<p>Réponse : <strong>403 Forbidden</strong>.</p>
<p>Curieux. Le service tourne, l&rsquo;IP source de mon test est dans le whitelist, le HMAC est correct. Pourquoi 403 ?</p>
<h2 id="investigation-côté-serveur">Investigation côté serveur</h2>
<p>Logs nginx côté NUC :</p>
<div class="code-block code-line-numbers open" data-start="0">
    <div class="code-header language-">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><pre tabindex="0"><code>$ sudo tail -100 /var/log/nginx/mcp-hugo.access.log | grep webhook</code></pre></div>
<p>Vide. Aucune requête n&rsquo;arrive sur nginx.</p>
<p>Logs <code>mcp-oauth-proxy</code> :</p>
<div class="code-block code-line-numbers open" data-start="0">
    <div class="code-header language-">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><pre tabindex="0"><code>$ sudo journalctl -u mcp-oauth-proxy -n 100 | grep webhook</code></pre></div>
<p>Vide aussi. La requête n&rsquo;arrive pas jusqu&rsquo;au service.</p>
<p>Soit elle est bloquée par firewall avant nginx (CrowdSec ou ufw), soit en amont par Cloudflare.</p>
<h2 id="la-vérité-côté-cloudflare">La vérité côté Cloudflare</h2>]]></description>
</item>
<item>
    <title>Post-mortem : mcp-installer régénérait les tokens à chaque relance</title>
    <link>https://www.arleo.eu/posts/postmortem-mcp-installer-idempotence/</link>
    <pubDate>Sat, 09 May 2026 12:45:36 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/postmortem-mcp-installer-idempotence/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/postmortem-mcp-installer-idempotence-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="le-bug">Le bug</h2>
<p><code>mcp-installer</code> est un script bash que j&rsquo;ai écrit pour automatiser l&rsquo;installation du <code>mcp-oauth-proxy</code> (FastAPI + nginx + systemd) sur un nouveau host. Workflow standard : clone, run, c&rsquo;est tout.</p>
<p>Sauf qu&rsquo;en relançant le script sur un host <strong>déjà installé</strong> (par exemple pour mettre à jour la version), j&rsquo;ai découvert un bug d&rsquo;idempotence : tous les secrets étaient régénérés.</p>
<div class="code-block code-line-numbers open" data-start="0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sudo ./install.sh
</span></span><span class="line"><span class="cl"><span class="o">[</span>+<span class="o">]</span> Generating MCP_TOKEN...
</span></span><span class="line"><span class="cl"><span class="o">[</span>+<span class="o">]</span> Generating CLIENT_ID...
</span></span><span class="line"><span class="cl"><span class="o">[</span>+<span class="o">]</span> Generating CLIENT_SECRET...
</span></span><span class="line"><span class="cl"><span class="o">[</span>+<span class="o">]</span> Generating TOKEN_SECRET...
</span></span><span class="line"><span class="cl"><span class="o">[</span>+<span class="o">]</span> Writing /etc/mcp-oauth-proxy/.env...</span></span></code></pre></div></div>
<p>Si tu as déjà un <code>.env</code> avec des tokens en service, l&rsquo;installer les <strong>écrase</strong>. Tous les clients OAuth déjà enregistrés (Claude.ai dans mon cas) se retrouvent avec des credentials invalides. La connexion casse.</p>
<h2 id="pourquoi-cest-arrivé">Pourquoi c&rsquo;est arrivé</h2>
<p>Le script utilisait cette logique :</p>
<div class="code-block code-line-numbers" data-start="0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">MCP_TOKEN</span><span class="o">=</span><span class="k">$(</span>openssl rand -hex 32<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">CLIENT_ID</span><span class="o">=</span><span class="k">$(</span>openssl rand -hex 16<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">CLIENT_SECRET</span><span class="o">=</span><span class="k">$(</span>openssl rand -hex 32<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">TOKEN_SECRET</span><span class="o">=</span><span class="k">$(</span>openssl rand -hex 32<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">cat &gt; /etc/mcp-oauth-proxy/.env <span class="s">&lt;&lt;EOF
</span></span></span><span class="line"><span class="cl"><span class="s">MCP_TOKEN=$MCP_TOKEN
</span></span></span><span class="line"><span class="cl"><span class="s">CLIENT_ID=$CLIENT_ID
</span></span></span><span class="line"><span class="cl"><span class="s">CLIENT_SECRET=$CLIENT_SECRET
</span></span></span><span class="line"><span class="cl"><span class="s">TOKEN_SECRET=$TOKEN_SECRET
</span></span></span><span class="line"><span class="cl"><span class="s">EOF</span></span></span></code></pre></div></div>
<p>Le pattern « génère puis écris » est correct pour une première install. Mais en relance, il ignore complètement le <code>.env</code> existant.</p>
<p>C&rsquo;est l&rsquo;erreur classique : l&rsquo;auteur (moi) a testé le script <strong>uniquement sur un host fresh</strong>. Le mode &ldquo;réinstall sur host existant&rdquo; n&rsquo;a jamais été testé.</p>
<h2 id="reproduction-du-bug">Reproduction du bug</h2>
<div class="code-block code-line-numbers open" data-start="0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sudo ./install.sh
</span></span><span class="line"><span class="cl"><span class="o">[</span>OK<span class="o">]</span> Installation <span class="nb">complete</span>
</span></span><span class="line"><span class="cl">$ cat /etc/mcp-oauth-proxy/.env <span class="p">|</span> head -1
</span></span><span class="line"><span class="cl"><span class="nv">MCP_TOKEN</span><span class="o">=</span>&lt;redacted-mcp-token-1&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ sudo ./install.sh
</span></span><span class="line"><span class="cl"><span class="o">[</span>OK<span class="o">]</span> Installation <span class="nb">complete</span>
</span></span><span class="line"><span class="cl">$ cat /etc/mcp-oauth-proxy/.env <span class="p">|</span> head -1
</span></span><span class="line"><span class="cl"><span class="nv">MCP_TOKEN</span><span class="o">=</span>&lt;redacted-mcp-token-2&gt;   ← DIFFÉRENT</span></span></code></pre></div></div>
<p>Test reproductible en 30 secondes. C&rsquo;est presque comique que je ne l&rsquo;aie pas vu plus tôt.</p>
<h2 id="la-leçon--idempotent-est-un-contrat-pas-un-espoir">La leçon : &ldquo;idempotent&rdquo; est un contrat, pas un espoir</h2>
<p>Un script d&rsquo;installation doit être <strong>idempotent par défaut</strong>. C&rsquo;est-à-dire : <code>./install.sh</code> 1 fois, 2 fois, 5 fois → le système est dans le même état stable.</p>
<p>J&rsquo;avais ce contrat <strong>dans ma tête</strong>. Je ne l&rsquo;avais pas dans <strong>le code</strong>.</p>
<h2 id="le-fix--pre-flight-checks--force-flag-explicite">Le fix : pre-flight checks + force flag explicite</h2>]]></description>
</item>
<item>
    <title>Post-mortem : 3 timeouts MCP — IPAddressDeny &#43; Cloudflare &#43; NFS</title>
    <link>https://www.arleo.eu/posts/postmortem-mcp-timeouts-cloudflare/</link>
    <pubDate>Sat, 09 May 2026 12:44:44 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/postmortem-mcp-timeouts-cloudflare/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/postmortem-mcp-timeouts-cloudflare-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="contexte">Contexte</h2>
<p>J&rsquo;ai mis en production un Hugo MCP Server (FastAPI, 7 tools) qui me permet d&rsquo;éditer arleo.eu depuis Claude.ai. Architecture : <code>claude.ai → mcp-oauth-proxy NUC → hugo-mcp-proxy NUC → MCP server VM</code>.</p>]]></description>
</item>
</channel>
</rss>
