Web Server audit fixes

Logging & Monitoring: fixing all 5 checks

The logging and monitoring audit checks whether nginx or Apache records requests and errors, who can read those logs, whether they rotate, and whether anything inspects a request before the app does.

Web Server Log Permissions and Log Rotation look only for /var/log/nginx, /var/log/apache2 and /var/log/httpd, so a host that logs to a custom path, to journald or to stdout in a container reports two skips while logging is working perfectly well. The other three checks skip only when no web server is detected.

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

Transcribed from the web-server-security-logging-monitoring audit script.

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.

  • Access Logging

    Soon

    Checks whether web server access logging is enabled globally and per-block

    MediumNo root4 results
  • Error Logging

    Soon

    Checks whether error logging is configured at a production-appropriate level

    LowNo root3 results
  • Web Server Log Permissions

    Soon

    Checks that web server logs are not world-readable

    MediumNeeds root3 results
  • Web Server Log Rotation

    Soon

    Checks logrotate configuration and log file sizes for the web server

    MediumNeeds root3 results
  • Web Application Firewall

    Soon

    Detects request-filtering / WAF presence (ModSecurity, naxsi, CrowdSec, fail2ban, CDN WAF)

    MediumNo root3 results
Shared script behaviour

How the Logging & Monitoring audit reads your server

Every check in this audit runs after the same preamble, and several of its results only make sense once you know what that preamble could and could not see.

All web-server scripts share one preamble. It detects nginx, Apache (apache2/httpd, APROOT = /etc/apache2 or /etc/httpd), Caddy and lighttpd. NGT is nginx -T output (effective config with every include expanded) when root, otherwise a concatenation of nginx.conf, conf.d/*.conf, sites-enabled/*. APC is the concatenated Apache config tree (main file, ports.conf, conf.d, conf-enabled, sites-enabled, mods-enabled, conf.modules.d), comments stripped; APM is apachectl -M (root) or the mods-enabled/*.load names. WROOTS are all root/DocumentRoot values. A live probe curls https://127.0.0.1/, http://127.0.0.1/, http://127.0.0.1:8080/ with the first real server_name/ServerName as Host:. hdr NAME reads a response header; code URL returns the HTTP status. Caddy and lighttpd are detected but their configs are not parsed, so most checks on those hosts rely on the live probe only. Messages ending in "(no live response, config only)" or "(default vhost only - no server_name found to probe)" mean the probe could not be used.

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

Logging & Monitoring questions

Lines in the effective nginx config that read access_log off, counted separately from the lines that name a log file. Logging on globally plus one or more off blocks is the WARN, and it is deliberately not a failure, because switching logging off for static assets is a normal thing to do. Open each block the count refers to and confirm it covers assets rather than an application or login route, since a request that is never logged cannot be investigated later.
The edge test looks for the string cloudflare in the Server, CF-Ray and X-Sucuri-ID headers of the probe response, and the probe runs from loopback, which bypasses the edge entirely. Cloudflare is effectively the only edge product it can name, so Sucuri and other CDN WAFs are missed. Fail2ban and CrowdSec are detected but deliberately not counted as request filtering: they ban addresses from log patterns after a request has been served, which does nothing about a single injection.
Logrotate recreates log files using the create line in its own config, so the mode named there overrides any chmod you applied by hand at the next rotation. Edit /etc/logrotate.d/nginx or /etc/logrotate.d/apache2 to create 0640 www-data adm, or whichever group your log readers use, then fix the current files. RHEL packages are the common source of the original warning, since they ship logs at 644 where Debian ships 640.
Audit your fleet

Run all 5 Logging & Monitoring 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