概括
当mod_remoteip
使用 apache HTTPD 前面的某个代理时,使用RemoteIPInternalProxy my.proxy.ip
HTTPD 服务器配置中的指令进行配置,如何检查传入请求是否真正由代理转发?显然:
<If "%{REMOTE_ADDR} == 'my.proxy.ip'">
不再起作用。
当且仅当请求来自代理 IP 时,哪个表达式才会返回 true,无论请求来自哪个客户端 IP?
长版本
在我们的内部网中,我们设置了一个代理,让客户端可以访问多个 Web 应用程序,例如:
+-------------+
| |
| Client |
| |
+-------------+
|
|
|
V
+--------------+ +-----------+
| | Authentication | |
| Proxy | <-----------------> | LDAP |
| | | |
+--------------+ +-----------+
|
|
| send header
| Trusted-User: <some.username>
|
V
+-----------------+
| |
+----------------+ | ...
| | |
| Applications |---+
| |
+----------------+
代理还可用作登录服务器,为代理应用程序提供 SSO。这是因为应用程序配置为接受某个 HTTP 标头Trusted-User
以包含要登录的用户的名称。
当然,这个标头很容易被伪造;应用程序 Web 服务器需要检查请求是否确实由代理发送,否则拒绝/忽略标头。应用程序使用 Apache HTTPD 作为 Web 服务器。
目前每个 Web 服务器在配置中都有以下相关部分VirtualHost
:
<If "%{REMOTE_ADDR} != 'my.proxy.ip'">
RequestHeader unset Trusted-User
</If>
到目前为止这都是有效的,但现在我要求某些应用程序应该看到实际的客户端 IP 而不是代理的 IP。
实现此目的的明显方法是mod_remoteip
使用如下配置:
<IfModule mod_remoteip.c>
RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy my.proxy.ip
</IfModule>
但是,一旦我这样做,取消设置Trusted-User
标头的条件就会破坏 SSO,因为这现在会在变量中看到客户端 IP REMOTE_ADDR
。所以我需要另一种方法来访问当前请求来自的 IP。
(诚然,这种检查并不是真正必要的,因为每个应用程序服务器上的防火墙配置都配置为仅接受来自代理的传入 HTTP 流量,但为了防止发生意外,我还是希望进行这种额外的检查。)
所有应用程序服务器上使用 apache HTTPD 2.4.23。
我尝试过
使用X-Forwarded-For
应用程序中的标头获取远程客户端 IP
如果存在的话,某些应用程序可以配置为使用此标头,但其他应用程序则不能;它们使用 PHP 表达式$_SERVER['REMOTE_ADDR']
或以任何语言编写的相应表达式来获取客户端 IP,我真的不想修补这些应用程序。
尝试访问remoteip-proxy-ip-list
变量
在覆盖客户端IP时,模块将中间主机列表存储在注释中
remoteip-proxy-ip-list
,mod_log_config
可以使用%{remoteip-proxy-ip-list}n
格式令牌进行记录。
此外Apache HTTPD 中的表达式函数参考:
功能
[...] | note | Lookup request note [...]
所以我尝试了类似的事情:
<If "! note('remoteip-proxy-ip-list') -strmatch '*my.proxy.ip*'">
RequestHeader unset Trusted-User
</If>
但是,这也始终会删除标头。通过自定义日志格式的检查显示,%{remoteip-proxy-ip-list}n
始终解析为空占位符-
,即使日志格式令牌%{c}a
显示代理 IP,也是如此预期的。
在我看来,我误解了“中间宿主”这个短语,因为这个变量根本没有发挥我认为它应该发挥的作用。
还有 nginx 吗?
由于有多个应用程序,其中一些具有复杂的配置,因此如果可能的话,我更愿意坚持使用 Apache。但是,如果我想要的东西无法通过 Apache HTTPD 实现,我会考虑使用 nginx 的解决方案。
答案1
我应该读一下表达式变量更仔细地看,正如那里所说:
[...] | REMOTE_ADDR | The IP address of the remote host [...] | CONN_REMOTE_ADDR | The peer IP address of the connection (see the mod_remoteip module) [...]
因此有一个预定义变量CONN_REMOTE_ADDR
,并且
<If "%{CONN_REMOTE_ADDR} != 'my.proxy.ip'">
RequestHeader unset Trusted-User
</If>
成功了。
答案2
我还没有测试过这个,但是您是否尝试过在配置开始时将变量设置为 REMOTE_ADDR,并在 If 语句中使用该变量:
Define ORIG_ADDR %{REMOTE_ADDR}
并在 If 语句中使用此变量
<If "${ORIG_ADDR} != 'my.proxy.ip'">
RequestHeader unset Trusted-User
</If>