<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title>Homelab - Balise - arleo.eu</title>
        <link>https://www.arleo.eu/tags/homelab/</link>
        <description>Homelab - Balise - arleo.eu</description>
        <generator>Hugo -- gohugo.io</generator><language>fr</language><lastBuildDate>Mon, 29 Jun 2026 07:00:00 &#43;0200</lastBuildDate><atom:link href="https://www.arleo.eu/tags/homelab/" rel="self" type="application/rss+xml" /><item>
    <title>Hugo SEO : noindex sur les taxonomies pour corriger l&#39;alerte Bing &#34;too many thin pages&#34;</title>
    <link>https://www.arleo.eu/posts/noindex-taxonomies-hugo-bing-seo/</link>
    <pubDate>Mon, 29 Jun 2026 07:00:00 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/noindex-taxonomies-hugo-bing-seo/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/noindex-taxonomies-hugo-bing-seo-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="contexte">Contexte</h2>
<p>Bing Webmaster Tools remontait une recommandation <strong>&ldquo;There are too many pages with insufficient content&rdquo;</strong> (sévérité : modérée) — 15 pages en erreur, toutes du même type :</p>
<ul>
<li><code>https://www.arleo.eu/en/tags/svg/</code></li>
<li><code>https://www.arleo.eu/en/tags/sonarr/</code></li>
<li><code>https://www.arleo.eu/en/categories/incidents/</code></li>
<li>etc.</li>
</ul>
<p>Ces pages sont les <strong>taxonomies Hugo</strong> — les listes par tag et par catégorie. Elles ne contiennent pas d&rsquo;article en elles-mêmes, juste une liste de liens. Pour Bing, c&rsquo;est du <em>thin content</em>.</p>
<hr>
<h2 id="stratégie-choisie">Stratégie choisie</h2>
<p>Trois options s&rsquo;offraient :</p>
<ol>
<li><strong>Bloquer via <code>robots.txt</code></strong> → déconseillé : empêche le crawl, masque les liens internes</li>
<li><strong>Enrichir le contenu des pages de taxonomie</strong> → trop coûteux pour un blog technique</li>
<li><strong><code>noindex,follow</code></strong> → la bonne réponse : on dit aux robots de ne pas indexer la page, mais de suivre les liens vers les articles</li>
</ol>
<p>L&rsquo;option 3 est retenue. Le <code>follow</code> préserve le maillage interne — les robots continuent de découvrir les articles via ces pages.</p>]]></description>
</item>
<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>CrowdSec AppSec &#43; OpenResty : WAF moderne sans ModSecurity</title>
    <link>https://www.arleo.eu/posts/crowdsec-appsec-openresty/</link>
    <pubDate>Mon, 18 May 2026 00:07:11 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/crowdsec-appsec-openresty/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/crowdsec-appsec-openresty-featured.jpg" referrerpolicy="no-referrer">
            </div><p>Après des années avec ModSecurity + OWASP CRS sur nginx, j&rsquo;ai migré arleo.eu vers une stack plus moderne : <strong>CrowdSec AppSec sur OpenResty</strong>. Le résultat est une architecture WAF inline mieux intégrée, plus facile à maintenir, et plus cohérente avec le reste de la stack de sécurité.</p>
<h2 id="pourquoi-abandonner-modsecurity-">Pourquoi abandonner ModSecurity ?</h2>
<p>ModSecurity v2 est en fin de vie active. Le maintien des règles OWASP CRS sur nginx classique génère de la friction : faux positifs fréquents, logs difficiles à corréler avec CrowdSec, et une configuration dispersée entre plusieurs outils sans vision unifiée.</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>Audit sécurité NUC : ModSecurity supprimé, 500 MB récupérés</title>
    <link>https://www.arleo.eu/posts/audit-securite-modsecurity-crowdsec/</link>
    <pubDate>Thu, 14 May 2026 05:31:23 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/audit-securite-modsecurity-crowdsec/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/audit-securite-modsecurity-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="-en-bref">⚡ En bref</h2>
<p>Un audit de la stack sécurité du NUC révèle une <strong>double inspection WAF redondante</strong> : ModSecurity + OWASP CRS chargent 11 872 règles en mémoire malgré <code>SecRuleEngine Off</code>, en parallèle d&rsquo;un CrowdSec AppSec déjà actif et couvrant le même périmètre. Après suppression du module nginx ModSecurity et 5 autres correctifs ciblés, nginx passe de <strong>~520 MB à ~27 MB PSS</strong>. Sécurité identique, empreinte divisée par 20.</p>
<hr>
<h2 id="-architecture-avant-audit">🏗️ Architecture avant audit</h2>
<p>La stack sécurité reposait sur six couches empilées :</p>]]></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>Stratégie 4 : séparer le contenu (MCP) de la structure (Git)</title>
    <link>https://www.arleo.eu/posts/strategie-4-mcp-vs-git/</link>
    <pubDate>Sat, 09 May 2026 12:40:58 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/strategie-4-mcp-vs-git/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/strategie-4-mcp-vs-git-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="le-problème">Le problème</h2>
<p>Tu as un site Hugo. Tu veux pouvoir :</p>
<ol>
<li><strong>Éditer le contenu via Claude.ai</strong> (publier un article, corriger une coquille, mettre à jour un draft) sans toucher à un terminal SSH.</li>
<li><strong>Versionner la structure</strong> (layouts, themes, hugo.toml, scripts de déploiement) dans Git, comme un dev sérieux.</li>
</ol>
<p>Premier réflexe : « tout dans Git ». Les articles aussi. Le MCP commit, push, le webhook GitHub fait un rebuild. Propre, dans la philosophie GitOps.</p>
<p>Sauf que ça ne marche pas si bien. Voici pourquoi, et la solution simple que j&rsquo;appelle la <strong>Stratégie 4</strong>.</p>
<h2 id="pourquoi--tout-dans-git--casse-en-pratique">Pourquoi « tout dans Git » casse en pratique</h2>
<p>Imaginons que ton MCP fait un <code>git commit</code> à chaque <code>create_page</code>. Stratégie naïve, mais souvent proposée. Voici les problèmes :</p>
<h3 id="problème-1--conflit-mcp--git">Problème 1 — Conflit MCP ↔ Git</h3>
<p>Tu push depuis ton laptop un nouveau layout (<code>layouts/index.html</code> modifié). Au même moment, le MCP est en train de commiter une nouvelle version d&rsquo;un article. Race condition, le MCP peut faire un <code>git pull --rebase</code> qui échoue, ou pire, écraser ton commit local.</p>
<h3 id="problème-2--identité-git-du-mcp">Problème 2 — Identité Git du MCP</h3>
<p>Le MCP commit en tant que qui ? Avec quelle clé GPG ? Si tu as une politique « commits signés obligatoires », le MCP doit gérer une clé GPG, qu&rsquo;il faut sécuriser, faire tourner, etc.</p>
<h3 id="problème-3--auto-commit-indésirable">Problème 3 — Auto-commit indésirable</h3>
<p>Tu fais un test, tu crées un article draft pour expérimenter, tu le supprimes. Mais le MCP a déjà commité. Maintenant tu as un commit &ldquo;wip test&rdquo; dans l&rsquo;historique, à rebaser ou squasher manuellement.</p>
<h3 id="problème-4--réversibilité-asymétrique">Problème 4 — Réversibilité asymétrique</h3>
<p>Un <code>git revert</code> côté repo n&rsquo;a aucun effet sur les fichiers que le MCP a déjà créés. Tu te retrouves avec un repo et un état filesystem désynchronisés.</p>
<h2 id="la-stratégie-4--séparer-les-zones">La Stratégie 4 : séparer les zones</h2>
<p>L&rsquo;idée : <strong>MCP et Git n&rsquo;écrivent jamais sur les mêmes fichiers</strong>.</p>
<table>
  <thead>
      <tr>
          <th>Zone</th>
          <th>Qui édite</th>
          <th>Versionné dans Git ?</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>content/**/*.md</code></td>
          <td><strong>MCP exclusivement</strong></td>
          <td>❌ NON (<code>.gitignore</code>)</td>
      </tr>
      <tr>
          <td><code>layouts/</code>, <code>themes/</code>, <code>static/</code>, <code>hugo.toml</code>, <code>deploy.sh</code></td>
          <td><strong>Git push exclusivement</strong></td>
          <td>✅ OUI</td>
      </tr>
  </tbody>
</table>
<p>Le <code>.gitignore</code> côté repo :</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>content/
public/
resources/</code></pre></div>
<p>Implications :</p>
<ul>
<li>Le MCP peut écrire dans <code>content/</code> quand il veut. Aucun conflit Git possible.</li>
<li>Tu peux faire <code>git reset --hard</code> côté repo en confiance — <code>content/</code> reste intact.</li>
<li>Pas besoin que le MCP gère une identité Git.</li>
<li>Pas de &ldquo;commit pollution&rdquo;.</li>
</ul>
<h2 id="trade-off--pas-de-versioning-du-contenu">Trade-off : pas de versioning du contenu</h2>
<p>Tu perds le versioning Git du contenu. C&rsquo;est une perte réelle :</p>
<ul>
<li>Pas de <code>git blame</code> sur un article pour voir qui a écrit quoi.</li>
<li>Pas de <code>git log content/csp-nonce/index.fr.md</code> pour voir l&rsquo;historique.</li>
<li>Pas de PR review pour les articles.</li>
</ul>
<p><strong>Mitigation</strong> : snapshots VM + backup <code>content/</code> chiffré quotidien sur QNAP. Ça couvre la <strong>récupération en cas de désastre</strong>, mais pas le versioning fin (qui-a-changé-quoi-quand).</p>
<p>Pour mon homelab perso (un seul auteur, articles techniques, pas de workflow de validation éditoriale), c&rsquo;est un trade-off acceptable. Pour un blog d&rsquo;équipe avec 10 contributeurs, je reconsidérerais.</p>
<h2 id="architecture-finale">Architecture finale</h2>]]></description>
</item>
<item>
    <title>Hugo MCP Server : connecter Claude.ai à un site Hugo statique</title>
    <link>https://www.arleo.eu/posts/hugo-mcp-server/</link>
    <pubDate>Sun, 03 May 2026 19:00:00 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/hugo-mcp-server/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/hugo-mcp-server-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="-en-bref">⚡ En bref</h2>
<p>Connecter Claude.ai à un site <strong>Hugo</strong> hébergé dans une VM KVM en 30 minutes : un serveur FastAPI expose 6 outils MCP (lire, créer, modifier, supprimer des pages, rebuilder le site) via JSON-RPC 2.0, un proxy OAuth réutilise l&rsquo;infrastructure existante, et chaque modification déclenche automatiquement un rebuild Hugo + une purge du cache Cloudflare.</p>
<p>Le code est disponible sur GitHub :</p>
<ul>
<li>🔌 Serveur MCP Hugo : <a href="https://github.com/jmrGrav/hugo-mcp" target="_blank" rel="noopener noreffer ">jmrGrav/hugo-mcp</a></li>
<li>🔐 Proxy OAuth : <a href="https://github.com/jmrGrav/mcp-oauth-proxy" target="_blank" rel="noopener noreffer ">jmrGrav/mcp-oauth-proxy</a></li>
</ul>
<h2 id="-pourquoi">🧠 Pourquoi</h2>
<p>Le protocole <a href="https://modelcontextprotocol.io/" target="_blank" rel="noopener noreffer ">MCP (Model Context Protocol)</a> d&rsquo;Anthropic permet à Claude.ai de se connecter à des sources de données externes via des outils standardisés. Contrairement à Grav CMS qui est dynamique (PHP), Hugo génère du HTML statique — ce qui rend la gestion de contenu via MCP encore plus puissante : chaque modification est compilée et déployée instantanément.</p>]]></description>
</item>
<item>
    <title>VM Media sur KVM : migrer Sonarr, Radarr et SABnzbd dans une VM isolée</title>
    <link>https://www.arleo.eu/posts/media-vm-migration/</link>
    <pubDate>Sun, 03 May 2026 18:00:00 &#43;0200</pubDate>
    <author>Jmr</author>
    <guid>https://www.arleo.eu/posts/media-vm-migration/</guid>
    <description><![CDATA[<div class="featured-image">
                <img src="/images/media-vm-migration-featured.jpg" referrerpolicy="no-referrer">
            </div><h2 id="-en-bref">⚡ En bref</h2>
<p>Migrer son media stack (Sonarr, Radarr, SABnzbd) dans une VM KVM dédiée sur Ubuntu 24.04 : isolation sécurité, snapshots facilités, NFS QNAP monté dans la VM, reverse proxy nginx sur l&rsquo;hôte. La migration conserve intégralement les bases de données SQLite et la configuration existante.</p>
<p><strong>Stack cible :</strong></p>
<ul>
<li>🖥️ <strong>Hôte</strong> : NUC8i3BEH, Ubuntu Server, nginx reverse proxy, Plex (transcodage GPU)</li>
<li>📦 <strong>VM media-vm</strong> : Ubuntu 24.04, 2 vCPU, 8 Go RAM, 120 Go (X5 NVMe), NFS QNAP</li>
<li>🎬 <strong>Services migrés</strong> : Sonarr (port 8989), Radarr (port 7878), SABnzbd (port 6789)</li>
</ul>
<h2 id="-pourquoi-isoler-le-media-stack-dans-une-vm">🧠 Pourquoi isoler le media stack dans une VM</h2>
<p>Sonarr, Radarr et SABnzbd présentent une surface d&rsquo;attaque non négligeable : accès réseau vers des indexeurs externes, exécution de scripts post-download, accès au système de fichiers de la bibliothèque. Les confiner dans une VM apporte :</p>]]></description>
</item>
</channel>
</rss>
