OpenClaw

Cómo securizar el acceso remoto a OpenClaw sin SSH: Apache, Let's Encrypt y Cloudflare Access

El device pairing de OpenClaw requiere SSH para cada nuevo navegador. Lo eliminamos con Apache + HTTPS + Cloudflare Access — ahora entramos con email y código.
Cómo securizar el acceso remoto a OpenClaw sin SSH: Apache, Let's Encrypt y Cloudflare Access
Foto de Markus Spiske en Unsplash
En: OpenClaw, Seguridad, Cloudflare, Tutorial

El problema: cada navegador nuevo requiere SSH

OpenClaw tiene un sistema de seguridad llamado device pairing. Cada vez que un navegador nuevo intenta conectarse al dashboard (Control UI), el gateway genera una solicitud de emparejamiento que alguien tiene que aprobar desde el servidor por SSH.

Para uso personal, es una pequeña molestia. Pero para un curso donde 20 alumnos necesitan acceder a sus propias instancias de OpenClaw, es un bloqueo total: cada alumno necesitaría acceso SSH al servidor para aprobar su propio dispositivo.

Necesitábamos una solución que cumpliera tres requisitos:

  1. Acceder al dashboard desde el navegador sin SSH
  2. Conexión cifrada (HTTPS) con certificado real
  3. Autenticación por email para controlar quién puede entrar

La solución: tres capas de seguridad

Después de varios intentos (incluyendo caminos que no funcionaron, que contamos al final), llegamos a una arquitectura con tres capas:

  1. Apache reverse proxy + Let's Encrypt — HTTPS con certificado real, proxy de WebSocket al gateway
  2. dangerouslyDisableDeviceAuth — elimina la necesidad de aprobar dispositivos por SSH
  3. Cloudflare Access — autenticación por email con código de un solo uso

El resultado: el alumno abre la URL, Cloudflare le pide su email, recibe un código, y entra al dashboard. Sin SSH, sin device pairing, con HTTPS.

Paso 1: Apache reverse proxy con WebSocket

El gateway de OpenClaw escucha en localhost:18789 con bind: loopback — no es accesible desde fuera. Apache actúa como intermediario: recibe el tráfico HTTPS en el puerto 443 y lo reenvía al gateway, incluyendo las conexiones WebSocket que el dashboard necesita para funcionar.

Los módulos de Apache necesarios (todos vienen preinstalados en las instancias Bitnami de Lightsail):

# Verificar que los módulos están habilitados
apache2ctl -M | grep -E 'proxy|rewrite|ssl|wstunnel'
# Necesarios: proxy, proxy_http, proxy_wstunnel, rewrite, ssl

Primero, creamos el vhost HTTP (para el redirect y para el ACME challenge de Let's Encrypt):

sudo tee /etc/apache2/sites-available/openclaw.conf > /dev/null <<'EOF'
<VirtualHost *:80>
    ServerName openclaw.<TU_DOMINIO>

    RewriteEngine On
    RewriteCond %{HTTP:X-Forwarded-Proto} !https
    RewriteCond %{HTTPS} off
    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

    ProxyPreserveHost On
    ProxyPass / http://localhost:18789/
    ProxyPassReverse / http://localhost:18789/

    RewriteCond %{HTTP:Upgrade} websocket [NC]
    RewriteCond %{HTTP:Connection} upgrade [NC]
    RewriteRule ^/?(.*) ws://localhost:18789/$1 [P,L]
</VirtualHost>
EOF

sudo a2ensite openclaw.conf
sudo systemctl reload apache2
💡
La condición RewriteCond %{HTTP:X-Forwarded-Proto} !https es clave: cuando Cloudflare reenvía tráfico al origin, envía la cabecera X-Forwarded-Proto: https. Sin esta condición, Apache redirige a HTTPS en bucle infinito porque ve HTTP desde Cloudflare.

Paso 2: Certificado HTTPS con Let's Encrypt

Con el vhost HTTP activo, certbot puede completar el challenge ACME y generar el certificado:

# Instalar certbot si no está disponible
sudo apt-get install -y certbot python3-certbot-apache

# Generar certificado (reemplaza con tu dominio)
sudo certbot --apache \
  -d openclaw.<TU_DOMINIO> \
  --non-interactive \
  --agree-tos \
  -m <TU_EMAIL>

Certbot crea automáticamente el vhost SSL (openclaw-le-ssl.conf). Lo editamos para añadir el proxy con WebSocket:

sudo tee /etc/apache2/sites-available/openclaw-le-ssl.conf > /dev/null <<'EOF'
<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName openclaw.<TU_DOMINIO>

    ProxyPreserveHost On
    ProxyPass / http://localhost:18789/
    ProxyPassReverse / http://localhost:18789/

    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} websocket [NC]
    RewriteCond %{HTTP:Connection} upgrade [NC]
    RewriteRule ^/?(.*) ws://localhost:18789/$1 [P,L]

    SSLCertificateFile /etc/letsencrypt/live/openclaw.<TU_DOMINIO>/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/openclaw.<TU_DOMINIO>/privkey.pem
    Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>
EOF

sudo systemctl reload apache2

El certificado se renueva automáticamente cada 90 días via un timer de systemd que certbot configura.

Para un curso: cada máquina de alumno genera su propio certificado con un solo comando. No uses wildcard — un certificado por máquina es más seguro y la renovación es automática.

Paso 3: Desactivar device pairing

Ahora que tenemos HTTPS, desactivamos el requisito de aprobar dispositivos por SSH:

# Desactivar device pairing
openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth true

# Añadir tu dominio a los orígenes permitidos
openclaw config set gateway.controlUi.allowedOrigins \
  '["https://openclaw.<TU_DOMINIO>"]'

# Reiniciar gateway
openclaw gateway restart

Con esto, cualquier navegador que tenga el token del gateway puede conectarse. El token se pasa en el fragmento de la URL:

https://openclaw.<TU_DOMINIO>/#token=<TU_GATEWAY_TOKEN>
🛡️
Importante: dangerouslyDisableDeviceAuth se llama "dangerous" por una razón — cualquiera con el token puede entrar. Por eso es OBLIGATORIO combinarlo con Cloudflare Access (paso 4). Sin esa capa, estarías exponiendo tu gateway a quien conozca la URL.

Paso 4: Cloudflare Access (autenticación por email)

Cloudflare Access añade la capa de autenticación que falta: antes de que nadie llegue a tu gateway, Cloudflare le pide que se identifique con su email. Si el email no está en tu lista, no pasa.

Configurar el registro DNS

El registro DNS de tu subdominio debe tener el proxy activado (nube naranja) para que Cloudflare intercepte el tráfico:

Tipo: A
Nombre: openclaw
Contenido: <IP_DE_TU_SERVIDOR>
Proxy: Activado (nube naranja)

Crear la aplicación de Access

En el dashboard de Cloudflare Zero Trust (one.dash.cloudflare.com):

  1. AccessApplicationsAdd an application
  2. Tipo: Self-hosted
  3. Application domain: openclaw.<TU_DOMINIO>
  4. Session duration: 24 hours

Crear la política de acceso

  1. Add a policy
  2. Policy name: Equipo autorizado
  3. Action: Allow
  4. Include → Emails: los emails que quieras autorizar

Cuando alguien intenta acceder, Cloudflare le pide su email, le envía un código de un solo uso, y solo si el email está en la lista le deja pasar. La sesión dura 24 horas — no le vuelve a pedir código hasta el día siguiente.

SSL/TLS mode: asegúrate de que el modo SSL de tu zona en Cloudflare está en Full o Full (Strict). Con "Flexible", Apache redirige en bucle infinito porque Cloudflare envía HTTP al origin y Apache lo redirige a HTTPS.

Gotchas que descubrimos (para que no te pase)

Llegamos a esta solución después de probar varios caminos que NO funcionaron. Aquí van los aprendizajes para que no pierdas el tiempo:

🔴
WebSockets en Cloudflare: OBLIGATORIO activarlos. Por defecto, Cloudflare puede tener el soporte de WebSocket desactivado en tu zona. Sin él, el dashboard de OpenClaw carga pero la conexión WebSocket nunca se establece. Ve a dash.cloudflare.com → tu dominio → Network → WebSockets → ON. Sin esto, nada funciona.
SSL/TLS mode: Full (Strict). Asegúrate de que el modo SSL de tu zona en Cloudflare está en Full o Full (Strict). Con Flexible, Apache redirige en bucle infinito. Y el tráfico entre Cloudflare y tu servidor va cifrado con tu certificado Let s Encrypt real.
⚠️
Cloudflare Tunnel NO funciona con OpenClaw. Probamos extensamente y el WebSocket falla silenciosamente. El dashboard carga pero la conexión WebSocket nunca se establece. No uses tunnels para esto — usa Apache reverse proxy directo. Y asegúrate de activar WebSockets en la configuración de Network de Cloudflare.
  • Cloudflare Access bloquea WebSocket a través de Tunnels — las peticiones de upgrade nunca llegan al origin. Funciona con Apache directo.
  • trusted-proxy y token son mutuamente excluyentes — OpenClaw no permite tener gateway.auth.mode: "trusted-proxy" y gateway.auth.token al mismo tiempo. Elegimos token + Access.
  • SSL "Flexible" = redirect loop — Cloudflare envía HTTP al origin → Apache redirige a HTTPS → Cloudflare reenvía HTTP → bucle infinito. Solución: SSL Full (Strict).
  • DNS proxy OFF para certbot — el ACME challenge de Let's Encrypt necesita que el tráfico llegue directo al servidor. Genera el certificado con proxy OFF, luego actívalo.
  • ProxyPreserveHost On es esencial — sin él, el gateway recibe localhost como Host y rechaza la conexión por origin mismatch.

Arquitectura: diagrama completo

Diagrama de arquitectura: Navegador, Cloudflare Access, Apache HTTPS, OpenClaw Gateway
Flujo completo: el navegador pasa por Cloudflare Access (email), Apache (HTTPS + WebSocket), y llega al gateway (token auth).

El flujo completo

Navegador del alumno
    │
    ▼
Cloudflare Edge (HTTPS)
    │ ← Access: ¿email autorizado? → Código por email
    ▼
Apache :443 (Let's Encrypt TLS)
    │ ← ProxyPass + mod_proxy_wstunnel
    ▼
OpenClaw Gateway :18789 (loopback)
    │ ← Token auth (sin device pairing)
    ▼
Dashboard Control UI ✓

Tres capas: Cloudflare Access controla quién (email), HTTPS cifra la comunicación, y el token autoriza la conexión WebSocket. El puerto 18789 nunca se expone al exterior.

Para un curso: setup por máquina en 5 comandos

Con un snapshot de Lightsail como base (que ya tiene Apache, certbot y OpenClaw configurados), cada nueva máquina de alumno se configura así:

# 1. Nuevo token de gateway (único por máquina)
openclaw config set gateway.auth.token $(openssl rand -base64 24)

# 2. DNS: crear A record → IP del alumno (proxy OFF inicialmente)
# openclaw-alumnoX.tudominio.com → IP_ALUMNO

# 3. Certificado SSL
sudo certbot --apache -d openclaw-alumnoX.<TU_DOMINIO> \
  --non-interactive --agree-tos -m <TU_EMAIL>

# 4. Actualizar Apache vhost con el dominio del alumno
sudo sed -i 's/openclaw.<TU_DOMINIO>/openclaw-alumnoX.<TU_DOMINIO>/g' \
  /etc/apache2/sites-available/openclaw-le-ssl.conf
sudo systemctl reload apache2

# 5. Activar proxy en DNS + añadir email del alumno a Access policy

En menos de dos minutos, cada alumno tiene su propia instancia con HTTPS, sin SSH, protegida por email.

Conclusión

El device pairing de OpenClaw es una buena medida de seguridad para uso individual. Pero para escenarios con múltiples usuarios — cursos, equipos, demos — necesitas una alternativa. La combinación de Apache + Let's Encrypt + Cloudflare Access te da seguridad real sin la fricción de SSH.

Lo más importante: nunca desactives el device pairing sin poner otra capa de autenticación encima. El dangerouslyDisableDeviceAuth sin Cloudflare Access es como quitar la cerradura de tu puerta y dejar la llave debajo del felpudo.

Si tienes preguntas o quieres montar algo similar, escríbenos a [email protected].

Más de Damos forma a la tecnología

En Orvium Labs, estamos listos para ayudarte a convertir tus ideas en realidad ¡Contáctanos hoy!

Contacto
¡Genial! Te has inscrito con éxito.
Bienvenido de nuevo! Has iniciado sesión correctamente.
Te has suscrito correctamente a Damos forma a la tecnología.
Su enlace ha caducado.
¡Éxito! Comprueba en tu correo electrónico el enlace mágico para iniciar sesión.
Éxito! Su información de facturación ha sido actualizada.
Su facturación no se actualizó.