<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[TonyHelper Tech Blog]]></title><description><![CDATA[TonyHelper Tech Blog]]></description><link>https://blog.tonyhelper.com</link><generator>RSS for Node</generator><lastBuildDate>Fri, 01 May 2026 09:53:33 GMT</lastBuildDate><atom:link href="https://blog.tonyhelper.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Cómo exponer Ollama en un servidor Linux local con Cloudflare Tunnel y un subdominio propio]]></title><description><![CDATA[En esta guía voy a explicar cómo dejar accesible una instancia local de Ollama desde Internet usando Cloudflare Tunnel y un subdominio propio, en este caso ollama.tonyhelper.com.
La idea es sencilla: ]]></description><link>https://blog.tonyhelper.com/c-mo-exponer-ollama-en-un-servidor-linux-local-con-cloudflare-tunnel-y-un-subdominio-propio</link><guid isPermaLink="true">https://blog.tonyhelper.com/c-mo-exponer-ollama-en-un-servidor-linux-local-con-cloudflare-tunnel-y-un-subdominio-propio</guid><dc:creator><![CDATA[Moisés De Abreu]]></dc:creator><pubDate>Wed, 15 Apr 2026 13:58:57 GMT</pubDate><content:encoded><![CDATA[<p>En esta guía voy a explicar cómo dejar accesible una instancia local de Ollama desde Internet usando Cloudflare Tunnel y un subdominio propio, en este caso <code>ollama.tonyhelper.com</code>.</p>
<p>La idea es sencilla: Ollama sigue ejecutándose en local dentro del servidor Linux, pero Cloudflare crea un túnel saliente seguro que permite acceder desde fuera sin abrir puertos en el router y sin depender de una IP pública fija.</p>
<h2>Objetivo</h2>
<p>Queremos conseguir que un servidor Linux local con Ollama responda a peticiones externas como esta:</p>
<pre><code class="language-bash">curl https://ollama.tonyhelper.com/api/tags
</code></pre>
<p>Y que internamente ese tráfico termine llegando a:</p>
<pre><code class="language-bash">http://localhost:11434
</code></pre>
<p>Esto es especialmente útil cuando el servidor está detrás de un router doméstico o de operador, donde la IP pública puede cambiar y donde no siempre interesa abrir puertos hacia el exterior.</p>
<h2>Qué necesitamos</h2>
<p>Antes de empezar, conviene tener esto preparado:</p>
<ul>
<li><p>Un dominio gestionado en Cloudflare, en este caso <code>tonyhelper.com</code>.</p>
</li>
<li><p>Un subdominio que vamos a dedicar a Ollama: <code>ollama.tonyhelper.com</code>.</p>
</li>
<li><p>Un servidor Linux local con Ollama ya instalado y funcionando.</p>
</li>
<li><p><code>cloudflared</code> instalado en el servidor.</p>
</li>
<li><p>Acceso a la cuenta de Cloudflare para autorizar la creación del túnel.</p>
</li>
</ul>
<h2>Paso 1. Comprobar que Ollama funciona en local</h2>
<p>Antes de tocar Cloudflare, lo primero es verificar que Ollama responde correctamente dentro del servidor.</p>
<pre><code class="language-bash">curl http://127.0.0.1:11434/api/tags
</code></pre>
<p>Si queremos comprobar que el modelo responde, podemos hacer una prueba sencilla:</p>
<pre><code class="language-bash">curl http://127.0.0.1:11434/api/generate \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemma4",
    "prompt": "Di hola en una frase",
    "stream": false
  }'
</code></pre>
<p>Si estas pruebas funcionan, significa que la base está bien y podemos continuar.</p>
<h2>Paso 2. Instalar <code>cloudflared</code></h2>
<p>En Ubuntu o Debian podemos instalar <code>cloudflared</code> desde el repositorio oficial de Cloudflare.</p>
<pre><code class="language-bash">sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg &gt;/dev/null
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update
sudo apt install cloudflared
cloudflared version
</code></pre>
<p>Con esto dejamos instalado el cliente que se encargará de mantener el túnel abierto desde el servidor hacia Cloudflare.</p>
<h2>Paso 3. Autorizar <code>cloudflared</code> con nuestra cuenta</h2>
<p>El siguiente paso es autenticar el servidor contra la cuenta de Cloudflare:</p>
<pre><code class="language-bash">cloudflared tunnel login
</code></pre>
<p>Este comando abre el navegador para iniciar sesión y seleccionar la zona del dominio. Al completarlo, Cloudflare guarda el certificado de cuenta en la carpeta de configuración local.</p>
<p>Podemos comprobarlo así:</p>
<pre><code class="language-bash">ls -l ~/.cloudflared/cert.pem
</code></pre>
<p>Ese archivo es importante porque permite crear y gestionar túneles desde la línea de comandos.</p>
<h2>Paso 4. Crear un túnel nombrado</h2>
<p>En este caso vamos a usar un nombre claro para el túnel:</p>
<pre><code class="language-bash">cloudflared tunnel create ollama-aiserver
</code></pre>
<p>Al ejecutarlo, Cloudflare genera un UUID para el túnel y un archivo JSON de credenciales dentro de <code>~/.cloudflared/</code>.</p>
<p>Podemos verlo con:</p>
<pre><code class="language-bash">cloudflared tunnel list
ls -l ~/.cloudflared/
</code></pre>
<p>Aquí aparecerá el nombre del túnel, su UUID y el archivo de credenciales correspondiente.</p>
<h2>Paso 5. Crear el DNS del subdominio</h2>
<p>Una vez creado el túnel, toca vincular el subdominio <code>ollama.tu-dominio.com</code> a ese túnel:</p>
<pre><code class="language-bash">cloudflared tunnel route dns ollama-aiserver ollama.tu-dominio.com
</code></pre>
<p>Este comando crea el registro DNS necesario en Cloudflare. A partir de ese momento, <code>ollama.tu-dominio.com</code> quedará apuntando al túnel.</p>
<h2>Paso 6. Crear el archivo de configuración</h2>
<p>Ahora hay que decirle al túnel qué debe hacer cuando reciba tráfico para ese subdominio.</p>
<p>Creamos o editamos el archivo:</p>
<pre><code class="language-bash">nano ~/.cloudflared/config.yml
</code></pre>
<p>Y añadimos este contenido, sustituyendo <code>TU_UUID</code> por el UUID real generado al crear el túnel:</p>
<pre><code class="language-yaml">tunnel: TU_UUID
credentials-file: /home/TU-USUARIO/.cloudflared/TU_UUID.json

ingress:
  - hostname: ollama.tu-dominio.com
    service: http://localhost:11434
    originRequest:
      httpHostHeader: localhost:11434

  - service: http_status:404
</code></pre>
<p>Este bloque hace algo muy concreto:</p>
<ul>
<li><p>Cuando llega tráfico para <code>ollama.tu-dominio.com</code>,</p>
</li>
<li><p>Cloudflare lo envía por el túnel,</p>
</li>
<li><p>y <code>cloudflared</code> lo reenvía a <code>http://localhost:11434</code>, que es donde escucha Ollama.</p>
</li>
</ul>
<p>La cabecera <code>httpHostHeader: localhost:11434</code> ayuda a mantener la compatibilidad esperada por Ollama en este escenario.</p>
<h2>Paso 7. Ejecutar el túnel</h2>
<p>Con el archivo listo, arrancamos el túnel:</p>
<pre><code class="language-bash">cloudflared tunnel run
</code></pre>
<p>También podría arrancarse indicando el nombre:</p>
<pre><code class="language-bash">cloudflared tunnel run ollama-aiserver
</code></pre>
<p>Si todo está correcto, el túnel quedará conectado y el subdominio empezará a funcionar como punto de acceso externo al servicio local.</p>
<h2>Paso 8. Probar desde fuera</h2>
<p>Desde otro equipo o una red externa, ya podemos probar el subdominio:</p>
<pre><code class="language-bash">curl https://ollama.tu-dominio.com/api/tags
</code></pre>
<p>Y también una llamada real al modelo:</p>
<pre><code class="language-bash">curl https://ollama.tu-dominio.com/api/generate \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemma4",
    "prompt": "Prueba externa",
    "stream": false
  }'
</code></pre>
<p>Si esta prueba responde correctamente, el despliegue ya está funcionando.</p>
<h2>Qué hemos conseguido con este enfoque</h2>
<p>La ventaja de este sistema es que no hemos necesitado:</p>
<ul>
<li><p>abrir puertos en el router,</p>
</li>
<li><p>contratar una IP fija,</p>
</li>
<li><p>configurar DDNS,</p>
</li>
<li><p>ni exponer directamente el puerto 11434 a Internet.</p>
</li>
</ul>
<p>El túnel sale desde el propio servidor Linux hacia Cloudflare, y Cloudflare se encarga de enrutar las peticiones al servicio local de Ollama.</p>
<h2>Siguiente paso recomendable</h2>
<p>Una vez comprobado que el acceso funciona, el siguiente paso lógico es endurecer la seguridad.</p>
<p>Lo más recomendable es proteger <code>ollama.tu-dominio.com</code> con Cloudflare Access, de modo que solo determinados usuarios o servicios autorizados puedan consumir la API. Esto es especialmente importante si el endpoint va a ser utilizado por aplicaciones remotas, un VPS o un cliente como Jan.</p>
<h2>Conclusión</h2>
<p>Exponer Ollama con Cloudflare Tunnel es una forma práctica y limpia de publicar un modelo local hacia Internet sin complicarse con la IP del operador ni con configuraciones agresivas en el router.</p>
<p>En este caso, el flujo correcto ha sido:</p>
<ol>
<li><p>comprobar Ollama en local,</p>
</li>
<li><p>instalar y autenticar <code>cloudflared</code>,</p>
</li>
<li><p>crear un túnel nombrado,</p>
</li>
<li><p>asociar el subdominio,</p>
</li>
<li><p>mapear el tráfico al servicio local,</p>
</li>
<li><p>arrancar el túnel,</p>
</li>
<li><p>y probar desde el exterior.</p>
</li>
</ol>
<p>Con esto ya tenemos una base sólida para conectar clientes remotos y seguir evolucionando la arquitectura de forma más segura.</p>
]]></content:encoded></item><item><title><![CDATA[Cómo montar Laravel en Docker: Guía práctica y comandos esenciales]]></title><description><![CDATA[Cómo montar Laravel en Docker
Si alguna vez has intentado mover un proyecto Laravel de tu entorno local a producción y te has encontrado con el clásico problema de "en mi máquina sí funciona", Docker ]]></description><link>https://blog.tonyhelper.com/c-mo-montar-laravel-en-docker-gu-a-pr-ctica-y-comandos-esenciales</link><guid isPermaLink="true">https://blog.tonyhelper.com/c-mo-montar-laravel-en-docker-gu-a-pr-ctica-y-comandos-esenciales</guid><dc:creator><![CDATA[Moisés De Abreu]]></dc:creator><pubDate>Mon, 16 Mar 2026 19:56:27 GMT</pubDate><content:encoded><![CDATA[<h1>Cómo montar Laravel en Docker</h1>
<p>Si alguna vez has intentado mover un proyecto Laravel de tu entorno local a producción y te has encontrado con el clásico problema de <em>"en mi máquina sí funciona"</em>, Docker es la solución que estabas buscando.</p>
<p>En este artículo, te voy a explicar cómo estructurar tu proyecto Laravel con Docker de forma profesional, cómo resolver los problemas más comunes (como el temido <code>Connection refused</code>) y te dejaré una lista interactiva (Cheat Sheet) de los comandos indispensables para tu día a día.</p>
<hr />
<h2>Estructura del Proyecto</h2>
<p>Antes de entrar en detalle, así es como se organiza nuestro proyecto:</p>
<pre><code class="language-plaintext">proyecto/
├── .dockerignore        ← Excluye archivos innecesarios del build
├── .env                 ← Única fuente de credenciales (¡NO commitear!)
├── .env.example         ← Plantilla documentada (SÍ commitear)
├── docker-compose.yml   ← Orquestador de todos los servicios
├── dockerfiles/
│   ├── entrypoint.sh    ← Script de arranque automático
│   ├── nginx.dockerfile
│   └── php.dockerfile
├── nginx/
│   └── default.conf     ← Configuración del servidor web
└── src/
    └── ...              ← Tu proyecto Laravel
</code></pre>
<blockquote>
<p>** Detalle importante:** El fichero <code>.dockerignore</code> evita que carpetas como <code>vendor/</code>, <code>node_modules/</code> o archivos <code>.env</code> se envíen al contexto de build de Docker, acelerando la construcción y evitando filtrar información sensible.</p>
</blockquote>
<hr />
<h2>La Arquitectura: ¿Qué necesitamos?</h2>
<p>Para ejecutar Laravel en Docker de forma robusta, dividimos nuestra aplicación en <strong>4 contenedores (servicios)</strong> principales:</p>
<ol>
<li><p><strong>Servidor Web (Nginx):</strong> El encargado de recibir las peticiones HTTP y pasarlas a PHP. Configurado con cabeceras de seguridad y <code>server_tokens off</code>.</p>
</li>
<li><p><strong>Aplicación (PHP-FPM):</strong> Donde reside el código fuente de Laravel y se ejecuta la lógica. Corre con un usuario no-root (<code>laravel</code>).</p>
</li>
<li><p><strong>Base de Datos (MySQL):</strong> El almacenamiento persistente con un <strong>healthcheck</strong> que garantiza que esté lista antes de que PHP arranque.</p>
</li>
<li><p><strong>Gestor de BD (phpMyAdmin) [Solo desarrollo]:</strong> Administración visual de la base de datos, activada solo con el perfil <code>dev</code>.</p>
</li>
</ol>
<p>Todo esto lo orquestamos a través de un archivo maestro llamado <code>docker-compose.yml</code>.</p>
<hr />
<h2>Variables de Entorno: Una Única Fuente de Verdad</h2>
<p>Una de las claves de seguridad más importantes es <strong>nunca hardcodear credenciales</strong> directamente en los ficheros de Docker. En su lugar, usamos un fichero <code>.env</code> centralizado en la raíz del proyecto.</p>
<h3>1. Crea tu <code>.env</code> a partir de la plantilla</h3>
<pre><code class="language-bash">cp .env.example .env
</code></pre>
<h3>2. Edita las credenciales en <code>.env</code></h3>
<pre><code class="language-ini"># .env (raíz del proyecto — NO commitear)
APP_DEBUG=false
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=tu_contraseña_segura
MYSQL_ROOT_PASSWORD=otra_contraseña_segura
</code></pre>
<h3>3. El <code>docker-compose.yml</code> las referencia con <code>${VARIABLE}</code></h3>
<pre><code class="language-yaml">services:
  php:
    build:
      context: .
      dockerfile: dockerfiles/php.dockerfile
    restart: unless-stopped
    environment:
      - DB_CONNECTION=mysql
      - DB_HOST=mysql          # Usamos el nombre del servicio Docker, ¡no 127.0.0.1!
      - DB_PORT=3306
      - DB_DATABASE=${DB_DATABASE:-laravel}
      - DB_USERNAME=${DB_USERNAME:-laravel}
      - DB_PASSWORD=${DB_PASSWORD}
      - APP_DEBUG=${APP_DEBUG:-false}
    depends_on:
      mysql:
        condition: service_healthy  # Espera a que MySQL esté REALMENTE listo
</code></pre>
<blockquote>
<p>** ¿Por qué no usamos <code>src/.env</code>?** Docker inyecta estas variables de entorno en el contenedor, sobrescribiendo las del <code>.env</code> de Laravel. Así se separa la configuración del código y se facilitan los despliegues automatizados en herramientas como <strong>Coolify</strong> o AWS.</p>
</blockquote>
<hr />
<h2>Healthchecks: Arranque Inteligente</h2>
<p>Uno de los problemas más comunes es que PHP intente conectar a MySQL cuando la base de datos aún no está lista. Para evitarlo, configuramos un <strong>healthcheck</strong> en MySQL:</p>
<pre><code class="language-yaml">mysql:
  image: mysql:8.0.32
  restart: unless-stopped
  healthcheck:
    test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
    interval: 5s
    timeout: 3s
    retries: 10
    start_period: 10s
</code></pre>
<p>Con <code>depends_on: mysql: condition: service_healthy</code>, Docker Compose esperará a que MySQL responda al <code>ping</code> antes de arrancar los contenedores de PHP y Nginx. ¡Adiós a los <code>Connection refused</code> en el arranque!</p>
<hr />
<h2>🛠️ Automatizando Migraciones y Seeders</h2>
<p>El archivo <code>entrypoint.sh</code> se ejecuta cada vez que arranca el contenedor PHP. Automatiza las migraciones y ejecuta los seeders <strong>de forma inteligente</strong>:</p>
<pre><code class="language-bash">#!/bin/sh
set -e

cd /var/www/html

echo "Database is ready. Running migrations..."
php artisan migrate --force

# Solo ejecutar seeders si existen ficheros Y no se han ejecutado antes
if [ ! -f /var/www/html/storage/.seeded ]; then
  SEEDER_COUNT=$(find /var/www/html/database/seeders -name '*.php' 2&gt;/dev/null | wc -l)
  if [ "$SEEDER_COUNT" -gt 0 ]; then
    echo "Running seeders ($SEEDER_COUNT found)..."
    php artisan db:seed --force
    touch /var/www/html/storage/.seeded
  else
    echo "No seeders found, skipping."
  fi
else
  echo "Seeders already executed, skipping."
fi

exec "$@"
</code></pre>
<p><strong>¿Qué hace de especial este script?</strong></p>
<ul>
<li><p><code>set -e</code>: Si cualquier comando falla, el contenedor se detiene inmediatamente (fail-fast).</p>
</li>
<li><p><strong>Seeders condicionales:</strong> Primero comprueba que existan ficheros <code>.php</code> en <code>database/seeders</code>. Si los hay y no se han ejecutado antes, se ejecutan y se crea un fichero marcador <code>.seeded</code>. En reinicios posteriores, se omite automáticamente.</p>
</li>
</ul>
<blockquote>
<p>** Importante:** Cualquier cambio en <code>entrypoint.sh</code> o en el <code>php.dockerfile</code> requiere que <strong>reconstruyas la imagen</strong> con el flag <code>--build</code>. ¡Si solo reinicias el contenedor usando <em>restart</em>, no se aplicarán los cambios!</p>
</blockquote>
<hr />
<h2>Seguridad en Nginx</h2>
<p>Nuestra configuración de Nginx incluye cabeceras de seguridad esenciales:</p>
<pre><code class="language-nginx">server {
    listen 80;
    server_name .localhost;
    root /var/www/html/public;

    # Ocultar versión de Nginx
    server_tokens off;

    # Cabeceras de seguridad
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Límites
    client_max_body_size 10M;
    fastcgi_read_timeout 60;

    # Bloquear acceso a ficheros ocultos
    location ~ /\.(?!well-known) {
        deny all;
    }
}
</code></pre>
<p>Estas cabeceras protegen contra ataques como clickjacking (<code>X-Frame-Options</code>), MIME-sniffing (<code>X-Content-Type-Options</code>) y ataques XSS (<code>X-XSS-Protection</code>).</p>
<hr />
<h2>phpMyAdmin: Solo en Desarrollo</h2>
<p>phpMyAdmin se gestiona con un <strong>perfil de Docker Compose</strong>, lo que significa que solo se levanta cuando lo necesitas explícitamente:</p>
<pre><code class="language-yaml">phpmyadmin:
  image: phpmyadmin/phpmyadmin:latest
  profiles: ["dev"]    # Solo se activa con --profile dev
  depends_on:
    mysql:
      condition: service_healthy
  ports:
    - "127.0.0.1:8081:80"
</code></pre>
<p>En producción, phpMyAdmin no se despliega, eliminando una superficie de ataque innecesaria.</p>
<hr />
<h2>Cheat Sheet: Los Comandos que Usamos Todo el Tiempo</h2>
<p>A continuación tienes una lista de los comandos más habituales al trabajar con contenedores, y los que te salvarán la vida en tu día a día (¡comprobado en este mismo blog!):</p>
<h3>Comandos de Ciclo de Vida</h3>
<p>Levantar tu proyecto o apagarlo:</p>
<ul>
<li><p><code>docker compose --profile dev up -d</code>: Levanta todos los contenedores <strong>incluyendo phpMyAdmin</strong> (modo desarrollo).</p>
</li>
<li><p><code>docker compose up -d</code>: Levanta los contenedores <strong>sin phpMyAdmin</strong> (modo producción).</p>
</li>
<li><p><code>docker compose up -d --build</code>: Levanta los contenedores forzando la reconstrucción de las imágenes. <strong>Úsalo si cambias un</strong> <code>Dockerfile</code> <strong>o copias un nuevo</strong> <code>entrypoint.sh</code>.</p>
</li>
<li><p><code>docker compose --profile dev up -d --build</code>: Igual que el anterior, pero incluyendo phpMyAdmin.</p>
</li>
<li><p><code>docker compose down</code>: Detiene y destruye los contenedores y redes, pero conserva los volúmenes (tus bases de datos).</p>
</li>
<li><p><code>docker compose down -v</code>: Detiene y destruye todos los contenedores, redes y destruye los volúmenes (tus bases de datos).</p>
</li>
<li><p><code>docker network inspect &lt;nombre_red&gt;</code>: Útil si Docker te dice que una red sigue en uso y no te permite eliminar un entorno temporal.</p>
</li>
</ul>
<h3>Ejecutando código de Laravel dentro de Docker</h3>
<p>Para usar <code>php artisan</code> y <code>composer</code>, <strong>no</strong> lo ejecutes en la terminal base de tu máquina (Windows/Mac), ya que intentará usar el PHP instalado localmente y probablemente fallará (por falta de dependencias u otras versiones de PHP). Ejecútalo <strong>dentro</strong> del contenedor:</p>
<ul>
<li><p><code>docker compose exec php php artisan migrate</code>: Ejecuta las migraciones manualmente.</p>
</li>
<li><p><code>docker compose exec php php artisan db:seed</code>: Ejecuta los seeders para poblar datos de prueba o roles base.</p>
</li>
<li><p><code>docker compose exec php php artisan config:clear</code>: Limpia la caché de configuración. <strong>¡Obligatorio si tocas tu</strong> <code>.env</code> <strong>local!</strong></p>
</li>
<li><p><code>docker compose exec --user root php composer dump-autoload</code>: Regenera el autoload de Composer como super-usuario. Útil si pasas código creado en Windows a Linux y hay errores de permisos ("Operation not permitted").</p>
</li>
</ul>
<h3>Debugging y Logs</h3>
<p>Cuando algo falla, como los <strong>errores HTTP 500</strong>, o un host <code>laravel.localhost</code> que devuelve <strong>"Connection Refused"</strong>, la consola de comandos es tu mejor aliado para rastrear el fallo:</p>
<ul>
<li><p><code>docker compose logs php</code>: Muestra lo que está pasando en el contenedor de PHP. Excelente para ver si el Entrypoint ejecutó las migraciones o si los seeders se saltaron correctamente.</p>
</li>
<li><p><code>docker compose logs -f server</code>: Sigue en tiempo real los logs de acceso (y errores <code>502 Bad Gateway</code>) del servidor Nginx.</p>
</li>
<li><p><code>docker compose ps</code>: Comprueba qué contenedores están encendidos en ese momento. Con healthchecks, verás el estado <code>healthy</code> en MySQL.</p>
</li>
<li><p><code>docker compose exec php ps aux</code>: Verifica qué procesos internos (por ejemplo: <code>php-fpm</code>) se están ejecutando dentro del contenedor en ese preciso instante.</p>
</li>
</ul>
<hr />
<h2>Reflexión Final</h2>
<p>Dockerizar Laravel implica una curva de aprendizaje mínima al principio, pero una vez configuras tus volúmenes adecuadamente y entiendes cómo funciona su red interna, el flujo de trabajo es impecable.</p>
<p>Evitas conflictos de versiones (¡adiós a fallos por ejecutar Laravel 8 en PHP 8.3 local!), aislas tus bases de datos para no "manchar" tu sistema operativo y, lo mejor de todo, logras que el comportamiento local sea <strong>exactamente igual al del servidor de producción</strong>.</p>
<p>Con las mejoras de seguridad (variables externalizadas, cabeceras HTTP, perfiles para desarrollo) y fiabilidad (healthchecks, seeders condicionales), tu stack está preparado para un entorno profesional real.</p>
<p>¿Y tú, ya usas Docker en tus proyectos de Laravel o sigues usando XAMPP/Laragon? ¡Compárteme tu experiencia en los comentarios!</p>
]]></content:encoded></item><item><title><![CDATA[Guía: Proteger tu Servidor Web (Caddy/OpenClaw) con Cloudflare Zero Trust y UFW]]></title><description><![CDATA[Tener un servidor web o un panel de control expuesto a internet es un riesgo constante. En esta guía, vamos a construir una doble barrera de seguridad de nivel empresarial para tu servidor Ubuntu:

El]]></description><link>https://blog.tonyhelper.com/gu-a-proteger-tu-servidor-web-caddy-openclaw-con-cloudflare-zero-trust-y-ufw</link><guid isPermaLink="true">https://blog.tonyhelper.com/gu-a-proteger-tu-servidor-web-caddy-openclaw-con-cloudflare-zero-trust-y-ufw</guid><dc:creator><![CDATA[Moisés De Abreu]]></dc:creator><pubDate>Mon, 02 Mar 2026 16:12:17 GMT</pubDate><content:encoded><![CDATA[<p>Tener un servidor web o un panel de control expuesto a internet es un riesgo constante. En esta guía, vamos a construir una doble barrera de seguridad de nivel empresarial para tu servidor Ubuntu:</p>
<ol>
<li><p><strong>El Muro Frontal:</strong> Usaremos Cloudflare Zero Trust para obligar a cualquier visitante a iniciar sesión con tu cuenta de Google antes de ver tu web.</p>
</li>
<li><p><strong>El Candado Trasero:</strong> Configuraremos el cortafuegos de Ubuntu (UFW) para que tu servidor rechace cualquier conexión directa a su IP pública, a menos que venga de Cloudflare o de tu propia casa.</p>
</li>
</ol>
<hr />
<h3>PARTE 1: Configurar Cloudflare Zero Trust con Google</h3>
<p><em>Requisito previo: Tu dominio debe estar gestionado en Cloudflare y con el proxy activado (la "nube naranja" en la pestaña DNS).</em></p>
<h4>Paso 1.1: Obtener tu "Team Domain" en Cloudflare</h4>
<p>Antes de ir a Google, necesitamos saber cuál es el identificador único de tu escudo en Cloudflare.</p>
<ol>
<li><p>Entra en tu cuenta de Cloudflare y accede al panel de <strong>Zero Trust</strong> (en el menú lateral izquierdo).</p>
</li>
<li><p>Ve abajo del todo en el menú lateral y haz clic en <strong>Settings</strong> (Ajustes).</p>
</li>
<li><p>En las pestañas superiores, elige <strong>Team name</strong>.</p>
</li>
<li><p>Apunta o copia la URL exacta que aparece bajo <strong>Team domain</strong> (será algo parecido a <code>https://tu-equipo.cloudflareaccess.com</code>). <em>¡Guarda esta URL porque la usaremos ahora!</em></p>
</li>
</ol>
<h4>Paso 1.2: Crear las credenciales en Google Cloud</h4>
<p>Vamos a autorizar a Cloudflare para que use los sistemas de inicio de sesión de Google como "portero".</p>
<ol>
<li><p>Ve a la <a href="https://console.cloud.google.com/">Consola de Google Cloud</a> e inicia sesión con tu cuenta.</p>
</li>
<li><p>Crea un proyecto nuevo (ej. "Seguridad Servidor").</p>
</li>
<li><p>Ve a <strong>API y servicios &gt; Pantalla de consentimiento de OAuth</strong>. Elige "Externo" y rellena tu email y el nombre de la app.</p>
</li>
<li><p>Ve a <strong>Credenciales &gt; Crear credenciales &gt; ID de cliente de OAuth</strong> y selecciona <strong>Aplicación web</strong>.</p>
</li>
<li><p>En <strong>Orígenes de JavaScript autorizados</strong>, pega tu Team Domain de Cloudflare: <code>https://tu-equipo.cloudflareaccess.com</code></p>
</li>
<li><p>En <strong>URI de redireccionamiento autorizados</strong>, pega tu Team Domain seguido de la ruta de respuesta: <code>https://tu-equipo.cloudflareaccess.com/cdn-cgi/access/callback</code></p>
</li>
<li><p>Haz clic en Crear. Copia el <strong>ID de cliente</strong> y el <strong>Secreto de cliente</strong> que aparecerán en pantalla.</p>
</li>
</ol>
<h4>Paso 1.3: Conectar Google con Cloudflare</h4>
<ol>
<li><p>Vuelve al panel de <strong>Zero Trust</strong> en Cloudflare.</p>
</li>
<li><p>En el menú lateral izquierdo, ve a <strong>Integrations &gt; Identity providers</strong>.</p>
</li>
<li><p>Haz clic en <strong>Add new identity provider</strong> y selecciona <strong>Google</strong>.</p>
</li>
<li><p>Pega el ID y el Secreto de cliente que copiaste de Google y haz clic en Save.</p>
</li>
</ol>
<h4>Paso 1.4: Proteger tu dominio (Crear la Aplicación y la Política)</h4>
<ol>
<li><p>En el menú lateral de Zero Trust, ve a <strong>Access controls &gt; Applications</strong> y pulsa <strong>Add an application</strong>. Selecciona <strong>Self-hosted</strong>.</p>
</li>
<li><p><strong>Pestaña 1 (Configuración):</strong> Ponle un nombre (ej. <code>OpenClaw-Admin</code>). En <em>Public hostname</em>, escribe tu subdominio (ej. <code>openclaw</code>) y selecciona tu dominio (ej. <code>tudominio.com</code>). Pulsa Next.</p>
</li>
<li><p><strong>Pestaña 2 (La Política VIP):</strong> Ponle un nombre a la regla (ej. <code>SoloYo</code>) y asegúrate de que la acción es <strong>Allow</strong>.</p>
</li>
<li><p>En la sección <em>Include</em>, abre el desplegable <strong>Selector</strong> y elige <strong>Emails</strong>. En el campo <em>Value</em>, escribe tu correo de Google exacto. Pulsa Next y luego <strong>Add application</strong>.</p>
</li>
</ol>
<hr />
<h3>PARTE 2: Cerrar la "Puerta Trasera" con UFW en Ubuntu</h3>
<p>Si alguien averigua la IP pública de tu servidor, podría escribirla en su navegador y saltarse todo el muro de Cloudflare. Vamos a blindar el servidor para que solo hable con Cloudflare.</p>
<h4>Paso 2.1: Limpiar las reglas web abiertas (El método infalible)</h4>
<p>Conéctate por SSH a tu servidor. Vamos a ver las reglas de tu cortafuegos numeradas para no cometer errores al borrarlas:</p>
<pre><code class="language-plaintext">sudo ufw status numbered
</code></pre>
<p>Debes borrar todas las reglas que abran los puertos 80, 443 o el puerto directo de tu aplicación (ej. 18789), <strong>dejando viva ÚNICAMENTE la regla de OpenSSH</strong>. Para borrar una regla, usa su número (siempre borrando de los números más altos a los más bajos para que no se desordenen). Ejemplo:</p>
<pre><code class="language-plaintext">sudo ufw delete 4
</code></pre>
<h4>Paso 2.2: Permitir exclusivamente las IPs de Cloudflare</h4>
<p>Copia y pega estos dos bloques de código en tu terminal para descargar la lista oficial de IPs de Cloudflare y permitirlas en tu servidor:</p>
<pre><code class="language-plaintext">for i in $(curl -s https://www.cloudflare.com/ips-v4); do
  sudo ufw allow from $i to any port 80,443 proto tcp
done
</code></pre>
<pre><code class="language-plaintext">for i in $(curl -s https://www.cloudflare.com/ips-v6); do
  sudo ufw allow from $i to any port 80,443 proto tcp
done
</code></pre>
<h4>Paso 2.3: (Opcional) Permitir tu IP personal para uso de DNS Local</h4>
<p>Si planeas modificar el archivo <code>hosts</code> de tu ordenador para acceder directamente a la IP del servidor usando el subdominio (haciendo <em>Split DNS</em>), Cloudflare no intervendrá y el UFW te bloqueará. Para evitarlo, añade la IP pública de tu casa a la lista de permitidos:</p>
<pre><code class="language-plaintext">sudo ufw allow from TU_IP_PERSONAL to any port 80,443 proto tcp
</code></pre>
<h4>Paso 2.4: Recargar el Firewall</h4>
<p>Aplica todos los cambios blindando el sistema:</p>
<pre><code class="language-plaintext">sudo ufw reload
</code></pre>
<hr />
<p><strong>¡Misión cumplida!</strong> Tu servidor es ahora una fortaleza. Nadie puede acceder a tus aplicaciones web sin pasar por el inicio de sesión de Google administrado por Cloudflare, y la puerta trasera directa a tu IP está cerrada a cal y canto.</p>
]]></content:encoded></item><item><title><![CDATA[Guía Paso a Paso: Cómo Conectar tu cuenta de ChatGPT Plus a OpenClaw (Sin API)]]></title><description><![CDATA[Si estás utilizando OpenClaw y quieres aprovechar tu suscripción de ChatGPT Plus ($20/mes) para utilizar sus modelos más inteligentes (ideal para programar y tareas complejas), puedes integrarlo direc]]></description><link>https://blog.tonyhelper.com/gu-a-paso-a-paso-c-mo-conectar-tu-cuenta-de-chatgpt-plus-a-openclaw-sin-api</link><guid isPermaLink="true">https://blog.tonyhelper.com/gu-a-paso-a-paso-c-mo-conectar-tu-cuenta-de-chatgpt-plus-a-openclaw-sin-api</guid><dc:creator><![CDATA[Moisés De Abreu]]></dc:creator><pubDate>Mon, 02 Mar 2026 16:11:49 GMT</pubDate><content:encoded><![CDATA[<p>Si estás utilizando <strong>OpenClaw</strong> y quieres aprovechar tu suscripción de ChatGPT Plus ($20/mes) para utilizar sus modelos más inteligentes (ideal para programar y tareas complejas), puedes integrarlo directamente en OpenClaw. ¡De esta forma, tu bot usará tu cuenta web y no tendrás que pagar por consumos extra de API!</p>
<p>A continuación, te explicamos cómo configurarlo, tanto si tienes OpenClaw instalado de forma nativa en tu servidor Linux, como si usas Docker.</p>
<hr />
<h3>Método 1: Instalación Nativa (Sin Docker) - <em>Recomendado</em></h3>
<p>Conéctate a tu servidor por SSH con el usuario administrador donde instalaste OpenClaw y lanza el asistente directamente hacia la configuración de Codex:</p>
<pre><code class="language-plaintext">openclaw onboard --out choice open codex
</code></pre>
<h4>Respuestas durante el asistente (Onboarding)</h4>
<p>Al ejecutar el comando, la terminal se volverá interactiva. Aquí tienes lo que debes responder a cada pregunta:</p>
<ol>
<li><p><strong>Advertencia de riesgo:</strong> El sistema te preguntará si entiendes que usar cuentas web tiene riesgos (como que OpenAI te cierre la sesión). Escribe <strong>"Yes"</strong> o pulsa <strong>"Y"</strong> y presiona Enter.</p>
</li>
<li><p><strong>Quick Start:</strong> Si te aparece una opción de "Quick start", simplemente pulsa <strong>Enter</strong> para continuar.</p>
</li>
<li><p><strong>Actualizar valores:</strong> Te preguntará si quieres usar los valores existentes o actualizarlos. Utiliza las flechas del teclado para seleccionar <strong>"Update values"</strong> (Actualizar valores) y pulsa Enter.</p>
</li>
<li><p><strong>La Autenticación:</strong> La terminal generará una URL larga. Cópiala, pégala en el navegador de tu ordenador e inicia sesión con la cuenta donde pagas ChatGPT Plus.</p>
</li>
<li><p><strong>El "error" de Localhost:</strong> Al darle a "Continuar" en la web, la página parecerá romperse y te llevará a una dirección que empieza por <code>http://localhost...</code>. ¡No te asustes, es normal! Copia toda esa URL de la barra de direcciones, vuelve a tu terminal, pégala y pulsa Enter.</p>
</li>
<li><p><strong>Seleccionar canal:</strong> El asistente te preguntará qué canal de chat quieres usar. Elige el que ya tenías configurado (por ejemplo, Telegram) para mantenerlo.</p>
</li>
<li><p><strong>Configure Skills (Habilidades):</strong> Selecciona <strong>"No"</strong> (es mejor configurarlas más adelante).</p>
</li>
<li><p><strong>Enable Hooks:</strong> Selecciona <strong>"Skip for now"</strong> (Saltar por ahora).</p>
</li>
</ol>
<p>Una vez finalizado el asistente, reinicia la pasarela principal para aplicar el nuevo cerebro:</p>
<pre><code class="language-plaintext">openclaw gateway restart
</code></pre>
<hr />
<h3>Método 2: Instalación con Docker</h3>
<p>Si optaste por aislar tu entorno usando contenedores Docker, los pasos y las respuestas al asistente son <strong>exactamente los mismos</strong> que en el Método 1, pero debes lanzar el comando a través de Docker.</p>
<p>Sustituye <code>tu_contenedor</code> por el nombre real de tu contenedor (suele ser <code>openclaw</code>):</p>
<pre><code class="language-plaintext">sudo docker exec -it tu_contenedor openclaw onboard --out choice open codex
</code></pre>
<p>Responde a las preguntas del asistente tal y como explicamos en el Método 1. Al terminar, OpenClaw no podrá reiniciarse solo dentro de Docker, así que fuerza el reinicio manualmente:</p>
<pre><code class="language-plaintext">sudo docker restart tu_contenedor
</code></pre>
<hr />
<h3>Paso Final: Seleccionar y Verificar el Nuevo Modelo</h3>
<p>Una vez reiniciado el sistema, debes comprobar que la conexión funciona correctamente.</p>
<h4>Desde tu aplicación de mensajería (Telegram/WhatsApp)</h4>
<p>Abre el chat con tu bot y envíale este comando para ver su estado actual:</p>
<pre><code class="language-plaintext">/status
</code></pre>
<p>Si no detecta el modelo automáticamente, despliega el menú interactivo enviando:</p>
<pre><code class="language-plaintext">/model
</code></pre>
<p>Selecciona tu nueva conexión Plus (Codex) en la lista.</p>
<h4>Desde la terminal del servidor (Para usuarios avanzados)</h4>
<p>Puedes ver un reporte profundo del estado de tus tokens y modelos ejecutando:</p>
<pre><code class="language-plaintext">openclaw models status
</code></pre>
<p>Y para fijar el modelo de OpenAI Codex como el predeterminado para todas las sesiones, ejecuta (ajusta el nombre si el status te da uno ligeramente distinto):</p>
<pre><code class="language-plaintext">openclaw models set openai/codex
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Guía: Instalar y Configurar Caddy Server en Ubuntu 24.04]]></title><description><![CDATA[Si alguna vez has peleado configurando certificados SSL con Nginx o Apache, te va a encantar Caddy. Es un servidor web moderno y un proxy inverso increíblemente rápido que incluye una característica e]]></description><link>https://blog.tonyhelper.com/gu-a-instalar-y-configurar-caddy-server-en-ubuntu-24-04</link><guid isPermaLink="true">https://blog.tonyhelper.com/gu-a-instalar-y-configurar-caddy-server-en-ubuntu-24-04</guid><dc:creator><![CDATA[Moisés De Abreu]]></dc:creator><pubDate>Sun, 01 Mar 2026 19:27:50 GMT</pubDate><content:encoded><![CDATA[<p>Si alguna vez has peleado configurando certificados SSL con Nginx o Apache, te va a encantar <strong>Caddy</strong>. Es un servidor web moderno y un proxy inverso increíblemente rápido que incluye una característica estrella: <strong>HTTPS automático y renovaciones automáticas por defecto</strong>.</p>
<p>En este tutorial, vamos a instalar la versión oficial de Caddy en nuestro servidor Ubuntu 24.04 (previamente securizado) y lo dejaremos listo para servir nuestra primera web o aplicación.</p>
<hr />
<h3>Paso 1: Instalar dependencias necesarias</h3>
<p>Conéctate a tu servidor por SSH con tu usuario administrador (con permisos sudo) y asegúrate de tener instalados los paquetes básicos para poder descargar repositorios externos de forma segura:</p>
<pre><code class="language-plaintext">sudo apt update
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
</code></pre>
<h3>Paso 2: Añadir el repositorio oficial de Caddy</h3>
<p>Para asegurarnos de tener siempre la última versión estable, vamos a descargar e instalar la llave GPG oficial de Caddy y a añadir su repositorio a nuestro sistema.</p>
<p>Primero, descarga la llave GPG:</p>
<pre><code class="language-plaintext">curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
</code></pre>
<p>Luego, añade el repositorio a tu lista de fuentes (sources.list):</p>
<pre><code class="language-plaintext">curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
</code></pre>
<h3>Paso 3: Instalar Caddy</h3>
<p>Ahora que el sistema sabe de dónde descargar Caddy, simplemente actualiza la lista de paquetes e instálalo:</p>
<pre><code class="language-plaintext">sudo apt update
sudo apt install caddy -y
</code></pre>
<p>Una vez terminada la instalación, Caddy se inicia automáticamente como un servicio del sistema. Puedes verificar que está corriendo felizmente con:</p>
<pre><code class="language-plaintext">sudo systemctl status caddy
</code></pre>
<h3>Paso 4: Abrir los puertos en el Firewall (UFW)</h3>
<p>Si seguiste nuestra guía de seguridad anterior, recordarás que cerramos todos los puertos excepto el del SSH. Para que el mundo pueda ver tus páginas web, necesitamos abrir los puertos HTTP (80) y HTTPS (443):</p>
<pre><code class="language-plaintext">sudo ufw allow 80,443/tcp
sudo ufw reload
</code></pre>
<p>Si ahora introduces la IP pública de tu servidor en tu navegador web, verás la página de bienvenida por defecto de Caddy. ¡Funciona!</p>
<h3>Paso 5: Conociendo el Caddyfile</h3>
<p>Toda la magia de Caddy ocurre en un único archivo de configuración llamado <strong>Caddyfile</strong>. Se encuentra en la ruta <code>/etc/caddy/Caddyfile</code>.</p>
<p>Para editarlo, usa tu editor de texto favorito:</p>
<pre><code class="language-plaintext">sudo nano /etc/caddy/Caddyfile
</code></pre>
<p>Un ejemplo básico para servir una web estática o hacer un proxy inverso hacia una aplicación (como un bot o una API) sería tan simple como esto:</p>
<pre><code class="language-plaintext">tudominio.com {
    reverse_proxy localhost:3000
}
</code></pre>
<p>Con esas tres simples líneas, Caddy generará el certificado SSL gratis, forzará la conexión a HTTPS y redirigirá el tráfico al puerto interno 3000 de tu servidor.</p>
<p>Siempre que modifiques el Caddyfile, recuerda recargar el servicio para aplicar los cambios sin apagar el servidor:</p>
<pre><code class="language-plaintext">sudo systemctl reload caddy
</code></pre>
<hr />
<p><strong>¡Y eso es todo!</strong> Tienes un servidor web de última generación funcionando de forma segura. Adiós a las renovaciones manuales de certificados y a las configuraciones interminables.</p>
]]></content:encoded></item><item><title><![CDATA[Guía: Cómo Configurar y Securizar un Servidor Ubuntu 24.04 desde Cero]]></title><description><![CDATA[Cuando contratas un VPS (Servidor Privado Virtual) nuevo, tu proveedor suele entregarte la dirección IP y las credenciales del usuario root. Operar y exponer a internet un servidor directamente desde ]]></description><link>https://blog.tonyhelper.com/como-configurar-y-securizar-un-servidor-ubuntu-24-04-desde-cero</link><guid isPermaLink="true">https://blog.tonyhelper.com/como-configurar-y-securizar-un-servidor-ubuntu-24-04-desde-cero</guid><dc:creator><![CDATA[Moisés De Abreu]]></dc:creator><pubDate>Sun, 01 Mar 2026 19:25:45 GMT</pubDate><content:encoded><![CDATA[<p>Cuando contratas un VPS (Servidor Privado Virtual) nuevo, tu proveedor suele entregarte la dirección IP y las credenciales del usuario <strong>root</strong>. Operar y exponer a internet un servidor directamente desde el superusuario es un riesgo de seguridad crítico.</p>
<p>En este tutorial, aprenderás a preparar y blindar tu servidor con Ubuntu 24.04 paso a paso antes de instalar cualquier aplicación.</p>
<hr />
<h3>Paso 1: Actualizar el sistema base</h3>
<p>Lo primero que debes hacer al entrar a tu servidor por primera vez es asegurarte de que todos los paquetes y parches de seguridad estén al día. Abre tu terminal, conéctate por SSH como <strong>root</strong> y ejecuta:</p>
<pre><code class="language-bash">apt update &amp;&amp; apt upgrade -y
</code></pre>
<h3>Paso 2: Crear un usuario administrador (Sudo)</h3>
<p>No usaremos <strong>root</strong> para las tareas diarias. Vamos a crear un usuario estándar y le otorgaremos permisos de administrador para cuando los necesite.</p>
<p>Ejecuta el siguiente comando para crear el usuario (sustituye <code>tu_usuario</code> por el nombre que desees):</p>
<pre><code class="language-bash">adduser tu_usuario
</code></pre>
<p>El sistema te pedirá que asignes una contraseña segura y algunos datos adicionales que puedes omitir pulsando la tecla Enter. A continuación, dale permisos de administrador a este nuevo usuario:</p>
<pre><code class="language-bash">usermod -aG sudo tu_usuario
</code></pre>
<h3>Paso 3: Configurar el Firewall (UFW)</h3>
<p>Ubuntu incluye un cortafuegos muy sencillo de usar llamado UFW, pero viene desactivado por defecto. Vamos a encenderlo permitiendo únicamente el tráfico esencial.</p>
<p>Permite las conexiones SSH (puerto 22) para no quedarte bloqueado fuera del servidor:</p>
<pre><code class="language-bash">ufw allow OpenSSH
</code></pre>
<p>Activa el firewall:</p>
<pre><code class="language-bash">ufw enable
</code></pre>
<h3>Paso 4: Configurar el acceso mediante claves SSH</h3>
<p>Para evitar ataques de fuerza bruta basados en contraseñas, configuraremos el acceso por llaves criptográficas.</p>
<p><strong>En tu ordenador personal (NO en el servidor):</strong><br />Abre la terminal de tu PC o Mac y genera un par de claves con cifrado moderno:</p>
<pre><code class="language-bash">ssh-keygen -t ed25519 -C "tu_correo@ejemplo.com"
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Guía: Proteger tu Servidor Web con Cloudflare Zero Trust y UFW]]></title><description><![CDATA[Tener un servidor web expuesto a internet es un riesgo constante. En esta guía, vamos a construir una doble barrera de seguridad de nivel empresarial para tu servidor Ubuntu:

El Muro Frontal: Usaremo]]></description><link>https://blog.tonyhelper.com/gu-a-definitiva-proteger-tu-servidor-web-con-cloudflare-zero-trust-y-ufw</link><guid isPermaLink="true">https://blog.tonyhelper.com/gu-a-definitiva-proteger-tu-servidor-web-con-cloudflare-zero-trust-y-ufw</guid><dc:creator><![CDATA[Moisés De Abreu]]></dc:creator><pubDate>Sun, 01 Mar 2026 19:02:20 GMT</pubDate><content:encoded><![CDATA[<p>Tener un servidor web expuesto a internet es un riesgo constante. En esta guía, vamos a construir una doble barrera de seguridad de nivel empresarial para tu servidor Ubuntu:</p>
<ol>
<li><p><strong>El Muro Frontal:</strong> Usaremos Cloudflare Zero Trust para obligar a cualquier visitante a iniciar sesión con tu cuenta de Google antes de ver tu web.</p>
</li>
<li><p><strong>El Candado Trasero:</strong> Configuraremos el cortafuegos de Ubuntu (UFW) para que tu servidor rechace cualquier conexión directa a su IP pública, a menos que venga de Cloudflare.</p>
</li>
</ol>
<hr />
<h2>PARTE 1: Configurar Cloudflare Zero Trust con Google</h2>
<blockquote>
<p><em>Requisito previo: Tu dominio debe estar gestionado en Cloudflare y con el proxy activado (la "nube naranja" en la pestaña DNS).</em></p>
</blockquote>
<h3>Paso 1.1: Obtener tu "Team Domain" en Cloudflare</h3>
<ol>
<li><p>Accede al panel de <strong>Zero Trust</strong> en Cloudflare.</p>
</li>
<li><p>Ve abajo del todo en el menú lateral y haz clic en <strong>Settings</strong>.</p>
</li>
<li><p>En las pestañas superiores, elige <strong>Team name</strong>.</p>
</li>
<li><p>Apunta la URL que aparece bajo <strong>Team domain</strong> (ej. <code>https://tu-equipo.cloudflareaccess.com</code>). <em>¡Guárdala porque la usaremos ahora!</em></p>
</li>
</ol>
<h3>Paso 1.2: Crear las credenciales en Google Cloud</h3>
<ol>
<li><p>Ve a la <a href="https://console.cloud.google.com/">Consola de Google Cloud</a> e inicia sesión.</p>
</li>
<li><p>Crea un proyecto nuevo.</p>
</li>
<li><p>Ve a <strong>API y servicios &gt; Pantalla de consentimiento de OAuth</strong>. Elige "Externo" y rellena tus datos.</p>
</li>
<li><p>Ve a <strong>Credenciales &gt; Crear credenciales &gt; ID de cliente de OAuth</strong> (Aplicación web).</p>
</li>
<li><p>En <strong>Orígenes de JavaScript autorizados</strong>, pega tu Team Domain: <code>https://tu-equipo.cloudflareaccess.com</code></p>
</li>
<li><p>En <strong>URI de redireccionamiento autorizados</strong>, añade: <code>https://tu-equipo.cloudflareaccess.com/cdn-cgi/access/callback</code></p>
</li>
<li><p>Haz clic en Crear. Copia el <strong>ID de cliente</strong> y el <strong>Secreto de cliente</strong>.</p>
</li>
</ol>
<h3>Paso 1.3: Conectar Google con Cloudflare</h3>
<ol>
<li><p>Vuelve al panel de <strong>Zero Trust</strong>.</p>
</li>
<li><p>Ve a <strong>Integrations &gt; Identity providers</strong>.</p>
</li>
<li><p>Haz clic en <strong>Add new identity provider</strong> y selecciona <strong>Google</strong>.</p>
</li>
<li><p>Pega el ID y el Secreto de cliente y guarda.</p>
</li>
</ol>
<h3>Paso 1.4: Proteger tu dominio (Crear el Muro)</h3>
<ol>
<li><p>Ve a <strong>Access controls &gt; Applications</strong> y pulsa <strong>Add an application</strong> (Self-hosted).</p>
</li>
<li><p><strong>Pestaña 1:</strong> Ponle un nombre. En <em>Public hostname</em>, escribe tu subdominio (ej. <code>openclaw</code>) y selecciona tu dominio. Pulsa Next.</p>
</li>
<li><p><strong>Pestaña 2:</strong> Ponle un nombre a la política (ej. <code>Administrador</code>) y asegúrate de que la acción es <strong>Allow</strong>.</p>
</li>
<li><p>En <em>Include</em>, elige <strong>Emails</strong> en el Selector y escribe tu correo exacto. Pulsa Next y guarda.</p>
</li>
</ol>
<hr />
<h2>PARTE 2: Cerrar la "Puerta Trasera" con UFW</h2>
<p>Vamos a blindar el servidor para que solo hable con Cloudflare y bloquee intentos de acceso por IP directa.</p>
<h3>Paso 2.1: Limpiar las reglas web abiertas</h3>
<p>Conéctate por SSH y lista tus reglas numeradas:</p>
<pre><code class="language-bash">sudo ufw status numbered
</code></pre>
]]></content:encoded></item></channel></rss>