Authorization based on custom Header (Apache)
Solution 1
This should do it. If you have a comma-separated list in header groupmembership
, then use the first regex expression. One value in the list has to match to grant access.
If you want to match an exact value in iv_groupmembership
, then uncomment the second third expression (and comment the first).
Edit:
- added
RequestHeader set role
example, uncomment as needed. I only tested this withHeader set role
(in the response, no backend), but should work withRequestHeader
the same way.
Edit2:
- removed
<Location/>
for clarity - added example for
X-Auth-Token
check combined withgroupmembership
Sample config:
# test for string in comma-separated values, one of the values must match
<If "%{HTTP:groupmembership} !~ /(^|,)(viewer|publisher|administrator)(,|$)/">
# combined: X-Auth-Token must be set or one of the roles must exist
# <If "%{HTTP:X-Auth-Token} == '' && %{HTTP:groupmembership} !~ /(^|,)(viewer|publisher|administrator)(,|$)/">
# alternative: test for a single value in "iv_groupmembership" (exact match)
# <If "! %{HTTP:iv_groupmembership} in { 'viewer', 'publisher', 'administrator' }">
Require all denied
</If>
# set role of groupmembership
# <If "%{HTTP:groupmembership} =~ /(^|,)viewer(,|$)/">
# RequestHeader set role viewer
# </If>
# <ElseIf "%{HTTP:groupmembership} =~ /(^|,)publisher(,|$)/">
# RequestHeader set role publisher
# </ElseIf>
# <ElseIf "%{HTTP:groupmembership} =~ /(^|,)administrator(,|$)/">
# RequestHeader set role administrator
# </ElseIf>
# set role of iv_groupmembership
# RequestHeader set role "expr=%{HTTP:iv_groupmembership}"
Modifying the headers in Apache (like RequestHeader set iv_groupmembership "viewer"
) to debug/test the config doesn't work,
you need to set the header very early.
https://httpd.apache.org/docs/2.4/expr.html#vars
The expression parser provides a number of variables of the form %{HTTP_HOST}. Note that the value of a variable may depend on the phase of the request processing in which it is evaluated. For example, an expression used in an <If > directive is evaluated before authentication is done. Therefore, %{REMOTE_USER} will not be set in this case.
You can test the configuration with wget from commandline, replace localhost
with your hostname.
# HTTP/1.1 403 Forbidden
wget -S -O - http://localhost
wget -S -O - --header='groupmembership: xviewer' http://localhost
wget -S -O - --header='groupmembership: administratorx' http://localhost
wget -S -O - --header='iv_groupmembership: xviewer' http://localhost
# HTTP/1.1 200 OK
wget -S -O - --header='groupmembership: foo,viewer' http://localhost
wget -S -O - --header='groupmembership: publisher,foo' http://localhost
wget -S -O - --header='iv_groupmembership: viewer' http://localhost
wget -S -O - --header='iv_groupmembership: publisher' http://localhost
wget -S -O - --header='iv_groupmembership: administrator' http://localhost
Tested with Apache/2.4.25 (Debian)
Solution 2
Thanks @Freddy,
I got it working with the Require
and Expressions directives for the authorization part and putting the RequestHeader
directive inside <Virtualhost>
for setting the Role-Headers.
The final config looks like so:
<VirtualHost *:443>
ServerName ...
DocumentRoot /var/www/html
SSLEngine on
SSLProtocol all -SSLv2 -SSLv3
SSLCipherSuite HIGH:3DES:!aNULL:!MD5:!SEED:!IDEA
SSLCertificateFile /etc/httpd/ssl/...
SSLCertificateKeyFile /etc/httpd/ssl/...
RewriteEngine on
#ProxyPreserveHost on
<Proxy *>
Allow from all
</Proxy>
ProxyRequests Off
# store variable values with dummy rewrite rules
RewriteRule . - [E=req_scheme:%{REQUEST_SCHEME}]
RewriteRule . - [E=http_host:%{HTTP_HOST}]
RewriteRule . - [E=req_uri:%{REQUEST_URI}]
# set header with variables
RequestHeader set X-RSC-Request "%{req_scheme}e://%{http_host}e%{req_uri}e"
RewriteCond %{HTTP:Upgrade} =websocket
RewriteRule /(.*) ws://localhost:3939/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket
RewriteRule /(.*) http://localhost:3939/$1 [P,L]
# test for string in comma-separated values, one of the values must match
<If "%{HTTP:iv_groupmembership} !~ /(^|,)(Viewer|Publisher|Administrator)(,|$)/ && -z %{HTTP:X-Auth-Token} && %{HTTP:Authorization} !~ /Key .+/">
Require all denied
</If>
# set role of groupmembership
<If "%{HTTP:iv_groupmembership} =~ /(^|,)Viewer(,|$)/">
RequestHeader set Role viewer
</If>
<ElseIf "%{HTTP:iv_groupmembership} =~ /(^|,)Publisher(,|$)/">
RequestHeader set Role publisher
</ElseIf>
<ElseIf "%{HTTP:iv_groupmembership} =~ /(^|,)Administrator(,|$)/">
RequestHeader set Role administrator
</ElseIf>
</Virtualhost>
Thanks a lot!
Edit: how can I add additional logic in the Require
directive, so that if the header X-Auth-Token
is present, access is granted anyway?
Edit2: Thanks again @Freddy for the pointer to Apache expression-logic, I adapted the test for existence of the "X-Auth-Token" Header with -z %{HTTP:X-Auth-Token}
and could add another condition to pass through requests containing a key inside the "authorization" header.
Related videos on Youtube
juo
Updated on September 18, 2022Comments
-
juo almost 2 years
I have a service running behind a Apache Reverse-Proxy that uses the custom headers "username" and "role" to identify users and their role.
I want Apache HTTPD to restrict access to to people whose custom HTTP-header "groupmembership" contains one of the following: "viewer","publisher","administrator".
The Apache sits behind another proxy which authenticates users and populates the HTTP Headers "username" and "groupmembership" where the contents of "groupmembership" is a comma-separated list with groups.
For reference I have included a draft of the architecture. http-proxy-auth
How would this be possible? I have tried using a require directive like
Require expr %{HTTP:iv_groupmembership} in { 'viewer', 'publisher', 'administrator' }
inside<Location />
to no avail.Could this instead work with mod_rewrite?
Here is the reverse-proxy config using mod_proxy and mod_rewrite:
RewriteEngine on <Proxy *> Allow from all </Proxy> ProxyRequests Off # store variable values with dummy rewrite rules RewriteRule . - [E=req_scheme:%{REQUEST_SCHEME}] RewriteRule . - [E=http_host:%{HTTP_HOST}] RewriteRule . - [E=req_uri:%{REQUEST_URI}] # set header with variables RequestHeader set X-RSC-Request "%{req_scheme}e://%{http_host}e%{req_uri}e" RewriteCond %{HTTP:Upgrade} =websocket RewriteRule /(.*) ws://localhost:3939/$1 [P,L] RewriteCond %{HTTP:Upgrade} !=websocket RewriteRule /(.*) http://localhost:3939/$1 [P,L] ProxyPass / http://172.17.0.1:3939/ ProxyPassReverse / http://172.17.0.1:3939/
Thanks for any hints.
Edit: Basically, the question boils down to: How can I check if the comma-separated list in the
groupmembership
Header contains either 'Administrator', 'Publisher' or 'Viewer' -
juo about 5 yearsNow, I actually need both: authorized access and setting a header "role" based on an exact match.
-
Gerrit about 5 yearsThere is one problem. The RewriteRule [P,L] lines will take precedence on the Location blocks.
-
Freddy about 5 yearsSee my Edit2, I also changed the syntax of the negated regex expr from
! %{..} =~
to%{..} !~
, just so you know when you adapt the example. -
juo about 5 yearsThanks @Freddy, it's working as expected, now =)