Anexo I: OpenClaw (Openbot) + Telegram (Abacombot) - De 0 a Avanzado
Asistente Sysadmin por chat con seguridad y sesiones aisladas
Anexo I: OpenClaw (Openbot) + Telegram (Abacombot) - De 0 a Avanzado
Introduccion
Este mini-curso te guia para montar desde cero un bot en Telegram que actua como un Sysadmin: recibe instrucciones por chat y responde con diagnosticos (y, opcionalmente, acciones controladas) para facilitar actividades del curso.
La ruta recomendada usa OpenClaw (Gateway + canales + sesiones + skills) y su postura de seguridad (pairing, allowlists, auditoria, aislamiento de sesiones). Al final se incluye una ruta alternativa: un bot Python simple (sin OpenClaw).
El bot se llama por defecto Abacombot, pero el nombre es configurable.
Enfoque de seguridad: por defecto el bot trabaja en modo read-only. Las acciones que cambian el sistema (reiniciar servicios, aplicar updates, etc.) deben habilitarse de forma explicita con una lista blanca y reglas de sudo muy acotadas.
Ubuntu Server LTS recomendado: 22.04 o 24.04.
Objetivos
- Crear un bot de Telegram (token) y conectarlo a un Gateway.
- Implementar un bot Sysadmin con comandos del curso (CPU/RAM, disco, red, servicios, logs, Docker).
- Asegurar el bot: token fuera del codigo, pairing/allowlists, mention gating en grupos, auditoria.
- Desplegar como servicio (daemon) con logs y troubleshooting.
- Extender a modo avanzado: skills propias + politicas de sesiones (aislamiento DM).
Arquitectura (vista rapida)
- Telegram: usuario envia un comando (ej:
/status). - Abacombot: valida chat permitido + rate limit.
- Runner: ejecuta un comando del sistema (solo allowlist) con timeout.
- Respuesta: devuelve salida resumida al chat.
Requisitos
En Telegram
- Una cuenta de Telegram.
- Token del bot creado con
@BotFather.
En el servidor (Ubuntu Server LTS)
- Acceso SSH con un usuario con permisos administrativos (para instalacion).
- Python 3.
systemd(por defecto en Ubuntu).- Firewall basico (
ufw) recomendado.
Paso 1: Crear el bot en Telegram (BotFather)
- Abre Telegram y busca
@BotFather. - Ejecuta
/newboty define:- Nombre:
Abacombot(o el que prefieras) - Username: debe terminar en
bot(ej:abacom_sysadmin_bot)
- Nombre:
- BotFather te entregara un token tipo:
123456789:AA...REDACTED...xyz
No pegues el token en repositorios ni capturas publicas.
Lo que podria salir mal: - Un atacante toma control del bot. - Se filtran datos del servidor por chat.
Como prevenirlo: 1. Guarda el token como secreto (archivo root-only en /etc o un secret manager). 2. Usa allowlist de chat IDs. 3. Mantén el bot en modo read-only por defecto.
Ruta recomendada: OpenClaw (Gateway) + Telegram
Esta ruta usa OpenClaw para:
- controlar acceso (pairing/allowlists)
- aislar sesiones
- auditar seguridad con
openclaw security audit - administrar el bot como daemon
1.1 Instalar OpenClaw
BASH
1$ curl -fsSL https://openclaw.ai/install.sh | bash- 1
- curl … | bash instala el CLI de OpenClaw usando el instalador oficial.
1.2 Ejecutar el onboarding wizard (y daemon)
BASH
1$ openclaw onboard --install-daemon- 1
- openclaw onboard –install-daemon configura gateway/canales y deja OpenClaw corriendo como servicio.
1.3 Configurar Telegram (token) en OpenClaw
Config minima (recomendada) para Telegram:
TEXT
{
channels: {
telegram: {
enabled: true,
botToken: "REDACTED",
dmPolicy: "pairing",
groups: { "*": { requireMention: true } },
},
},
}
- 1
- dmPolicy: pairing + requireMention evita que un grupo/DM desconocido active el bot sin aprobacion.
1.4 Verificar salud y postura de seguridad
- 1
- openclaw gateway status confirma que el gateway esta arriba.
- 2
- openclaw health entrega un snapshot de salud (utile para pegar como evidencia).
- 3
- openclaw security audit –deep detecta footguns (exposicion, allowlists, permisos) y puede sugerir fixes.
1.5 Aprobar pairing (primer DM)
Por defecto, los DMs entran en modo pairing: el bot entrega un codigo y no procesa hasta aprobar.
- 1
- openclaw pairing list telegram lista solicitudes pendientes.
- 2
- openclaw pairing approve aprueba el DM y habilita la conversacion.
1.6 Aislamiento de sesiones DM (multi-usuario)
Si mas de una persona puede escribirle al bot por DM, evita fuga de contexto usando session.dmScope.
TEXT
{ session: { dmScope: "per-channel-peer" } }
Referencias
- OpenClaw - Getting Started
- OpenClaw - Onboarding Wizard
- OpenClaw - Gateway Security
- OpenClaw - Session Management
- OpenClaw - Telegram
- dmScope: per-channel-peer separa sesiones por canal+persona (recomendado para inbox compartido).
Ruta alternativa (sin OpenClaw): bot Python + Telegram
Esta ruta implementa un bot minimalista en Python. Es util como laboratorio para entender:
- allowlist de chats
- rate limit
- ejecucion segura (sin shell)
- despliegue con systemd
Si quieres las protecciones y ergonomia de OpenClaw (pairing, auditoria, sesiones, skills), usa la ruta recomendada.
Paso 2 (alternativa): Preparar el servidor (usuario, paquetes, firewall)
2.1 Crear un usuario dedicado
BASH
1$ sudo adduser --disabled-password --gecos "" openbot
Adding user `openbot' ...
Adding new group `openbot' (1001) ...
Adding new user `openbot' (1001) with group `openbot' ...
Creating home directory `/home/openbot' ...- 1
- adduser openbot crea un usuario sin password para ejecutar el servicio con permisos minimos.
2.2 Instalar dependencias
BASH
- 1
- apt update actualiza el indice de paquetes.
- 2
- apt install instala Python + venv + ufw para el despliegue.
2.3 Firewall (UFW)
BASH
- 1
- ufw allow OpenSSH evita bloquear tu acceso SSH.
- 2
- ufw enable activa el firewall.
Paso 3: Estructura del proyecto (Openbot)
Vamos a instalar el proyecto en /opt/abacombot.
- 1
- mkdir -p /opt/abacombot crea el directorio del servicio.
- 2
- chown asigna ownership al usuario del servicio.
Estructura sugerida:
TEXT
/opt/abacombot/
venv/
abacombot/
__init__.py
main.py
security.py
commands.py
plugins/
__init__.py
read_only.py
requirements.txt
Paso 4: Instalar el bot (Python + venv)
4.1 Crear venv e instalar dependencias
BASH
- 1
- python3 -m venv crea un entorno virtual aislado.
- 2
- pip install –upgrade pip actualiza pip dentro del venv.
requirements.txt recomendado (en el host):
TEXT
python-telegram-bot==21.6
python-dotenv==1.0.1
Paso 5: Codigo base del Abacombot
Este es un esqueleto seguro para:
- validar chats permitidos
- rate limit basico
- ejecutar comandos sin
shell=True - limitar tiempo y tamano de salida
5.1 Archivo: abacombot/security.py
PYTHON
import os
import time
from dataclasses import dataclass
@dataclass(frozen=True)
class SecurityConfig:
allowed_chat_ids: set[int]
min_seconds_between_commands: float
class RateLimiter:
def __init__(self, min_seconds_between_commands: float):
self._min = float(min_seconds_between_commands)
self._last_by_chat: dict[int, float] = {}
def allow(self, chat_id: int) -> bool:
now = time.time()
last = self._last_by_chat.get(chat_id, 0.0)
if now - last < self._min:
return False
self._last_by_chat[chat_id] = now
return True
def parse_allowed_chat_ids(raw: str) -> set[int]:
ids: set[int] = set()
for part in (raw or "").split(","):
part = part.strip()
if not part:
continue
ids.add(int(part))
return ids
def load_security_config() -> SecurityConfig:
allowed = parse_allowed_chat_ids(os.environ.get("ABACOM_ALLOWED_CHAT_IDS", ""))
return SecurityConfig(
allowed_chat_ids=allowed,
min_seconds_between_commands=float(os.environ.get("ABACOM_RATE_LIMIT_SECONDS", "2")),
)- 1
- SecurityConfig + RateLimiter implementan controles basicos de acceso y anti-spam (defensa minima para un bot por chat).
5.2 Archivo: abacombot/commands.py
PYTHON
from __future__ import annotations
import subprocess
from dataclasses import dataclass
@dataclass(frozen=True)
class Cmd:
name: str
argv: list[str]
READ_ONLY_COMMANDS: dict[str, Cmd] = {
"status": Cmd("status", ["/usr/bin/uptime"]),
"disk": Cmd("disk", ["/usr/bin/df", "-h", "/"]),
"mem": Cmd("mem", ["/usr/bin/free", "-h"]),
"ip": Cmd("ip", ["/usr/sbin/ip", "-br", "addr"]),
"ports": Cmd("ports", ["/usr/sbin/ss", "-lntup"]),
"journal": Cmd("journal", ["/usr/bin/journalctl", "-n", "50", "--no-pager"]),
}
def run_cmd(cmd: Cmd, timeout_seconds: int = 8) -> str:
completed = subprocess.run(
cmd.argv,
check=False,
capture_output=True,
text=True,
timeout=timeout_seconds,
)
out = (completed.stdout or "") + ("\n" if completed.stderr else "") + (completed.stderr or "")
out = out.strip()
if not out:
return f"(sin salida) exit_code={completed.returncode}"
# Limite para no spamear Telegram
if len(out) > 3500:
return out[:3500] + "\n... (salida truncada)"
return out- 1
-
subprocess.run(…, capture_output=True, timeout=…) ejecuta argv fijo con timeout y captura stdout/stderr (sin
shell=True).
5.3 Archivo: abacombot/main.py
PYTHON
import os
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes
from abacombot.commands import READ_ONLY_COMMANDS, run_cmd
from abacombot.security import RateLimiter, load_security_config
SEC = load_security_config()
RL = RateLimiter(SEC.min_seconds_between_commands)
def is_allowed(update: Update) -> bool:
chat_id = update.effective_chat.id if update.effective_chat else None
if chat_id is None:
return False
if not SEC.allowed_chat_ids:
# Modo inseguro (solo para laboratorio). Recomendado: configurar allowlist.
return True
return chat_id in SEC.allowed_chat_ids
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
if not is_allowed(update):
return
name = os.environ.get("ABACOM_BOT_NAME", "Abacombot")
await update.message.reply_text(
f"{name} listo. Usa /help para ver comandos disponibles."
)
async def help_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
if not is_allowed(update):
return
cmds = ", ".join(sorted(f"/{k}" for k in READ_ONLY_COMMANDS.keys()))
await update.message.reply_text(
"Comandos (read-only):\n" + cmds + "\n\nEj: /disk, /mem, /ports"
)
async def run_readonly(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
if not is_allowed(update):
return
chat_id = update.effective_chat.id
if not RL.allow(chat_id):
await update.message.reply_text("Rate limit: espera unos segundos y reintenta.")
return
cmd_name = update.message.text.lstrip("/").strip()
cmd = READ_ONLY_COMMANDS.get(cmd_name)
if not cmd:
await update.message.reply_text("Comando no reconocido. Usa /help")
return
try:
output = run_cmd(cmd)
except Exception as e:
await update.message.reply_text(f"Error ejecutando comando: {e}")
return
await update.message.reply_text(f"$ {cmd.name}\n{output}")
def main() -> None:
token = os.environ.get("TELEGRAM_BOT_TOKEN", "").strip()
if not token:
raise SystemExit("TELEGRAM_BOT_TOKEN no configurado")
app = Application.builder().token(token).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(CommandHandler("help", help_cmd))
# Handlers read-only
for name in READ_ONLY_COMMANDS.keys():
app.add_handler(CommandHandler(name, run_readonly))
app.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
main()- 1
- allowlist + rate limit + handlers por comando evita ejecucion arbitraria: solo rutas explicitamente registradas.
Paso 6: Configuracion por variables de entorno (sin hardcodear token)
Recomendado: archivo root-only en /etc/abacombot.env.
BASH
- 1
- install -m 600 crea un archivo solo-leible por root.
- 2
- sudoedit edita de forma segura sin abrir permisos innecesarios.
Contenido sugerido:
BASH
- 1
- TELEGRAM_BOT_TOKEN token del bot (secreto).
- 2
- ABACOM_BOT_NAME nombre visible en mensajes.
- 3
- ABACOM_ALLOWED_CHAT_IDS allowlist de chats autorizados (recomendado).
- 4
- ABACOM_RATE_LIMIT_SECONDS evita spam/abuso.
Paso 7: Despliegue con systemd
Crear unit file:
BASH
1$ sudo tee /etc/systemd/system/abacombot.service >/dev/null <<'EOF'
[Unit]
Description=Abacombot (Openbot Telegram)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=openbot
Group=openbot
WorkingDirectory=/opt/abacombot
EnvironmentFile=/etc/abacombot.env
ExecStart=/opt/abacombot/venv/bin/python -m abacombot.main
Restart=on-failure
RestartSec=3
# Hardening basico
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
[Install]
WantedBy=multi-user.target
EOF- 1
- /etc/systemd/system/abacombot.service define como arrancar el bot como servicio.
Activar el servicio:
BASH
- 1
- systemctl daemon-reload recarga units.
- 2
- enable –now habilita al boot y arranca inmediatamente.
- 3
- status valida estado, PID y ultimos logs.
Logs en vivo:
BASH
1$ sudo journalctl -u abacombot -f- 1
- journalctl -u abacombot -f sigue el log del servicio en tiempo real.
Paso 8: Seguridad para comandos con impacto (modo avanzado)
Por defecto, este anexo implementa solo comandos read-only. Si necesitas comandos como reiniciar nginx, debes:
- crear un comando explicito (no permitir ejecutar texto libre)
- limitarlo por allowlist
- usar
sudoerssolo para esa accion
Ejemplo: permitir reiniciar nginx sin password (solo para openbot).
BASH
1$ sudo visudo -f /etc/sudoers.d/abacombot- 1
- visudo valida sintaxis para no romper sudo.
Contenido recomendado (ejemplo):
TEXT
openbot ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx
openbot ALL=(root) NOPASSWD: /usr/bin/systemctl status nginx
- 1
-
Permite solo
systemctl restart nginx. - 2
-
Permite consultar
systemctl status nginx.
Nunca implementes un comando tipo “ejecutar cualquier cosa” (shell) desde Telegram.
Lo que podria salir mal: - RCE (Remote Code Execution) por chat. - Exfiltracion de archivos y llaves. - Downtime por comandos maliciosos.
Como prevenirlo: 1. Usa un diccionario de comandos (allowlist) con argv fijo. 2. Ejecuta con subprocess.run([...], shell=False). 3. Usa timeouts y truncado de salida. 4. Aplica allowlist de chats + rate limit.
Troubleshooting
El bot no responde
- 1
- systemctl status confirma que el servicio corre.
- 2
- journalctl -n 200 muestra el error real (token, import, permisos, red).
Token invalido / 401
- Revisa
TELEGRAM_BOT_TOKENen/etc/abacombot.env. - Regenera token con BotFather si sospechas filtracion.
Comandos fallan por paths
- En servidores, comandos pueden estar en rutas distintas. Prefiere rutas absolutas (
/usr/bin/df,/usr/sbin/ss).