Telegram Alerts Without Leaking the Exit IP
TL;DR: While disk logs are disabled to preserve privacy, operators still need real-time awareness of access events, vault status, and bandwidth consumption. Out-of-band Telegram Bot API notifications deliver critical events without writing client source IPs to disk or revealing node public IPs.
This article is Part 3 of a 7-part series on building a multi-hop, fail-closed VPS VPN chain with Ansible, LUKS encrypted vaults, Telegram monitoring, and zero-log policy routing.
- Part 1: VPS Baseline: Install, Harden, and Disable Logs
- Part 2: Encrypted Vault for VPN Secrets and Residual Logs
- Part 3: Telegram Alerts Without Leaking the Exit IP
- Part 4: Ansible Playbook for Edge Hop Installation
- Part 5: VPN Chain and End-to-End Encryption Without Persisted Logs
- Part 6: VPN Failover Between VPS Nodes
- Part 7: Operational Tasks: Heal, Add, Remove, and Replace Hops
To minimize metadata exposure while retaining operational visibility, configure Telegram notifications for three key events:
| Event Type | Trigger Schedule / Condition | Payload Contents |
|---|---|---|
| SSH Login | Real-time on session opening | Hostname, username, timestamp |
| Daily Status | Cron schedule (08:30 UTC daily) | Uptime, system load, RAM/disk %, LUKS vault status, active peer counts |
| Traffic Cap | Bandwidth threshold check | Daily/monthly vnstat totals, quota utilization percentage |
Crucial rule: Never transmit the node’s public IP address in alert payloads. Identify nodes by their internal logical hostname (e.g., vpn-node-01).
Use pam_exec in the SSH PAM stack to trigger a lightweight notification script whenever an SSH session opens.
Add the following line to /etc/pam.d/sshd:
session optional pam_exec.so seteuid /vault/bin/vpn-login-alert
Create the notifier script inside the encrypted vault at /vault/bin/vpn-login-alert:
#!/bin/bash
# /vault/bin/vpn-login-alert — transmit login notification asynchronously without disk logging
set -euo pipefail
# Read credentials from the encrypted LUKS vault (/vault/secrets)
[ -f /vault/secrets/telegram.token ] || exit 0
token=$(cat /vault/secrets/telegram.token)
chat_id=$(cat /vault/secrets/telegram.chat)
text="[ALERT] SSH login on ${HOSTNAME} | User: ${PAM_USER} | Time: $(date -u +%FT%TZ)"
# Dispatch HTTP request in background; suppress errors so login is never blocked
curl -fsS --max-time 8 \
-H "Content-Type: application/json" \
-d "$(jq -n --arg t "$text" --arg c "$chat_id" '{chat_id:$c, text:$t}')" \
"https://api.telegram.org/bot${token}/sendMessage" >/dev/null 2>&1 &
Ensure /vault/bin/vpn-login-alert is executable (chmod +x) and bot token files stored inside the encrypted vault are restricted:
chmod 0755 /vault/bin/vpn-login-alert
chmod 0600 /vault/secrets/telegram.token /vault/secrets/telegram.chat
Configure a daily systemd timer or cron job at 08:30 UTC executing /vault/bin/vpn-daily-report to collect node metrics and dispatch a consolidated summary:
#!/bin/bash
# /vault/bin/vpn-daily-report
set -euo pipefail
[ -f /vault/secrets/telegram.token ] || exit 0
token=$(cat /vault/secrets/telegram.token)
chat_id=$(cat /vault/secrets/telegram.chat)
uptime_str=$(uptime -p)
load_str=$(cat /proc/loadavg | awk '{print $1, $2, $3}')
mem_free=$(free -m | awk '/Mem:/ {print $4"MB / "$2"MB"}')
vault_status=$(cryptsetup status vpn-vault >/dev/null 2>&1 && echo "ONLINE" || echo "LOCKED")
wg_peers=$(wg show wg0 peers 2>/dev/null | wc -l || echo "0")
f2b_bans=$(fail2ban-client status sshd 2>/dev/null | grep "Currently banned" | awk '{print $4}' || echo "0")
report=$(cat <<EOF
[STATUS] ${HOSTNAME} Daily Report
Uptime: ${uptime_str}
Load: ${load_str}
Memory Available: ${mem_free}
LUKS Vault: ${vault_status}
WireGuard Peers: ${wg_peers}
Active Fail2ban Bans: ${f2b_bans}
EOF
)
curl -fsS --max-time 10 \
-H "Content-Type: application/json" \
-d "$(jq -n --arg t "$report" --arg c "$chat_id" '{chat_id:$c, text:$t}')" \
"https://api.telegram.org/bot${token}/sendMessage" >/dev/null 2>&1
Prevent provider overage charges by monitoring network interface statistics via vnstat.
Create /vault/bin/vpn-bandwidth-check:
#!/bin/bash
# /vault/bin/vpn-bandwidth-check
# Alert if monthly usage exceeds 90% of bandwidth allotment (e.g., 3 TB limit)
set -euo pipefail
MAX_MONTHLY_GB=3000
USED_GB=$(vnstat --json m 1 | jq -r '.interfaces[0].traffic.month[0].rx + .interfaces[0].traffic.month[0].tx' | awk '{print int($1 / 1073741824)}')
if [ "$USED_GB" -gt $((MAX_MONTHLY_GB * 90 / 100)) ]; then
[ -f /vault/secrets/telegram.token ] || exit 0
token=$(cat /vault/secrets/telegram.token)
chat_id=$(cat /vault/secrets/telegram.chat)
msg="[WARNING] ${HOSTNAME} bandwidth usage has reached ${USED_GB} GB / ${MAX_MONTHLY_GB} GB quota!"
curl -fsS --max-time 8 \
-H "Content-Type: application/json" \
-d "$(jq -n --arg t "$msg" --arg c "$chat_id" '{chat_id:$c, text:$t}')" \
"https://api.telegram.org/bot${token}/sendMessage" >/dev/null 2>&1
fi
Inject notification files and scripts via Ansible without exposing tokens in process arguments or Git commits:
ansible-playbook -i hosts.yaml playbooks/vpn-node-security.yaml \
-e target_vpn=vpn-1 \
-e deadman_enabled=false
Store Telegram API bot keys in ansible-vault encrypted variable files.
- Avoid
psCommand Leaks: Never pass the API token directly as a command-line flag (e.g.curl https://api.telegram.org/bot<TOKEN>/...). Always read credentials from secure files or environment variables. - Asynchronous Execution: Run PAM scripts in the background using
&so API timeouts or Telegram connectivity issues never block user SSH logins. - Payload Sanitization: Ensure error streams (
stderr) are piped to/dev/nullto prevent curl or jq errors from leaking tokens into systemic error logs.
Test the notification pipeline by initiating a new SSH session on custom port 28422 (or configured non-standard port):
ssh -p 28422 [email protected]
Verify that a Telegram message arrives in your designated channel within 2–3 seconds containing the hostname and timestamp without revealing the client IP.
Next in the series: Part 4: Ansible Playbook for Edge Hop Installation - Automating baseline setup, vault creation, and security alerts across nodes with idempotent Ansible playbooks.