<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>Postmortem - Tag - arleo.eu</title><link>https://www.arleo.eu/en/tags/postmortem/</link><description>Postmortem - Tag - arleo.eu</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 25 May 2026 20:51:17 +0200</lastBuildDate><atom:link href="https://www.arleo.eu/en/tags/postmortem/" rel="self" type="application/rss+xml"/><item><title>Postmortem — CrowdSec AppSec: Heuristic False Positive on Sonarr/Radarr</title><link>https://www.arleo.eu/en/posts/postmortem-crowdsec-appsec-false-positive-sonarr/</link><pubDate>Mon, 25 May 2026 20:51:17 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/en/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="summary">Summary</h2>
<p>On <strong>May 25, 2026 at around 10:02 PM (local time)</strong>, Sonarr and Radarr became completely inaccessible from the home IP (<code>82.XX.XX.XX</code>), returning <strong>403</strong> on every URL including <code>/login</code>. The service was fully operational. Initial suspicion fell on the day&rsquo;s <code>crowdsec-cf-sync</code> refactor deployment — the real cause was a <strong>CrowdSec AppSec heuristic false positive</strong>.</p>
<hr>
<h2 id="timeline">Timeline</h2>
<table>
  <thead>
      <tr>
          <th>Time (local)</th>
          <th>Event</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>~10:00 PM</td>
          <td>Sonarr browser session cookie expired</td>
      </tr>
      <tr>
          <td>10:02:31 PM</td>
          <td>Browser loads Sonarr library → attempts to fetch 20+ <code>/MediaCover/*.jpg</code> simultaneously</td>
      </tr>
      <tr>
          <td>10:02:31 PM</td>
          <td>Sonarr returns 302 → <code>/login</code> for each image (invalid session)</td>
      </tr>
      <tr>
          <td>10:02:34 PM</td>
          <td>SignalR WebSocket connection succeeds (101) via <code>access_token</code> in URL</td>
      </tr>
      <tr>
          <td>~10:05 PM</td>
          <td>CrowdSec AppSec triggers heuristic rule <code>http-probing</code>: burst of failed requests from same IP</td>
      </tr>
      <tr>
          <td>10:11:37 PM</td>
          <td>All requests from <code>82.XX.XX.XX</code> return 403 — <code>cs_reason=heuristic</code> in nginx logs</td>
      </tr>
      <tr>
          <td>10:13:34 PM</td>
          <td>Even <code>/login</code> is blocked — IP cannot authenticate</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="root-cause">Root Cause</h2>
<p>CrowdSec AppSec maintains an <strong>in-memory heuristic state</strong>, separate from LAPI decisions. When the browser simultaneously tries to load many resources and receives 302/403 from the upstream application (Sonarr), AppSec interprets the burst of failures as aggressive probing (<code>http-probing</code>) and blocks the source IP.</p>]]></description></item><item><title>Postmortem: TypeIt broken by Mermaid in LoveIt theme</title><link>https://www.arleo.eu/en/posts/postmortem-typeit-mermaid/</link><pubDate>Thu, 14 May 2026 09:46:31 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/en/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>The LoveIt theme&rsquo;s typewriter animation (TypeIt) stopped working on the home after adding Mermaid diagrams to posts. Cause: a <code>#id-1</code> DOM selector shared between both libraries. When Mermaid finds an orphan block in a home summary, its initialization crashes, and the JS init chain stops before reaching TypeIt. Fix: add `</p>]]></description></item><item><title>Post-mortem: Cloudflare Bot Management blocked MCP webhooks</title><link>https://www.arleo.eu/en/posts/postmortem-cf-bot-blocking-mcp/</link><pubDate>Sat, 09 May 2026 13:06:02 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/en/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="the-symptom">The symptom</h2>
<p>I just finished a webhook endpoint in <code>hugo-mcp-proxy</code> that will receive notifications from GitHub on every push to the arleo.eu repo. Clean implementation: HMAC-SHA256, rate limiting, IPAddressAllow GitHub ranges in systemd.</p>
<p>Functional test from an external client:</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="Copy to clipboard"><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>Response: <strong>403 Forbidden</strong>.</p>
<p>Strange. The service is running, my source IP is whitelisted, the HMAC is correct. Why 403?</p>
<h2 id="server-side-investigation">Server-side investigation</h2>
<p>NUC nginx logs:</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="Copy to clipboard"><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>Empty. No request reaches nginx.</p>
<p><code>mcp-oauth-proxy</code> logs:</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="Copy to clipboard"><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>Empty too. The request doesn&rsquo;t even reach the service.</p>
<p>Either it&rsquo;s blocked by the firewall before nginx (CrowdSec or ufw), or upstream by Cloudflare.</p>
<h2 id="the-truth-at-cloudflare">The truth at Cloudflare</h2>]]></description></item><item><title>Post-mortem: mcp-installer regenerated tokens on every rerun</title><link>https://www.arleo.eu/en/posts/postmortem-mcp-installer-idempotence/</link><pubDate>Sat, 09 May 2026 13:05:11 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/en/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="the-bug">The bug</h2>
<p><code>mcp-installer</code> is a bash script I wrote to automate the installation of <code>mcp-oauth-proxy</code> (FastAPI + nginx + systemd) on a new host. Standard workflow: clone, run, done.</p>
<p>Except when re-running the script on an <strong>already installed</strong> host (e.g. to update the version), I discovered an idempotence bug: all secrets were regenerated.</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="Copy to clipboard"><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>If you already have a <code>.env</code> with tokens in service, the installer <strong>overwrites</strong> them. All already-registered OAuth clients (Claude.ai in my case) end up with invalid credentials. Connection breaks.</p>
<h2 id="why-it-happened">Why it happened</h2>
<p>The script used this logic:</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="Copy to clipboard"><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>The &ldquo;generate then write&rdquo; pattern is correct for first install. But on rerun, it completely ignores the existing <code>.env</code>.</p>
<p>It&rsquo;s the classic mistake: the author (me) tested the script <strong>only on a fresh host</strong>. The &ldquo;reinstall on existing host&rdquo; mode was never tested.</p>
<h2 id="bug-reproduction">Bug reproduction</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="Copy to clipboard"><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>a7f3e9d2c4b8a1...
</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>8c2f6a1b9e4d7c...   ← DIFFERENT</span></span></code></pre></div></div>
<p>Reproducible test in 30 seconds. Almost comical I didn&rsquo;t catch it sooner.</p>
<h2 id="the-lesson-idempotent-is-a-contract-not-a-hope">The lesson: &ldquo;idempotent&rdquo; is a contract, not a hope</h2>
<p>An installation script must be <strong>idempotent by default</strong>. Meaning: <code>./install.sh</code> once, twice, five times → system in the same stable state.</p>
<p>I had this contract <strong>in my head</strong>. I didn&rsquo;t have it <strong>in the code</strong>.</p>
<h2 id="the-fix-pre-flight-checks--explicit-force-flag">The fix: pre-flight checks + explicit force flag</h2>]]></description></item><item><title>Post-mortem: 3 MCP timeouts — IPAddressDeny + Cloudflare + NFS</title><link>https://www.arleo.eu/en/posts/postmortem-mcp-timeouts-cloudflare/</link><pubDate>Sat, 09 May 2026 13:04:20 +0200</pubDate><author>Jmr</author><guid>https://www.arleo.eu/en/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="context">Context</h2>
<p>I deployed a Hugo MCP Server (FastAPI, 7 tools) that lets me edit arleo.eu from Claude.ai. Architecture: <code>claude.ai → mcp-oauth-proxy NUC → hugo-mcp-proxy NUC → MCP server VM</code>.</p>]]></description></item></channel></rss>