Skip to content

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 allowedSenderIds is set, only those user IDs may issue commands — even inside a group the bot is a member of.
  • When allowedSenderIds is 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 any allowedChatIds entry 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:

  1. Inbound webhook signature. Every webhook delivery must carry a valid HMAC-SHA256 signature over the raw body, keyed by webhookSecret (or password when webhookSecret is 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 dedicated webhookSecret so a leak of the API password — which is sent to the relay on every outbound call — cannot be used to forge inbound webhooks.
  2. 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-Id
  • CF-Access-Client-Secret

Best practices

  1. Set allowedSenderIds — list only the user IDs (and allowedNumbers for iMessage) you control; this is the perimeter
  2. Prefer private chats — if you do add the bot to a group, you must set allowedSenderIds, or the bot refuses to start
  3. Set a distinct webhookSecret for BlueBubbles so the API password can’t forge inbound webhooks
  4. Monitor the logs — the bot logs all commands and unauthorised attempts
  5. Rotate the Telegram token if compromised — revoke via BotFather and update bot.json
  6. Keep the bot updated — pull the latest image regularly