Check apache-httpd-security¶
Overview¶
Checks the local security posture of an Apache httpd installation: which modules the server has loaded, which account its worker processes run under, and the ownership and permissions of the configuration files, the process ID file, the lock file directory and the core dump directory. Every value is read back from the running installation rather than from a configuration file, so a path left at a compiled-in default and a directive overridden further down the configuration are both reported as they actually take effect. Each finding maps to a copy-pasteable recommendation. Alerts when a module widens the attack surface without being needed, when the worker account is not a dedicated unprivileged system account, or when a file or directory the server relies on can be modified by somebody other than root. Individual checks can be excluded with --ignore. Requires root or sudo.
The checks follow the "Minimize Apache Modules" and "Principles, Permissions, and Ownership" chapters of the CIS Apache HTTP Server 2.4 Benchmark.
Important Notes:
- The check is part of the Apache httpd and Apache apache2 Service Sets, where it runs through the
-sudocheck command. A host that has not deployed the sudoers file makes the service report UNKNOWN until it has. - The shipped service template excludes the status module check (
--ignore=^Status module), because the same Service Sets deploy the status check, which readsmod_status. Drop that default on a host where the module is not wanted. - Requires root or sudo. Both
httpdandapachectlrefuse to parse the configuration as an unprivileged account, because they create the runtime directory while doing so. - The check re-parses the configuration from disk. A change that has been written but not reloaded is therefore reported as if it were already in force. The worker account is the exception: it is additionally compared against the accounts the running processes actually use.
- The module checks report what CIS recommends disabling. A module a site knowingly needs is excluded with
--ignore, for example--ignore=^Status module$on a host whose monitoring readsmod_status. The check still lists everything else. - A check that finds more than three modules names the first three and counts the rest. The
Detailcolumn carries the full list. - The ownership and permission checks cover the configuration files the server itself reports reading, plus the runtime directories it resolves. They do not walk the whole installation tree, which would be both slow and unbounded.
- The benchmark treats the core dump, lock, process ID and scoreboard file checks as ones an auditor confirms rather than as fully automatable. This check does the legwork and reports what it found; the judgement stays with the operator.
- A scoreboard file that is not configured at all is compliant, and reported as such.
Data Collection¶
Three invocations of the Apache control binary per run, all of which only read: -M for the loaded modules, -S for the resolved runtime settings (server root, document root, process ID file, mutex mechanisms and directories, user and group with their numeric ids), and -t -D DUMP_INCLUDES for the list of configuration files. The paths that come out of these are then inspected with stat. UID_MIN is read from /etc/login.defs, and the running worker processes are enumerated via psutil. The core dump directory and the scoreboard file are the only two values the runtime dump does not resolve; they are read from the configuration files the server itself named. Nothing is stored between runs.
The binary is probed automatically: httpd first, which covers the Red Hat and SUSE families, then apachectl, which is the entry point on Debian and Ubuntu because apache2 refuses to parse its own configuration without the variables from /etc/apache2/envvars. Use --command to point at a binary in a non-standard location.
Fact Sheet¶
| Fact | Value |
|---|---|
| Check Plugin Download | https://github.com/Linuxfabrik/monitoring-plugins/tree/main/check-plugins/apache-httpd-security |
| Nagios/Icinga Check Name | check_apache_httpd_security |
| Check Interval Recommendation | Every day |
| Can be called without parameters | Yes |
| Runs on | Linux |
| Compiled for Windows | No |
| Requirements | command-line tool httpd or apachectl; root or sudo |
| 3rd Party Python modules | psutil |
| Perfdata compatible with Prometheus | Yes |
Help¶
usage: apache-httpd-security [-h] [-V] [--always-ok] [--brief]
[--command COMMAND] [--ignore IGNORE]
[--match MATCH]
[--no-match-severity {ok,warn,crit,unknown}]
[--no-perfdata] [--severity {warn,crit}]
[--timeout TIMEOUT]
Checks the local security posture of an Apache httpd installation: which
modules the server has loaded, which account its worker processes run under,
and the ownership and permissions of the configuration files, the process ID
file, the lock file directory and the core dump directory. Every value is read
back from the running installation rather than from a configuration file, so a
path left at a compiled-in default and a directive overridden further down the
configuration are both reported as they actually take effect. Each finding
maps to a copy-pasteable recommendation. Alerts when a module widens the
attack surface without being needed, when the worker account is not a
dedicated unprivileged system account, or when a file or directory the server
relies on can be modified by somebody other than root. Individual checks can
be excluded with --ignore. Requires root or sudo.
options:
-h, --help show this help message and exit
-V, --version show program's version number and exit
--always-ok Always returns OK.
--brief Hide the rows that are within the thresholds and show
only those in a WARN or CRIT state. Perfdata and
alerting are unaffected: every item still emits
performance data and still drives the overall check
state, so this is safe to leave on.
--command COMMAND Path to the Apache httpd control binary. Probed
automatically if not given: `httpd` first, then
`apachectl`. Example: `--command=/usr/sbin/httpd`
--ignore IGNORE Any check whose name matches this Python regex will be
dropped from the report. Use it for a finding the site
knowingly accepts, for example the status module on a
host whose monitoring reads it. Case-sensitive by
default; use `(?i)` for case-insensitive matching. Can
be specified multiple times. Example:
`--ignore=^Status module$`
--match MATCH Only report the checks whose name matches this Python
regex. Filter by this Python regular expression. Case-
sensitive by default; use `(?i)` for case-insensitive
matching. Can be specified multiple times. If both
`--match` and `--ignore` are given, an item must match
`--match` AND not match `--ignore` to be reported
(include first, exclude second). Examples:
`(?i)example` to match "example" regardless of case.
`^(?!.*example).*$` to match any string except
"example" (negative lookahead).
--no-match-severity {ok,warn,crit,unknown}
State to report when no item matches the filters and
nothing is checked. Default: ok
--no-perfdata Suppress the performance data section from the output.
The status message and the exit code are unaffected,
so alerting keeps working while trending data is
dropped.
--severity {warn,crit}
State to report for a failed check. One of `warn` or
`crit`. Default: warn
--timeout TIMEOUT Network timeout in seconds. Default: 8 (seconds)
Documentation:
https://linuxfabrik.github.io/monitoring-plugins/check-plugins/apache-httpd-security/
Usage Examples¶
A stock installation, which loads every module the distribution packages:
sudo ./apache-httpd-security --brief
7 of 16 checks failed.
Recommendations:
* WebDAV modules: Comment out the `LoadModule` lines for `mod_dav`, `mod_dav_fs` and `mod_dav_lock`.
* Status module: Comment out the `LoadModule` line for `mod_status`, or exclude this check with `--ignore=^Status module$` if the module feeds your monitoring.
* Autoindex module: Comment out the `LoadModule` line for `mod_autoindex` so a directory without an index file stops listing its content.
* Proxy modules: Comment out the `LoadModule` lines for the `mod_proxy` family unless the host really is a reverse proxy.
* User directories module: Comment out the `LoadModule` line for `mod_userdir` so home directories stop being served.
* Info module: Comment out the `LoadModule` line for `mod_info`; it exposes the whole configuration, including credentials of other modules.
* Basic and digest auth: Comment out the `LoadModule` lines for `mod_auth_basic` and `mod_auth_digest`.
Check ! Result ! State
------------------------+---------------------------------------------------------------------------+----------
WebDAV modules ! dav_fs_module, dav_lock_module, dav_module ! [WARNING]
Status module ! status_module ! [WARNING]
Autoindex module ! autoindex_module ! [WARNING]
Proxy modules ! proxy_ajp_module, proxy_balancer_module, proxy_connect_module and 11 more ! [WARNING]
User directories module ! userdir_module ! [WARNING]
Info module ! info_module ! [WARNING]
Basic and digest auth ! auth_basic_module, auth_digest_module ! [WARNING]
The same host, ignoring the module a site knowingly runs:
sudo ./apache-httpd-security --brief --ignore=^Status
6 of 15 checks failed.
A host whose worker account and runtime paths are wrong. The Detail column carries the reasoning:
sudo ./apache-httpd-security --brief
11 of 16 checks failed.
Check ! Result ! Detail ! State
--------------------+---------------------------+-----------------------------------------------------------------------------+----------
Worker account ! nobody:nobody (uid 65534) ! `nobody` is shared with other daemons; uid 65534 is not below UID_MIN 1000. ! [WARNING]
Config other write ! 1 of 16 files ! /etc/httpd/conf.d/zz-bad.conf (0646) ! [WARNING]
Core dump directory ! /var/www/html ! inside the document root. ! [WARNING]
Lock file ! default in /tmp ! writable beyond its owner (1777). ! [WARNING]
A hardened host:
sudo ./apache-httpd-security
Everything is ok. All 16 checks passed.
Check ! Result ! State
------------------------+------------------------------+------
Log config module ! log_config_module loaded ! [OK]
WebDAV modules ! WebDAV modules not loaded ! [OK]
...
States¶
- Returns OK if every check that could be carried out passed.
- Returns WARN (or CRIT with
--severity=crit) if at least one check failed:- a module CIS recommends disabling is loaded, or the log config module is missing,
- the worker account runs as root, is an account shared with other daemons (
daemon,nfsnobody,nobody,nogroup), has a uid at or aboveUID_MIN, or a running process uses an account other than the configured one, - a configuration file is not owned by
root:root, or is writable by other, - the core dump directory, the lock file directory, the process ID file directory or the scoreboard file directory sits inside the document root, is not owned by root, or is writable beyond its owner.
- Returns UNKNOWN if neither
httpdnorapachectlis found, if the binary given via--commanddoes not exist, or if the configuration does not parse, in which case the binary produces no output at all. - A check that cannot be carried out, because a directory could not be read for example, is reported as not evaluated. It does not count towards the result and does not drive the state.
- If
--matchand--ignorebetween them exclude every check, the plugin prints "Nothing checked." and returns the state given by--no-match-severity(OK by default). --always-okmasks a WARN or CRIT as OK.
Perfdata / Metrics¶
| Name | Type | Description |
|---|---|---|
| apache_httpd_checks_evaluated | Number | Number of checks that could be carried out on this run. |
| apache_httpd_checks_failed | Number | Number of checks that failed. |
| apache_httpd_modules_loaded | Number | Total number of modules the server has loaded. |
Troubleshooting¶
returned nothing ... The server configuration does not parse.¶
The control binary exits without printing anything when the configuration test fails. Run httpd -t (or apachectl -t on Debian and Ubuntu) by hand to see the syntax error, fix it, and the check reports again. Note that the server keeps running on its last good configuration while this is the case, so the check failing here says nothing about availability.
Apache httpd does not seem to be installed¶
Neither httpd nor apachectl was found in the PATH of the account running the check. On a host where the binary lives outside the usual locations, point at it with --command=/path/to/httpd.
Every module check fires on a fresh installation¶
That is expected on the Red Hat family, which ships a LoadModule line for every module it packages. Work through the recommendations, or exclude the ones the host knowingly needs with --ignore. The Debian family ships a much smaller default set and starts from a better position.
The status module check fires on a host you monitor¶
mod_status is what the apache-httpd-status check reads, so a host running that check will keep failing this one. Exclude it deliberately with --ignore=^Status module$ and keep the rest of the report.
The worker account check fires although the account is correct¶
Compare the numeric id with UID_MIN from /etc/login.defs. An account created as a regular user rather than as a system account gets an id at or above that boundary, which is what the check reports. Recreate it with useradd --system, or accept the finding deliberately with --ignore.
Credits, License¶
- Authors: Linuxfabrik GmbH, Zurich
- License: The Unlicense, see LICENSE file.