The ModSecurity-pipy
connector is the connection between Pipy and libmodsecurity (ModSecurity v3). This Pipy Native Module Interface (NMI) module provides a communication channel between Pipy and libmodsecurity. This connector is required to use LibModSecurity with Pipy.
ModSecurity-pipy
takes the form of Pipy Native Module (NMI). This module simply servers as a layer of communication between Pipy and ModSecurity.
Note: This module depends on libmodsecurity rather than ModSecurity (version 2.9 or less).
Before compiling this NMI module make sure you have libmodsecurity installed. You can download it from the ModSecurity git repo. For information pertaining to the compilation and installation of libmodsecurity please consult the documentation provided along with it.
With libmodsecurity installed, you can proceed with the compilation of the ModSecurity-pipy connector, which follows the Pipy Native module installation procedure. From the module source directory:
Make sure you adjust
Makefile
with location where you have installedlibmodsecurity
and dependencies it has been compiled with.
make
ModSecurity for Pipy exports below PJS variables to be used from Pipy PJS script.
- __msc_inbound: PJS object which should contain the
__inbound
object properties - __msc_intervention: PJS object, with properties
status
,url
,log
,disruptive
. This object is populated by NMI module - __msc_warnings: PJS array, all ModSecurity transaction related warnings will be populated into this var
- __msc_rules_file: String, Use this variable to set the ModSecurity Rules File name
- __msc_rules_remote: PJS Object, with properties
key
, andurl
, if you want to load ModSecurity Rules from remote URL - __msc_rules: String, You can provide ModSecurity rules as a String
- __msc_transaction_id: String, you can pass any transaction id for tracing purposes.
Populate this object with properties from __inbound
object.
.onStart(() => (
__msc_inbound = {...__inbound},
))
NMI module populate this Object after performing ModSecurity rules scanning on Connection, URI, Request Headers, Request Body, Response Headers, and Response Body. Structure of this PJS object as follows:
disruptive
Boolean flag, when set indicates a disruptive action.status
Number, contains the status code. Status code of 200 indicates successurl
String, eitherundefined
or contains the URL which should be followed to perform re-direction.log
String, eitherundefined
or contains the log description generated from rule which caused this disruption.
Specifies the location of the modsecurity configuration file, e.g:
.onStart(() => (
__msc_inbound = {...__inbound},
__msc_warnings = [],
__msc_rules_file = 'modsecurity.conf'
))
Specifies from where (on the internet) a modsecurity configuration file will be downloaded. It also specifies the key that will be used to authenticate to that server:
.onStart(() => (
__msc_inbound = {...__inbound},
__msc_warnings = [],
__msc_rules_remote = {
key: 'my-server-key',
url: 'https://my-own-server/rules/download'
}
))
Allows for the direct inclusion of a ModSecurity rule into the Pipy pipeline:
.onStart(() => (
__msc_inbound = {...__inbound},
__msc_warnings = [],
__msc_rules = `
SecRuleEngine On
SecResponseBodyAccess On
SecDebugLog modsec_debug.log
SecDebugLogLevel 9
SecRuleRemoveById 10
SecStatusEngine On
`
))
Allows to pass transaction ID from Pipy instead of generating it in the library. This can be useful for tracing purposes, e.g. consider this configuration:
.onStart(() => (
__msc_inbound = {...__inbound},
__msc_warnings = [],
__msc_rules_file = 'modsecurity.conf',
__msc_transaction_id = 'host:port-transaction-id'
))
pipy()
.import({
__msc_inbound: 'mod-sec',
__msc_intervention: 'mod-sec',
__msc_warnings: 'mod-sec',
__msc_rules_file: 'mod-sec',
__msc_rules_remote: 'mod-sec',
__msc_rules: 'mod-sec',
__msc_transaction_id: 'mod-sec',
})
.listen(8080)
.demuxHTTP().to(
$=>$
.onStart(() => (
__msc_inbound = {...__inbound},
__msc_warnings = [],
__msc_rules = `
SecRuleEngine On
SecResponseBodyAccess On
SecDebugLog modsec_debug.log
SecDebugLogLevel 9
SecRuleRemoveById 10
SecStatusEngine On
`,
__msc_transaction_id = 'localhost:8080-tx'
))
.use('./bin/modsecurity-nmi.so','request')
.branchMessage(
() => (__msc_intervention.disruptive), (
$=>$.replaceMessage(()=>(
(__msc_intervention?.log && console.log(__msc_intervention.log)),
new Message({ status: __msc_intervention.status }, 'Forbidden' )
))
),
() => (__msc_intervention.status && __msc_intervention.status != 200), (
$=>$.replaceMessage(()=>(
(__msc_intervention?.url) && console.log(`Redirecting to ${__msc_intervention.url}`),
new Message({ status: __msc_intervention.status,
location: __msc_intervention.url })
))
),
() => (__msc_intervention.status == 200),
(
$=>$.muxHTTP().to(
$=>$.connect('upstream-service:port')
)
)
)
.use('./bin/modsecurity-nmi.so','response')
.branchMessage(
() => (__msc_intervention.disruptive), (
$=>$.replaceMessage(()=>(
(__msc_intervention?.log && console.log(__msc_intervention.log)),
new Message({ status: __msc_intervention.status }, 'Forbidden' )
))
)
)
.handleMessageEnd(() => __msc_warnings.forEach(w => console.log(w)))
)