我有一个在 Apache 反向代理后面运行的服务,它使用自定义标头“用户名”和“角色”来识别用户及其角色。
我希望 Apache HTTPD 将访问限制为自定义 HTTP 标头“groupmembership”包含以下之一的人员:“viewer”、“publisher”、“administrator”。
Apache 位于另一个代理后面,该代理对用户进行身份验证并填充 HTTP 标头“用户名”和“组成员”,其中“组成员”的内容是以逗号分隔的组列表。
为了参考,我附上了一份建筑草图。 http-代理-身份验证
这怎么可能呢?我尝试使用类似Require expr %{HTTP:iv_groupmembership} in { 'viewer', 'publisher', 'administrator' }
inside 的require 指令<Location />
,但无济于事。
这能与 mod_rewrite 一起使用吗?
以下是使用 mod_proxy 和 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/
谢谢任何提示。
编辑:基本上,问题归结为:如何检查groupmembership
标题中的逗号分隔列表是否包含“管理员”,“发布者”或“查看者”
答案1
这应该可以做到。如果 header 中有一个逗号分隔的列表groupmembership
,则使用第一个正则表达式。列表中的一个值必须匹配才能授予访问权限。
如果您想要匹配中的精确值iv_groupmembership
,则取消注释第二个第三个表达式(并注释第一个)。
编辑:
- 添加了
RequestHeader set role
示例,根据需要取消注释。我只用Header set role
(在响应中,没有后端)测试了这一点,但应该可以用RequestHeader
相同的方式工作。
编辑2:
<Location/>
为清晰起见删除X-Auth-Token
添加了与检查相结合的示例groupmembership
示例配置:
# 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}"
修改 Apache 中的标题(如RequestHeader set iv_groupmembership "viewer"
)来调试/测试配置不起作用,您需要尽早设置标题。
https://httpd.apache.org/docs/2.4/expr.html#vars
表达式解析器提供了许多形式为 %{HTTP_HOST} 的变量。请注意,变量的值可能取决于请求处理中对其进行评估的阶段。例如,在 <If > 指令中使用的表达式在进行身份验证之前进行评估。因此,在这种情况下不会设置 %{REMOTE_USER}。
您可以从命令行使用 wget 测试配置,并将其替换localhost
为您的主机名。
# 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
使用 Apache/2.4.25 (Debian) 测试
答案2
感谢@Freddy,我使用Require
和表达式指令完成了授权部分的工作,并将RequestHeader
指令放入其中<Virtualhost>
以设置 Role-Headers。
最终配置如下:
<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>
多谢!
编辑:我如何在Require
指令中添加额外的逻辑,以便如果标题X-Auth-Token
存在,无论如何都授予访问权限?
编辑2:再次感谢@Freddy 指向 Apache 表达式逻辑,我调整了“X-Auth-Token”标头存在性的测试,-z %{HTTP:X-Auth-Token}
并可以添加另一个条件来传递包含“授权”标头内的密钥的请求。