基于自定义标头的授权(Apache)

基于自定义标头的授权(Apache)

我有一个在 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}并可以添加另一个条件来传递包含“授权”标头内的密钥的请求。

相关内容