<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title>Cloudflare - Balise - arleo.eu</title>
        <link>https://www.arleo.eu/tags/cloudflare/</link>
        <description>Cloudflare - Balise - arleo.eu</description>
        <generator>Hugo -- gohugo.io</generator><language>fr</language><lastBuildDate>Fri, 29 May 2026 20:30:00 &#43;0200</lastBuildDate><atom:link href="https://www.arleo.eu/tags/cloudflare/" rel="self" type="application/rss+xml" /><item>
    <title>SEO Hugo : 404 Googlebot, aliases noindex et normalisation des sitemaps</title>
    <link>https://www.arleo.eu/posts/debug-seo-404-broken-links/</link>
    <pubDate>Fri, 29 May 2026 20:30:00 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/debug-seo-404-broken-links/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/debug-seo-404-broken-links-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="contexte">Contexte</h2>
<p>Google Search Console remontait quatre catégories de problèmes sur arleo.eu :</p>
<ul>
<li><strong>404 Googlebot</strong> : <code>/fr/tag/cloudflare</code>, <code>/en/tag/nginx</code>, <code>/fr/tag/javascript</code>… URLs avec préfixe <code>/fr/</code> ou singulier <code>/tag/</code> jamais servi par nginx</li>
<li><strong>16 pages &ldquo;Exclue par la balise noindex&rdquo;</strong> : toutes des pages de redirection générées par <code>aliases:</code> dans le frontmatter Hugo</li>
<li><strong>Balise robots</strong> : <code>noodp</code> hardcodée dans le thème LoveIt</li>
<li><strong>Sitemap FR/EN</strong> : 104 vs 105 URLs — un tag doublon FR et deux tags manquants</li>
</ul>
<hr>
<h2 id="acte-1--aliases-hugo--redirections-nginx-301">Acte 1 : aliases Hugo → redirections nginx 301</h2>
<h3 id="pourquoi-hugo-génère-des-pages-noindex">Pourquoi Hugo génère des pages noindex</h3>
<p>Hugo génère les entrées <code>aliases:</code> du frontmatter comme des fichiers HTML statiques :</p>]]></description>
</item>
<item>
    <title>CSP A&#43; sur Hugo &#43; Cloudflare : de hash-based à origin allowlist, monitoring auto et durcissement</title>
    <link>https://www.arleo.eu/posts/csp-a-plus-hugo-cloudflare-origin-allowlist/</link>
    <pubDate>Thu, 28 May 2026 23:40:12 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/csp-a-plus-hugo-cloudflare-origin-allowlist/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/csp-a-plus-hugo-cloudflare-origin-allowlist-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="contexte">Contexte</h2>
<p>arleo.eu tourne sur Hugo (VM KVM) → OpenResty (NUC) → Cloudflare (CDN/WAF). Objectif : score <strong>A+ sur Mozilla Observatory</strong> avec une CSP stricte qui résiste aux injections côté edge.</p>
<p>Le parcours a traversé trois stratégies en quelques semaines — nonces, hashes, puis origin allowlist — avant d&rsquo;aboutir à une solution stable et automatisée.</p>
<hr>
<h2 id="acte-1--pourquoi-hash-based-a-échoué">Acte 1 : pourquoi hash-based a échoué</h2>
<p>L&rsquo;idée de départ semblait solide : Hugo Pipes externalise tout le JS avec SRI, on liste les hashes dans la CSP, résultat propre. En pratique, deux problèmes ont rendu l&rsquo;approche impossible.</p>]]></description>
</item>
<item>
    <title>SRI sur Hugo : hashes automatiques, auto-update et alerting BetterStack</title>
    <link>https://www.arleo.eu/posts/sri-cdn-hugo-automate/</link>
    <pubDate>Sun, 17 May 2026 00:00:00 &#43;0000</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/sri-cdn-hugo-automate/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/sri-cdn-hugo-automate-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="pourquoi-le-sri-">Pourquoi le SRI ?</h2>
<p>Quand votre site charge des ressources depuis un CDN tiers — FontAwesome, Mermaid, Animate.css — vous faites confiance à un tiers sur lequel vous n&rsquo;avez aucun contrôle. Si jsdelivr.net est compromis ou si une version « immuable » est mutée silencieusement, votre site peut devenir vecteur d&rsquo;attaque.</p>
<p>Le <strong>Subresource Integrity</strong> (SRI) résout ça simplement : chaque balise <code>&lt;link&gt;</code> ou <code>&lt;script&gt;</code> porte un attribut <code>integrity=&quot;sha256-…&quot;</code> que le navigateur vérifie avant d&rsquo;exécuter la ressource. Si le hash ne correspond pas, le navigateur bloque le chargement.</p>]]></description>
</item>
<item>
    <title>CSP Hash sur Hugo : migrer du nonce vers le hash pour préserver le cache CDN</title>
    <link>https://www.arleo.eu/posts/csp-hash-hugo/</link>
    <pubDate>Sat, 16 May 2026 00:00:00 &#43;0000</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/csp-hash-hugo/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/csp-hash-hugo-featured.png" referrerpolicy="no-referrer">
            </div>Le nonce CSP et le cache CDN sont incompatibles par design. Sur un site Hugo statique, le hash SHA-256 est l&rsquo;approche native : il est calculé au build, reste stable entre les requêtes, et laisse Cloudflare cacher le HTML.]]></description>
</item>
<item>
    <title>Plugin Cloudflare hugo-mcp : purge ciblée plutôt que totale</title>
    <link>https://www.arleo.eu/posts/hugo-mcp-plugin-cloudflare/</link>
    <pubDate>Thu, 14 May 2026 09:42:22 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/hugo-mcp-plugin-cloudflare/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/hugo-mcp-plugin-cloudflare-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="tldr">TL;DR</h2>
<p>Le plugin Cloudflare de <a href="https://github.com/jmrGrav/hugo-mcp/releases/tag/v2.0.0" target="_blank" rel="noopener noreffer ">hugo-mcp v2.0</a> implémente 3 modes de purge cache (<code>full</code>, <code>partial</code>, <code>smart</code>). Le mode <code>partial</code> calcule les URLs liées à invalider (canonique + sitemap + RSS + listing + home) pour préserver 95% du cache CDN à chaque modification. Concrètement : 6 URLs purgées au lieu de tout vider. Ce post détaille le calcul, les pièges, et pourquoi <code>smart</code> est devenu le défaut.</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 : 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>
<item>
    <title>De 46 hashes à zéro : migration CSP vers les nonces dynamiques</title>
    <link>https://www.arleo.eu/posts/csp-nonce/</link>
    <pubDate>Wed, 15 Apr 2026 13:22:00 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/csp-nonce/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/csp-nonce-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="-en-bref">⚡ En bref</h2>
<p>La CSP initiale listait 46 hashes SHA-256 pour couvrir les scripts et styles inline — ingérable, fragile, et à la limite des 4 096 caractères de Cloudflare. Cette migration vers des <strong>nonces dynamiques</strong> réduit la CSP à ~600 caractères, supprime toute maintenance manuelle des hashes, et ajoute un reporting structuré des violations dans BetterStack.</p>
<p>Le plugin Grav utilisé est disponible sur GitHub :
🔌 Plugin : <a href="https://github.com/jmrGrav/csp-nonce" target="_blank" rel="noopener noreffer ">jmrGrav/grav-plugin-csp-nonce</a></p>]]></description>
</item>
<item>
    <title>Scripts Infrastructure</title>
    <link>https://www.arleo.eu/scripts/</link>
    <pubDate>Sun, 12 Apr 2026 21:24:00 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/scripts/</guid>
    <description><![CDATA[<h2 id="1-crowdsec-cf-syncpy">1. crowdsec-cf-sync.py</h2>
<p><strong>Emplacement :</strong> <code>/usr/local/bin/crowdsec-cf-sync.py</code>
<strong>Service systemd :</strong> <code>crowdsec-cf-sync.service</code>
<strong>Logs :</strong> <code>/var/log/crowdsec/cf-sync.log</code></p>
<h3 id="installation">Installation</h3>
<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">cp crowdsec-cf-sync.py /usr/local/bin/
</span></span><span class="line"><span class="cl">chmod +x /usr/local/bin/crowdsec-cf-sync.py
</span></span><span class="line"><span class="cl">systemctl <span class="nb">enable</span> crowdsec-cf-sync
</span></span><span class="line"><span class="cl">systemctl start crowdsec-cf-sync</span></span></code></pre></div></div>
<h3 id="fonctionnalités">Fonctionnalités</h3>
<ul>
<li>Synchronise les bans CrowdSec actifs → Cloudflare IP Access Rules (tag <code>crowdsec-local-ban</code>)</li>
<li>Reporte les IPs bannies → AbuseIPDB (fenêtre 48h, dédupliqué)</li>
<li><strong>Escalade récidivistes</strong> : 1er ban CrowdSec gère | 2ème → 24h | 3ème+ → 7j (fenêtre 7j)</li>
<li><strong>ModSecurity</strong> score ≥ 5 → ban CF immédiat 2h (tag <code>modsec-ban</code>) + report AbuseIPDB</li>
<li><strong>Ban /24</strong> automatique : 2+ IPs du même bloc en 7j → ban CF + CrowdSec 24h (tag <code>crowdsec-cidr-ban</code>)</li>
</ul>
<h3 id="bugs-corrigés-avril-2026">Bugs corrigés (avril 2026)</h3>
<ul>
<li><code>cs_origin</code> vs <code>origin</code> dans <code>get_recent_local_bans()</code> — le champ JSON s&rsquo;appelle <code>cs_origin</code></li>
<li>Pagination API REST <code>/v1/decisions?limit=1000</code> rate les bans <code>cscli</code> → remplacé par <code>cscli decisions list --origin</code></li>
<li><code>Items</code> → <code>items</code> (minuscule) dans le parsing JSON de la allowlist CrowdSec</li>
</ul>
<blockquote>
<p>⚠️ <strong>Important</strong> : les vrais tokens (<code>CF_API_TOKEN</code>, <code>CF_ZONE_ID</code>, <code>CS_API_KEY</code>, <code>ABUSEIPDB_KEY</code>) doivent être stockés dans <code>/etc/secrets/</code> avec <code>chmod 600</code> et chargés via variables d&rsquo;environnement, pas hardcodés dans le script. Les valeurs <code>VOTRE_*</code> ci-dessous sont des placeholders.</p>]]></description>
</item>
<item>
    <title>Automatiser le ban d&#39;IPs avec Cloudflare WAF, CrowdSec et AbuseIPDB</title>
    <link>https://www.arleo.eu/posts/crowdsec-cloudflare-waf-autoban/</link>
    <pubDate>Fri, 10 Apr 2026 00:23:00 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/crowdsec-cloudflare-waf-autoban/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/crowdsec-cloudflare-waf-autoban-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="-en-bref">⚡ En bref</h2>
<p>Le monitoring passif ne suffit pas. Ce pipeline automatise la <strong>fermeture de la boucle en moins de 5 minutes</strong> entre une attaque détectée par Cloudflare WAF et le ban effectif de l&rsquo;IP dans CrowdSec, sa synchronisation vers Cloudflare et son signalement sur AbuseIPDB. Un script Python poll l&rsquo;API GraphQL Cloudflare toutes les 5 minutes, applique un seuil de 3 hits, et déclenche le ban avec escalade récidiviste.</p>]]></description>
</item>
</channel>
</rss>
