• keystone policy.json 的学习总结


    keystone的policy.json文件位于:
    /etc/keystone/policy.json


    其内容如下:
    1 {
    2 "admin_required": "role:admin or is_admin:1",

    34 "identity:get_project": "rule:admin_required",
    35 "identity:list_projects": "rule:admin_required",
    36 "identity:list_user_projects": "rule:admin_or_owner",
    37 "identity:create_project": "rule:admin_required",
    38 "identity:update_project": "rule:admin_required",
    39 "identity:delete_project": "rule:admin_required",

    41 "identity:get_user": "rule:admin_required",
    42 "identity:list_users": "rule:admin_required",
    43 "identity:create_user": "rule:admin_required",
    44 "identity:update_user": "rule:admin_required",
    45 "identity:delete_user": "rule:admin_required",
    46 "identity:change_password": "rule:admin_or_owner",

    此文件的格式为:“规则名”,再冒号空格,再“判定条件”。从规则名可以看出,规则名是代表的是某种动作,例如创建用户这个动作,对应动作名为“identity:create_user”,其判定条件是“rule:admin_required”。
    判定条件“rule:admin_required”表示调用规则“admin_required”。“admin_required”在第2行,其判定条件是“role:admin or is_admin:1”,这就比较容易理解,如果当前用户是admin角色或者元数据中“is_admin”字段的值为1,就返回真。
    判定条件中可以用“or”或者“and”等,甚至使用变量。
    由于未找到官方对于配置policy.json的详细说明,本文的出现的规则语法,全部借鉴于网络。


    从规则名可以知道,大概每个keystone的API在policy.json中都有对应的规则名。
    经过验证,V2版本的API只验证规则“admin_required”的判定条件,而其它规则不生效。原因在于以下文件中:

    /usr/lib/python2.7/site-packages/keystone/common/wsgi.py
    185 class Application(BaseApplication):
    274 def assert_admin(self, context):
    299 creds['roles'] = user_token_ref.role_names
    300 # Accept either is_admin or the admin role
    301 self.policy_api.enforce(creds, 'admin_required', {})
    assert_admin函数只调用了规则“admin_required”。


    /usr/lib/python2.7/site-packages/keystone/identity/controllers.py
    30 class User(controller.V2Controller):
    31
    32 @controller.v2_deprecated
    33 def get_user(self, context, user_id):
    34 self.assert_admin(context)
    35 ref = self.identity_api.get_user(user_id)
    36 return {'user': self.v3_to_v2_user(ref)}
    每个动作的具体实现函数里,调用assert_admin函数进行判定条件的检查,所以只检查规则“admin_required”。
    例如,如果想使V2的API不检查判定条件(即所有用户都能执行该动作),注释掉self.assert_admin(context)即可。
    例如,如果想使V2的API换一个规则进行判定,把“admin_required”替换成别的规则。
    操作系统中keystone命令默认是调用V2版本的API,故只生效规则“admin_required”。


    V3版本的API支持policy.json中的所有规则。V3版本主要是多了“domain”的概念。V2中的tenant在V3中改名为project,任何一个project或者user只能归属于一个domain。
    使用keystone相关命令创建的租户或用户,默认属于域“default”。


    下面对policy.json的工作原理做一个总结。
    其中最关键的文件是以下文件:

    /usr/lib/python2.7/site-packages/keystone/openstack/common/policy.py
    118 class Rules(dict):
    Rules自动载入policy.json中的每条规则,返回字典对象rules。
    它是动态的,对policy.json的修改是不需要重启keystone服务的(修改Python原代码需要重启keystone服务)。


    /usr/lib/python2.7/site-packages/keystone/openstack/common/policy.py
    174 class Enforcer(object):
    262 def enforce(self, rule, target, creds, do_raise=False,
    263 exc=None, *args, **kwargs):
    296 try:
    298 result = self.rules[rule](target, creds, self)
    V3 API的每个动作执行时,都会调用enforce函数,判定是否符合policy.json中的规则,原因见下文。
    rule是规则名,如“identity:create_endpoint”。
    self.rules是keystone.openstack.common.policy.Rules类型,即包含policy.json每条规则的字典。
    self.rules[rule]是keystone.openstack.common.policy.RuleCheck类型,即此处将调用RuleCheck函数,检查规则的判定条件。


    /usr/lib/python2.7/site-packages/keystone/openstack/common/policy.py
    830 class RuleCheck(Check):
    831 def __call__(self, target, creds, enforcer):
    834 try:
    835 return enforcer.rules[self.match](target, creds, enforcer)
    self.match是判定条件,即冒号右边的字符串。
    enforcer.rules是keystone.openstack.common.policy.Rules类型,它根据self.match,决定调用何种Check函数,即enforcer.rules[self.match]可能是GenericCheck、RoleCheck、OrCheck、AndCheck等类型,然后调用对应函数。
    OrCheck或AndCheck类型是当判定条件含有or或and才出现的类型。在OrCheck或AndCheck函数内部,把判定条件再拆解成GenericCheck或RoleCheck类型。例如,"domain_id:%(domain_id)s"或"role:service"。


    /usr/lib/python2.7/site-packages/keystone/openstack/common/policy.py
    867 class GenericCheck(Check):
    880 try:
    881 match = self.match % target
    887 try:
    889 leftval = ast.literal_eval(self.kind)
    891 try:
    892 leftval = creds[self.kind]
    895 return match == six.text_type(leftval)
    GenericCheck函数的逻辑比较复杂,涉及字典变量target和creds。


    /usr/lib/python2.7/site-packages/keystone/common/controller.py
    85 def protected(callback=None):
    86 """Wraps API calls with role based access controls (RBAC).
    152 self.policy_api.enforce(creds,
    153 action,
    154 utils.flatten_dict(policy_dict))
    161 def filterprotected(*filters):
    162 """Wraps filtered API calls with role based access controls (RBAC)." ""
    193 self.policy_api.enforce(creds,
    194 action,
    195 utils.flatten_dict(target))
    V3版本每个动作的具体实现函数中,都会调用protected或filterprotected函数,而这两个函数调用policy.py的enforce函数,传递了target(或policy_dict)变量。
    utils.flatten_dict函数的作用是返回一个一维字典,即GenericCheck函数中的target变量是一维字典。


    target是目标的意思,字典变量target中存储了操作对象的domain_id或user_id等。有以下几种方式传递方式:
    1、列出domain中所有的用户,可以在URL中传递?domain_id=参数,如:
    # curl http://controller:35357/v3/users?domain_id=660450adcc194c0bbf9e462bb21b0935 -H "X-Auth-Token:f469cb22b6384a5b8dd343e480fc7bba"|python -mjson.tool

    2、列出用户的project信息,URL中传递了user_id,如:
    # curl http://controller:35357/v3/users/735c4d1fc8eb4bf8b96ee6866b441d9d/projects -H "X-Auth-Token:f469cb22b6384a5b8dd343e480fc7bba"|python -mjson.tool

    3、如创建用户、创建项目、删除用户等,传递了操作对象,如在哪个域创建用户,删除哪个用户等:
    # curl -X POST http://controller:35357/v3/users -H "Content-type: application/json" -H "X-Auth-Token:22142d114ddc454a9fbf6d282793840e" -d '{"user": {"default_project_id": "c0d6c4a09b7649a19c394a6cd946f53f","domain_id": "660450adcc194c0bbf9e462bb21b0935","enabled": true,"name": "evecom001","password":"123456"}}'|python -mjson.tool

    以上几种传递方式,其实就两类:一类是通过filterprotected函数传递;另一类是通过protected函数传递。总之target的内容是来自于http数据,若是使用curl命令调用API,则来自于URL或-d参数。


    可以尝试在GenericCheck函数中添加打印target内容的代码,观察调用不同API时target内容的变化。以下是不同动作获得domain_id的不同方法:
    list_users: domain_id=target['domain_id']
    create_user: domain_id=target['user.domain_id']
    delete_user: domain_id=target['target.user.domain_id']
    list_projects: domain_id=target['domain_id']
    create_project: domain_id=target['project.domain_id']
    delete_project: domain_id=target['target.project.domain_id']
    只有打印了才知道不同动作获得domain_id的方法是什么(或者去源代码找)。


    知道了target的关键字,就可以编辑policy.json,判断当前执行操作的用户的domain_id,与被操作对象的domain_id是否一致:
    "target_list_users": "domain_id:%(domain_id)s",
    "target_create_user": "domain_id:%(user.domain_id)s",
    "target_delete_user": "domain_id:%(target.user.domain_id)s",
    "target_list_projects": "domain_id:%(domain_id)s",
    "target_create_project": "domain_id:%(project.domain_id)s",
    "target_delete_project": "domain_id:%(target.project.domain_id)s",

    "identity:list_projects": "rule:target_list_projects",
    "identity:create_project": "rule:target_create_project",
    "identity:delete_project": "rule:target_delete_project",
    "identity:list_users": "rule:target_list_users",
    "identity:create_user": "rule:target_create_user",
    "identity:delete_user": "rule:target_delete_user",


    按照上述policy.json,当enforce函数检查规则“target_create_user”时,调用的是GenericCheck函数。
    881 match = self.match % target
    match变量的值为target['user.domain_id'],即被操作对象的domain_id。

    892 leftval = creds[self.kind]
    leftval变量的值为creds[domain_id],即执行操作者的domain_id。

    895 return match == six.text_type(leftval)
    最后enforce函数返回match是否与leftval相等的比较结果(True of False)。


    至于字典变量creds,打印其关键字为:
    'is_delegated_auth'
    'access_token_id'
    'user_id'
    'roles'
    'trustee_id'
    'trustor_id'
    'consumer_id'
    'token'
    'domain_id'
    'project_id'
    'trust_id'
    有点特别奇怪,默认管理员账户admin的creds没有'domain_id'这个关键字。如果admin的creds有'domain_id'这个关键字,值应该为“default”。初步猜测是由于admin是用keystone命令创建造成的,因为keystone命令是调用V2 API,没有domain的概念,创建用户时,只指定了tenant,即V3中的project。换句话说,如果要判断用户的domain_id,创建这个用户的时候,要指定其domain_id。同理适用于project_id。


    字典变量creds的关键字比较稳定,不像target的关键字不同的API都不一样。至于字典变量creds的内容如何产生,我未去寻找其源代码,因为想得通:多半是通过token就能知道是哪个用户,知道哪个用户,系统自然有它的信息。


    除了keystone可以使用policy.json定义权限,其它openstack组件也有policy.json:
    /etc/nova/policy.json
    /etc/heat/policy.json
    /etc/keystone/policy.json
    /etc/glance/policy.json
    /etc/neutron/policy.json
    /etc/cinder/policy.json
    /etc/ceilometer/policy.json

  • 相关阅读:
    [LeetCode]1290. 二进制链表转整数
    [LeetCode]1295. 统计位数为偶数的数字
    map 用法 拿到map数组每一个数据
    父子组件相互传参
    父组件给子组件传参 el-dialog 试例
    如何用JS判断div中内容为空,当为空时隐藏div
    完整的Vue+element-ui table组件实现表格内容的编辑删除和新行添加小实例
    Git操作
    charles的使用
    移动端的一些问题
  • 原文地址:https://www.cnblogs.com/endoresu/p/5018433.html
Copyright © 2020-2023  润新知