coreruleset / coreruleset/modsecurity-crs-docker
feat: install CRS plugins at container start from the plugin registry
Nobody has claimed this yet.
- Dominant language
- Dockerfile
- Stars
- 448
- Forks
- 88
- Avg merge
- 6m
- Merged PRs (30d)
- 2
Description
What
Let a container install CRS plugins at start from a list given in the environment, resolving names against the plugin registry:
docker run -e CRS_PLUGINS="fake-bot,wordpress-rule-exclusions@v1.2.0" owasp/modsecurity-crs:nginx
Each entry is resolved to a repository and a release, unpacked into /opt/owasp-crs/plugins, and picked up by the activation that already exists.
Why
Today the only ways to run a plugin are to build a derived image or to mount the plugin files into /opt/owasp-crs/plugins yourself. Both are fine for a fixed deployment and awkward for anyone trying a plugin out, and neither is discoverable from the README.
What is already in place
src/opt/modsecurity/activate-plugins.shuncomments the plugin includes insetup.confwhen it finds*-config.conf,*-before.confor*-after.confunder/opt/owasp-crs/plugins, and comments them back out when it does not. Nothing new is needed to enable a plugin once its files are on disk.- It runs as
/docker-entrypoint.d/94-activate-plugins.shon nginx and fromdocker-entrypoint.shon apache, so a download step has to run before it. CRS_DISABLE_PLUGINSalready exists as the off switch./opt/owasp-crsis owned by the runtime user (httpd/nginx), so creatingplugins/at start works. Note the directory does not exist in the published images — the minimal CRS tarball does not carry it.
What this depends on
Name resolution cannot be conventional. coreruleset/<name>-plugin holds for only 15 of the 30 registry rows. The rest deviate:
dokuwiki-rule-exclusions coreruleset/dokuwki-rule-exclusions-plugin (typo in the link text)
dos-protection-modsecurity coreruleset/dos-protection-plugin-modsecurity
roundcube-rule-exclusions-plugin EsadCetiner/roundcube-rule-exclusions-plugin
sogo-rule-exclusions-plugin EsadCetiner/sogo-rule-exclusions-plugin
iredadmin-rule-exclusions-plugin EsadCetiner/iredadmin-rule-exclusions-plugin
plausible-rule-exclusions-plugin EsadCetiner/plausible-rule-exclusions-plugin
wordpress-hardening-plugin eilandert/wordpress-hardening-plugin
vimbadmin-crs-plugin eilandert/vimbadmin-crs-plugin
netnea-crs-upgrading-plugin netnea/netnea-crs-upgrading-plugin
machine-learning-integration-plugin coreruleset/machine-learning-integration-plugin
performance-plugin coreruleset/performance-plugin
database-logging-plugin coreruleset/database-logging-plugin
referer-hardening-plugin coreruleset/referer-hardening-plugin
false-positive-report-plugin coreruleset/false-positive-report-plugin
traffic-observation-plugin coreruleset/traffic-observation-plugin
Some names already end in -plugin, some do not; four repositories are owned by third parties. So this feature needs the machine-readable index proposed in coreruleset/plugin-registry#21 (registry.json at a stable URL) rather than string manipulation, and it should not be built before that exists. Parsing the README table at runtime would be worse than the status quo.
coreruleset/plugin-registry#37 is the other half: the per-plugin plugin.yaml describes a plugin's configuration variables, which is what would let us map container environment variables onto plugin settings instead of requiring a mounted *-config.conf.
Versions have to be pinned, and not every plugin has a release. Sampling four:
| plugin | releases |
|---|---|
fake-bot |
2, latest v1.1.0 |
wordpress-rule-exclusions |
4, latest v1.2.0 |
roundcube-rule-exclusions |
4, latest v1.0.4 |
antivirus |
none |
Installing from a moving branch would mean the rule set changes under a container restart, so a plugin without a release needs a decided answer: refuse it, or require an explicit commit SHA.
Trust
This is the part that needs a decision before any code.
The images verify the CRS tarball with gpg --verify against the CRS signing key. Downloading plugins at start would add rules to the same engine without that check — plugin releases carry no signatures today, and coreruleset/plugin-registry#21 puts signing out of scope for its first iteration. Four registry entries are third-party repositories, so "install by name" would mean fetching and loading code from outside the project on the strength of a name in a table.
Suggested posture, to be argued:
- off unless
CRS_PLUGINSis set, and never a default - restrict to registry entries marked
type: officialunless a second variable explicitly opts into third-party plugins - require a pinned version, with an optional
sha256per entry that is verified when present - keep the build-time route (
docker buildwith a plugin list, producing an image with the plugins baked in and scanned) documented as the recommended production path, with the runtime variable positioned as a convenience for trying things out - fail closed: if an entry cannot be resolved, verified or unpacked, the container does not start rather than starting with a partial rule set
Runtime constraints worth stating in the design
- egress at container start, which some deployments do not allow — the feature has to be entirely inert when the variable is unset
- air-gapped and offline installs, where mounting the files stays the only option
- read-only root filesystem variants (see #172), where writing to
/opt/owasp-crs/pluginsis not possible - startup latency and the failure mode when the registry or a release is unreachable
- rule ID collisions between plugins, which the registry ranges make detectable
Acceptance criteria
-
CRS_PLUGINSaccepting a comma-separated list ofnameorname@version, documented in the README with the trust caveats - resolution through the registry index from coreruleset/plugin-registry#21, no name-to-repository guessing
- pinned versions only, with the no-release case decided and documented
- the download step runs before plugin activation, so the existing
activate-plugins.shenables what was installed - inert when unset: no network access, no directory creation, identical behaviour to today
- fail closed on resolution, verification or unpack errors
- verification of downloaded artifacts, or an explicit written statement of what is not verified and why
- a test that installs a plugin and asserts its rules are loaded, plus one asserting the unset case changes nothing
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with src/opt/modsecurity/activate-plugins.sh and the nginx and Apache entrypoint paths, then review the registry and plugin.yaml dependencies named in the issue. The work is not ready to implement until version, trust, verification, and no-release decisions are settled. Done requires the documented acceptance criteria, including pre-activation installation, fail-closed behavior, inert unset behavior, and installation and unset-case tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, dockerfile, shell
- Domain
- devops, infrastructure, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 28/100