mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2025-09-29 19:24:29 +03:00
More tested regression tests.
Cleaned up script.
This commit is contained in:
248
apache2/t/regression/action/10-logging.t
Normal file
248
apache2/t/regression/action/10-logging.t
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
### Logging tests
|
||||||
|
|
||||||
|
# log/nolog
|
||||||
|
{
|
||||||
|
type => "action",
|
||||||
|
comment => "log",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecAuditEngine RelevantOnly
|
||||||
|
SecAuditLog "$ENV{AUDIT_LOG}"
|
||||||
|
SecAction "phase:1,pass,log"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => "action",
|
||||||
|
comment => "nolog",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecAuditEngine RelevantOnly
|
||||||
|
SecAuditLog "$ENV{AUDIT_LOG}"
|
||||||
|
SecAction "phase:1,pass,nolog"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
-error => [ qr/ModSecurity: /, 1 ],
|
||||||
|
-audit => [ qr/./, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
# auditlog/noauditlog
|
||||||
|
{
|
||||||
|
type => "action",
|
||||||
|
comment => "auditlog",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecAuditEngine RelevantOnly
|
||||||
|
SecAuditLog "$ENV{AUDIT_LOG}"
|
||||||
|
SecAction "phase:1,pass,auditlog"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ],
|
||||||
|
audit => [ qr/Message: Warning. Unconditional match in SecAction\./, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => "action",
|
||||||
|
comment => "noauditlog",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecAuditEngine RelevantOnly
|
||||||
|
SecAuditLog "$ENV{AUDIT_LOG}"
|
||||||
|
SecAction "phase:1,pass,noauditlog"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ],
|
||||||
|
-audit => [ qr/./, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
# All log/nolog auditlog/noauditlog combos
|
||||||
|
{
|
||||||
|
type => "action",
|
||||||
|
comment => "log,auditlog",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecAuditEngine RelevantOnly
|
||||||
|
SecAuditLog "$ENV{AUDIT_LOG}"
|
||||||
|
SecAction "phase:1,pass,log,auditlog"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ],
|
||||||
|
audit => [ qr/Message: Warning. Unconditional match in SecAction\./, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => "action",
|
||||||
|
comment => "log,noauditlog",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecAuditEngine RelevantOnly
|
||||||
|
SecAuditLog "$ENV{AUDIT_LOG}"
|
||||||
|
SecAction "phase:1,pass,log,noauditlog"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ],
|
||||||
|
-audit => [ qr/./, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => "action",
|
||||||
|
comment => "nolog,auditlog",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecAuditEngine RelevantOnly
|
||||||
|
SecAuditLog "$ENV{AUDIT_LOG}"
|
||||||
|
SecAction "phase:1,pass,nolog,auditlog"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
-error => [ qr/ModSecurity: /, 1 ],
|
||||||
|
# ENH: No message, but should have data. Is this intended?
|
||||||
|
audit => [ qr/-H--\s+Stopwatch: /s, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => "action",
|
||||||
|
comment => "nolog,noauditlog",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecAuditEngine RelevantOnly
|
||||||
|
SecAuditLog "$ENV{AUDIT_LOG}"
|
||||||
|
SecAction "phase:1,pass,nolog,noauditlog"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
-error => [ qr/ModSecurity: /, 1 ],
|
||||||
|
-audit => [ qr/./, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => "action",
|
||||||
|
comment => "auditlog,log",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecAuditEngine RelevantOnly
|
||||||
|
SecAuditLog "$ENV{AUDIT_LOG}"
|
||||||
|
SecAction "phase:1,pass,auditlog,log"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ],
|
||||||
|
audit => [ qr/Message: Warning. Unconditional match in SecAction\./, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => "action",
|
||||||
|
comment => "auditlog,nolog",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecAuditEngine RelevantOnly
|
||||||
|
SecAuditLog "$ENV{AUDIT_LOG}"
|
||||||
|
SecAction "phase:1,pass,auditlog,nolog"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
-error => [ qr/ModSecurity: /, 1 ],
|
||||||
|
-audit => [ qr/./, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => "action",
|
||||||
|
comment => "noauditlog,log",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecAuditEngine RelevantOnly
|
||||||
|
SecAuditLog "$ENV{AUDIT_LOG}"
|
||||||
|
SecAction "phase:1,pass,noauditlog,log"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ],
|
||||||
|
-audit => [ qr/./, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => "action",
|
||||||
|
comment => "noauditlog,nolog",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecAuditEngine RelevantOnly
|
||||||
|
SecAuditLog "$ENV{AUDIT_LOG}"
|
||||||
|
SecAction "phase:1,pass,noauditlog,nolog"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
-error => [ qr/ModSecurity: /, 1 ],
|
||||||
|
-audit => [ qr/./, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
@@ -132,18 +132,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Verify concurrent log contents
|
# Verify concurrent log contents
|
||||||
$LOG{$id}{fd} = new FileHandle($alogdatafn, O_RDONLY);
|
if (defined match_file($alogdatafn, qr/^--[^-]+-A--.*$id.*-Z--$/s)) {
|
||||||
$LOG{$id}{fd}->blocking(0);
|
|
||||||
$LOG{$id}{buf} = "";
|
|
||||||
my $alogdata = match_log($id, qr/^--[^-]+-A--.*$id.*-Z--$/s, 1);
|
|
||||||
if (defined $alogdata) {
|
|
||||||
$LOG{$id}{fd}->close();
|
|
||||||
delete $LOG{$id};
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
dbg("LOGDATA: \"$alogdata\"");
|
dbg("LOGDATA: \"$FILE{$alogdatafn}{buf}\"");
|
||||||
die "Audit log data did not match.\n";
|
die "Audit log data did not match.\n";
|
||||||
},
|
},
|
||||||
match_response => {
|
match_response => {
|
||||||
|
@@ -2,13 +2,31 @@
|
|||||||
|
|
||||||
### TODO:
|
### TODO:
|
||||||
# SecTmpDir
|
# SecTmpDir
|
||||||
# SecUploadDir
|
|
||||||
# SecUploadKeepFiles
|
# SecUploadKeepFiles
|
||||||
# SecWebAppId
|
# SecWebAppId
|
||||||
# SecDataDir
|
|
||||||
# SecChrootDir
|
# SecChrootDir
|
||||||
# SecGuardianLog
|
# SecGuardianLog
|
||||||
|
|
||||||
|
# SecDefaultAction
|
||||||
|
{
|
||||||
|
type => "config",
|
||||||
|
comment => "SecDefaultAction",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine on
|
||||||
|
SecDefaultAction "phase:1,deny,status:500"
|
||||||
|
SecRule REQUEST_URI "test.txt"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
error => [ qr/ModSecurity: Access denied with code 500 \(phase 1\)/, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^500$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
# SecServerSignature
|
# SecServerSignature
|
||||||
{
|
{
|
||||||
type => "config",
|
type => "config",
|
||||||
@@ -28,22 +46,78 @@
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
# SecDefaultAction
|
# SecDataDir
|
||||||
{
|
{
|
||||||
type => "config",
|
type => "config",
|
||||||
comment => "SecServerSignature On",
|
comment => "SecDataDir",
|
||||||
conf => qq(
|
conf => qq(
|
||||||
SecRuleEngine on
|
SecRuleEngine On
|
||||||
SecDefaultAction "phase:1,deny,status:500"
|
SecDataDir "$ENV{DATA_DIR}"
|
||||||
SecRule REQUEST_URI "test.txt"
|
SecAction initcol:ip=%{REMOTE_ADDR},setvar:ip.dummy=1,pass
|
||||||
),
|
),
|
||||||
match_log => {
|
match_log => {
|
||||||
error => [ qr/ModSecurity: Access denied with code 500 \(phase 1\)/, 1 ],
|
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ],
|
||||||
|
},
|
||||||
|
match_file => {
|
||||||
|
"$ENV{DATA_DIR}/ip.pag" => qr/\x00\x06dummy\x00\x00\x021\x00/,
|
||||||
},
|
},
|
||||||
match_response => {
|
match_response => {
|
||||||
status => qr/^500$/,
|
status => qr/^200$/,
|
||||||
},
|
},
|
||||||
request => new HTTP::Request(
|
request => new HTTP::Request(
|
||||||
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
# SecTmpDir/SecUploadDir/SecUploadKeepFiles
|
||||||
|
{
|
||||||
|
type => "config",
|
||||||
|
comment => "SecTmpDir/SecUploadDir/SecUploadKeepFiles",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecRequestBodyAccess On
|
||||||
|
SecDebugLog $ENV{DEBUG_LOG}
|
||||||
|
SecDebugLogLevel 4
|
||||||
|
SecTmpDir "$ENV{TEMP_DIR}"
|
||||||
|
SecUploadKeepFiles On
|
||||||
|
SecUploadDir "$ENV{UPLOAD_DIR}"
|
||||||
|
),
|
||||||
|
test => sub {
|
||||||
|
# Get the filename and make sure the file exists
|
||||||
|
my $fn = match_log(debug => qr/Moved file from .* to ".*"\./, 5);
|
||||||
|
die "Failed to determine uploaded filename\n" unless (defined $fn);
|
||||||
|
|
||||||
|
$fn =~ s/Moved file from .* to "(.*)"\..*/$1/;
|
||||||
|
die "File does not exist: $fn\n" unless (-e $fn);
|
||||||
|
|
||||||
|
# Check the contents of the file
|
||||||
|
return 0 if (match_file($fn, qr/^TESTFILE$/m));
|
||||||
|
|
||||||
|
msg("Failed to match contents of uploaded file: $fn");
|
||||||
|
return 1;
|
||||||
|
},
|
||||||
|
match_log => {
|
||||||
|
debug => [ qr/Created temporary file: $ENV{TEMP_DIR}/, 1 ],
|
||||||
|
-debug => [ qr/Failed to /, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
[
|
||||||
|
"Content-Type" => "multipart/form-data; boundary=---------------------------19813181771830765643996187206",
|
||||||
|
],
|
||||||
|
q(-----------------------------19813181771830765643996187206
|
||||||
|
Content-Disposition: form-data; name="upload-file"; filename="test"
|
||||||
|
Content-Type: application/octet-stream
|
||||||
|
|
||||||
|
TESTFILE
|
||||||
|
-----------------------------19813181771830765643996187206
|
||||||
|
Content-Disposition: form-data; name="file"
|
||||||
|
|
||||||
|
Upload File
|
||||||
|
-----------------------------19813181771830765643996187206--),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
35
apache2/t/regression/config/20-chroot.t
Normal file
35
apache2/t/regression/config/20-chroot.t
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
### SecChroot tests
|
||||||
|
# TODO: Will not work as we need root access
|
||||||
|
|
||||||
|
#{
|
||||||
|
# type => "config",
|
||||||
|
# comment => "SecChroot",
|
||||||
|
# httpd_opts => qw(
|
||||||
|
# -DCHROOT
|
||||||
|
# ),
|
||||||
|
# conf => qq(
|
||||||
|
# # These will be in the chroot
|
||||||
|
# PidFile /logs/httpd.pid
|
||||||
|
# ScoreBoardFile /logs/httpd.scoreboard
|
||||||
|
# User nobody
|
||||||
|
# Group nogroup
|
||||||
|
#
|
||||||
|
# SecAuditEngine On
|
||||||
|
# SecDebugLog $ENV{DEBUG_LOG}
|
||||||
|
# SecDebugLogLevel 9
|
||||||
|
# SecAuditLog $ENV{AUDIT_LOG}
|
||||||
|
# SecAuditLogStorageDir "/logs/audit"
|
||||||
|
# SecAuditLogType Concurrent
|
||||||
|
# SecChrootDir "$ENV{TEST_SERVER_ROOT}"
|
||||||
|
# ),
|
||||||
|
# match_log => {
|
||||||
|
# debug => [ qr/./, 1 ],
|
||||||
|
# audit => [ qr/./, 1 ],
|
||||||
|
# },
|
||||||
|
# match_response => {
|
||||||
|
# status => qr/^200$/,
|
||||||
|
# },
|
||||||
|
# request => new HTTP::Request(
|
||||||
|
# GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
# ),
|
||||||
|
#},
|
24
apache2/t/regression/rule/00-basics.t
Normal file
24
apache2/t/regression/rule/00-basics.t
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
### Tests for basic rule components
|
||||||
|
|
||||||
|
# SecAction
|
||||||
|
{
|
||||||
|
type => "config",
|
||||||
|
comment => "SecAction (override default)",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecDebugLog $ENV{DEBUG_LOG}
|
||||||
|
SecDebugLogLevel 4
|
||||||
|
SecAction "nolog"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
-error => [ qr/ModSecurity: /, 1 ],
|
||||||
|
-audit => [ qr/./, 1 ],
|
||||||
|
debug => [ qr/Warning\. Unconditional match in SecAction\./, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
4
apache2/t/regression/rule/00-inheritance.t
Normal file
4
apache2/t/regression/rule/00-inheritance.t
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
### Tests for rule inheritance
|
||||||
|
|
||||||
|
### TODO:
|
||||||
|
# SecRuleInheritance
|
129
apache2/t/regression/rule/20-exceptions.t
Normal file
129
apache2/t/regression/rule/20-exceptions.t
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
### Tests for rule exceptions
|
||||||
|
|
||||||
|
# SecRuleRemoveByMsg
|
||||||
|
|
||||||
|
# SecRuleRemoveById
|
||||||
|
{
|
||||||
|
type => "config",
|
||||||
|
comment => "SecRuleRemoveById (single)",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecDebugLog $ENV{DEBUG_LOG}
|
||||||
|
SecDebugLogLevel 9
|
||||||
|
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1"
|
||||||
|
SecRuleRemoveById 1
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
-error => [ qr/ModSecurity: /, 1 ],
|
||||||
|
-audit => [ qr/./, 1 ],
|
||||||
|
debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ],
|
||||||
|
-debug => [ qr/Access denied/, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => "config",
|
||||||
|
comment => "SecRuleRemoveById (multiple)",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecDebugLog $ENV{DEBUG_LOG}
|
||||||
|
SecDebugLogLevel 9
|
||||||
|
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1"
|
||||||
|
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:2"
|
||||||
|
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:3"
|
||||||
|
SecRuleRemoveById 1 2 3
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
-error => [ qr/ModSecurity: /, 1 ],
|
||||||
|
-audit => [ qr/./, 1 ],
|
||||||
|
debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ],
|
||||||
|
-debug => [ qr/Access denied/, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => "config",
|
||||||
|
comment => "SecRuleRemoveById (range)",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecDebugLog $ENV{DEBUG_LOG}
|
||||||
|
SecDebugLogLevel 9
|
||||||
|
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1"
|
||||||
|
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:2"
|
||||||
|
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:3"
|
||||||
|
SecRuleRemoveById 1-3
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
-error => [ qr/ModSecurity: /, 1 ],
|
||||||
|
-audit => [ qr/./, 1 ],
|
||||||
|
debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ],
|
||||||
|
-debug => [ qr/Access denied/, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type => "config",
|
||||||
|
comment => "SecRuleRemoveById (multiple + range)",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecDebugLog $ENV{DEBUG_LOG}
|
||||||
|
SecDebugLogLevel 9
|
||||||
|
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1"
|
||||||
|
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:2"
|
||||||
|
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:3"
|
||||||
|
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:4"
|
||||||
|
SecRuleRemoveById 1 2-4
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
-error => [ qr/ModSecurity: /, 1 ],
|
||||||
|
-audit => [ qr/./, 1 ],
|
||||||
|
debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ],
|
||||||
|
-debug => [ qr/Access denied/, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
# SecRuleRemoveByMsg
|
||||||
|
{
|
||||||
|
type => "config",
|
||||||
|
comment => "SecRuleRemoveByMsg",
|
||||||
|
conf => qq(
|
||||||
|
SecRuleEngine On
|
||||||
|
SecDebugLog $ENV{DEBUG_LOG}
|
||||||
|
SecDebugLogLevel 9
|
||||||
|
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1,msg:'testing rule'"
|
||||||
|
SecRuleRemoveByMsg "testing rule"
|
||||||
|
),
|
||||||
|
match_log => {
|
||||||
|
-error => [ qr/ModSecurity: /, 1 ],
|
||||||
|
-audit => [ qr/./, 1 ],
|
||||||
|
debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ],
|
||||||
|
-debug => [ qr/Access denied/, 1 ],
|
||||||
|
},
|
||||||
|
match_response => {
|
||||||
|
status => qr/^200$/,
|
||||||
|
},
|
||||||
|
request => new HTTP::Request(
|
||||||
|
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
|
||||||
|
),
|
||||||
|
},
|
@@ -1,8 +1,10 @@
|
|||||||
### Base configuration for starting Apache httpd
|
### Base configuration for starting Apache httpd
|
||||||
|
|
||||||
# File locations
|
<IfDefine !CHROOT>
|
||||||
PidFile @MSC_REGRESSION_LOGS_DIR@/httpd.pid
|
# File locations
|
||||||
ScoreBoardFile @MSC_REGRESSION_LOGS_DIR@/httpd.scoreboard
|
PidFile @MSC_REGRESSION_LOGS_DIR@/httpd.pid
|
||||||
|
ScoreBoardFile @MSC_REGRESSION_LOGS_DIR@/httpd.scoreboard
|
||||||
|
</IfDefine>
|
||||||
|
|
||||||
<IfModule !mod_proxy.c>
|
<IfModule !mod_proxy.c>
|
||||||
LoadModule proxy_module modules/mod_proxy.so
|
LoadModule proxy_module modules/mod_proxy.so
|
||||||
@@ -24,9 +26,10 @@ ServerName localhost
|
|||||||
LogLevel debug
|
LogLevel debug
|
||||||
ErrorLog @MSC_REGRESSION_LOGS_DIR@/error.log
|
ErrorLog @MSC_REGRESSION_LOGS_DIR@/error.log
|
||||||
|
|
||||||
DocumentRoot @MSC_REGRESSION_DOCROOT_DIR@
|
<IfDefine !CHROOT>
|
||||||
<Directory "@MSC_REGRESSION_DOCROOT_DIR@">
|
DocumentRoot @MSC_REGRESSION_DOCROOT_DIR@
|
||||||
|
<Directory "@MSC_REGRESSION_DOCROOT_DIR@">
|
||||||
Options Indexes FollowSymLinks
|
Options Indexes FollowSymLinks
|
||||||
AllowOverride None
|
AllowOverride None
|
||||||
</Directory>
|
</Directory>
|
||||||
|
</IfDefine>
|
||||||
|
@@ -26,14 +26,17 @@ my $SCRIPT = basename($0);
|
|||||||
my $SCRIPT_DIR = File::Spec->rel2abs(dirname($0));
|
my $SCRIPT_DIR = File::Spec->rel2abs(dirname($0));
|
||||||
my $REG_DIR = "$SCRIPT_DIR/regression";
|
my $REG_DIR = "$SCRIPT_DIR/regression";
|
||||||
my $SROOT_DIR = "$REG_DIR/server_root";
|
my $SROOT_DIR = "$REG_DIR/server_root";
|
||||||
|
my $DATA_DIR = "$SROOT_DIR/data";
|
||||||
|
my $TEMP_DIR = "$SROOT_DIR/tmp";
|
||||||
|
my $UPLOAD_DIR = "$SROOT_DIR/upload";
|
||||||
my $CONF_DIR = "$SROOT_DIR/conf";
|
my $CONF_DIR = "$SROOT_DIR/conf";
|
||||||
my $LOGS_DIR = "$SROOT_DIR/logs";
|
my $FILES_DIR = "$SROOT_DIR/logs";
|
||||||
my $PID_FILE = "$LOGS_DIR/httpd.pid";
|
my $PID_FILE = "$FILES_DIR/httpd.pid";
|
||||||
my $HTTPD = q(@APXS_HTTPD@);
|
my $HTTPD = q(@APXS_HTTPD@);
|
||||||
my $PASSED = 0;
|
my $PASSED = 0;
|
||||||
my $TOTAL = 0;
|
my $TOTAL = 0;
|
||||||
my %C = ();
|
my %C = ();
|
||||||
my %LOG = ();
|
my %FILE = ();
|
||||||
my $UA_NAME = "ModSecurity Regression Tests/1.2.3";
|
my $UA_NAME = "ModSecurity Regression Tests/1.2.3";
|
||||||
my $UA = LWP::UserAgent->new;
|
my $UA = LWP::UserAgent->new;
|
||||||
$UA->agent($UA_NAME);
|
$UA->agent($UA_NAME);
|
||||||
@@ -89,9 +92,9 @@ else {
|
|||||||
usage("Invalid Apache startup script: $HTTPD\n") unless (-e $HTTPD);
|
usage("Invalid Apache startup script: $HTTPD\n") unless (-e $HTTPD);
|
||||||
|
|
||||||
### Defaults
|
### Defaults
|
||||||
$opt{A} = "$LOGS_DIR/modsec_audit.log" unless (defined $opt{A});
|
$opt{A} = "$FILES_DIR/modsec_audit.log" unless (defined $opt{A});
|
||||||
$opt{D} = "$LOGS_DIR/modsec_debug.log" unless (defined $opt{D});
|
$opt{D} = "$FILES_DIR/modsec_debug.log" unless (defined $opt{D});
|
||||||
$opt{E} = "$LOGS_DIR/error.log" unless (defined $opt{E});
|
$opt{E} = "$FILES_DIR/error.log" unless (defined $opt{E});
|
||||||
$opt{C} = "$CONF_DIR/httpd.conf" unless (defined $opt{C});
|
$opt{C} = "$CONF_DIR/httpd.conf" unless (defined $opt{C});
|
||||||
$opt{H} = "$SROOT_DIR/htdocs" unless (defined $opt{H});
|
$opt{H} = "$SROOT_DIR/htdocs" unless (defined $opt{H});
|
||||||
$opt{p} = 8088 unless (defined $opt{p});
|
$opt{p} = 8088 unless (defined $opt{p});
|
||||||
@@ -107,8 +110,11 @@ unless (defined $opt{S}) {
|
|||||||
SERVER_PORT => $opt{p},
|
SERVER_PORT => $opt{p},
|
||||||
SERVER_NAME => "localhost",
|
SERVER_NAME => "localhost",
|
||||||
TEST_SERVER_ROOT => $SROOT_DIR,
|
TEST_SERVER_ROOT => $SROOT_DIR,
|
||||||
|
DATA_DIR => $DATA_DIR,
|
||||||
|
TEMP_DIR => $TEMP_DIR,
|
||||||
|
UPLOAD_DIR => $UPLOAD_DIR,
|
||||||
CONF_DIR => $CONF_DIR,
|
CONF_DIR => $CONF_DIR,
|
||||||
LOGS_DIR => $LOGS_DIR,
|
LOGS_DIR => $FILES_DIR,
|
||||||
SCRIPT_DIR => $SCRIPT_DIR,
|
SCRIPT_DIR => $SCRIPT_DIR,
|
||||||
REGRESSION_DIR => $REG_DIR,
|
REGRESSION_DIR => $REG_DIR,
|
||||||
DIST_ROOT => File::Spec->rel2abs(dirname("$SCRIPT_DIR/../../..")),
|
DIST_ROOT => File::Spec->rel2abs(dirname("$SCRIPT_DIR/../../..")),
|
||||||
@@ -120,7 +126,7 @@ unless (defined $opt{S}) {
|
|||||||
USER_AGENT => $UA_NAME,
|
USER_AGENT => $UA_NAME,
|
||||||
);
|
);
|
||||||
|
|
||||||
dbg("OPTIONS: ", \%opt);
|
#dbg("OPTIONS: ", \%opt);
|
||||||
|
|
||||||
if (-e "$PID_FILE") {
|
if (-e "$PID_FILE") {
|
||||||
msg("Shutting down previous instance: $PID_FILE");
|
msg("Shutting down previous instance: $PID_FILE");
|
||||||
@@ -193,10 +199,10 @@ sub runfile {
|
|||||||
print CONF (ref $t{conf} eq "CODE" ? eval { &{$t{conf}} } : $t{conf});
|
print CONF (ref $t{conf} eq "CODE" ? eval { &{$t{conf}} } : $t{conf});
|
||||||
msg("$@") if ($@);
|
msg("$@") if ($@);
|
||||||
close CONF;
|
close CONF;
|
||||||
$httpd_up = httpd_start("Include $conf_fn") ? 0 : 1;
|
$httpd_up = httpd_start(\%t, "Include $conf_fn") ? 0 : 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$httpd_up = httpd_start() ? 0 : 1;
|
$httpd_up = httpd_start(\%t) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Run any prerun setup
|
# Run any prerun setup
|
||||||
@@ -229,7 +235,7 @@ sub runfile {
|
|||||||
}
|
}
|
||||||
elsif (!$neg and !defined $match) {
|
elsif (!$neg and !defined $match) {
|
||||||
$rc = 1;
|
$rc = 1;
|
||||||
msg("response $mtype no match: $m");
|
msg("response $mtype failed to match: $m");
|
||||||
dbg($resp);
|
dbg($resp);
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
@@ -239,13 +245,13 @@ sub runfile {
|
|||||||
|
|
||||||
# Run any arbitrary perl tests
|
# Run any arbitrary perl tests
|
||||||
if ($rc == 0 and exists $t{test} and defined $t{test}) {
|
if ($rc == 0 and exists $t{test} and defined $t{test}) {
|
||||||
dbg("Executing perl test(s)...");
|
#dbg("Executing perl test(s)...");
|
||||||
$rc = eval { &{$t{test}} };
|
$rc = eval { &{$t{test}} };
|
||||||
if (! defined $rc) {
|
if (! defined $rc) {
|
||||||
msg("Error running test: $@");
|
msg("Error running test: $@");
|
||||||
$rc = -1;
|
$rc = -1;
|
||||||
}
|
}
|
||||||
dbg("Perl tests returned: $rc");
|
#dbg("Perl tests returned: $rc");
|
||||||
}
|
}
|
||||||
|
|
||||||
# Search for all log matches
|
# Search for all log matches
|
||||||
@@ -257,14 +263,36 @@ sub runfile {
|
|||||||
if ($neg and defined $match) {
|
if ($neg and defined $match) {
|
||||||
$rc = 1;
|
$rc = 1;
|
||||||
msg("$mtype log matched: $m->[0]");
|
msg("$mtype log matched: $m->[0]");
|
||||||
dbg("$LOG{$mtype}{buf}");
|
msg("Log: $FILE{$mtype}{fn}");
|
||||||
|
dbg(escape("$FILE{$mtype}{buf}"));
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
elsif (!$neg and !defined $match) {
|
elsif (!$neg and !defined $match) {
|
||||||
$rc = 1;
|
$rc = 1;
|
||||||
msg("$mtype log no match: $m->[0]");
|
msg("$mtype log failed to match: $m->[0]");
|
||||||
dbg("$LOG{$mtype}{buf}");
|
msg("Log: $FILE{$mtype}{fn}");
|
||||||
|
dbg(escape("$FILE{$mtype}{buf}"));
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Search for all file matches
|
||||||
|
if ($rc == 0 and exists $t{match_file} and defined $t{match_file}) {
|
||||||
|
for my $key (keys %{ $t{match_file} || {}}) {
|
||||||
|
my($neg,$fn) = ($key =~ m/^(-?)(.*)$/);
|
||||||
|
my $m = $t{match_file}{$key};
|
||||||
|
my $match = match_file($fn, $m);
|
||||||
|
if ($neg and defined $match) {
|
||||||
|
$rc = 1;
|
||||||
|
msg("$fn file matched: $m");
|
||||||
|
dbg(escape("$FILE{$fn}{buf}"));
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
elsif (!$neg and !defined $match) {
|
||||||
|
$rc = 1;
|
||||||
|
msg("$fn file failed match: $m");
|
||||||
|
dbg(escape("$FILE{$fn}{buf}"));
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,7 +313,7 @@ sub runfile {
|
|||||||
msg(sprintf("%s) %s%s: %s%s", $id, $t{type}, (exists($t{comment}) ? " - $t{comment}" : ""), ($rc ? "failed" : "passed"), ((defined($out) && $out ne "")? " ($out)" : "")));
|
msg(sprintf("%s) %s%s: %s%s", $id, $t{type}, (exists($t{comment}) ? " - $t{comment}" : ""), ($rc ? "failed" : "passed"), ((defined($out) && $out ne "")? " ($out)" : "")));
|
||||||
|
|
||||||
if ($httpd_up) {
|
if ($httpd_up) {
|
||||||
$httpd_up = httpd_stop() ? 0 : 1;
|
$httpd_up = httpd_stop(\%t) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -362,7 +390,7 @@ sub match_response {
|
|||||||
sub match_log {
|
sub match_log {
|
||||||
my($name, $re, $timeout) = @_;
|
my($name, $re, $timeout) = @_;
|
||||||
my $t0 = gettimeofday;
|
my $t0 = gettimeofday;
|
||||||
my($fh,$rbuf) = ($LOG{$name}{fd}, \$LOG{$name}{buf});
|
my($fh,$rbuf) = ($FILE{$name}{fd}, \$FILE{$name}{buf});
|
||||||
my $n = length($$rbuf);
|
my $n = length($$rbuf);
|
||||||
|
|
||||||
msg("Warning: Empty regular expression.") if (!defined $re or $re eq "");
|
msg("Warning: Empty regular expression.") if (!defined $re or $re eq "");
|
||||||
@@ -380,10 +408,29 @@ sub match_log {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub match_file {
|
||||||
|
my($neg,$fn) = ($_[0] =~ m/^(-?)(.*)$/);
|
||||||
|
unless (exists $FILE{$fn}) {
|
||||||
|
$FILE{$fn}{fn} = $fn;
|
||||||
|
$FILE{$fn}{fd} = new FileHandle($fn, O_RDWR|O_CREAT);
|
||||||
|
$FILE{$fn}{fd}->blocking(0);
|
||||||
|
$FILE{$fn}{buf} = "";
|
||||||
|
}
|
||||||
|
return match_log($_[0], $_[1]); # timeout makes no sense
|
||||||
|
}
|
||||||
|
|
||||||
|
sub quote_shell {
|
||||||
|
my($s) = @_;
|
||||||
|
return $s unless ($s =~ m|[^\w!%+,\-./:@^]|);
|
||||||
|
$s =~ s/(['\\])/\\$1/g;
|
||||||
|
return "'$s'";
|
||||||
|
}
|
||||||
|
|
||||||
sub escape {
|
sub escape {
|
||||||
my @new = ();
|
my @new = ();
|
||||||
for my $c (split(//, $_[0])) {
|
for my $c (split(//, $_[0])) {
|
||||||
push @new, ((ord($c) >= 0x20 and ord($c) <= 0x7e) ? $c : sprintf("\\x%02x", ord($c)));
|
my $oc = ord($c);
|
||||||
|
push @new, ((($oc >= 0x20 and $oc <= 0x7e) or $oc == 0x0a or $oc == 0x0d) ? $c : sprintf("\\x%02x", ord($c)));
|
||||||
}
|
}
|
||||||
join('', @new);
|
join('', @new);
|
||||||
}
|
}
|
||||||
@@ -432,7 +479,8 @@ sub done {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub httpd_start {
|
sub httpd_start {
|
||||||
httpd_reset_logs();
|
my $t = shift;
|
||||||
|
httpd_reset_fd($t);
|
||||||
my @p = (
|
my @p = (
|
||||||
$HTTPD,
|
$HTTPD,
|
||||||
-d => $opt{S},
|
-d => $opt{S},
|
||||||
@@ -441,9 +489,6 @@ sub httpd_start {
|
|||||||
-k => "start",
|
-k => "start",
|
||||||
);
|
);
|
||||||
|
|
||||||
# dbg("EXEC: ", \@p);
|
|
||||||
# dbg("Httpd start");
|
|
||||||
|
|
||||||
my $httpd_out;
|
my $httpd_out;
|
||||||
my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1);
|
my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1);
|
||||||
my $out = join("\\n", split(/\n/, <$httpd_out>));
|
my $out = join("\\n", split(/\n/, <$httpd_out>));
|
||||||
@@ -453,7 +498,7 @@ sub httpd_start {
|
|||||||
my $rc = $?;
|
my $rc = $?;
|
||||||
if ( WIFEXITED($rc) ) {
|
if ( WIFEXITED($rc) ) {
|
||||||
$rc = WEXITSTATUS($rc);
|
$rc = WEXITSTATUS($rc);
|
||||||
# dbg("Httpd start returned with $rc.");
|
dbg("Httpd start returned with $rc.") if ($rc);
|
||||||
}
|
}
|
||||||
elsif( WIFSIGNALED($rc) ) {
|
elsif( WIFSIGNALED($rc) ) {
|
||||||
msg("Httpd start failed with signal " . WTERMSIG($rc) . ".");
|
msg("Httpd start failed with signal " . WTERMSIG($rc) . ".");
|
||||||
@@ -465,20 +510,24 @@ sub httpd_start {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (defined $out and $out ne "") {
|
if (defined $out and $out ne "") {
|
||||||
|
dbg(join(" ", map { quote_shell($_) } @p));
|
||||||
msg("Httpd start failed with error messages:\n$out");
|
msg("Httpd start failed with error messages:\n$out");
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Look for startup msg
|
# Look for startup msg
|
||||||
unless (defined match_log("error", qr/resuming normal operations/, 10)) {
|
unless (defined match_log("error", qr/resuming normal operations/, 10)) {
|
||||||
quit(1, "Httpd server failed to start.");
|
dbg(join(" ", map { quote_shell($_) } @p));
|
||||||
|
dbg(match_log("error", qr/(^.*ModSecurity: .*)/sm, 10));
|
||||||
|
msg("Httpd server failed to start.");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $rc;
|
return $rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub httpd_stop {
|
sub httpd_stop {
|
||||||
httpd_reset_logs();
|
my $t = shift;
|
||||||
my @p = (
|
my @p = (
|
||||||
$HTTPD,
|
$HTTPD,
|
||||||
-d => $opt{S},
|
-d => $opt{S},
|
||||||
@@ -487,9 +536,6 @@ sub httpd_stop {
|
|||||||
-k => "stop",
|
-k => "stop",
|
||||||
);
|
);
|
||||||
|
|
||||||
#dbg("EXEC: ", \@p);
|
|
||||||
# dbg("Httpd stop");
|
|
||||||
|
|
||||||
my $httpd_out;
|
my $httpd_out;
|
||||||
my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1);
|
my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1);
|
||||||
my $out = join("\\n", split(/\n/, <$httpd_out>));
|
my $out = join("\\n", split(/\n/, <$httpd_out>));
|
||||||
@@ -504,7 +550,7 @@ sub httpd_stop {
|
|||||||
my $rc = $?;
|
my $rc = $?;
|
||||||
if ( WIFEXITED($rc) ) {
|
if ( WIFEXITED($rc) ) {
|
||||||
$rc = WEXITSTATUS($rc);
|
$rc = WEXITSTATUS($rc);
|
||||||
# dbg("Httpd stop returned with $rc.");
|
dbg("Httpd stop returned with $rc.") if ($rc);
|
||||||
}
|
}
|
||||||
elsif( WIFSIGNALED($rc) ) {
|
elsif( WIFSIGNALED($rc) ) {
|
||||||
msg("Httpd stop failed with signal " . WTERMSIG($rc) . ".");
|
msg("Httpd stop failed with signal " . WTERMSIG($rc) . ".");
|
||||||
@@ -517,14 +563,17 @@ sub httpd_stop {
|
|||||||
|
|
||||||
# Look for startup msg
|
# Look for startup msg
|
||||||
unless (defined match_log("error", qr/caught SIG[A-Z]+, shutting down/, 10)) {
|
unless (defined match_log("error", qr/caught SIG[A-Z]+, shutting down/, 10)) {
|
||||||
quit(1, "Httpd server failed to shutdown.");
|
dbg(join(" ", map { quote_shell($_) } @p));
|
||||||
|
msg("Httpd server failed to shutdown.");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $rc;
|
return $rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub httpd_reload {
|
sub httpd_reload {
|
||||||
httpd_reset_logs();
|
my $t = shift;
|
||||||
|
httpd_reset_fd($t);
|
||||||
my @p = (
|
my @p = (
|
||||||
$HTTPD,
|
$HTTPD,
|
||||||
-d => $opt{S},
|
-d => $opt{S},
|
||||||
@@ -533,9 +582,6 @@ sub httpd_reload {
|
|||||||
-k => "graceful",
|
-k => "graceful",
|
||||||
);
|
);
|
||||||
|
|
||||||
# dbg("EXEC: ", join(' ', map { "'$_'" } @p));
|
|
||||||
# dbg("Httpd reload");
|
|
||||||
|
|
||||||
my $httpd_out;
|
my $httpd_out;
|
||||||
my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1);
|
my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1);
|
||||||
my $out = join("\\n", split(/\n/, <$httpd_out>));
|
my $out = join("\\n", split(/\n/, <$httpd_out>));
|
||||||
@@ -550,7 +596,7 @@ sub httpd_reload {
|
|||||||
my $rc = $?;
|
my $rc = $?;
|
||||||
if ( WIFEXITED($rc) ) {
|
if ( WIFEXITED($rc) ) {
|
||||||
$rc = WEXITSTATUS($rc);
|
$rc = WEXITSTATUS($rc);
|
||||||
# dbg("Httpd reload returned with $rc.");
|
dbg("Httpd reload returned with $rc.") if ($rc);
|
||||||
}
|
}
|
||||||
elsif( WIFSIGNALED($rc) ) {
|
elsif( WIFSIGNALED($rc) ) {
|
||||||
msg("Httpd reload failed with signal " . WTERMSIG($rc) . ".");
|
msg("Httpd reload failed with signal " . WTERMSIG($rc) . ".");
|
||||||
@@ -563,36 +609,74 @@ sub httpd_reload {
|
|||||||
|
|
||||||
# Look for startup msg
|
# Look for startup msg
|
||||||
unless (defined match_log("error", qr/resuming normal operations/, 10)) {
|
unless (defined match_log("error", qr/resuming normal operations/, 10)) {
|
||||||
quit(1, "Httpd server failed to reload.");
|
dbg(join(" ", map { quote_shell($_) } @p));
|
||||||
|
msg("Httpd server failed to reload.");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $rc;
|
return $rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub httpd_reset_logs {
|
sub httpd_reset_fd {
|
||||||
# Error
|
my($t) = @_;
|
||||||
if (!defined $LOG{error}{fd}) {
|
|
||||||
$LOG{error}{fd} = new FileHandle($opt{E}, O_RDWR|O_CREAT)
|
# Cleanup
|
||||||
|
for my $key (keys %FILE) {
|
||||||
|
if (exists $FILE{$key}{fd} and defined $FILE{$key}{fd}) {
|
||||||
|
$FILE{$key}{fd}->close();
|
||||||
}
|
}
|
||||||
$LOG{error}{fd}->blocking(0);
|
delete $FILE{$key};
|
||||||
$LOG{error}{fd}->sysseek(0, 2);
|
}
|
||||||
$LOG{error}{buf} = "";
|
|
||||||
|
# Error
|
||||||
|
$FILE{error}{fn} = $opt{E};
|
||||||
|
$FILE{error}{fd} = new FileHandle($opt{E}, O_RDWR|O_CREAT);
|
||||||
|
$FILE{error}{fd}->blocking(0);
|
||||||
|
$FILE{error}{fd}->sysseek(0, 2);
|
||||||
|
$FILE{error}{buf} = "";
|
||||||
|
|
||||||
# Audit
|
# Audit
|
||||||
if (!defined $LOG{audit}{fd}) {
|
$FILE{audit}{fn} = $opt{A};
|
||||||
$LOG{audit}{fd} = new FileHandle($opt{A}, O_RDWR|O_CREAT);
|
$FILE{audit}{fd} = new FileHandle($opt{A}, O_RDWR|O_CREAT);
|
||||||
}
|
$FILE{audit}{fd}->blocking(0);
|
||||||
$LOG{audit}{fd}->blocking(0);
|
$FILE{audit}{fd}->sysseek(0, 2);
|
||||||
$LOG{audit}{fd}->sysseek(0, 2);
|
$FILE{audit}{buf} = "";
|
||||||
$LOG{audit}{buf} = "";
|
|
||||||
|
|
||||||
# Debug
|
# Debug
|
||||||
if (!defined $LOG{debug}{fd}) {
|
$FILE{debug}{fn} = $opt{D};
|
||||||
$LOG{debug}{fd} = new FileHandle($opt{D}, O_RDWR|O_CREAT);
|
$FILE{debug}{fd} = new FileHandle($opt{D}, O_RDWR|O_CREAT);
|
||||||
|
$FILE{debug}{fd}->blocking(0);
|
||||||
|
$FILE{debug}{fd}->sysseek(0, 2);
|
||||||
|
$FILE{debug}{buf} = "";
|
||||||
|
|
||||||
|
# Any extras listed in "match_log"
|
||||||
|
if ($t and exists $t->{match_log}) {
|
||||||
|
for my $k (keys %{ $t->{match_log} || {} }) {
|
||||||
|
my($neg,$fn) = ($k =~ m/^(-?)(.*)$/);
|
||||||
|
next if (!$fn or exists $FILE{$fn});
|
||||||
|
#dbg("Opening additional log: $fn");
|
||||||
|
$FILE{$fn}{fn} = $fn;
|
||||||
|
$FILE{$fn}{fd} = new FileHandle($fn, O_RDWR|O_CREAT);
|
||||||
|
$FILE{$fn}{fd}->blocking(0);
|
||||||
|
$FILE{$fn}{fd}->sysseek(0, 2);
|
||||||
|
$FILE{$fn}{buf} = "";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Any extras listed in "match_file"
|
||||||
|
if ($t and exists $t->{match_file}) {
|
||||||
|
for my $k (keys %{ $t->{match_file} || {} }) {
|
||||||
|
my($neg,$fn) = ($k =~ m/^(-?)(.*)$/);
|
||||||
|
next if (!$fn or exists $FILE{$fn});
|
||||||
|
#dbg("Opening file: $fn");
|
||||||
|
$FILE{$fn}{fn} = $fn;
|
||||||
|
$FILE{$fn}{fd} = new FileHandle($fn, O_RDWR|O_CREAT);
|
||||||
|
$FILE{$fn}{fd}->blocking(0);
|
||||||
|
$FILE{$fn}{buf} = "";
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$LOG{debug}{fd}->blocking(0);
|
|
||||||
$LOG{debug}{fd}->sysseek(0, 2);
|
|
||||||
$LOG{debug}{buf} = "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub encode_chunked {
|
sub encode_chunked {
|
||||||
|
Reference in New Issue
Block a user