You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Installing a CRS plugin today is a manual copy. You find the plugin in the registry table in the README, clone or download the repository, copy the files from its plugins/ directory into the CRS plugins/ directory, and edit the config. Nothing records what you installed or at which version, so there is no way to tell later whether a plugin is current, which release it came from, or whether the files on disk still match what upstream published.
Once the registry is machine-readable (coreruleset/plugin-registry#21) the toolchain can do this from a source it can resolve against.
Proposal
crs-toolchain plugin install <name>
Resolves <name> against the published registry index, resolves a release tag, downloads that release, verifies it, and copies the plugin's files into the plugins directory.
# into <CRS_ROOT>/plugins, CRS root from the global -d flag
crs-toolchain -d /path/to/coreruleset plugin install fake-bot
# explicit target directory
crs-toolchain plugin install fake-bot --plugins-dir /etc/crs/plugins
# pin a version instead of taking the newest release
crs-toolchain plugin install fake-bot --version v1.1.0
Behaviour
Resolve the plugin by name against registry.json from plugin-registry#21. Unknown name is an error that lists near matches. The registry entry supplies the repository, the rule ID range, type, status, and the license.
Resolve a version to a concrete release tag, never a branch. --version pins it; otherwise the newest release. The resolved tag is what gets reported and recorded, not latest.
Download the release from the resolved tag.
Verify it (see below).
Copy the contents of the plugin's plugins/ directory into the target directory. Refuse to overwrite existing files unless --force, so a partial or hand-modified install is never silently clobbered.
Record what was installed — name, resolved tag, source repository, and the digest of what was fetched — so a later plugin list or upgrade can tell what is on disk and where it came from. plugin-registry#21 calls for installers to record this.
Report the rule ID range and the plugin's status and type from the registry, and warn when the range overlaps a range already present in the target directory.
Target directory precedence: --plugins-dir if given, otherwise <CRS_ROOT>/plugins derived from the existing global -d. This needs a plugins directory added to the context package, which currently knows only rules/, regex-assembly/, and tests/regression/tests/.
Signature verification, and what blocks it
The intent is to verify cosign signatures on the downloaded release. Two things stand in the way today, and both are worth settling before this is built rather than during.
Nothing is signed yet. Every plugin release currently publishes zero release assets:
repository
newest release
assets
fake-bot-plugin
v1.1.0
none
google-oauth2-plugin
v1.0.0
none
wordpress-rule-exclusions-plugin
v1.2.0
none
dos-protection-plugin
no releases at all
—
body-decompress-plugin
no releases at all
—
So there is no signed artifact to verify, and for the last two there is no tag to resolve either. Step 2 above does not hold for every registered plugin until plugins publish releases.
The registry will not say what to expect. plugin-registry#21 is explicit that the registry attests an allocated rule ID range and a reviewed registration, and that it is "not a supply-chain guarantee", with signing deferred: "signing or checksums can attach to those tags later if we want that, and I am not proposing it for the first iteration."
That matters because "verify the signature if there is one" is not a security property. An attacker who can serve a modified artifact can also serve it without a signature, and a best-effort verifier accepts it. Verification is only meaningful when the installer knows in advance that a given plugin must be signed and by whom.
Two ways to get there, and this issue should pick one:
Registry-driven policy.registry.yaml carries a signing block per plugin — the expected cosign identity and OIDC issuer for keyless verification, or a public key. When present, verification is mandatory and failure aborts the install. This is the version that actually protects anything, and it needs a follow-up in plugin-registry beyond the scope of fix: goreleaser #21.
Explicit opt-in.--require-signature, with the identity and issuer passed as flags. Fails closed when asked to verify and unable to. Useful for an operator who already knows what they expect, and buildable now, but it protects only the people who remember to use it.
Suggested path: build the installer with verification designed in and fail-closed, ship it initially with explicit opt-in, and switch to registry-driven policy once plugin-registry can express it. What should not happen is a silent "verified when convenient" default, because it reads as a guarantee it does not provide.
Open questions
What gets downloaded. Release tarball, or just the plugin's plugins/ directory at the tag? A tarball is what cosign would sign; the directory is what actually gets installed. If plugins start attaching signed archives as release assets, this resolves itself.
Config files.<name>-config.conf carries user settings. Copy it on first install only, and leave it alone on upgrade? Otherwise an upgrade silently reverts the operator's configuration.
Lua plugins. Some plugins ship .lua files and need a Lua-enabled ModSecurity. Should install warn when the plugin needs Lua, or is that the operator's problem?
Private plugins. Two registered plugins are private repositories. Install presumably just fails for those without credentials, but it should fail with a clear message rather than a 404.
Out of scope
plugin list, plugin upgrade, and plugin uninstall. They want the same install record and are natural follow-ups, but this issue is only the install path.
Problem
Installing a CRS plugin today is a manual copy. You find the plugin in the registry table in the README, clone or download the repository, copy the files from its
plugins/directory into the CRSplugins/directory, and edit the config. Nothing records what you installed or at which version, so there is no way to tell later whether a plugin is current, which release it came from, or whether the files on disk still match what upstream published.Once the registry is machine-readable (coreruleset/plugin-registry#21) the toolchain can do this from a source it can resolve against.
Proposal
Resolves
<name>against the published registry index, resolves a release tag, downloads that release, verifies it, and copies the plugin's files into the plugins directory.Behaviour
registry.jsonfrom plugin-registry#21. Unknown name is an error that lists near matches. The registry entry supplies the repository, the rule ID range,type,status, and the license.--versionpins it; otherwise the newest release. The resolved tag is what gets reported and recorded, notlatest.plugins/directory into the target directory. Refuse to overwrite existing files unless--force, so a partial or hand-modified install is never silently clobbered.plugin listor upgrade can tell what is on disk and where it came from. plugin-registry#21 calls for installers to record this.statusandtypefrom the registry, and warn when the range overlaps a range already present in the target directory.Target directory precedence:
--plugins-dirif given, otherwise<CRS_ROOT>/pluginsderived from the existing global-d. This needs a plugins directory added to thecontextpackage, which currently knows onlyrules/,regex-assembly/, andtests/regression/tests/.Signature verification, and what blocks it
The intent is to verify cosign signatures on the downloaded release. Two things stand in the way today, and both are worth settling before this is built rather than during.
Nothing is signed yet. Every plugin release currently publishes zero release assets:
fake-bot-plugingoogle-oauth2-pluginwordpress-rule-exclusions-plugindos-protection-pluginbody-decompress-pluginSo there is no signed artifact to verify, and for the last two there is no tag to resolve either. Step 2 above does not hold for every registered plugin until plugins publish releases.
The registry will not say what to expect. plugin-registry#21 is explicit that the registry attests an allocated rule ID range and a reviewed registration, and that it is "not a supply-chain guarantee", with signing deferred: "signing or checksums can attach to those tags later if we want that, and I am not proposing it for the first iteration."
That matters because "verify the signature if there is one" is not a security property. An attacker who can serve a modified artifact can also serve it without a signature, and a best-effort verifier accepts it. Verification is only meaningful when the installer knows in advance that a given plugin must be signed and by whom.
Two ways to get there, and this issue should pick one:
registry.yamlcarries a signing block per plugin — the expected cosign identity and OIDC issuer for keyless verification, or a public key. When present, verification is mandatory and failure aborts the install. This is the version that actually protects anything, and it needs a follow-up in plugin-registry beyond the scope of fix: goreleaser #21.--require-signature, with the identity and issuer passed as flags. Fails closed when asked to verify and unable to. Useful for an operator who already knows what they expect, and buildable now, but it protects only the people who remember to use it.Suggested path: build the installer with verification designed in and fail-closed, ship it initially with explicit opt-in, and switch to registry-driven policy once plugin-registry can express it. What should not happen is a silent "verified when convenient" default, because it reads as a guarantee it does not provide.
Open questions
plugins/directory at the tag? A tarball is what cosign would sign; the directory is what actually gets installed. If plugins start attaching signed archives as release assets, this resolves itself.<name>-config.confcarries user settings. Copy it on first install only, and leave it alone on upgrade? Otherwise an upgrade silently reverts the operator's configuration..luafiles and need a Lua-enabled ModSecurity. Should install warn when the plugin needs Lua, or is that the operator's problem?Out of scope
plugin list,plugin upgrade, andplugin uninstall. They want the same install record and are natural follow-ups, but this issue is only the install path.Depends on
registry.jsonat a stable URLplugin.yamldescriptor, for configuration variables