Bot Security
fleet-bot has access to powerful commands (shell execution, deployments, secret management). The security model ensures only authorised users can interact with it.
Authentication
Telegram
Commands are authorised by sender (user) ID, gated by allowedSenderIds in bot.json:
{ "adapters": { "telegram": { "allowedSenderIds": [221714512] } }}- When
allowedSenderIdsis set, only those user IDs may issue commands — even inside a group the bot is a member of. - When
allowedSenderIdsis empty, the bot default-denies: only a private 1:1 chat (where the chat ID equals the sender’s user ID) is accepted, and the bot refuses to start if anyallowedChatIdsentry is a group or channel (a negative ID). This prevents any member of an allowlisted group from issuing commands.
allowedChatIds is an independent, optional chat-level filter (which chats the bot listens to at all); it does not by itself authorise senders. Unauthorised messages are dropped with a log entry — no response is sent.
BlueBubbles (iMessage)
Two layers apply:
- Inbound webhook signature. Every webhook delivery must carry a valid HMAC-SHA256 signature over the raw body, keyed by
webhookSecret(orpasswordwhenwebhookSecretis unset). The signature is constant-time compared, and replayed or stale deliveries (signed timestamp outside a 10-minute window, keyed on the message guid) are rejected. Configure a dedicatedwebhookSecretso a leak of the API password — which is sent to the relay on every outbound call — cannot be used to forge inbound webhooks. - Sender allowlist. After signature verification, the sender’s phone number is checked against
allowedNumbers.
{ "adapters": { "imessage": { "webhookSecret": "a-strong-random-secret", "allowedNumbers": ["+447123456789"] } }}If the BlueBubbles server is behind Cloudflare Access, the adapter includes CF-Access-Client-Id and CF-Access-Client-Secret headers on every request, adding network-level authentication.
Destructive command protection
Commands that modify state (stop, restart, deploy, WAF changes, shell) require inline keyboard confirmation. The bot sends a message with Confirm/Cancel buttons. This prevents accidental execution.
The confirmation is tied to the requesting user — other users cannot confirm someone else’s destructive command.
Shell access
The /sh command executes arbitrary shell commands on the host. It is:
- Marked as destructive (requires confirmation)
- Only available to authorised sender IDs
- Logged with the full command text
Network security
Docker network mode
The bot container runs with network_mode: host so it can reach the Docker daemon and systemd bus. This means:
- The bot can call
docker compose,systemctl, etc. - It binds to the host network (no port mapping needed)
BlueBubbles + Cloudflare Access
If your BlueBubbles server is behind Cloudflare Access, configure the service token credentials in bot.json. The adapter sends these headers with every API call:
CF-Access-Client-IdCF-Access-Client-Secret
Best practices
- Set
allowedSenderIds— list only the user IDs (andallowedNumbersfor iMessage) you control; this is the perimeter - Prefer private chats — if you do add the bot to a group, you must set
allowedSenderIds, or the bot refuses to start - Set a distinct
webhookSecretfor BlueBubbles so the API password can’t forge inbound webhooks - Monitor the logs — the bot logs all commands and unauthorised attempts
- Rotate the Telegram token if compromised — revoke via BotFather and update
bot.json - Keep the bot updated — pull the latest image regularly