Skip to content

Latest commit

 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cockpit-tailscale

A plain-language Tailscale overview plugin for Cockpit — see your whole tailnet at a glance, with warnings that tell you what to do, not what protocol failed.

Cockpit Python Dependencies License: MIT

Adds a Tailscale page to Cockpit's menu that answers three questions: Is it working? What is connected? Do I need to do anything? No jargon — no DERP, NAT traversal, ACLs or key-expiry terminology anywhere in the UI. Every warning is an observation plus an action:

"iPhone loses its connection in 5 days. Open the Tailscale app on the phone and sign in again."

All good Needs attention
Green status: all devices listed, everything fine Amber status: actionable warnings above the device list

This is a personal hobby project I build for my own use and publish in case it's useful to someone else. I work on it in my spare time, so issues and PRs are welcome but replies may be slow. Use at your own risk.

Features

  • One-line status — a breathing green lamp and "Everything looks fine — 4 of 6 devices connected", or an amber "3 things need your attention"
  • Positive assurance — when nothing is pending: "All 7 devices are known and acknowledged by you. Last new device joined 11 days ago."
  • Actionable warnings — key about to expire, unknown new device, device removed from the network, device waiting for approval, rarely-used device suddenly online, outdated clients
  • Acknowledgments — new-device and removed-device warnings stay until you press "Yes, that was me". They never silently expire, so nothing sneaks past you while you're on holiday.
  • Activity journal — a plain-language log of what happened: devices joining, leaving, being approved, renewing their keys
  • Device list — icon by device type, owner, OS, connected/last-seen state, expandable details with copy buttons for the MagicDNS name and Tailscale IP
  • Cleanup list — devices not seen for 60+ days that should be removed
  • Push alerts (optional) — ntfy and/or SMTP email, with sensible cooldowns so you are never spammed
  • Monthly summary — one push/email per month: device count, what joined and left, pending updates, cleanup candidates
  • Online history — SQLite log of every device's connectivity, powering a 30-day "online % of the time" stat per device

How it works

New to Cockpit plugins? A plugin is just a directory of static files under /usr/share/cockpit/ — the install script below puts everything in place and the page appears in Cockpit's left-hand menu after a browser reload. No compilation, no services beyond a cron job.

Tailscale API ──> poller.py (cron, every 5 min) ──> SQLite (history + journal)
   (OAuth,                  │
 devices:read)              ├──> /var/lib/cockpit-tailscale/snapshot.json
                            │         │ cockpit.file().watch() — live updates
                            │         ▼
                            │    Cockpit plugin page ("Tailscale" in the menu)
                            │         │ "Yes, that was me" ──> acks.json
                            │         ▼                        (read by poller)
                            └──> alerts (ntfy / email / stdout) + monthly summary
  • poller.py — single-file Python, stdlib only. Fetches the device list, records online/offline observations, diffs against history to detect new and removed devices, keeps the event journal, and atomically writes a JSON snapshot.
  • cockpit-tailscale/ — the plugin: vanilla HTML/CSS/JS, no build step, no frameworks. Reads the snapshot through Cockpit's file API and re-renders live whenever the poller writes.

Why you might like it

  • Zero dependencies. Python stdlib + vanilla JS + Cockpit itself. One ~470-line file does all the backend work. Easy to audit.
  • Minimal blast radius. The Tailscale OAuth client needs only the devices:read scope — the plugin cannot change your tailnet. All administration stays in Tailscale's admin console, which the panel links to.
  • Cockpit-native. No web server, no new open port, no extra auth surface — the page rides on your Cockpit session.
  • Polling, not webhooks — by design. Webhooks would require a publicly reachable endpoint; many home servers deliberately have none.

Limitations

  • Polling latency. New-device detection lags up to one poll interval (default 5 min); "connected" is derived from lastSeen and can lag a few minutes in both directions.
  • History starts at install. The rarely-online alert needs ~1 day of observations to arm and ~30 days to be meaningful. The first poll is a baseline — devices already present never trigger "new device".
  • Household scale. Built for one tailnet with a handful of devices. Owner names come from a manual config mapping, because the API's user field is stripped on tagged devices. Nothing paginates.
  • Approval warnings only fire if device approval is enabled in your tailnet.
  • Young project. Used on one server so far; expect rough edges.

Installation

Requirements: Cockpit ≥ 264, Python 3.10+, a Tailscale account. Any Linux distribution that runs Cockpit should work (developed on Ubuntu Server 24.04).

git clone https://github.com/cgillinger/cockpit_tailscale.git
cd cockpit_tailscale

# 1. Create an OAuth client with ONLY the devices:read scope:
#    https://login.tailscale.com/admin/settings/oauth

# 2. Configure (config.json is gitignored — it holds your secret):
cp config.example.json config.json
nano config.json     # fill in oauth_client_id / oauth_client_secret

# 3. Install plugin + state dir + cron job, and run the first poll:
sudo bash scripts/install.sh

Reload Cockpit in your browser — Tailscale appears in the menu. Uninstall with sudo bash scripts/uninstall.sh (add --keep-data to keep the history database).

Configuration reference

Key Meaning
tailnet - means the OAuth client's own tailnet
owners device shortname → display name shown in the UI
device_types shortname → server/nas/phone/tablet/computer icon override
alerts.key_expiry_warn_days warn this many days before a device key expires (default 14)
alerts.rare_online_threshold_pct "rarely online" threshold (default 5 %)
alerts.stale_device_days cleanup-list threshold (default 60)
alerts.ntfy enabled, url, topic — use a long secret topic on ntfy.sh
alerts.email enabled plus SMTP host/port/user/password/from/to

Alerts always log to the poller log even when both channels are disabled. Cooldowns: new device fires once ever, device removed / approval / key expiry every 3 days, rarely-online at most once per day. The monthly summary goes out on the first poll of each month.

Push notifications on your phone (ntfy)

  1. Generate a long random topic name — it works like a password: echo "tailscale-$(openssl rand -hex 12)"
  2. Put it in config.json under alerts.ntfy with "enabled": true (and store it in your password manager).
  3. Install the ntfy app (iOS/Android), choose Subscribe to topic and enter the same topic name.
  4. Test it: curl -d "Test" "https://ntfy.sh/YOUR-TOPIC?title=Tailscale" — the message should appear on your phone within seconds.

No account or server needed — ntfy.sh relays messages for free. Anyone who knows the topic name can read your alerts, hence the long random name. For full privacy, self-host ntfy or use the token field with a paid plan.

State files

Everything lives in /var/lib/cockpit-tailscale/: tailscale.db (history + event journal), snapshot.json (what the panel renders), acks.json (your "Yes, that was me" confirmations) and poller.log. The acknowledgment button writes acks.json through Cockpit with your session's privileges — if you log in to Cockpit as a different user than the poller runs as, that user needs write access to the state directory.

Development

Outside Cockpit the page falls back to fetching data/snapshot.json, so the whole chain runs locally without credentials. The mock device file uses relative timestamps (-3d, +5d) resolved at load time:

python3 poller.py --config tests/dev_config.json --mock tests/sample_devices.json
cd cockpit-tailscale && python3 -m http.server 8000   # open http://localhost:8000

Non-goals

ACL/policy editing, subnet routes, exit nodes, per-device traffic stats, or replacing the Tailscale admin console. Read-only by design.

Part of a Cockpit plugin suite

I build a small family of Cockpit plugins for home servers, all dependency-light and made to be readable at a glance:

  • cockpit-temps — hardware temperature history with thresholds
  • cockpit-smart — S.M.A.R.T. disk health with trend tracking
  • cockpit-pcloud — pCloud storage quota and backup folder status
  • cockpit-tailscale — plain-language Tailscale network overview (this plugin)

Browse them all via the cockpit-plugin topic.

License

MIT

About

Plain-language Tailscale overview plugin for Cockpit: tailnet device status, actionable warnings and ntfy/email alerts. No jargon, read-only, stdlib-only Python.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages