EST. 2021  •  OSLO, NORWAY

Skui.io

Homelab & Self-Hosting

← Back
homelab

The Scan You Stopped Reading

A vulnerability scan is a snapshot. I built the layer that keeps it honest — a fleet view that caught one gone stale for forty-nine days, and a queue that shows only the handful that are actually mine.

The Scan You Stopped Reading
Contents

Overview
#

A while back I built a little security dashboard — a scanner that walks every container on my servers, checks each image against the vulnerability database, and files the results as a report I can actually read. The interesting part, the time before, wasn’t the wall of red. It was learning to split it: the handful of findings that were mine to fix, and the long tail that belonged to whoever built the image. I fixed mine. I acknowledged theirs. The board went green. I felt good.

Then I closed the tab and didn’t open it again for two months.

That’s the part nobody warns you about. A scanner doesn’t fail loudly. It just quietly stops being true.

The scan that went quiet
#

When I finally came back, the reports all looked fine. Same green-ish boards, same acknowledged piles, the same numbers I half-remembered. Reassuring — right up until I noticed the only thing that actually mattered: the date.

One server’s report was from June. It was August. That scan was forty-nine days old, and I’d been treating it as the current state of the world the entire time.

A security report with no freshness on it isn’t a report. It’s a screenshot of a moment you’ve already left.

So the first thing I built on top of the dashboard wasn’t a cleverer scan. It was a fleet view — every server on one screen — and the loudest thing on it is when each one was last scanned. Fresh, aging, or stale. The board can be perfectly green and still be lying to you, and now the staleness says so out loud, before a single finding gets a word in.

Mine had exactly one server shouting stale. It had been shouting into a closed tab since June.

Re-running the world
#

The fix for a stale scan is boring: run it again. So I did — every box, fresh.

The numbers had moved. One stack that read fifteen findings in June came back with thirty-five. I hadn’t touched it. I hadn’t installed a thing. The supply chain just kept moving underneath it — new CVEs landing in images I was already running, at their pace, not mine.

That’s the second thing a one-time scan hides. It’s not only that it goes stale — it’s that stale in this direction is the default. Left alone, the number only ever climbs, because the vulnerabilities are being discovered in other people’s code long after you pulled it.

The queue that knows what’s mine
#

Thirty-five findings on one box. Eighteen on another. A few more scattered around the estate. On paper, a rough week.

But I’d already learned the lesson the time before: almost none of it is mine. So instead of a wall of red per server, I built the thing I actually wanted — one queue, across every server, that hides everything that isn’t my move.

It sorts every finding into three piles and only ever shows me two of them:

  • Mine. An image I build — bump a dependency, rebuild, gone.
  • A pull away. An image I run where the maintainer already shipped a newer one and I simply haven’t pulled it yet. Pure laziness tax.
  • Theirs. Everything else. Already on the latest they published, vulnerable until upstream rebuilds. Hidden — because there’s nothing to do but wait, and a to-do list full of waiting isn’t a to-do list.

Across fifty-odd findings on the whole estate, the queue showed me three. Two dependency bumps in my own repos, and one image I was a single pull behind on. Everything else — the postgres, the traefik, the long tail of software other people maintain — folded away into a count I don’t have to read.

Fifty-something down to three. Not by fixing forty-seven things. By admitting forty-seven of them were never mine to fix, and getting them off the screen.

The boring part
#

So I worked the three.

One was a critical on the Postgres image behind one of my apps. The remedy the queue handed me was the least dramatic sentence in all of security: a newer image is published — pull it and recreate. So I did. docker pull, recreate the container, thirty seconds, data untouched on its volume. The crit was gone — not because I’m clever, but because someone else had already done the hard part and I’d simply been a version behind.

That’s the shape of nearly every finding that’s genuinely yours. Not a heroic patch at 2 a.m. A bump, a pull, a rebuild. The work isn’t hard. The work is knowing which three, out of fifty, are worth standing up for.

The point
#

A vulnerability scanner is easy to install and easy to misunderstand. You run it once, you get a number, you fix what you can, and you feel done. But “done” was never a state you were in. The container you’re running today has more known vulnerabilities than it did yesterday, and you didn’t lift a finger to earn them.

So the dashboard isn’t the point. The scan isn’t the point. Two small disciplines are:

Keep it fresh. A finding is only worth reading next to the date it was found. The moment a scan stops re-running, it stops being security and starts being nostalgia. Make staleness the loudest thing on the screen, or it will quietly become the quietest.

Read only what’s yours. Most of the red isn’t your move, and pretending it is just trains you to ignore the whole board — right up until the one that is yours slips in and hides inside the noise. Split it. Surface the handful. Let the rest fold into a number you can glance at and trust.

The board is green again. It’ll be a little less green by next week, in code I didn’t write, at a pace I don’t set.

The trick was never keeping it green. It’s making sure that the day one of mine turns red, I’m still reading.