DEV Community

Cover image for I kept seeing weird stuff on my VPS, so I built a tool to stop guessing
Carlos Salamanca
Carlos Salamanca

Posted on • Edited on

I kept seeing weird stuff on my VPS, so I built a tool to stop guessing

A few weeks ago I noticed some things I couldn't explain on one of my VPS instances — nothing catastrophic, just enough "that's not right" to make me nervous. I went looking for a lightweight tool to audit and harden a fresh Linux server and mostly found two things: hand-rolled bash scripts of wildly varying quality, or heavyweight compliance scanners built for enterprises running hundreds of servers, not someone with a couple of $5/mo boxes.

So I built vpsguard — a single Go binary that audits, hardens, and monitors a Linux VPS, with no dependencies beyond what's already on the box.

What it actually does

vpsguard audit, harden, and monitor catching a simulated SSH key intrusion

That's a real recording — audit finding real issues on a stock Ubuntu box, harden fixing them, and monitor catching a simulated attacker adding their own SSH key. No staged screenshots.

Twelve checks in total: SSH config, firewall (ufw and firewalld — more on that below), fail2ban, suspicious user accounts, SSH key hygiene, cron, pending updates, exposed database ports, Docker socket permissions, kernel/patch status, and (as a beta check) AWS EC2's IMDSv1 exposure — the same SSRF vector behind the Capital One breach.

Every harden remediation is idempotent — run it ten times, get the same end state, no duplicated config lines — and every config file it touches gets backed up first.

The part that actually matters: monitor

Audit is nice, but the thing that motivated this whole project was catching change — someone adding an SSH key you didn't add, a new UID-0 account, a new process running as root. vpsguard monitor snapshots server state and diffs it against the last run — that's the second half of the GIF above, where it catches the CRIT the instant the attacker's key gets added.

Wire it to cron (vpsguard install-cron) and point it at a Slack/Discord webhook or email, and you get pinged instead of finding out three weeks later when you happen to check a log.

Running it from CI/CD

If you'd rather bake this into a pipeline than remember to run it yourself, there's a GitHub Action that audits a whole fleet on a schedule:

name: VPS security audit
on:
  schedule:
    - cron: "0 6 * * *" # daily at 06:00 UTC
  workflow_dispatch:

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: salamancacm/vpsguard-action@v1
        with:
          hosts: web-1.example.com,db-1.example.com
          ssh-user: root
          ssh-key: ${{ secrets.VPS_SSH_KEY }}
          ssh-known-hosts: ${{ secrets.VPS_KNOWN_HOSTS }}
          fail-on: CRIT
Enter fullscreen mode Exit fullscreen mode

It installs vpsguard on the runner, connects to each host with your own SSH key (never vpsguard's — it doesn't handle credentials), runs the audit, and writes a summary table straight into the workflow's job summary. Set fail-on to CRIT, WARN, or NONE depending on whether you want a bad finding to actually fail the build or just get reported; it also exposes crit-count, warn-count, and unreachable-count as outputs if you want to branch on them yourself.

A few things I'm actually proud of

  • It's honest about what it hasn't verified. The AWS IMDSv1 check is tagged [BETA] in its own output, because I built and unit-tested it but never ran it against a real EC2 instance (that would mean spending real money on infra I didn't need). Rather than pretend otherwise, findings from unverified checks are marked, everywhere — table output and --json both.
  • Real bugs got caught by testing against real systems, not mocks. Validating the RHEL/dnf code path against an actual Rocky Linux container surfaced two genuine bugs: harden was trying to dnf install ufw (not in RHEL's repos at all) and the audit checks had no concept of firewalld — RHEL's actual default firewall — so a properly-secured RHEL box would've been reported as having no firewall at all. Fixed, and now covered by real Rocky Linux testing.
  • vpsguard fleet audits multiple hosts over SSH at once, using your own SSH config/agent — the same mechanism the GitHub Action above wraps.

Installing it

curl -fsSL https://raw.githubusercontent.com/salamancacm/vpsguard/main/install.sh | sh
Enter fullscreen mode Exit fullscreen mode

(Checksum-verified against the release, refuses to install on a mismatch — read the script first if piping curl to sh isn't your thing, which is fair.) Also available via go install, and Homebrew-on-Linux.

What's next

Kernel/patch checking on RHEL is solid; broader cloud-provider coverage (Security Group/VPC-level blindness is a real gap right now — vpsguard only sees the OS firewall, not AWS/GCP's network layer) is on the list. If you run a VPS and have five minutes, vpsguard audit is read-only and won't touch anything — I'd genuinely like to know what it gets wrong on your setup.

Repo's here: https://github.com/salamancacm/vpsguard — issues, PRs, and "this check is wrong" reports all welcome.

Top comments (0)