在上一篇博文中我们已经简要介绍了 Keystone,该篇就重点介绍 Keystone Token(Scoped)认证的流程。
有一张图很好概括了 Keystone 的工作流程(图的原始来源目前尚未找到):
下边我们主要介绍该图虚拟机创建前的流程。
获取 Token
注:因写作本文时,手头尚无搭建好的 OpenStack 用于实验,所以获取 Token 部分的例子摘录自博文 OpenStack Keystone 工作流
获取临时 Token
假设我们已经在 OpenStack 创建了用户和相应的租户,那我们可以通过用户名密码获得临时 Token:
1 | 示例请求: |
获取用户可以访问的租户
这里其实有个疑问,就是既然已经拿到了(临时) Token 了,直接用这个 Token 去访问资源(list servers)不就行了吗,为什么还要获取用户的租户?
看起来可行,实际却不可行,主要原因如下:
- 本质上,租户才是资源的集合,用户要有获取资源的权限,必须绑定到具体的租户上;而且在 OpenStack 中,一个用户可以对应于多个租户,在访问资源时必须指出要访问哪个租户的资源。以上所获取到的临时 Token 其实是一个 Unscoped Token,也即没有租户(及域)信息的 Token。所以,为了能够访问到具体租户的资源,需要找出该用户的租户,然后选择一个租户再次请求一个 Scoped Token,这样才可以访问该租户的资源。
1 | GET http://192.168.56.2:5000/v2.0/tenants |
获取指定租户的 Token
这里最重要的一点在于获取到的 Token 包含了资源的服务目录(Service Catalog)
。其中重要的是 端点(Endpoints)
,也即 OpenStack 各 API 服务(如 nova-api)的 URL(由这些 URL 可以路由到对应的后台服务,进而访问资源)。
1 | 示例请求: |
接下来,我们就可以通过 URL 列出所有虚拟机:
1 | GET http://192.168.56.2:8774/v2/0e877c09c1924963800c7534bc03106e/servers |
那 Keystone 是怎么认证这个随请求一起发过来的 Token 呢?请看下文
Token 认证源码分析
其实,我们要是去 Keystone 源码中找 Token 认证(不是 CRUD)相关的代码还是真的找不到,拿这一块代码在哪里呢?且让我们来看看 Nova 的 PasteDeploy 配置文件:
1 | [composite:osapi_compute] |
从这个配置文件中,当 Nova 的认证方式为 keystone(在 /etc/nova/nova.conf 中指定)时,Token 的认证是通过 keystonemiddleware 模块来进行的。当然,keystonemiddleware 需要到 keystone 模块获取 Token 的详细信息。所以,大家可以看出,如果每个模块都选择了 keystone 认证,那对这些模块的请求都需要走一次 Token 认证,这对 Keystone 模块的压力是比较大的。
下边就让我们进入源码分析(不会很细,重在流程)。
keystonemiddleware
keystonemiddleware/auth_token/__init__.py
文件对 keystonemiddleware 的功能已经介绍的相当明了:
1 | Token-based Authentication Middleware. |
也即,keystonemiddleware(本身是也一个 WSGI 应用):
- 验证 Token 是否合法
- 拒绝非法请求(除非该中间件处于 delay_auth_decision 模式)
- 收集并转发合法 Token 的信息(如用户名)
该中间件接受两种类型的 Token,即 auth_token 和 service_token。这里我们只关注前者。
另外,我们还可以参考官方的文档:
接下来,我们来看一下源码骨架:
1 | # keystonemiddleware/auth_token/__init__.py |
1. 去除请求中跟认证相关的头部
为了防止伪造认证的请求,去除掉跟认证信息相关的头部是非常有必要的,请看详细:
1 | # keystonemiddleware/auth_token/__init__.py |
2. 获取 Token
获取 Token 分了几个主要步骤:
- 从缓存中获取(如有,需进行合法性检查)
- 如无,则利用 PKI 或从 Keystone(identity server) 获取 Token。
注:PKI 属于本地认证,这样可以减轻 Keystone 的压力。
详细请看源码:
1 | # keystonemiddleware/auth_token/__init__.py |
2. 验证 Token
主要验证 Token 是否已经失效:
1 | # keystonemiddleware/auth_token/__init__.py |
4. 判断认证状态
1 | # keystonemiddleware/auth_token/__init__.py |
5. 设置头部信息
这部分特别重要,因为这部分会把请求用户已认证的详细身份信息填写到请求头部中,作为 keystonemiddleware 下一个 WSGI 应用的认证信息(请看下文的 Nova Context 部分)。
1 | # keystonemiddleware/auth_token/__init__.py |
Nova Context
请求经过 keystonemiddleware 处理后,就会进行下一步,设置具体模块 Nova 的请求上下文 context,详细源码如下:
1 | # nova/api/auth.py |
接下来的请求操作就可以利用以上设置的上下文了。
访问控制和授权
如果你足够细心,会发现其实以上 Keystonemiddleware 的 Token 认证并没有完成一件很重要的事——访问控制和授权
。这也不能怪 Keystonemiddleware,因为这跟其主要定位于校验 Token 合法性有关系,关注的是大的方面的权限验证;而对于很多模块而言,如 nova,资源多种多样,每种资源的操作权限可能都不一样,如果让 Keystonemiddleware 也来做这些很细的东西,那它将会是非常复杂,还不如把这些小的方面的权限控制放到具体模块去,由具体模块来控制实施。这样各个模块在复用 Keystonemiddleware 的同时还能够保持灵活性。
这进一步的访问控制是通过 Policy 机制来完成的。具体可以参考以前博文 OpenStack 源码系列之 Keystone:简介及基本概念介绍的“访问管理和授权”部分。