Capture intruder photos on failed authentication attempts — local login,
sudo, GDM lock screen, SSH — and forward them to a Telegram bot, with
timestamp (IST), hostname, Wi-Fi SSID, IP address, and PAM context (which
user / which service / which TTY).
At screenguard configure time the tool scans /dev/video* and asks which
cameras to use. Pick one, several, or all of them; every selected camera is
captured on each event and the photos are bundled into a single Telegram
message.
The whole thing runs from a self-contained Python venv at /opt/screenguard/
so it doesn't touch the system Python (PEP 668 friendly).
- PAM hook via
pam_exec.soplaced on the failure path of/etc/pam.d/common-auth. Successful logins never trigger a capture. - Interactive camera picker at install: any subset of
/dev/video*works, including an IR sensor (the gray-format ffmpeg fallback is auto-applied when the camera kind is detected asir). - Captures run in series via ffmpeg (~1 s per camera). Two cameras → ~2 s end-to-end.
- Photos and metadata are written to
/var/lib/screenguard/captures/. After a successful Telegram upload, photos are deleted and onlymeta.jsonis kept. - Offline-safe: if Telegram can't be reached, the bundle is retained and
screenguard flushre-uploads everything later. - Trigger-side throttle (5 s default) so a typo-heavy
sudosession doesn't spam the bot. - Idempotent installer + reversible uninstaller (PAM config is backed up before patching and restored on uninstall).
- Linux with v4l2 (
/dev/video*devices) - Python 3.11+
python3-venv,ffmpeg,v4l-utils— installer apt-gets these for you- A Telegram bot and your own chat ID (
@BotFather+getUpdates)
git clone https://github.com/legitcoconut/screenguard.git
cd screenguard
sudo ./install.sh
What install.sh does:
| Target | Purpose |
|---|---|
/opt/screenguard/venv/ |
Python venv with the package + requests |
/opt/screenguard/{install,uninstall}.sh, /opt/screenguard/scripts/ |
Kept for later uninstall |
/usr/local/bin/screenguard |
Shell wrapper that execs the venv's entry point |
/usr/local/sbin/screenguard-trigger |
Fast PAM hook (backgrounded capture) |
/usr/local/share/man/man1/screenguard.1.gz |
man screenguard |
/etc/screenguard/ |
Empty dir for config.toml (root 0700) |
/var/lib/screenguard/captures/ |
Per-event bundles (root 0700) |
/etc/pam.d/common-auth |
Patched; original kept at common-auth.screenguard.bak |
Then configure and smoke-test:
sudo screenguard configure # bot token, chat id, pick cameras (verifies token via getMe)
sudo screenguard test # captures from all selected cameras + posts to your bot
man screenguard # full docs
configure shows a numbered list of detected cameras:
Available cameras:
[1] /dev/video0 MJPG,YUYV kind=rgb
[2] /dev/video1 — kind=unknown
[3] /dev/video2 GREY kind=ir
[4] /dev/video3 — kind=unknown
Select camera(s) — e.g. "1", "1,3", "1-3", "all":
Pick any subset. Every selected camera fires on each event; the photos go out as a single Telegram media-group message.
In a fresh terminal:
sudo -k # clear sudo's cached auth
sudo true # then enter a WRONG password once
Within ~5 s a photo bundle should arrive in your bot chat with caption like:
screenguard ALERT: failed auth attempt
time: 01:59 AM IST, 21 May 2026
host: legitbook
wifi: VITC-HOS2-4
ip: 172.16.0.2
user: intern
svc: sudo
tty: /dev/pts/3
(tty and rhost may be blank for some services.)
screenguard configure # interactive setup
screenguard test # capture both cameras, post to Telegram
screenguard list # show all /dev/video* devices + kind
screenguard capture [...] # one capture+upload cycle (the trigger calls this)
screenguard flush # retry uploads that failed earlier (offline)
screenguard status # version, install kind, paths, queue length
screenguard help # detailed help
screenguard uninstall # delegates to /opt/screenguard/uninstall.sh under sudo
screenguard --version
sudo screenguard uninstall # keeps /etc/screenguard and /var/lib/screenguard
sudo screenguard uninstall --purge # also removes config + captures
This restores /etc/pam.d/common-auth from the backup taken at install time
(falling back to a sentinel-strip if the backup was deleted), removes all
installed files, and runs mandb to refresh the man index.
If you just want to iterate without touching the system:
sudo apt install python3-venv
./setup.sh
source .venv/bin/activate
screenguard configure
screenguard test
The CLI auto-detects whether /etc/screenguard/ exists and routes config +
data accordingly (/etc/screenguard/ + /var/lib/screenguard/ for system,
~/.config/screenguard/ + ~/.local/share/screenguard/ for dev).
install.sh runs scripts/pam-patch.py install, which:
- Saves a copy of
/etc/pam.d/common-authtocommon-auth.screenguard.bak. - Bumps the
success=Ncount on eachpam_unix.so/pam_sss.soline by 1, so a successful authentication still jumps pastpam_deny.sowithout touching our new module. - Inserts a sentinel-marked block immediately before
pam_deny.so:
# === BEGIN screenguard (managed; do not edit) ===
auth [default=ignore] pam_exec.so quiet seteuid /usr/local/sbin/screenguard-trigger
# === END screenguard ===
Because the line uses [default=ignore] and the trigger always exits 0, this
hook can never block or delay authentication, even if the camera, network, or
Telegram is broken.
The trigger script forks a detached screenguard capture and returns
immediately (sub-millisecond), so the auth path is not stalled by the ~1–2 s
ffmpeg + upload work.
- CLI:
configure,list,test,capture,flush,status,help,uninstall - Dual capture (RGB + IR) via ffmpeg
- Telegram upload with hostname / Wi-Fi / IP / IST time / PAM context
- Offline queue +
flush - System install via venv, man page, idempotent PAM patch + reversible uninstall
- NetworkManager dispatcher to auto-
flushwhen Wi-Fi comes back - IR illuminator activation (laptop-specific; see
howdyfor prior art) - Rate limit / auto-prune of old bundles in
/var/lib/screenguard/captures/