ModSecurity With The OWASP Core Rule Set, Pinned

ModSecurity from EPEL in httpd with the OWASP Core Rule Set 4.30, its tarball pinned by the SHA-256 of a signature-checked copy: anomaly scoring at paranoia 1, each change checked in a copy of the httpd tree first, loopback until you open it. The live test sends a SQL injection, a script tag, a scanner and a traversal; each is refused by its own rule. Original role, live-tested on Rocky Linux 10.

ansibleSecurity & Secrets

Verification

Live-tested

Really deployed to a container sandbox, proven idempotent (a second run changes nothing), verified against the role’s assertions, then torn down.

Conformance

  • Static validation (yamllint · ansible-lint)

Provenance

  • SHA-256 checksum
  • Signature (pending)

Functional

  • Live-tested - applied, verified, destroyed

Last verified 2026-10-03 · podman 4.9.3 · ansible 2.21.4 · how we verify

Cite it in your README

badge · attribution

Paste this beside the module in the repository that uses it. The badge is rendered from this artifact's verification record, so it reads live-tested because the record says so, and the link lands on this page.

README.md, GitLab, Gitea
[![IaC Bazaar: live-tested](https://www.iac-bazaar.com/api/artifacts/ansible-modsecurity-crs/badge)](https://www.iac-bazaar.com/catalog/ansible-modsecurity-crs?utm_source=syndication&utm_medium=readme&utm_campaign=artifact)

Ansible role 1.0.0, live-tested on IaC Bazaar: [ModSecurity With The OWASP Core Rule Set, Pinned](https://www.iac-bazaar.com/catalog/ansible-modsecurity-crs?utm_source=syndication&utm_medium=readme&utm_campaign=artifact)

```yaml
# ModSecurity With The OWASP Core Rule Set, Pinned: https://www.iac-bazaar.com/catalog/ansible-modsecurity-crs (download from your IaC Bazaar account)
```

Preview:IaC Bazaar: live-tested

Documentation

modsecurity-crs

ModSecurity from EPEL in Apache httpd, with the OWASP Core Rule Set 4.30.0 from the project's own release: the rules' tarball pinned by the SHA-256 of the copy whose signature checked out, the engine refusing by CRS's anomaly score at paranoia level 1, every change checked inside a copy of the whole httpd configuration before it lands, and httpd on loopback until you open it. Original role for EL 10, live-tested with podman on Rocky Linux 10.

Two sources. EL 10 packages the engine (mod_security 2.9.9, in EPEL) but not the rules: there is no Core Rule Set package for it (measured). The role installs httpd, mod_security and tar by name, and the rules from their GitHub release. CRS publishes no checksum file for that tarball, only a PGP signature, so the signature was checked once, by hand, against the key coreruleset.org publishes (36006F0E 0BA16783 21588211 38EEACA1 AB8A6E72: a good signature, and a bad one for a tampered copy), and the SHA-256 of that copy is pinned beside the version. get_url refuses anything else. A new version is a new pair, checked the same way.

The distribution's httpd, a rule file of ours. EPEL's mod_security.conf turns the engine On and then includes modsecurity.d/*.conf; the role's file there sets the engine mode, the paranoia level and the anomaly thresholds, includes the rules, and switches off the rule IDs you name. httpd cannot check one file alone, so each change is checked by httpd -t inside a copy of the whole tree with that file replaced - which refuses an invalid engine value, a missing rules directory and a duplicate rule ID (measured) - and applied with a graceful reload. The package's listener is *:80; the role narrows it to 127.0.0.1:80, and that one change restarts httpd, because a graceful reload after a Listen change left httpd failed (measured). With this catalogue's httpd-tls role on the same host, set modsecurity_crs_manage_listen: false and let it own the listeners: the rules apply to every site httpd serves.

Proven by attacks. The live test asks for a page plainly (200) and with a SQL injection, a script tag, a scanner's user agent and a path traversal: each is refused with 403 by the anomaly score (rule 949110) and traced in httpd's error log to its own rule (942100, 941100, 913100, 930100). A double-encoded quote, which only paranoia level 2 refuses (920230), is served, so level 1 is the one in force, and the audit log names the rules' version. Measured on the way: DetectionOnly serves every one of these, an inbound threshold of 1000 serves them all, and removing rule 942100 serves that injection, so each switch does what it says. EPEL's file leaves response bodies uninspected (SecResponseBodyAccess Off) and this role keeps it so: the outbound threshold scores headers, not pages.

License

Commercial - IaC Bazaar EULA. (c) IaC Bazaar.

Usage code & full reference need an account

The complete copy-paste usage, the full input/output reference, and operational notes are free with an account - shown here and bundled in the download. Sign in and this section fills in.

  • Variables
  • Test

Related modules

Live-tested

ansible-lynis

Lynis from EPEL on a systemd timer, with the units the package keeps only in its docs, your skipped tests in custom.prf, and a minimum hardening index below which the audit unit fails, so a falling score shows as a failed unit. The live test runs the audit, reads the index and the tests that ran, and finds the timer enabled. Original role, live-tested on Rocky Linux 10.

View module
Live-tested

ansible-suricata

Suricata from EPEL watching the default-route interface as the suricata user, ET Open fetched at install and refreshed daily, plus a canary rule that proves the sensor is alive. A root-run check left root-owned logs and a daemon recording nothing; the role prevents it. The live test raises the canary alert while a second lookup is logged, not alerted. Original role, live-tested on Rocky Linux 10.

View module
Live-tested

ansible-aide

AIDE from AppStream on EL 10: a watched tree, a baseline database built once, and the oneshot unit and timer the package does not ship. The live test passes on an unchanged host, FAILS when a file appears inside a watched path and names it, ignores one inside an excluded path, and passes again once the change is gone. Original role, live-tested on Rocky Linux 10.

View module
Live-tested

ansible-389-ds

389 Directory Server from AppStream. A fresh instance listens on every interface and lets anonymous clients read the tree; this role binds both listeners to loopback and limits anonymous clients to the rootDSE. The live test adds a user who binds, sees a wrong password and an anonymous search refused, and reads the LDAPS certificate. Original role, live-tested on Rocky Linux 10.

View module
Live-tested

ansible-krb5-kdc

An MIT Kerberos KDC: the realm created once with a stash, kadmind behind an ACL, a default policy that locks an account after five wrong passwords, every listener on loopback. The KDC starts on a broken config without a word, so the live test proves each setting by behaviour: the ticket lifetime, the refusals, the lockout. Original role, live-tested on Rocky Linux 10.

View module
Live-tested

ansible-authelia

Authelia from the upstream release (sha256-verified) as a hardened system service on loopback with file users and sqlite; its secrets are generated once on the host and reach the service as AUTHELIA_*_FILE variables. The live test hashes a password with Authelia's hasher, logs in, sees a wrong password refused and an anonymous visitor sent to the portal. Original role, live-tested on Rocky 10.

View module