keystone

1、 什么是keystone

Keystone(OpenStack Identity Service)是OpenStack框架中的身份管理服务(Identity service),负责管理身份验证、资源服务目录、基于用户角色的访问控制和服务令牌管理功能。 Keystone的作用类似一个服务总线,Nova、Glance、Horizon、Swift、Cinder、Neutron等服务都需要通过Keystone来注册其服务的Endpoint(可理解为服务的访问点或者URL),针对这些服务的任何调用都需要经过Keystone的身份认证,并获得服务的Endpoint来进行访问。

PNG

  • 用户身份验证

    • 用户身份信息管理和存储
    • 管理用户相关的 project、role、group 和 domain等
    • 用户 credential 的存放、验证、令牌管理等
  • 权限管理

    • 用户目录管理(service、endpoint)

2、 keystone基本概念

用户管理:

(1)Domain域:域包含project。

(2)project(tenant)项目(租户):一个用于分组或者隔离资源的容器。一个 project(tenant)可能对应一个客户、账号、组织或者项目。在 OpenStack 中,用户至少必须在一个project(tenant)中。project(tenant)容器的可使用资源的限制成为 project(tenant)Quota,它包括project(tenant)内各子资源的quota。

(3)User 用户:一个使用 OpenStack 云服务的人、系统或者服务的数字表示。 用户需要登录,然后被分配 token 去访问资源。用户可以被分配到一个project(tenant),并赋予不同的角色。

(4)Role 角色:Keystone 中,被访问的服务会判断访问它的用户的角色,以及每个role访问资源或者操作的权限。系统默认使用 admin 和 member两个角色。User 验证的时候必须带有制定的 project,roles会被分配到指定的project。

默认的角色有admin和user两种。 有domain admin和cloud admin,cloud admin是云管理员,可以对域进行管理,domain admin是域管理员,对某个域内进行管理,管理project。Project admin,对project进行管理。

(5)Group 用户组:一个用户组包含多个用户,一个用户也可以属于多个用户组,通过给用户组赋予权限,可以给用户进行批量授权。

(6)Policy 策略:OpenStack 对用户的验证除了 OpenStack 的身份验证以外,还需要鉴别用户对某个服务是否有访问权限。Policy 机制就是用来控制某一个User在某个project(Tenant)中某个操作的权限。这个 User 能执行什么操作,不能执行什么操作,就是通过 policy 机制来实现的。对于 Keystone 服务来说,policy 就是一个json 文件,默认是 /etc/keystone/policy.json。通过配置这个文件,Keystone Service 实现了对 User 的基于用户角色的权限管理。

(7) Credential 用户证据:用来证明用户身份的证据,可以是用户名和密码、用户名和API key,或者一个 Keystone 分配的身份token。

(8)Authentication 身份验证:验证用户身份的过程。Keystone 服务通过检查用户的 Credential 来确定用户的身份。最开始,使用用户名/密码或者用户名/API key作为credential。当用户的credential被验证后,Keystone 会给用户分配一个 authentication token 供该用户后续的请求使用。

(9)Token 令牌:一个用于访问 OpenStack API 和资源的字符串。一个 token 可能在任何时间被撤销(revoke),因此其有效期是有限的。OpenStack中,token 是和特定的project(tenant)是绑定的,因此如果 user 如果访问多个project(tenant)的话他就需要多个token。 还有一种unscoped token,不能访问project,只能用来获取project列表

(10)Service 服务:一个 OpenStack 服务,比如Nova、Swift或者Glance等。每个服务提供一个或者多个 endpoint 供用户访问资源以及进行操作。

(11)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 可以给外部用户访问 PNG

3、keystone组件、代码结构。

3.1 keystone组件

Keystone分六个services:

Identity(认证):用户身份验证和用户与用户组的数据提供。可以由该模块提供也可以由其他后端模块提供,如LDAP。
Resource(资源):项目(project)和domain(域)的数据。可以由该模块提供也可以由其他后端模块提供,如LDAP。
Assignment(角色分配):为用户分配某一角色,来控制用户的访问权限。
Token(令牌)管理:用户身份认证后,会提供给用户一个可以请求后续资源的令牌,token服务是用来验证并管理该令牌。
Catalog(目录)管理:对外提供一个服务的查询目录,即每隔服务的可访问Endpoint列表。服务目录和令牌是同时返回给用户的。
Policy(安全策略/访问控制):一个机于规则的身份验证引擎,通过配置文件来定义各种操作与用户角色之间的匹配关系。

3.2 keystone代码结构

PNG

(1)etc目录下:

default_catalog.templates :服务目录模板文件
keystone.conf.sample:主配置文件
keystone-paste.ini:paste deploy 配置文件
logging.conf.sample:日志配置文件
policy.json: 访问控制配置文件

(2)cmd 目录下:

cli.py 是个 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.

(3)keystone 目录下:

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

routes.py 定义了该组件的 routes。
controller.py 文件定义了该组件所管理的对象,比如 assignment 的controller.py 文件定义了 Tenant、Role、Project 等类。
core.py 定了两个类 Manager 和 Driver。Manager 类对外提供该组件操作具体对象的方法入口; Driver 类定义了该组件需要其Driver实现类所提供的接口。
backend 目录下的每个文件是每个具体driver 的实现
contrib 目录包括resource extension 和 extension resource 的类文件。

4、 Keystone主要工作详述

4.1keystone安装和启动过程

4.1.1 keystone安装配置

(1)建立数据库,用户并授权

    CREATE DATABASE keystone;
    GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'localhost' IDENTIFIED BY 'KEYSTONE_DBPASS';
    GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%' IDENTIFIED BY 'KEYSTONE_DBPASS';

(2)配置

    [DEFAULT]
    #配置admin token,用于引导keystone启动,配置为None就会被忽略。
    admin_token = ADMIN_TOKEN         

    [database]
    #数据库连接配置
    connection = mysql+pymysql://keystone:KEYSTONE_DBPASS@controller/keystone   

    [token]
    #token生成的方法fernet|pkiz|pki|uuid。
    provider = fernet

注:

a. 建议在生产环境中删除admin_token配置,需要删除AdminTokenAuthMiddleware配置,在keystone-paste.ini中可以配置删除pipline中的admin_token_auth即可。

(3)同步数据库 su -s /bin/sh -c "keystone-manage db_sync" keystone

(4)配置Apache服务器

Listen 5000
Listen 35357

<VirtualHost *:5000>
    WSGIDaemonProcess keystone-public processes=5 threads=1 user=keystone group=keystone display-name=%{GROUP}
    WSGIProcessGroup keystone-public
    WSGIScriptAlias / /usr/bin/keystone-wsgi-public
    WSGIApplicationGroup %{GLOBAL}
    WSGIPassAuthorization On
    ErrorLogFormat "%{cu}t %M"
    ErrorLog /var/log/httpd/keystone-error.log
    CustomLog /var/log/httpd/keystone-access.log combined

    <Directory /usr/bin>
        Require all granted
    </Directory>
</VirtualHost>

<VirtualHost *:35357>
    WSGIDaemonProcess keystone-admin processes=5 threads=1 user=keystone group=keystone display-name=%{GROUP}
    WSGIProcessGroup keystone-admin
    WSGIScriptAlias / /usr/bin/keystone-wsgi-admin
    WSGIApplicationGroup %{GLOBAL}
    WSGIPassAuthorization On
    ErrorLogFormat "%{cu}t %M"
    ErrorLog /var/log/httpd/keystone-error.log
    CustomLog /var/log/httpd/keystone-access.log combined

    <Directory /usr/bin>
        Require all granted
    </Directory>
</VirtualHost>    

(5)启动服务器

systemctl enable httpd.service
systemctl start httpd.service
也可以通过keystone-all命令启动。

4.1.2 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 版本。比较下 admin 和 public:

名称 middlewares factory 功能区别
admin s3_extension, crud_extension, admin_service keystone.service:admin_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 增加了 project、user、role、user group 的管理功能。

public

user_crud_extension, public_servic

keystone.service:public_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/v3, 给 public 使用的 OS_AUTH_URL=http://controller:5000/v3.

4.2 Keystone与其他模块之间的关联配置

  • 新建用户
  • 新建endpoint
  • 配置文件中修改

    [keystone_authtoken]

       auth_uri = http://controller:5000
       auth_url = http://controller:35357
       auth_plugin = password
       project_domain_id = default
       user_domain_id = default
       project_name = service
       username = glance
       password = GLANCE_PASS
       memcache_servers = 127.0.0.1:11211
    

    [paste_deploy]

       flavor = keystone
    

4.3 用户的username/password身份验证和token生成

4.3.1 用户身份认证

分两种情况:(1)username+password (2) token

PNG

4.3.2 token的生成

(1)token具有时效性,因此较为安全,对应的是project。
(2)生成时间: 登陆时、切换project时
(3)类型:unscoped token, scoped token
(4)生成方式:
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))

Fernet:        
token =self.pack( msgpack.packb( (version,) + TrustScopedPayload.assemble(
        user_id,
        methods,
        project_id,
        expires_at,
        audit_ids,
        trust_id)))

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,但是只有80%左右,感觉这压缩率也不高。
Fernet 是专为 API token 设计的一种轻量级安全消息格式,不需要存储于数据库,减少了磁盘的 IO,带来了一定的性能提升 。为了提高安全性,需要采用 Key Rotation 更换密钥。

uuid方式:

PNG

PKI/ PKIZ/ Fernet方式:

<<<<<<< HEAD

PNG

======= PNG

90047b981402c0c7c386850731556df633393751

比较各种方式的优缺点:

Token 类型 UUID PKI PKIZ Fernet
大小 32 Byte KB 级别 KB 级别 约 255 Byte
支持本地认证 不支持 支持 支持 不支持
Keystone 负载
存储于数据库
携带信息 user, catalog 等 user, catalog 等 user 等
涉及加密方式 非对称加密 非对称加密 对称加密(AES)
是否压缩
版本支持 D G J K
优点 生成的Token,长度较短,使用方便。url使用方便,回收的方式,就是从后端删除即可,实现逻辑较为清晰。 使用证书及私钥生成,可以线下验证(不需要走keystone认证服务) 较PKI长度上缩小了很多 设计的逻辑,引入序列化,负载格式加以控制,基于此进行加密,长度比PKI、PKIZ要短。
缺点 需要持久化后端存储,每次访问需要keystone相关服务进行认证。 长度负载重,不推荐用于生产部署,经常由于长度超长引起错误。 长度负载较重,同上PKI 对称加密算法,安全性低,需要周期性更换秘钥
Token 类型的选择涉及多个因素,包括 Keystone server 的负载、region 数量、安全因素、维护成本以及 token 本身的成熟度。region 的数量影响 PKI/PKIZ token 的大小,从安全的角度上看,UUID 无需维护密钥,PKI 需要妥善保管 Keystone server 上的私钥,Fernet 需要周期性的更换密钥。

(5)配置

[token]
#token持久存储
driver = keystone.token.backends.sql.Token
#token有效期
expiration = 86400

(6) 存储

 a.  kvs
    key-value-store,和memcache类似,存储于内存中,每隔一段时间会追加写入文件,在keystone中,在生产环境中使用memcache和sql来代替。

b.  memcache
    memcache 将token存放到memcache中,token本身创建之后不会被修改,只会被读,所以很适合放到cache中,加速访问,放到cache中也不需要写脚本定期清理数据库中的过期token。

    通过keystoneclient middleware filter来实现,该中间件安装在每个node上

    通过如下配置来实现

    编辑/etc/keystone/keystone.conf的Token字段:
    [token]
    caching = true
    provider = keystone.token.providers.uuid.Provider
    driver = keystone.token.persistence.backends.memcache.Token

    在节点上配置:
    [keystone_authtoken]
    auth_uri = http://keystone_server:5000/
    auth_host = keystone_server
    auth_port = 35357
    auth_protocol = http
    admin_tenant_name = service
    admin_user = nova
    admin_password = password
    memcache_servers = 127.0.0.1:11211
    token_cache_time = 3600 #token本地缓存的失效时间设置为1个小时。
    cache = true

c.     memcache_pool
    memcache_pool 在memcache的基础上,实现了memcache的连接池,可以在线程之间复用连接。
    memcache driver的缺点在于,memcache本身是分布式的设计,但是并不是高可用的,如果controller-1上的的cache服务被重启,这个节点上的所有token都会丢失掉,会带来一些错误。
    比这更困难的是,如果controller1网络不可达或者宕机,那么我们会观察到几乎每个openstack api请求都会有3s以上的卡顿。这是因为openstack默认使用python-memcached访问memcache,它提供的操作keystone的client继承自Thread.local类,和构建它的线程绑定。openstack服务启动后,会启动一定数量的子进程,每个http request到达,会有一个子进程接收,孵化一个线程去处理这个请求。如果用到memcache,线程会调用python-memcached 构建一个client类,通过这个client的实例对memcache进行操作。如果访问到网络不可达的memcache节点,卡住,操作超时,将这个节点标识为30秒内不可用,在这个线程内,不会再因此而卡住,但是这个http请求结束之后,下一个http请求过来,重新孵化的线程会reinit这个client,新的client丢失了旧的client的状态,还是可能会访问到卡住的memcache节点上。
    社区之所以要做memcache_pool,就是为了解决这个问题,将client统一由pool管理起来,memcache节点的状态,也由pool管理起来,这样每个子进程里只会卡第一次,所以强烈推荐使用memcache_pool驱动而不是memcache。社区将memcache_pool的代码从keystone复制到oslo_cache项目中,希望所有使用memcache的项目都通过它的memcachepool去访问,避免这个问题。其中,nova在M版本支持,heat在L版本支持。

d.     sql
    sql将token存放在数据库中,使用这种方法需要定期清理过期token,防止token表太大影响性能。

4.4 权限控制机制RBAC

4.4.1 RBAC

一个用户可以在不同的租户中被分配不同的角色,一个用户也可以在同一个租户中分配多个角色。

  /etc/[服务名称]/policy.json控制着哪些用户可以拥有什么样的服务,如:/etc/nova/policy.json定义了Compute服务的访问策略,/etc/glance/policy.json定义Image服务的访问策略,以及/etc/keystone/policy.json定义Identity服务的访问策略。

  实例:

  在/etc/nova/policy.json中的配置设定卷创建的操作对用户无任何限制,在租户中的用户拥有的任何角色均可以创建卷:      "volume:create": [],

  如果你需要仅拥有compute-user角色的用户才可以创建卷,你就需要添加一行”role:compute-user”,具体配置如下:      "volume:create": ["role:compute-user"]。

4.4.2 实现

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中:

 def inner(self, context, *args, **kwargs):
            if 'is_admin' in context and context['is_admin']:
                LOG.warning(_LW('RBAC: Bypassing authorization'))

protected/filterprotected 最终会调用 /keystone/openstack/common/policy.py 中的 enforce 方法。keystone 与 openstack 其它component 中的 RBAC 实现的不同之处在于 keystone 中有时候需要做 token 验证再做 policy 检查,而其他的component只需要做policy检查。

4.4.3 新增role

(1)在horizon或者命令行新建role
(2)在policy中添加该role
(3)为用户分配该role
例如:如果希望一个用户只拥有管理镜像的权限,可以etc/glance/policy.json中,加入该角色,不在其他的etc/*/policy.json中增加该角色,并把之前默认的开放给所有用户的权限限制给已有的用户。

5、 Keystone的实际应用

5.1 身份认证和权限控制

keystone实现用户身份验证和权限控制两个功能。

5.2 只提供权限控制,身份认证集成sso。

keystone只提供权限控制,身份验证的功能通过其他身份验证系统来实现。

例如:LDAP管理 Idenity (users, groups, and group memberships), Keystone采用SQL管理Assignment (roles, tenants, domains).

PNG

     1)、用户在openatck控制台的登录页面中输入用户名、口令等,提交到公司认证系统进行登录认证。

  2)、公司认证系统认证通过后,返回访问凭证(通常是访问token)、用户名。

  3)、openstack控制台通过http协议将第二步收到的访问token和用户名通过keystone提供的认证接口发给keystone,发送的http请求大致如下:
    POST http://192.168.0.1:5000/v3/tokens
    REMOTE_USER:  用户名
    EXTERNAL_ACCESS_TOKEN: 访问token
    Accept: application/json
    Content-Type: application/json

    {"auth" : { } }    

    REMOTE_USER头是keystone进行外部认证必须要有的,请求中存在该头keystone才会执行外部认证,其值为用户名;EXTERNAL_ACCESS_TOKEN头是自定义的,其值是公司认证系统返回的访问token。
    http body是json格式,只有auth属性,内容为空。其他的如口令等都不需要。

 4)、keystone收到该请求后,识别出认证请求中存在REMOTE_USER头,就进行外部认证的处理,主要是根据REMOTE_USER头中的用户名查询keystone的user表,看用户记录是否存在(所以需要将公司用户信息同步到keystone的user表中,不需同步密码字段),存在就通过了keystone的认证,后续处理流程和进行用户名/密码验证的相同,主要是颁发keystone的unscoped token等(该token不和具体的project/tenant绑定)。
    基于安全的原因,keystone会到公司认证系统验证访问token(EXTERNAL_ACCESS_TOKEN头中携带),确保该token是公司认证系统颁发,且没过期。
     该验证也可采用其他方式,如keystone回调openstack控制台来验证该访问token,或openstack控制台和keystone之间采用对称或非对称密钥,keystone对opentack传过来的签名进行验证(该签名放在另一个http头中,去掉EXTERNAL_ACCESS_TOKEN头)。外加iptables进行控制台ip地址限制。
5)、返回keystone unscoped token给控制台。

参考文献:

results matching ""

    No results matching ""