设为首页 收藏本站
查看: 1378|回复: 0

[经验分享] 探索 OpenStack 之(13):研究 Keystone

[复制链接]

尚未签到

发表于 2015-4-11 15:51:53 | 显示全部楼层 |阅读模式
  Keystone 是 OpenStack Identity Service 的项目名称。本文就试着尽可能深入地研究 Keystone。

1. Keystone 的功能
  做为 OpenStack 云系统的入口,Keystone 提供了云系统入口所需要的许多功能:
  (1). 用户身份验证:系统得知道用户是不是合法的用户。为此,Keystone 需要对 user 进行管理和保存;管理用户相关的 tenant、role、group 和 domain等;用户 credential 的存放、验证、令牌管理等。
  (2). 服务目录列表:用户需要知道系统提供的服务目录。为此,Keystone 得提供服务目录的管理,包括 service、endpoint 等。

1.1 Keystone 管理的相关概念
  (1)User 用户:一个使用 OpenStack 云服务的人、系统或者服务的数字表示。 用户需要登录,然后被分配 token 去访问资源。用户可以被分配到一个tenant。
(2)Credential 用户证据:用来证明用户身份的证据,可以是用户名和密码、用户名和API key,或者一个 Keystone 分配的身份token。
(3)Authentication 身份验证:验证用户身份的过程。Keystone 服务通过检查用户的 Credential 来确定用户的身份。最开始,使用用户名/密码或者用户名/API key作为credential。当用户的credential被验证后,Kestone 会给用户分配一个 authentication token 供该用户后续的请求使用。
(4)Token 令牌:一个用于访问 OpenStack API 和资源的 alpha 数字字符串。一个 token 可能在任何时间被撤销(revoke),因此其有效期是有限的。OpenStack中,token 是和特定的 tenant 绑定的,因此如果 user 如果访问多个tenant的话他就需要多个tocken。
(5)Tenant 租户:一个用于分组或者隔离资源的容器。一个 tenant 可能对应一个客户、账号、组织或者项目。在 OpenStack 中,用户至少必须在一个tenant中。tenant 容器的可使用资源的限制成为 Tenant Quota,它包括tenant 内各子资源的quota。
(6)Service 服务:一个 OpenStack 服务,比如Nova、Swift或者Glance等。每个服务提供一个或者多个 endpoint 供用户访问资源以及进行操作。
(7)Endpoint 端点:一个网络可访问的服务地址,通过它你可以访问一个服务,通常是个 URL 地址。不同 region 有不同的service endpoint。endpoint告诉也可告诉 OpenStack service 去哪里访问特定的 servcie。比如,当 Nova 需要访问 Glance 服务去获取 image 时,Nova 通过访问 Keystone 拿到 Glance 的 endpoint,然后通过访问该 endpoint 去获取Glance服务。我们可以通过Endpoint的 region 属性去定义多个 region。Endpoint 该使用对象分为三类:


  • adminurl 给 admin 用户使用
  • internalurl 给 OpenStack 内部服务使用来跟别的服务通信
  • publicurl 其它用户可以访问的地址
  比如创建一个使用不同 IP 的 endpoint:

$ keystone endpoint-create \
--region RegionOne \
--service-id=1ff4ece13c3e48d8a6461faebd9cd38f \
--publicurl='https://public-ip:8776/v1/%(tenant_id)s' \
--internalurl='https://management-ip:8776/v1/%(tenant_id)s' \
--adminurl='https://management-ip:8776/v1/%(tenant_id)s'
然后你可以配置 OpenStack service 使用另一个 service 的 endpoint 的 internalurl 去访问另一个资源。
  (8)Role 角色:一个 role 可看着一个ACL的集合。Keystone 中,分配给用户的 token 包含了 role 列表。被访问的服务会判断访问它的用户的角色,以及每个role访问资源或者操作的权限。系统默认使用 admin 和 _member_ role。User 验证的时候必须带有制定的 tenant,roles 会被分配到指定的 tenant。
  (9)Policy 策略:OpenStack 对用户的验证除了 OpenStack 的身份验证以外,还需要鉴别用户对某个服务是否有访问权限。Policy 机制就是用来控制某一个 User 在某个 Tenant 中某个操作的权限。这个 User 能执行什么操作,不能执行什么操作,就是通过 policy 机制来实现的。对于 Keystone 服务来说,policy 就是一个json 文件,默认是 /etc/keystone/policy.json。通过配置这个文件,Keystone Service 实现了对 User 的基于用户角色的权限管理。
  这些Keystone 管理对象之间的关系:
DSC0000.jpg

1.2 Keystone 管理这些概念的方法

组件名称管理对象生成方法保存方式配置项
identityuser,以及 user group-sql, kvs, ldap  [identity]
  driver = keystone.identity.backends.[sql|kvs|ldap].Identity

token用户的临时 tokenpki,pkiz,uuidsql, kvs,memcached
[token]

driver = keystone.token.persistence.backends.[sql|kvs|memcached].Token


provider=keystone.token.providers.[pkiz|pki|uuid].Provider
credentialEC2 credential sql
[credential]

driver = keystone.credential.backends.sql.Credential


catalogregion,service,endpoint sql|kvs| template  [catalog]
  driver = keystone.catalog.backends.[sql|kvs| template].Catalog

assignmenttenant,domain,role 以及它们与 user 之间的关系external, password, token   [assignment]
  methods = external, password, token
  password = keystone.auth.plugins.password.Password

trusttrust sql,kvs   [trust]
  driver = keystone.trust.backends.[ssql|kvs].Trust

policyKeystone service 的用户鉴权策略 sql
[default]

policy_file = policy.json

[policy]

  driver = keystone.policy.backends.sql.Policy


1.3 Keystone 代码结构
  Keystone 的代码结果和 OpenStack 的其它组件一样,非常有层次感:
DSC0001.jpg
  (1)bin 目录下:


  • keystone-manage 是个 CLI 工具,它通过和 Keystone service 交互来做一些无法使用 Keystone REST API 来进行的操作,包括:

    • db_sync: Sync the database.
    • db_version: Print the current migration version of the database.
    • mapping_purge: Purge the identity mapping table.
    • pki_setup: Initialize the certificates used to sign tokens.
    • saml_idp_metadata: Generate identity provider metadata.
    • ssl_setup: Generate certificates for SSL.
    • token_flush: Purge expired tokens.


  • keystone-all 用于启动 Keystone 的服务,下面有章节描述其服务启动过程。
  (2)keystone 目录下:




    • 每个Keystone 组件,比如 catalog, token 等都有一个单独的目录。
    • 每个组件目录中:








      • routes.py 定义了该组件的 routes (routes 见 探索 OpenStack 之(11):cinder-api Service 启动过程分析 以及 WSGI / Paste deploy / Router 等介绍)。其中,identity 和 assignment 分别定义了 admin 和 public 使用的 routes,分别供 admin service 和 public service 使用。
      • controller.py 文件定义了该组件所管理的对象,比如 assignment 的controller.py 文件定义了 Tenant、Role、Project 等类。
      • core.py 定了了两个类 Manager 和 Driver。Manager 类对外提供该组件操作具体对象的方法入口; Driver 类定义了该组件需要其Driver实现类所提供的接口。
      • backend 目录下的每个文件是每个具体driver 的实现


    • contrib 目录包括resource extension 和 extension resource 的类文件



1.4 Keystone 服务启动
  /usr/bin/keystone-all 会启动 keystone 的两个service:admin and main,它们分别对应 /etc/keystone/keystone-paste.ini 文件中的两个composite:



[composite:main]
use = egg:Paste#urlmap
/v2.0 = public_api
/v3 = api_v3
/ = public_version_api
[composite:admin]
use = egg:Paste#urlmap
/v2.0 = admin_api
/v3 = api_v3
/ = admin_version_api
  可见 admin service 提供给administrator 使用;main 提供给 public 使用。它们分别都有 V2.0 和 V3 版本,只是目前的 keystone Cli 只支持 V2.0. 本文的分析以 V2.0 为对象,最后会看看两个版本你的区别。比较下 admin 和 public:

名称middlewaresfactory功能区别
admin比 public 多 s3_extensionkeystone.service:public_app_factory  从 factory 函数来看, admin service 比 public service 多了 identity 管理功能, 以及 assignment 的admin/public 区别:
  1. admin 多了对 GET /users/{user_id} 的支持,多了 get_all_projects, get_project,get_user_roles 等功能
  2. keystone 对 admin service 提供 admin extensions, 比如 OS-KSADM 等;对 public service 提供 public extensions。
  简单总结一下, public service 主要提供了身份验证和目录服务功能;admin service 增加了 tenant、user、role、user group 的管理功能。

public  sizelimit url_normalize build_auth_context token_auth admin_token_auth xml_body_v2
  json_body ec2_extension user_crud_extension

keystone.service:admin_app_factory
  
  /usr/bin/keystone-all 会启动 admin 和 public 两个 service,分别绑定不同 host 和 端口。默认的话,绑定host 都是 0.0.0.0; admin 的绑定端口是 35357 (admin_port), public 的绑定端口是 5000 (public_port)。因此,给 admin 使用的 OS_AUTH_URL 为 http://controller:35357/v2.0, 给 public 使用的 OS_AUTH_URL=http://controller:5000/v2.0.

2. Keystone 中的几个重点

2.1 用户的 username/password 身份验证和 token 生成
  HTTP Verb:POST /tokens
  HTTP params: {'auth': {u'tenantName': u'admin', u'passwordCredentials': {u'username': u'admin', u'password': u'1111'}}}
  method:
  基本步骤:
  (3)通过 user name 获取 user id,最终会从 identity backend 中获取到。
  (6)校验用户的 password 和 identity backend 中的 password 的一致性。sql backend 的话,直接比较两个值。
  (8)获取 project info,接下来检查 domain,project,tenant 是否都是 enabled。
  (19)获取 catalog 和 roles。
  (22)使用  user_ref,tenant_ref,metadata_ref,expiry,audit_id 构造 token_auth_data。
  user_user_ref: {'name': u'admin', 'domain_id': u'default', 'enabled': True, u'email': u'admin@admin.com', 'id': u'1dc0db32a936496ebfc50be54924a7cc'}, tenant_ref: {'domain_id': u'default', 'description': u'Admin Tenant', 'enabled': True, 'id': u'43f66bb82e684bbe9eb9ef6892bd7fd6', 'name': u'admin'},metadata_ref: {'roles': [u'ea26bd3b3d0b4ce187ab606cd83eff69', u'4135d78453fb4975a959cd860bacdca0']}, expiry: 2015-02-08 15:41:31.064022, bind: None, audit_id: None
  (23)再基于 catalog,roles 和 token_auth_data,创建 token data,创建好了以后该 token 会被保存到 persistence backend 中。 token data其内容包括:


  • user data,比如name,domain id,id, role names 等
  • tenant data,包括id,name,enabled 等。
  • catalog data,包括catalog 数组。比如:



  • "endpoints": [
    {
    "adminURL": "http://controller:8776/v2/43f66bb82e684bbe9eb9ef6892bd7fd6",
    "region": "regionOne",
    "internalURL": "http://controller:8776/v2/43f66bb82e684bbe9eb9ef6892bd7fd6",
    "id": "652eb96c0d2f41e0ab17f4b61a1b8ee3",
    "publicURL": "http://controller:8776/v2/43f66bb82e684bbe9eb9ef6892bd7fd6"
    }
    ],
    "endpoints_links": [],
    "type": "volumev2",
    "name": "cinderv2"
  • metadata,比如 is_admin 以及 role ids。其中,is_admin 仅仅在headers中的 token 等于 keystone.conf 中设置的 admin_token 时设置为true。
  • token data,比如issued_at,token id,expire 等
  • trust data
  其中,token_id 是调用相应的 token provider 的 _get_token_id 方法生成的:


  • UUID:uuid.uuid4().hex
  • PKI:token_id = str(cms.cms_sign_token(jsonutils.dumps(token_data), CONF.signing.certfile,CONF.signing.keyfile))
  • PKIZ:token_id = str(cms.pkiz_sign(jsonutils.dumps(token_data),CONF.signing.certfile,CONF.signing.keyfile))
  从中可见:


  • UUDI 的 token id 只是一个纯粹的32位十六进制字符串
  • PKI (Public Key Infrastructure) 的 token id 是使用 cert file 和 key file 对 token data 加密的结果
  • PKIZ 的 token id 是使用 pkiz_sign 函数,使用 cert file 和 key file 对 token data 加密的结果。从下面的 code 可以看出,pkiz 生成的 token id 其实就是使用 zlib 对 PKI 生成的 token id 进行压缩,然后加上 PKIZ_ 前缀而已。其原因应该是 PKI 的 token id 太长了。供结果估计,PKIZ 能压缩 PKI 一半左右,感觉这压缩率也不高。



  • def pkiz_sign(text, signing_cert_file_name, signing_key_file_name, compression_level=6):
    signed = cms_sign_data(text, signing_cert_file_name, signing_key_file_name, PKIZ_CMS_FORM)
    compressed = zlib.compress(signed, compression_level)
    encoded = PKIZ_PREFIX + base64.urlsafe_b64encode(
    compressed).decode('utf-8')
    return encoded
    DSC0002.jpg


2.2 keystone 验证及 keystonemiddleware filter
  客户端在调用 POST /tokens 后拿到返回结果,如果用户名和密码验证成功,则拿到诸如user,tenant,token,metadata,catalog等数据。从 catalog 中找到要访问的 service 的 endpoint 的 URL,然后在 headers 中放入 {X-Auth-Token,token id},就可以访问该 service 了。service 的 WSGI App 在收到该 Request 后,首先要验证该 token id 是否有效,该验证一般都使用一个 keystonemiddleware 的 middleware filter 来完成,其 AuthProtocol.__call__ 是处理Request 的入口函数。其处理过程大致如下:
DSC0003.jpg
  这篇文章 understanding-openstack-authentication-keystone-pki 仔细分析了UUID/PKI/PKIZ token id 的生成和验证过程。

2.3 Role 和 Policy 策略
  上面 2.1 和 2.2 只是验证 user 的 credential,至于 user 有没有权限来执行某个操作则取决于 Role 和 基于 Roles 的鉴权。每个 OpenStack 组件分别实现了鉴权功能,Keystone 和 其它组件中的实现有一些不同。
  Keystone API V3 与 V2 相比,对 policy 的支持有很多的增强。V2 的支持和 OpenStack 其它组件比如cinder,nova 等差不多,只支持基于 policy.json 文件的 policy 控制;而 V3 中,支持对 policy 的 CURD 操作,以及对 endpoint,service 等的 policy 操作等。

2.3.1 Keystone 中的 RBAC (Role Based Access Controll)
  Keystone 在每个子模块的 controller.py 文件中的各个控制类的方法上实施 RBAC。有几种方法,比如:
  @controller.protected()
def create_region(self, context, region)
  @controller.filterprotected('type', 'name')
def list_services(self, context, filters)
  或者直接在函数体中使用 self.assert_admin(context):
  def get_services(self, context):
    self.assert_admin(context)
    service_list = self.catalog_api.list_services()
    return {'OS-KSADM:services': service_list}
  其中,protected 和 filterprotected 在 /keystone/common/controller.py 中实现。
  protected/filterprotected 最终会调用 /keystone/openstack/common/policy.py 中的 enforce 方法,具体见 2.3.2 (2)中的描述。keystone 与 openstack 其它component 中的 RBAC 实现的不同之处在于 keystone 中有时候需要做 token 验证再做 policy 检查。

2.3.2 Keystone V3 API 中的 policy 操作
  支持的policy 操作:






  • POST /v3/policies 创建一个 policy
  • GET /v3/policies 获取所有 policys
  • GET /v3/policys/{policy id} 获取一个policy
  • PATCH /v3/policies/?{policy_id}? 修改一个 policy
  • DELETE /v3/policies/?{policy_id}? 删除一个 policy

2.3.3 Keystone V3 API 中的 Endpoint Policy Assignment
  Keystone 的 V3 API 的 OS-ENDPOINT-POLICY 扩展资源提供了 API 来支持:


  • 给特定的 endpoint 分配 policy。 REST API: /endpoints/{endpoint_id}/OS-ENDPOINT-POLICY/policy
  • 给特定的 service 的所有 endpoint 分配 policy。 REST API:PUT/DELETE /policies/{policy_id}/OS-ENDPOINT-POLICY/services/{service_id}
  • 给指定 region 中的指定 service 的所有 endpoint 分配 policy。REST API:GET/HEAD/PUT/DELETE /policies/{policy_id}/OS-ENDPOINT-POLICY/services/{service_id}/regions/{region_id}
  支持 policy 存放在 sql db 中。
  它的实现在 /keystone/contrib/endpoint_policy 中。

2.3.4 Cinder 中的 RBAC (Role Based Access Controll)
  (1)使用 /etc/cinder/policy.json 文件
  (2)Volume/Backup/ConsistencyGroup 等 API 类中的接口函数上的 @wrap_check_policy 或者在函数体内显式调用 check_policy(context, 'get', volume) 来判断调用用户的 role 是否满足 policy.json 中定义的要求。比如 /volume/api.py 中的 delete 方法:
  @wrap_check_policy
    def delete(self, context, volume, force=False, unmanage_only=False)



def wrap_check_policy(func):
"""Check policy corresponding to the wrapped methods prior to execution. This decorator requires the first 3 args of the wrapped function  to be (self, context, volume)    """
    @functools.wraps(func)
def wrapped(self, context, target_obj, *args, **kwargs):
check_policy(context, func.__name__, target_obj)
return func(self, context, target_obj, *args, **kwargs)
return wrapped
def check_policy(context, action, target_obj=None):
target = {'project_id': context.project_id, 'user_id': context.user_id,}
target.update(target_obj or {})
_action = 'volume:%s' % action
cinder.policy.enforce(context, _action, target)

  def enforce(self, rule, target, creds, do_raise=False, exc=None, *args, **kwargs):
  #rule:volume:get_all
  #target: {'project_id': u'43f66bb82e684bbe9eb9ef6892bd7fd6', 'user_id': u'1dc0db32a936496ebfc50be54924a7cc'},  
  #creds 中包括 user role 等信息
  self.load_rules() #重新读取 policy.json 文件
  ...
  result = self.rules[rule](target, creds, self) #使用特定的 rule 来检查 user 的权限
  ...
  return True or PolicyNotAuthorized(rule)
  cinder 的 policy.json 文件片段:



{
"context_is_admin": "role:admin",
"admin_or_owner":  "is_admin:True or project_id:%(project_id)s",
"default": "rule:admin_or_owner",
"admin_api": "is_admin:True",
"volume:create": "", #'volume' 是 API 的类别名称,比如’volume‘,’snapshot‘等。任何role的经过验证的用户都可以调用该API。
"volume:get_volume_admin_metadata": "rule:admin_api", #'get_volume_admin_metadata' 是 API 函数名称。该函数需要 is_admin:True。
"volume:delete_volume_admin_metadata": "rule:admin_api",
"volume:extend": "",
"volume:update_readonly_flag": "",
"volume:retype": "",

  这篇文章 HAVANA KEYSTONE中policy.json的解析过程 详细分析了该 enfore 过程。
  如果要添加新的 role 来限制 cinder api 的话,需要(1)在keystone 中创建新的 role (2)修改 cinder 的 policy.json 文件,修改policy条件 (3)设置 user 的role 到新的role.
  其它模块,比如 nova, glance 等,和 cinder 模块类似,只是区分了 admin role 和 非 admin role 的用户。
  

3. V3 版本 Keystone API
DSC0004.jpg
  V3 与 V2 相比,Keystone 的 API 得到了很大的增强:
  1. 增加了 Domain,Group,Policy, Catalog 等对象的操作API
  2. 似乎又将 Tenant 改成了 Project
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-56065-1-1.html 上篇帖子: [OpenStack] OpenStack之Glance笔记(转载) 下篇帖子: 别以为真懂Openstack: 虚拟机创建的50个步骤和100个知识点(4)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表