// OPERACIÓN: El Bunker de Seguridad

// Despliegue seguro: Blogger + Servidor de documentación en Hetzner + Cloudflare + Tailscale


*DISCLAMER* -> Por seguridad y cuidado en revelación de secretos se hace omisión de capturas de logs o detalles técnicos que puedan comprometer la seguridad de la información.


0x00 - Generación de Identidad Criptográfica (Local)

No usamos contraseñas. 

Usamos llaves asimétricas.

Generamos un par de claves Ed25519, el algoritmo de curva elíptica más eficiente y seguro disponible actualmente para autenticación SSH:

$ ssh-keygen -t ed25519 -C "operador@ejemplo.com"
  • -t ed25519 — Algoritmo de firma digital basado en Curve25519. Claves mas cortas, verificación más rápida y resistencia superior frente a ataques de canal lateral comparado con RSA.
  • -C — Comentario identificador embebido en la clave pública. Útil para auditar ~/.ssh/authorized_keys en servidores con multiples operadores.

Resultado: Se generan dos archivos: la clave privada (permanece exclusivamente en la máquina local, nunca se transfiere) y la clave pública (.pub) que se deposita en el servidor remoto.


0x01 - Configuración del Servidor (Hetzner)

Instancia aprovisionada con Ubuntu 24.04 LTS

Primer paso tras el despliegue: actualizar el sistema e instalar el stack web.

A. Actualización del sistema y Web Stack

$ sudo apt update && sudo apt upgrade -y
$ sudo apt install nginx php8.3-fpm php-common php-mysql -y

Esto despliega Nginx como reverse proxy / servidor web y PHP 8.3-FPM (FastCGI Process Manager) para el procesamiento de contenido dinamico.

B. Hardening SSH - Cambio de Puerto y Desactivación de Passwords

Editamos la configuración del daemon SSH:

$ sudo nano /etc/ssh/sshd_config

Modificaciones aplicadas:

  • Port 4222 — Reubicamos el servicio SSH fuera del puerto estandar 22. (Esto no es seguridad real, pero elimina el ruido de bots automatizados que escanean puertos por defecto).
  • PasswordAuthentication no — Se deshabilita la autenticación por contraseña. Solo se aceptan claves criptográficas.
CRÍTICO: Antes de reiniciar el servicio SSH con el nuevo puerto, asegurate de tener la clave pública correctamente depositada en el servidor. Si pierdes el acceso, la única vía de recuperacion es la consola web del proveedor, que por cierto se ve muy mal.
$ sudo systemctl enable ssh   # Persistencia tras reboot
$ sudo systemctl restart ssh  # Aplicar cambios

0x02 - Cortafuegos Estratégico (UFW)

Definimos las reglas de acceso a nivel de red. 

Política base: denegar todo el tráfico entrante y permitir solo lo estrictamente necesario.

$ sudo ufw default deny incoming   # DROP en todo el tráfico entrante
$ sudo ufw default allow outgoing  # Permite tráfico de salida

# Puertos públicos (Cloudflare -> Servidor)
$ sudo ufw allow 80/tcp            # HTTP
$ sudo ufw allow 443/tcp           # HTTPS

# SSH restringido exclusivamente a la interfaz Tailscale
$ sudo ufw allow in on tailscale0 to any port 4222 proto tcp

$ sudo ufw enable

La regla clave es la última antes del enable: el acceso SSH por el puerto 4222 solo se permite a través de la interfaz tailscale0

Esto significa que el puerto SSH es completamente invisible desde internet público

Solo los dispositivos autorizados dentro de la red Tailscale pueden alcanzarlo.

0x03 - DNS (Cloudflare)

Configuración DNS para el dominio ejemplo.com

Se divide el tráfico entre el blog (Blogger) y el servidor de documentación (Hetzner).

RegistroNombreValorProxyFuncion
A@IPs de Google (Blogger)ProxiedRaiz del blog
CNAMEwwwghs.google.comDNS OnlyAlias www del blog
Adoc192.168.1.100ProxiedServidor Hetzner (documentacion)
NOTA: El registro CNAME para "www" debe estar en modo DNS Only (nube gris) cuando apunta a ghs.google.com, ya que Blogger requiere resolver el CNAME directamente sin proxy intermedio. 
Los registros A con proxy activo (nube naranja) pasan el tráfico a través de la red Cloudflare, ocultando la IP real del servidor de origen.

SSL/TLS en Cloudflare: Configurado en modo Full (Strict)

Esto exige un certificado válido en el servidor de origen, garantizando cifrado extremo a extremo entre el cliente, Cloudflare y el servidor Hetzner.

0x04 - Blindaje del Servidor Web (Nginx + SSL de Origen)

Instalamos certificados de origen emitidos por Cloudflare para cifrar el tramo Cloudflare <-> Servidor Hetzner

Sin esto, el tráfico entre ambos puntos viajaría en texto plano.

A. Ubicación de Certificados

  • Certificado: /etc/ssl/certs/cloudflare.crt
  • Clave privada: /etc/ssl/private/cloudflare.key
# Permisos restrictivos sobre la clave privada
$ sudo chmod 600 /etc/ssl/private/cloudflare.key
$ sudo chown root:root /etc/ssl/private/cloudflare.key

B. Virtual Host - /etc/nginx/sites-available/portfolio

server {
listen 443 ssl;
server_name doc.ejemplo.com;

ssl_certificate     /etc/ssl/certs/cloudflare.crt;
ssl_certificate_key /etc/ssl/private/cloudflare.key;

# Protocolos y cifrados recomendados
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;

root /var/www/html;
index index.php;

location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
}

# Denegar acceso a archivos ocultos
location ~ /\. {
deny all;
}
}
NOTA: Se recomienda agregar directivas ssl_protocols y ssl_prefer_server_ciphers para forzar TLS 1.2 o más y deshabilitar cifrados débiles. 
También se incluye un bloque "location" para denegar acceso a archivos ocultos (dotfiles) que podrían exponer información sensible.

Activar el sitio y verificar la configuración:

$ sudo ln -s /etc/nginx/sites-available/portfolio /etc/nginx/sites-enabled/
$ sudo nginx -t            # Validar sintaxis
$ sudo systemctl reload nginx

0x05 - Capa Tailscale

Tailscale crea una red mesh privada basada en WireGuard

Cada dispositivo autorizado recibe una IP interna del rango 100.x.x.x (CGNAT). 

El servidor se vuelve invisible a internet público para administración.

# Instalación 
$ curl -fsSL https://tailscale.com/install.sh | sh

# Activación y vinculación con la cuenta
$ sudo tailscale up

Tras la activación, el servidor obtiene una IP privada dentro de la red Tailscale (ejemplo: 100.x.x.x). 

Esta IP es la única vía de acceso SSH al servidor, ya que el firewall UFW bloquea el puerto 4222 en todas las interfaces excepto tailscale0.

Flujo de conexion SSH:

$ ssh -p 4222 operador@100.x.x.x

Donde 100.x.x.x es la IP asignada por Tailscale al servidor. 

Esta IP solo es alcanzable desde dispositivos autenticados en la misma red Tailscale.

0x06 - Bloqueo de Archivos

Si un atacante obtuviera acceso al servidor, no podria modificar los archivos críticos del sitio.

# Activar flag de inmutabilidad
$ sudo chattr +i /var/www/html/index.php

# Verificar el flag
$ lsattr /var/www/html/index.php
# Salida esperada: ----i--------e-- /var/www/html/index.php

# Desactivar (solo cuando se necesite editar)
$ sudo chattr -i /var/www/html/index.php

El atributo +i (immutable) del sistema de archivos ext4 impide cualquier operación de escritura, renombrado o eliminación sobre el archivo, incluso por el usuario root

El flag debe ser removido explícitamente con chattr -i antes de poder realizar modificaciones.

NOTA: Considerar aplicar inmutabilidad a otros archivos estáticos criticos del directorio web. 
También es recomendable auditar periódicamente los atributos con "lsattr" para detectar alteraciones no autorizadas.

0x07 - Comandos de Rescate Rápido 

Referencia de diagnostico y recuperación ante fallos comunes:

ProblemaComando / AccionDescripción 
Web no cargasudo nginx -tValida la sintaxis de la configuracion de Nginx. Si hay errores, los muestra con el numero de linea exacto.
SSH no conectaConsola web de HetznerAcceder vía consola del proveedor y ejecutar sudo systemctl restart ssh. Verificar que el puerto 4222 sigue configurado en sshd_config.
IP pública cambiossh -p 4222 operador@100.x.x.xLa IP de Tailscale es persistente e independiente de la IP pública. Conectar siempre a través de la red Tailscale.
Certificado SSL expiradoPanel Cloudflare > SSL > Origin CertificatesRegenerar el certificado de origen y reemplazar los archivos en el servidor. Recargar Nginx.
PHP no procesasudo systemctl status php8.3-fpmVerificar que el servicio FPM esta activo. Reiniciar con sudo systemctl restart php8.3-fpm si es necesario.

[EOF] -  “The Security Bunker”