Salta al contenuto

Come ho creato ed automatizzato questo sito web

How I Built and Automated This Website

2026-02-25
Come ho creato ed automatizzato questo sito web

Se stai leggendo questo post, significa che il sistema di generazione statica del blog del mio nuovo sito sta funzionando! Ho deciso di inaugurare questo spazio condividendo i dettagli tecnici di come ho costruito, messo in sicurezza e automatizzato l'infrastruttura di questo sito personale web.

Essendo un Cloud Architect, ho voluto trattare il mio Raspberry Pi come un vero e proprio server di produzione, applicando concetti di hardening, proxying inverso e automazione. Ecco come ho fatto.

L'Infrastruttura di Base

Tutto gira su un Raspberry Pi con architettura ARM64. Invece di affidarmi a soluzioni preconfezionate o hosting gestiti, ho preferito avere il controllo totale del sistema operativo e della rete.

Il cuore dell'esposizione web è affidato a Nginx, configurato come reverse proxy. Nginx non si limita a servire questi file HTML statici che stai leggendo, ma fa da smistatore (e buttafuori) per vari servizi interni.

Sicurezza e Hardening: Non si entra facilmente

Esporre un Raspberry Pi su internet significa ricevere decine di scansioni automatiche da bot e script malevoli ogni minuto. Per mitigare questo traffico "spazzatura", ho implementato diverse barriere:

  1. Catch-All Block: Nginx è configurato con un blocco server di default che chiude istantaneamente la connessione (restituendo un codice 444 No Response) a chiunque provi ad accedere al server tramite l'indirizzo IP diretto o domini non autorizzati. Si passa solo se si conosce il dominio esatto.
  2. GeoIP Filtering: Tramite il modulo libnginx-mod-http-geoip2, Nginx rifiuta a livello perimetrale le connessioni provenienti da paesi ritenuti non necessari o ad alto rischio per il mio use-case.
  3. Fail2Ban con Notifiche Telegram: Ho configurato decine di jail e filtri regex personalizzati (es. nginx-404.conf, nginx-bot.conf). Se un IP genera troppi errori 404 o cerca vulnerabilità note, Fail2Ban lo blocca a livello di firewall e un bot Telegram mi avvisa immediatamente in chat della ban (e del successivo unban).

Servizi Esposti e Proxying

Il sito non è solo una vetrina, ma un hub personale. Tramite Nginx, ho esposto in sicurezza:

  • File Browser: Un'interfaccia web per accedere al mio file system remoto /mnt/ext/share. Il processo gira con un utente di sistema isolato e utilizza le Access Control Lists (ACL) di Linux per limitare i danni in caso di compromissione.
  • Plex Media Server: Con un redirect diretto verso la porta protetta.
  • L'Assistente AI Chat: Una delle "chicche" del sito. È un backend Node.js che dialoga direttamente con le API di Google Gemini. Nginx passa le connessioni tramite proxy alla porta locale del processo Node, offrendo una connessione crittografata e rate-limitata verso l'esterno.

HTTPS e Automazione con Certbot Script

Tutto il traffico è rigorosamente in HTTPS. Ho utilizzato Certbot (Let's Encrypt) per automatizzare la generazione e il rinnovo dei certificati TLS/SSL per Nginx. Ma non mi sono fermato qui: ho creato un Deploy Hook (plex-cert-renew.sh) che, ogni volta che Certbot rinnova il certificato web, intercetta i nuovi file PEM, li converte in formato PKCS12 (.p12) e riavvia automaticamente Plex, in modo che anche il traffico multimediale goda sempre di certificati aggiornati.

Generazione del Blog e Deploy Script

Ed eccoci infine al blog. Volevo un sistema leggero, senza database (niente WordPress o fantasmi del passato!). Ho scritto un piccolo script in Python (build_blog.py) che legge i file Markdown (.md) da una cartella locale, ne estrae il Frontmatter (titolo, data, descrizione) e genera file HTML statici che vengono poi iniettati nella pagina indice.

Per mettere tutto online in un colpo solo, ho condensato le operazioni in un unico deploy.sh. Questo script bash:

  1. Fa il pull da GitHub.
  2. Sincronizza i file statici sulla Web Root con rsync.
  3. Valida e aggiorna le configurazioni di Nginx e Fail2Ban.
  4. Inietta i segreti (API Key di Gemini, e Token di Telegram) estraendoli sicuramente da file locali criptati del SO, senza passare da git.
  5. Riavvia i processi Node.js tramite pm2 e ricarica i demoni.

Tutto automatizzato. Scrivo un file Markdown su VS Code, committo e spingo su GitHub, lancio ./deploy.sh sul Raspberry, e il sito è aggiornato.

Spero che questo sguardo sotto il cofano ti sia piaciuto!

If you're reading this post, it means my new site's static generation system is working! I decided to kick off this space by sharing the technical details of how I built, secured, and automated the infrastructure for this personal web site.

Being a Cloud Architect, I wanted to treat my Raspberry Pi as a real production server, applying concepts of hardening, reverse proxying, and automation. Here's how I did it.

The Basic Infrastructure

Everything runs on a Raspberry Pi with an ARM64 architecture. Instead of relying on pre-packaged solutions or managed hosting, I preferred to have complete control over the operating system and network.

The core of web exposure is entrusted to Nginx, configured as a reverse proxy. Nginx doesn't just serve these static HTML files you're reading, but acts as a dispatcher (and bouncer) for various internal services.

Security and Hardening: Not Easily Entered

Exposing a Raspberry Pi to the internet means receiving dozens of automated scans from bots and malicious scripts every minute. To mitigate this "garbage" traffic, I implemented several barriers:

  1. Catch-All Block: Nginx is configured with a default server block that instantly closes the connection (returning a 444 No Response code) to anyone trying to access the server via direct IP address or unauthorized domains. Access is only granted if you know the exact domain.
  2. GeoIP Filtering: Using the libnginx-mod-http-geoip2 module, Nginx rejects connections from countries deemed unnecessary or high-risk for my use case at the perimeter level.
  3. Fail2Ban with Telegram Notifications: I configured dozens of jails and custom regex filters (e.g., nginx-404.conf, nginx-bot.conf). If an IP generates too many 404 errors or attempts known vulnerabilities, Fail2Ban blocks it at the firewall level and a Telegram bot immediately notifies me in chat of the ban (and subsequent unban).

Exposed Services and Proxying

The site isn't just a showcase, but a personal hub. Through Nginx, I securely exposed:

  • File Browser: A web interface to access my remote file system /mnt/ext/share. The process runs with an isolated system user and uses Linux Access Control Lists (ACLs) to limit damage in case of compromise.
  • Plex Media Server: With a direct redirect to the protected port.
  • The AI Chat Assistant: One of the site's "gems". It's a Node.js backend that communicates directly with the Google Gemini APIs. Nginx proxies connections to the local port of the Node process, offering an encrypted and rate-limited connection to the outside world.

HTTPS and Automation with Certbot Script

All traffic is strictly over HTTPS. I used Certbot (Let's Encrypt) to automate the generation and renewal of TLS/SSL certificates for Nginx. But I didn't stop there: I created a Deploy Hook (plex-cert-renew.sh) that, every time Certbot renews the web certificate, intercepts the new PEM files, converts them to PKCS12 format (.p12), and automatically restarts Plex, so that multimedia traffic always enjoys updated certificates.

Blog Generation and Deploy Script

And finally, we come to the blog. I wanted a lightweight system, without a database (no WordPress or ghosts of the past!). I wrote a small script in Python (build_blog.py) that reads the Markdown files (.md) from a local folder, extracts the Frontmatter (title, date, description), and generates static HTML files that are then injected into the index page.

To put everything online in one go, I condensed the operations into a single deploy.sh. This bash script:

  1. Pulls from GitHub.
  2. Synchronizes static files to the Web Root with rsync.
  3. Validates and updates Nginx and Fail2Ban configurations.
  4. Injects secrets (Gemini API Key, and Telegram Token) extracting them securely from encrypted OS files, without going through git.
  5. Restarts Node.js processes via pm2 and reloads daemons.

All automated. I write a Markdown file in VS Code, commit and push to GitHub, launch ./deploy.sh on the Raspberry Pi, and the site is updated.

I hope you enjoyed this look under the hood!