Web Server audit fixes

Configuration Hardening: fixing all 6 checks

Configuration hardening inspects the permissions on your config files, private keys and document roots, and the caps on request size, timeouts and rate that keep one client from exhausting the server.

The two permission checks skip on missing evidence rather than missing privilege: config permissions skips when neither /etc/nginx nor an Apache config root exists, and web root ownership skips when the config the audit could read carried no root or DocumentRoot line. Risky Apache Modules skips on every nginx-only host by design, and again when Apache is installed but no module list could be built.

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

Transcribed from the web-server-security-configuration-hardening 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.

  • Web Server Config Permissions

    Soon

    Checks the web server config directory for world-writable config files and world-readable private keys

    HighNeeds root3 results
  • Web Root Ownership

    Soon

    Checks whether document roots are world-writable or owned by the web server worker user

    HighNeeds root4 results
  • Request Size Limits

    Soon

    Checks whether a request body size cap is set via client_max_body_size or LimitRequestBody

    MediumNo root3 results
  • Connection Timeouts

    Soon

    Checks whether request timeouts are configured to blunt slow-request attacks

    LowNo root3 results
  • Rate Limiting

    Soon

    Checks whether rate or connection limiting is configured for login and API paths

    MediumNo root3 results
  • Risky Apache Modules

    Soon

    Checks the enabled Apache module list for optional modules that widen the attack surface

    LowNo root3 results
Shared script behaviour

How the Configuration Hardening 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 6 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

Configuration Hardening questions

They do not skip, which is the trap. The find runs as the audit account with errors discarded, so a directory it cannot traverse simply returns nothing and the check reports PASS. /etc/ssl/private and /etc/letsencrypt are the usual blind spots, which means a world-readable private key can pass on an unprivileged run. Re-run as root or with sudo before you trust a PASS on either of these two.
Because it only reads the server itself. For nginx it counts occurrences of limit_req or limit_conn anywhere in the config text; for Apache it looks in the loaded module list for evasive, ratelimit or qos. A limit enforced at a CDN, a load balancer or inside the application is invisible to it, so the WARN is right about the server and wrong about your stack. The same grep is loose in the other direction: a limit_req_zone that no location ever applies is enough for a PASS.
Both reject real traffic if you pick a number before you look at the traffic. Size the body cap from your largest genuine upload and set it on the upload location rather than globally, or users get a 413. Put rate limits on login and API paths first, with a burst allowance sized to normal use. Validate with nginx -t or apachectl configtest and reload rather than restart, so the running config keeps serving if the new one is wrong.
Audit your fleet

Run all 6 Configuration Hardening 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