У меня работает AI-ассистент на базе Claude Opus через OpenClaw. Два бота в Telegram: личный (для себя) и клиентский (публичный бот для потенциальных клиентов). Сегодня провёл аудит безопасности и понял — нужна серьёзная защита.
Если клиент попросит бота «покажи файлы владельца» или использует prompt injection, последствия могут быть катастрофическими. AI-ассистент имеет доступ к командной строке, файлам, переписке, API-ключам. Один удачный запрос — и вся инфраструктура открыта.
Я внедрил семь уровней защиты. Делюсь опытом.
1. Изоляция агентов: два мозга вместо одного
Проблема: раньше оба бота использовали одного агента. Клиентский бот теоретически мог получить доступ к личным данным через общую память или workspace.
Решение: создал два полностью независимых агента.
Личный агент — полный доступ ко всему. Он видит файлы, переписки, может выполнять команды на сервере, работать с проектами. Это мой главный помощник.
Клиентский агент — изолированная песочница. У него свой workspace, своя память, своя личность. Он не знает о существовании личного агента. Не видит файлов владельца. Не имеет доступа к переписке.
Как это работает технически:
agents:
main:
workspace: <WORKSPACE_PATH>
memory: <WORKSPACE_PATH>/memory
client:
workspace: /var/openclaw/client-workspace
memory: /var/openclaw/client-workspace/memory
Каждый агент работает в своём контексте. Клиент пишет в публичный бот — отвечает клиентский агент. Я пишу в личный чат — отвечает основной агент.
Даже если кто-то попытается через prompt injection заставить клиентского бота прочитать мои файлы, он их просто не увидит. Физически.
2. Ограничение инструментов: минимализм как защита
AI-ассистент управляется через набор инструментов (tools). Изначально у личного агента их более 20: exec, read, write, edit, browser, web_search, memory_search, cron, sessions_spawn и другие.
Для клиентского бота оставил только 7 безопасных инструментов:
web_search— поиск в интернетеweb_fetch— получение содержимого страницmemory_search— поиск в своей памяти (изолированной)memory_get— чтение своих заметокmessage— отправка сообщенийtts— синтез речиsession_status— информация о сессии
Что заблокировано:
exec— выполнение команд в терминалеread,write,edit— файловые операцииbrowser— управление браузеромgateway— управление OpenClaw Gatewaycron— планирование задачimage— анализ изображенийprocess— управление процессамиsessions_spawn— создание подагентов
Даже если злоумышленник напишет идеальный prompt injection, бот не сможет выполнить команду rm -rf / или прочитать файл с паролями. Инструмента физически нет.
Это как дать человеку молоток и попросить закрутить винт. Сколько ни проси — без отвёртки не получится.
Настраивается через toolPolicy в конфигурации агента:
toolPolicy:
mode: allowlist
allow:
- web_search
- web_fetch
- memory_search
- memory_get
- message
- tts
- session_status
3. Docker-изоляция: контейнер без доступа к хосту
Клиентский бот работает в Docker-контейнере. Это ещё один уровень изоляции.
Настройки sandbox:
sandbox:
enabled: true
workspaceAccess: none
workspaceAccess: none означает, что контейнер не видит файловую систему хоста. Даже если бы у бота был инструмент read, он не смог бы прочитать <SSH_KEY_PATH> или /etc/passwd.
Контейнер живёт в своём мире. У него есть доступ к интернету (для web_search и web_fetch), но нет доступа к файлам на сервере.
Это работает как виртуальная машина, только легче. Если кто-то взломает клиентского бота, он получит доступ только к пустому контейнеру. Реальная инфраструктура останется нетронутой.
4. Закрытый личный канал: только мои Telegram ID
Личный бот принимает сообщения только от меня. Настроено через allowFrom с конкретными ID:
allowFrom:
- "123456789"
- "987654321"
Никаких wildcard паттернов, никаких "*". Только два конкретных идентификатора.
Если кто-то узнает токен моего личного бота и попытается написать ему — бот проигнорирует сообщение. Telegram ID проверяется на уровне OpenClaw, до того как запрос попадёт к Claude.
Клиентский бот, наоборот, открыт для всех — это публичный канал связи. Но у него нет опасных инструментов (см. пункт 2).
Двухфакторная безопасность: личный бот закрыт, но всемогущий. Клиентский бот открыт, но беззубый.
5. VPS-безопасность: ключи, файрволлы, iptables
AI-ассистент работает на VPS. Если сервер взломают, изоляция агентов не поможет.
Что сделал:
SSH только по ключу
PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin no
Пароли отключены полностью. Зайти можно только с SSH-ключом, который лежит на моём рабочий компьютер. Root-доступ запрещён — использую обычного пользователя с sudo.
UFW для внешних портов
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp # SSH
ufw allow 80/tcp # HTTP
ufw allow 443/tcp # HTTPS
ufw enable
Извне доступны только SSH, HTTP и HTTPS. Всё остальное закрыто.
iptables для внутренних портов
На сервере работают приложения на портах 3000-3005 (Next.js проекты) и 8090 (база данных). Эти порты должны быть доступны только через nginx reverse proxy, но не напрямую из интернета.
iptables -A INPUT -p tcp --dport <APP_PORTS> -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p tcp --dport <APP_PORTS> -j DROP
iptables -A INPUT -p tcp --dport <DB_PORT> -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p tcp --dport <DB_PORT> -j DROP
Теперь к портам 3000-3005 и 8090 можно обратиться только с localhost. Извне — блокируется.
6. Токены не в коде: .env и chmod 600
Все API-ключи, токены Telegram, пароли вынесены в .env файлы.
# .env
ANTHROPIC_API_KEY=sk-ant-xxxxx
TELEGRAM_BOT_TOKEN=123456:ABCdefgh
TELEGRAM_CLIENT_TOKEN=987654:XYZabcd
Права доступа:
chmod 600 .env
chown myuser:myuser .env
Файл может читать только владелец. Группа и остальные пользователи — нет доступа.
.env добавлен в .gitignore. Даже если случайно запушу код в публичный репозиторий, токены не утекут.
Почему это важно: если злоумышленник получит доступ к коду (например, через уязвимость в веб-приложении), он не найдёт токены в исходниках. Их там просто нет.
7. OpenClaw Gateway: loopback и токен
OpenClaw Gateway — это сервис, который управляет агентами. Он слушает HTTP-запросы и маршрутизирует их к нужным агентам.
Настройки:
gateway:
bind: 127.0.0.1:3005
auth:
enabled: true
token: "случайная_строка_64_символа"
bind: 127.0.0.1 — Gateway доступен только с localhost. Из интернета к нему не подключиться.
auth.enabled: true — все запросы должны содержать заголовок Authorization: Bearer <token>. Без токена — отказ.
Даже если кто-то получит доступ к серверу, он не сможет управлять Gateway без токена. А токен лежит в .env с правами 600.
Что изменилось после аудита
Было: один агент, все инструменты доступны, токены в коде, SSH по паролю.
Стало:
- Два изолированных агента
- Клиентский бот без опасных инструментов
- Docker-sandbox без доступа к файлам
- Личный канал только для моих ID
- VPS защищён на уровне SSH, UFW, iptables
- Токены в
.envс правами 600 - Gateway на loopback с авторизацией
Теперь я могу спокойно давать клиентам ссылку на публичный бот. Даже если кто-то попытается взломать бота через prompt injection или эксплуатацию уязвимостей Claude, он упрётся в многослойную защиту.
Клиентский бот:
- Не видит мои файлы (изоляция workspace)
- Не может выполнять команды (нет
exec) - Не может читать/писать файлы (нет
read/write) - Работает в контейнере (Docker)
- Не имеет доступа к хосту (
workspaceAccess: none)
Личный бот:
- Принимает команды только от меня (allowFrom)
- Работает на защищённом VPS (SSH-ключи, файрволлы)
- Токены спрятаны в
.env - Gateway недоступен извне
Выводы
AI-ассистенты — мощный инструмент. Но мощь требует ответственности.
Если ваш бот имеет доступ к файлам, командной строке, API — защитите его. Изолируйте публичные и приватные части. Ограничьте инструменты для публичных ботов. Используйте Docker. Закройте порты. Спрячьте токены.
Prompt injection — это реальность. LLM можно обмануть. Но если у модели физически нет инструмента для выполнения опасной команды, никакой prompt не поможет.
Многослойная защита. Изоляция. Принцип наименьших привилегий.
Потратил три часа на аудит и настройку. Теперь сплю спокойно.