/images/avatar.png

Break things. Fix them. Learn.

This site runs on an Intel NUC hosted at home, behind a standard fiber connection. Its main purpose is to serve as an experimentation ground for testing server configurations, automation scripts, and open source security tools.

Not a professional website — a homelab: we break things, fix them, and learn.

Migration in progress
The site is gradually migrating from Grav CMS to Hugo. Old URLs are preserved, but the visual rendering is evolving. If you spot a bug, report it.

🛠️ Tech stack

Tool Role Link
🔒 CrowdSec Community IDS/IPS Dashboard
☁️ Cloudflare CDN · WAF · DNS · DDoS Dashboard
📊 BetterStack Monitoring · Alerts · Logs Status page
🌐 Hugo Static site generator gohugo.io
🛡️ ModSecurity Local WAF · OWASP CRS 4.x OWASP CRS
nginx Reverse proxy · TLS 1.3 nginx.org

🌟 Don’t miss

Three articles that capture the spirit of this homelab:

📚 Full documentation is in Documentation and automation scripts in Scripts.

🐛 Found a vulnerability?

If you discover a bug, misconfiguration, or security vulnerability on this server, please report it. This homelab is public and I learn from my mistakes.

📨 Responsible disclosure: www.arleo.eu/security.txt

Any contribution to improving security is welcome.

Hugo SEO: noindex on taxonomies (Bing thin-content fix)

Context

Bing Webmaster Tools was raising a “There are too many pages with insufficient content” recommendation (severity: moderate) — 15 pages flagged, all of the same type:

  • https://www.arleo.eu/en/tags/svg/
  • https://www.arleo.eu/en/tags/sonarr/
  • https://www.arleo.eu/en/categories/incidents/
  • etc.

These are Hugo taxonomy pages — tag and category listing pages. They contain no article content of their own, just a list of links. From Bing’s perspective, that’s thin content.

This follows the same investigation habit as an earlier Googlebot 404s and sitemap normalization fix on this site: check the search console first, understand why Hugo generated the offending pages, then fix at the template level instead of patching each URL by hand.

Hugo SEO: Googlebot 404s, noindex aliases and sitemap normalization

Context

Google Search Console was reporting four categories of issues on arleo.eu:

  • Googlebot 404s: /fr/tag/cloudflare, /en/tag/nginx, /fr/tag/javascript… URLs with /fr/ prefix or singular /tag/ never served by nginx
  • 16 “Excluded by noindex tag” pages: all redirect pages generated by aliases: in Hugo frontmatter
  • Robots tag: noodp hardcoded in the LoveIt theme
  • FR/EN sitemap: 104 vs 105 URLs — a duplicate FR tag and two missing tags

Act 1: Hugo aliases → nginx 301 redirects

Why Hugo generates noindex pages

Hugo generates aliases: frontmatter entries as static HTML files:

Hugo: freezing Mermaid diagrams to static dark/light SVGs

Problem

Mermaid diagrams on arleo.eu were rendering client-side via cdn.jsdelivr.net. Three concrete consequences:

  1. CSP constraintscript-src cdn.jsdelivr.net and worker-src cdn.jsdelivr.net become mandatory (Mermaid v11 uses Web Workers for its parsers).
  2. Render flash — the diagram appears after JS execution, creating a visible delay.
  3. Dark theme ignored — Mermaid initialized the SVG in light mode even when the site theme was dark.

The solution: generate SVGs at build time with mmdc, outside any browser context.

CSP A+ on Hugo + Cloudflare: from hash-based to origin allowlist, auto-monitoring and hardening

Context

arleo.eu runs on Hugo (KVM VM) → OpenResty (NUC) → Cloudflare (CDN/WAF). Goal: A+ score on Mozilla Observatory with a strict CSP that resists edge-side injections.

The journey went through three strategies over a few weeks — nonces, hashes, then origin allowlist — before landing on a stable, automated solution.


Act 1: why hash-based failed

The initial idea seemed solid: Hugo Pipes externalizes all JS with SRI, we list the hashes in the CSP, clean result. In practice, two problems made the approach impossible.

Postmortem — CrowdSec AppSec: Heuristic False Positive on Sonarr/Radarr

Summary

On May 25, 2026 at around 10:02 PM (local time), Sonarr and Radarr became completely inaccessible from the home IP (82.XX.XX.XX), returning 403 on every URL including /login. The service was fully operational. Initial suspicion fell on the day’s crowdsec-cf-sync refactor deployment — the real cause was a CrowdSec AppSec heuristic false positive.


Timeline

Time (local) Event
~10:00 PM Sonarr browser session cookie expired
10:02:31 PM Browser loads Sonarr library → attempts to fetch 20+ /MediaCover/*.jpg simultaneously
10:02:31 PM Sonarr returns 302 → /login for each image (invalid session)
10:02:34 PM SignalR WebSocket connection succeeds (101) via access_token in URL
~10:05 PM CrowdSec AppSec triggers heuristic rule http-probing: burst of failed requests from same IP
10:11:37 PM All requests from 82.XX.XX.XX return 403 — cs_reason=heuristic in nginx logs
10:13:34 PM Even /login is blocked — IP cannot authenticate

Root Cause

CrowdSec AppSec maintains an in-memory heuristic state, 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 (http-probing) and blocks the source IP.