This module enables Regular Expression filtering on URL, including HTTP method, URI, QUERY_STRING and body content (DATA).
This module is specially designed for Apache server to act as a secure
reverse proxy server, filtering access to CGI and their parameters.
Here is an example:
# Deny all EAccessEnable on # Log grants and denies to logs/eaccess_log EAccessLogLevel 1 # Allow cgi toto, called by GET, without any QUERY_STRING. EAccessRule permit "^GET /cgi-bin/toto$" # Allow cgi titi, called by GET, with a QUERY_STRING starting with the text # "field1=". EAccessRule permit "^GET /cgi-bin/titi\?field1=" # The same, called by POST. EAccessRule permit "^POST /cgi-bin/titi\|field1=" # Allow cgi tata, called by GET, with 2 arguments. Each argument contains # up to 32 characters. EAccessRule permit "^GET /cgi-bin/tata\?field1=[^&|]{0,32}&field2=[^&|]{0,32}$" # Deny all others cgi EAccessRule deny "^GET /cgi-bin/" EAccessRule deny "^GET /.*\.cgi" # Allow all others URL, called by GET EAccessRule permit "^GET /" |
Extract apache:
% tar zxf apache_1.x.y.tar.gz |
Extract mod_eaccess:
% tar zxf mod_eaccess-x.y.tar.gz |
Configure apache with mod_eaccess:
% cd apache_1.x.y % ./configure --add-module=../mod_eaccess-x.y/mod_eaccess.c ... |
Compile:
% make |
See Limitation if you want special installation (specialy for complete body checks).
on
| off
EAccessEnable on
The EAccessEnable directive enables or disables the runtime extended access control engine.
If it is set to off
this module does no runtime processing at
all.
If it is set to on
this module does runtime processing and
then first sets default policy to deny all.
The EAccessRule
directive is the real extended access control
workhorse. The directive can occur more than once. Each directive then
defines one single access control rule. The definition order of these rules
is important, because this order is used when applying the rules at
run-time.
pattern can be extended regular expression which gets applied to the current URL. The following "options" can be used for pattern:
"Pattern" is an Apache directive argument, so if pattern includes the " character, then you also need to use a \escape.
action can be one of permit
,
deny
, warning
,
auth/basic
, auth/securid
or
exec
:
permit
"pattern"
deny
[=
err-code] "pattern"
warning
"pattern"
auth/basic
[=
TTL] "pattern"
["realm"]
auth/securid
[=
TTL] "pattern"
["redirect"]
exec
[=
err-code] "pattern"
"command"
%s
" in "command" string. If you are using
mod_eaccess with BODY_HACK (see
Limitation), then "%s
" will be
converted to the body filename; command can read this file to
access body content.
If you are not using BODY_HACK, then "%s
" will
be converted to "-
"; command can then read its
stdin to access body content.
%s
" and the URL has no body, then it will be
converted to the keyword none
.
For each requested URL, the module constructs the following string for controls:
?
:
|
:
Then this string is unescaped: `%encoded' characters are translated into
their corresponding values, except for:
%00 ('\0') => "\0",
%07 ('\a') => "\a",
%08 ('\b') => "\b",
%0A ('\n') => "\n",
%0B ('\v') => "\v",
%0C ('\f') => "\f",
%0D ('\r') => "\r",
%26 ('&') => ".",
%7C ('|') => ".".
CR-LF (character \r followed by character \r)
are also translated into string \n.
This makes URL arguments and body easier to match.
Then the unescaped string is used by the module to try to match a
pattern defined in EAccessRule
.
As default policy is set to deny all when extended access control is set to on, the algorithm used for each URL by the module is:
EAccessRule
directive:
!
pattern), then:
permit
action: access is granted and loop stops,
deny
action: access is denied (error 403 or
err-code) and loop stops,
warning
action: a
** WARNING **
is logged,
auth/*
action:
auth/basic
: access is denied (error 401
with the realm is returned) and loop stops,
auth/securid
: access is redirected (error
302 with the the redirect is returned) and loop stops,
auth/basic
: access is denied (error
401 with the realm is returned) and loop stops,
auth/securid
: access is redirected
(error 302 with the redirect is returned) and loop
stops,
exec
action:
In fact, for auth/*
action, if option is set in the
rule, we do not trust the web server because authentication is first
checked by mod_eaccess.
If option is not set, we do trust the web server and then do
not check if an authentication is set in the HTTP header.
EAccessLog logs/eaccess_log
The EAccessLog
directive sets the name of the file to which the server logs any extended access controls it performs.
If the name begins with a pipe ('|'), followed by a command, then it is
assumed to be a program that receives the agent log information on its
standard input.
Else, it must be a filename. If the filename does not begin with a slash
('/') then it is assumed to be relative to the Server Root.
When EAccessLogLevel
is set, each action logs a line in the common log format (host, ident, authuser, date), followed by a text, depending upon the action:
RE #nnn grants access to '
METHOD URI[?args[|data]]'
RE #nnn denies access to '
METHOD URI[?args[|data]]'
default denies access to '
METHOD URI[?args[|data]]'
RE #nnn *** WARNING! *** '
METHOD URI[?args[|data]]'
RE #nnn AUTH not needed '
METHOD URI[?args[|data]]'
EAccessRule auth/*
,
and no auth HTTP header is present,
RE #nnn AUTH starting on '
METHOD URI[?args[|data]]'
RE #nnn AUTH unTTLed for '
METHOD URI[?args[|data]]'
RE #nnn AUTH not expired '
METHOD URI[?args[|data]]'
RE #nnn AUTH too old for '
METHOD URI[?args[|data]]'
EAccessRule auth/*
, and auth HTTP header is expired (error 401 for auth/basic or 302 for auth/securid is returned),
RE #nnn AUTH removed for '
METHOD URI[?args[|data]]'
EAccessRule auth/*
, and auth HTTP header is expired (this header is then removed),
RE #nnn AUTH err 401 for '
METHOD URI[?args[|data]]'
EAccessRule auth/basic
, and no Authenticate:
HTTP header is present,
RE #nnn AUTH err 302 for '
METHOD URI[?args[|data]]'
EAccessRule auth/securid
, and no Cookie: AceHandle
HTTP header is present,
RE #nnn grants access to '
METHOD URI[?args[|<datas...>
]]'
[because '
stdout...'
]<datas...>
is used instead of body content;
RE #nnn denies access to '
METHOD URI[?args[|<datas...>
]]' because '
stderr...'
Notice: To disable the logging, it is not recommended to set filename to /dev/null
, because although the module does not create output to a logfile it still creates the logfile output internally. This will slow down the server! To disable logging use EAccessLogLevel 0
.
EAccessLogLevel 1
The EAccessLogLevel
directive set the verbosity level of the extended access control logfile.
Level 0 means no logging.
Level 1 logs which EAccessRule
grants or denies access to URL.
Level > 1 is for debugging.
EAccessCache logs/eaccess_auth
The EAccessCache
directive sets the name of the file to which the server caches user authentication when auth/* is used in EAccessRule
. If the name does not begin with a slash ('/') then it is assumed to be relative to the Server Root.
Notice: To avoid dump of the cache, MD5 digest of the authentications is stored...
EAccessOptim 1
The EAccessOptim
directive sets the optimization level.
Level 0
(default value) means no optimization: only regular
expressions strings are stored in memory.
Level 1
means more optimization: compiled regular expressions
are also stored in memory. With a bad regex lib, this may cost a lot... Use
GNU regex.
EAccessTmpDir /tmp
The EAccessTmpDir
directive sets the temporary directory
used to store body files.
This directive is only available if you compile mod_eaccess with
-DEACCESS_BODY_HACK
(See Limitation).
Only the beginning of the body can be checked; limit is near DEFAULT_BUFSIZE (defined in buff.c), but also depends on system settings:
When Apache receives a connection, it is read from a socket and put into a buffer. Default socket buffer size is controlled with:
So the first socket read of an incomming connection (limit: default socket buffer size, as Apache does not set it) is put into buffer (limit: DEFAULT_BUFSIZE, set to 4096 in buff.c). It is then a good idea to have default socket buffer size greater than DEFAULT_BUFSIZE: we recommend to use at least 8192.
Finally, as the beginning of the HTTP connection includes headers and body, EAccess will use at most for body: DEFAULT_BUFSIZE - sizeof (HTTP headers).
To bypass this limit, we must patch Apache code. The raison is simple: when Apache has read once the body, it cannot read it again (because of socket...). The little patch mod_eaccess.patch is supplied to enable complete body checks. Do the following:
Patch apache:
% cd apache_1.x.y % patch -b -p 0 < ../mod_eaccess-x.y/mod_eaccess.patch |
% cd apache_1.x.y % make EXTRA_CFLAGS=-DEACCESS_BODY_HACK |
As we said, the string used to match a RE is:
Unparsed URI | URI | because |
/some/path?some+args | /some/path | |
/some/p%61th | /some/path | 0x61 is 'a' |
/some/../path | /path | .. optimization |
/good%3Ffoo/../bad | /bad | 0x3F is '?', but path optim. occurs before %unescape |
/good%3Ffoo/%2E./bad | /good?foo/../bad | %2E is '.', but no path optim., just %unescape |
And the string used to match a RE is exactly:
When EAccess is used with Apache running as a web-server, this is fine because this string is really the URI Apache is looking for.
But when EAccess is used with Apache running as a reverse-proxy, this may be dangerous; suppose for example the URL the reverse-proxy receives is:
The URI in the reverse-proxy running with EAccess will be:
This may be very dangerous if EAccess rules are:
Conclusion: when using EAccess on Apache running as a reverse-proxy, it is a good idea to use the first rule: