📅 Day 216 — Building Remote Power Control for a Mac over Telegram, with Two Sender Checks
🔄 Topic
Building remote power control for a second machine (an M4 Mac) over Telegram meant answering two separate questions at once: who is allowed to send this command, and does the network path even stay reliable enough for the answer to matter. I fixed a broken Tailnet SSH route and shipped a Telegram bot that checks identity twice before it will touch the machine at all.
🎯 Goal
Restore reliable Tailnet SSH to the machine acting as the bridge, and give myself remote wake/sleep/status control over the M4 through Telegram without widening who or what can issue that control.
🛠 What I Did
I treated the network path and the command surface as two separate problems with two separate fixes, rather than one bundled “make remote control work” task.
Main areas covered:
- restored Tailnet SSH to
ubuntu-serverthrough normal UFW rules, after temporary LAN and firewall test rules had been added and needed to be fully removed rather than left as a convenient shortcut - found the Tailnet IPv4 address and reply route could silently drop, so built and enabled
tailscale-ensure-address.service— a small daemon that checks and restores the address and reply route every two seconds, rather than relying on Tailscale’s own reconnect timing to always be fast enough - deployed a dedicated Telegram bot (
@M4PowerControl_Bot) exposing exactly three commands —/status,/wake,/sleep— and nothing else - required both the chat ID and the sender ID to match the owner before any command is accepted, not just one or the other — a private chat alone isn’t proof of who’s actually typing if either identifier could be spoofed or a chat could be joined by someone else
- kept the receiver’s command surface constrained to the existing M4-only power actions, adding no shell, no SSH key, and no general command capability to the bot itself
- verified with real, live checks rather than trusting the deploy: confirmed the deployed Python’s hash matched the reviewed source, confirmed the live
ExecStartpointed at the installed release path, ran the bot’s own test suite (5 passing), sent an owner-only/statuscanary through the real bot and independently confirmed the M4’s actual state matched what the bot reported, and confirmed the webhook was empty (polling only, no exposed endpoint)
🔗 Key Cybersecurity Connections
Checking both chat ID and sender ID is defense against a single point of identity failure: a private chat is a reasonable first filter, but treating it as sufficient proof of identity conflates “this conversation looks private” with “I’ve verified who’s on the other end” — the same category of mistake as trusting a session’s existence over verifying the specific user it claims to represent. Requiring both to match closes that gap cheaply.
Scoping the bot to exactly three named commands, with no shell or SSH capability added anywhere in the chain, is least privilege applied to a remote-control surface: the bot can do exactly the three things it’s for, and nothing it isn’t, which bounds the damage of any future bug in the bot’s own code to those three actions. The self-healing Tailscale daemon is a smaller but real lesson in its own right — a security control that depends on network reachability is only as trustworthy as the network path’s own reliability, so making that path self-repairing is itself part of the security posture, not just an availability nicety.
🔍 Investigation Questions
- Does a remote-control bot verify identity through more than one independent signal, or does “private chat” alone stand in for authentication?
- Is the bot’s command surface scoped to exactly what it needs, with no shell or credential capability layered on for convenience?
- Was a temporary firewall or LAN rule added during debugging actually removed, or does it linger as an unreviewed opening?
- Does the deployed binary’s hash actually match the reviewed source, confirmed directly rather than assumed from the deploy step?
- Does a network dependency this control relies on (like a Tailnet route) have any self-healing behavior, or does a transient drop silently break the control?
🚨 Detection Opportunities
Checks for a remote-control bot and its network dependency:
- a command-issuing bot trusting a single identity signal (like chat membership) instead of multiple independent checks
- a bot’s command surface exposing shell, SSH, or general execution capability beyond its stated purpose
- a temporary firewall or network rule added for debugging that was never removed
- deployed binary hash diverging from the reviewed source in version control
- a network route or address with no self-healing mechanism, silently dropping without alerting anything downstream
Example:
project=m4-power-telegram-bot
signal=command_accepted_on_single_identity_signal
risk_area=insufficient_sender_verification
triage=require_chat_id_and_sender_id_match_before_any_action
🧭 MITRE ATT&CK Techniques
Possible mapping for the risk being controlled:
- T1078 — Valid Accounts (the dual-identity check exists specifically to raise the bar against a compromised or spoofed single identity signal being sufficient)
🗺 Visual Investigation Diagram
Tailnet SSH broken to ubuntu-server
↓
Restore via normal UFW rules, remove temporary test rules
↓
Address/route can silently drop
↓
tailscale-ensure-address.service: checks + restores every 2s
↓
Telegram bot: /status /wake /sleep only
↓
Requires chat ID AND sender ID to match owner
↓
No shell, no SSH key, no general command capability added
↓
Verified live: hash match, real canary, independent state check
⚠ Challenges
The tempting shortcut during debugging was to leave the temporary LAN and firewall test rules in place “just for now” since the real fix was still in progress — removing them fully, and confirming they were gone, took discipline separate from getting the actual feature working.
📚 What I Learned
I learned that a single identity signal — even one that feels private, like a Telegram chat — isn’t the same as verified identity, and that the cost of checking a second, independent signal is low compared to the cost of being wrong about the first one. I also learned that a security control’s network dependency deserves the same reliability engineering as the control itself, since a control that can’t be reached isn’t protecting anything.
➡ Next Steps
- Audit other remote-control surfaces for single-signal identity checks that could be strengthened the same way
- Monitor
tailscale-ensure-address.serviceover time to confirm it’s actually catching real drops, not just running idle - Periodically re-verify deployed binary hashes against source, not just at initial deployment
- Review whether other temporary debugging rules exist elsewhere in the firewall configuration
🧠 Reflection
Building the actual bot was the fast part; verifying that the deployed artifact matched the reviewed source, that the identity check actually required both signals, and that no debugging shortcut had been left open took longer — and was the part that actually earned trust in the result.
🧩 Lessons Learned
What worked
Requiring two independent identity signals before accepting a command, scoping the bot to exactly three named actions, and making the underlying network route self-healing.
What broke
Tailnet SSH had been broken by temporary debugging rules, and the Tailnet address/route could silently drop with nothing catching it.
Why it mattered
A remote-control surface for physical machine power is exactly the kind of capability that needs both strong identity verification and a reliable network path — either one alone isn’t enough.
Fix / takeaway
Verify identity through more than one independent signal, scope command surfaces to exactly what’s needed, and make network dependencies self-healing rather than assuming they’ll always just work.
📈 Skill Progression Context
This supports my cybersecurity progression because multi-factor identity verification, least-privilege command scoping, and treating network reliability as part of a security control’s own trust model are all core access-control and resilience engineering practices.
😄 TL;DR
Gave myself remote power control over a second Mac through Telegram — but the bot checks who’s actually asking twice, and the network path underneath it now heals itself instead of silently dropping.
