OSSN/OSSN-0068
重复的令牌撤销请求可能导致服务降级或中断
总结
目前,单个用户在任何给定时间范围内发起 Keystone 令牌撤销请求的频率没有限制。如果用户重复发起令牌请求,然后立即撤销令牌,可能会导致性能下降,并且可能针对 Keystone 发起 DoS(拒绝服务)攻击。
受影响的服务 / 软件
所有使用 Keystone 的服务。Mitaka, Liberty, Kilo, Nova, Juno, Havana, Icehouse, Grizzly, Folsom, Essex。
讨论
令牌撤销可以自助完成,且对任何用户(包括服务用户)发起的令牌撤销次数没有强制限制。
如果令牌撤销快速连续进行,由于 revocation_event 表中的条目不断增加,响应时间会开始变长。
由于没有速率限制机制,单个用户可能导致 OpenStack 认证服务响应时间变慢,从而导致类似 DoS 攻击的情况。
确实存在对撤销事件的清理机制,基于令牌过期时间加上 expiration_buffer(默认值为 30 分钟)。但是,对于默认令牌 TTL 为 3600 秒的情况,用户在那个时间内可能潜在地填充大约数千个事件。
建议的操作
对于当前的稳定 OpenStack 版本(Mitaka 及更早版本),建议操作员部署外部速率限制代理或 Web 应用程序防火墙,为 Keystone 提供第一层保护。
可以考虑以下解决方案,但关键在于操作员在配置任何速率限制功能时,应仔细规划并考虑 OpenStack 云中用户和服务的各个性能需求。
Repose
速率限制过滤器
Repose 提供了一个速率限制过滤器,可以限制每个 IP 地址以及特定 HTTP 方法(DELETE,与此 OSSN 相关)的请求。
以下配置可用于单个节点。对于更复杂的部署,可以构建集群,利用分布式数据存储。
system-model.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<system-model xmlns="http://docs.openrepose.org/repose/system-model/v2.0">
<repose-cluster id="repose">
<nodes>
<node id="repose_node1" hostname="localhost" http-port="8080"/>
</nodes>
<filters>
<filter name="ip-user"/>
<filter name="rate-limiting"/>
</filters>
<services>
</services>
<destinations>
<endpoint id="keystone" protocol="http" hostname="http://idenity-server.acme.com" root-path="/" port="35357" default="true"/>
</destinations>
</repose-cluster>
<phone-home enabled="false"
origin-service-id="your-service"
contact-email="your@service.com"/>
</system-model>
ip-user.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<ip-user xmlns="http://docs.openrepose.org/repose/ip-user/v1.0">
<user-header name="X-PP-User" quality="0.4"/>
<group-header name="X-PP-Groups" quality="0.4"/>
<group name="ipv4-group">
<cidr-ip>192.168.0.0/24</cidr-ip>
</group>
<group name="match-all">
<cidr-ip>0.0.0.0/0</cidr-ip>
<cidr-ip>0::0/0</cidr-ip>
</group>
</ip-user>
- 注意:使用 ip-user 过滤器意味着向 Repose 发送请求的每个 IP 地址都将拥有自己的速率限制桶。因此,任何超出限制的 IP 地址都将被阻止 - 但仅阻止该 IP 地址。如果您将 NAT 连接发送到 Repose,则应考虑它们也将被视为单个 IP 地址,并相应地分组。
rate-limiting.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<rate-limiting xmlns="http://docs.openrepose.org/repose/rate-limiting/v1.0">
<request-endpoint uri-regex="/limits" include-absolute-limits="false"/>
<global-limit-group>
<limit id="global" uri="*" uri-regex=".*" value="1000" unit="MINUTE"/>
</global-limit-group>
<limit-group id="limited" groups="limited" default="true">
<limit id="all" uri="/auth/token" uri-regex="/.*" http-methods="DELETE" unit="MINUTE" value="10"/>
</limit-group>
<limit-group id="unlimited" groups="unlimited" default="false"/>
</rate-limiting>
</code>
需要注意的关键点:速率限制仅限于 DELETE 请求(这是用于撤销令牌的 http 方法),以及 URI /auth/token。任何超过每分钟 10 个撤销请求的 IP 地址都将被阻止 1 分钟。
更多详细信息可以在 openrepose wiki 上找到
https://repose.atlassian.net/wiki/display/REPOSE/Rate+Limiting+filter
其他可能的解决方案
NGINX
NGINX 提供了 limit_req_module,可用于提供全局速率限制。使用 map,可以将其限制为仅 DELETE 方法。
nginx.conf
http {
map $request_method $keystone {
default "";
DELETE $binary_remote_addr;
}
limit_req_zone $keystone zone=keystone:10m rate=10r/m;
server {
...
location /auth/token {
limit_req zone=keystone;
...
}
}
更多详细信息可以在 nginx 网站上找到
https://nginx.ac.cn/en/docs/http/ngx_http_limit_req_module.html
HAProxy
HAProxy 可以使用 stick-tables 和通用计数器 (gpc) 提供固有的速率限制
/etc/init.d/haproxy.cfg
# Monitors the number of request sent by an IP over a period of 10 seconds
stick-table type ip size 1m expire 10s store gpc0,http_req_rate(10s)
tcp-request connection track-sc1 src
tcp-request connection reject if { src_get_gpc0 gt 0 }
更多详细信息可以在 haproxy 网站上找到
http://blog.haproxy.com/2012/02/27/use-a-load-balancer-as-a-first-row-of-defense-against-ddos
Apache
这里可以探索许多解决方案。
mod_ratelimit
https://httpd.apache.org/docs/2.4/mod/mod_ratelimit.html
mod_qos
http://opensource.adnovum.ch/mod_qos/dos.html
mod_evasive
mod_security
联系方式 / 参考文献
- 作者:Luke Hinds, Red Hat
- 此 OSSN:https://wiki.openstack.org/wiki/OSSN/OSSN-0068
- 原始 LaunchPad Bug:https://bugs.launchpad.net/nova/+bug/1553324
- OpenStack Security ML:openstack-security@lists.openstack.org
- OpenStack Security Group:https://launchpad.net/~openstack-ossg