VPS & Server audit fixes

Application Security: fixing all 5 checks

The application security audit covers what sits on top of the operating system: certificate expiry and renewal, end-of-life runtimes, and databases that answer without credentials.

Every check here is conditional on the thing it audits being present, which makes this the audit that skips most on a perfectly healthy host: no /etc/letsencrypt/live directory, no certbot binary, no recognised application stack, and no MySQL, PostgreSQL, MongoDB or Redis each produce a skip rather than a pass. TLS terminated at a CDN or load balancer, an ACME client that is not certbot, and a containerised database all land in that category, so the certificates and engines are fine and the audit simply could not see them.

5
checks in this audit
2
rated high severity
2
need root or sudo
~10s
automated run time

Transcribed from the vps-security-application-security audit script. For what the audit covers rather than how to fix it, see the Application Security checklist.

Every check

Find your finding

Listed in the order the audit runs them. Take the check name from the third field of the result line and open its page.

  • SSL Certificate Expiry

    Soon

    Checks Let's Encrypt certificates for upcoming expiry

    HighNeeds root3 results
  • SSL Certificate Auto-Renewal

    Soon

    Checks whether certbot renewal is automated via timer or cron

    MediumNo root3 results
  • End-of-Life Runtimes

    Soon

    Detects installed web/app/database stack versions for CVE review

    MediumNo root3 results
  • Database Account Exposure

    Soon

    Detects the database engines present, then checks each for accounts reachable without credentials or from any host

    HighNeeds root4 results
  • Host Resources

    Soon

    Reports virtualization platform, CPU, memory and disk allocation

    LowNo root2 results
Why checks skip

A SKIP is never a pass

A skipped check verified nothing at all. The most common cause is privilege: 2 of the 5 checks in this audit need root for a complete result. Before every run, each script works out which of four privilege modes it is in.

The four privilege modes an audit script can run in, and what each one means for checks that need root
ModeHow it is reachedWhat it means for your results
rootThe audit runs as UID 0.Every privileged branch runs directly. No check skips for lack of permission.
nopasssudo -n true succeeds, so the account has passwordless sudo.Privileged commands run through sudo -n.
passA sudo password was supplied in the CtrlOps audit settings and accepted.Privileged commands run through sudo -S.
noneNone of the above worked.CAN_ROOT=0. Every privileged branch returns SKIP with a "Need root" message rather than guessing.

If many checks skip with a “need root” message, run the audit as root, grant the audit account sudo (with or without NOPASSWD), or supply the sudo password in the CtrlOps audit settings.

FAQ

Application Security questions

It is a single check that probes every engine it detects on the host, and the verdict is the worst status among them, so one unauthenticated Redis reports FAIL while a clean MySQL beside it only appears in the "clean:" portion of the message. Read the message rather than the status, because it lists engines reachable without credentials, engines with over-broad host scope, clean engines and engines it could not audit as separate groups. A host with no engine at all reports SKIP and not PASS, deliberately, because nothing was measured.
No. It compares the detected versions against a fixed list, and that list is behind: PHP 5, 7 and 8.0, Node 8 through 16, MySQL 5.5 and 5.6, PostgreSQL 9 through 11, MongoDB 3 and 4, and Redis 2 through 5 are flagged, while newer end-of-life releases such as PHP 8.1, Node 18 and 20, and PostgreSQL 12 and 13 pass. Docker is printed in the version list but never evaluated at all. It also reads only what is on PATH, so psql --version reports the client rather than the server, and runtimes inside containers are invisible.
The threshold is 30 days, checked with openssl x509 -checkend against cert.pem or fullchain.pem in each /etc/letsencrypt/live lineage, and certbot itself only renews at 30 days on a timer that fires twice a day, so a certificate at 29.9 days can trip the check hours before renewal runs. Run certbot renew --dry-run first, since it exercises the whole renewal path without touching the live certificate or your rate limit, and fix what it reports, usually port 80 blocked for HTTP-01, expired DNS plugin credentials, or a moved webroot. Two cases fail permanently until you act: lineages for domains you no longer serve, which certbot delete --cert-name removes, and Let's Encrypt short-lived certificate profiles, which are always inside a 30-day window.
Audit your fleet

Run all 5 Application Security checks, in one click

CtrlOps runs this audit over your existing SSH connection - no agents, no scripts to manage. $7/user/month after a 1 month free trial - no credit card required.

Start instantly· No credit card· No sneaky autorenewals