在最新的devstack上玩耍的时候,token ID的长度发生了变化,并不是正常UUID的32字节长度
[lihui@openstack ~]$ openstack token issue +------------+---------------------------------------------------------------------------------------------------------------------------------------------+ | Field | Value | +------------+---------------------------------------------------------------------------------------------------------------------------------------------+ | expires | 2017-04-07T19:22:44+0000 | | id | gAAAAABY59j0KSkY4bs7niTxOXb-4Ap8vz2iFPuKGvO8HxjJdeupoZ3xTkM- | | | RhYP9JM8aLQWZOM2DtY9p7b0G6YCehRq7trqpWvIK51QMTfqB0d2yC3O4yeXQ29s6lzdPHW6pHQDUnZ6uh6JZHEXXZb42G1uBn17NnKVwJwRHfXPHGKGeKTi04s | | project_id | f26ed882adff490b8f65ef3653e97b39 | | user_id | d7d21e3aee1d4bc5b51a5265952ca481 | +------------+---------------------------------------------------------------------------------------------------------------------------------------------+
简单看了下长度,有180多
>>> token = 'gAAAAABY59j0KSkY4bs7niTxOXb-4Ap8vz2iFPuKGvO8HxjJdeupoZ3xTkM-RhYP9JM8aLQWZOM2DtY9p7b0G6YCehRq7trqpWvIK51QMTfqB0d2yC3O4yeXQ29s6lzdPHW6pHQDUnZ6uh6JZHEXXZb42G1uBn17NnKVwJwRHfXPHGKGeKTi04s' >>> print len(token) 183
具体token id的生成是哪种format,在keystone的配置文件/etc/keystone/keystone.conf里可以找到如下:
[token] driver = sql provider = fernet
可以看到我这里配置的format并不是uuid,而是fernet
假如也是通过devstack来搭建,可以直接修改keystone源码来达到配置的目的,具体位置在devstack/lib/keystone:
# Select Keystone's token provider (and format) # Choose from 'uuid', 'pki', 'pkiz', or 'fernet' KEYSTONE_TOKEN_FORMAT=${KEYSTONE_TOKEN_FORMAT:-fernet} KEYSTONE_TOKEN_FORMAT=$(echo ${KEYSTONE_TOKEN_FORMAT} | tr '[:upper:]' '[:lower:]')
从这里看到这里有4中format可配置,KEYSTONE_TOKEN_FORMAT默认这里配置的是fernet,假如想恢复成自己习惯的UUID格式的token,直接将fernet改成uuid即可
下面简单描述下这四种format
【uuid】
这里是随机生成的32byte字符串,就像这样
>>> import uuid >>> uuid.uuid4().hex '8cc622b1ec654c228fc2d22afd3ad7e8' >>> uuid.uuid4().hex '9830fab1620d4a5ca9f0fd1ba18c03eb' >>> uuid.uuid4().hex '7bd7f1e261434bba9ae4be27d7ec54b2' >>> uuid.uuid4().hex '6e0a55f1a882469fbcf6909a06b65b0a'
这里的token不携带任何其它信息,在其它API收到了token之后,无法确认这个token到底是否还有效,比如一般token的持续时间设置24小时,假如这个token已经过期了,除此之外这个token到底用得对不对,比如和对应的tenant是否一致,这都是需要keystone来进行交互验证,也就是需要持久化保存token,当存储过多的时候,肯定会导致性能不好了
【fernet】
由于uuid方式只是简单的32字节长度随机字符串,没有加密方式,fernet就是针对这种情况做的改进,根据社区的说法:Fernet token,它采用 cryptography 对称加密库(symmetric cryptography,加密密钥和解密密钥相同) 加密 token,具体由 AES-CBC 加密和散列函数 SHA256 签名。Fernet 是专为 API token 设计的一种轻量级安全消息格式,不需要存储于数据库,减少了磁盘的 IO,带来了一定的性能提升。为了提高安全性,需要采用 Key Rotation 更换密钥。
说得天花乱坠,直接看源码,在keystone/token/providers/fernet/token_formatters.py
def create_token(self, user_id, expires_at, audit_ids, methods=None, domain_id=None, project_id=None, trust_id=None, federated_info=None, access_token_id=None): """Given a set of payload attributes, generate a Fernet token.""" for payload_class in PAYLOAD_CLASSES: if payload_class.create_arguments_apply( project_id=project_id, domain_id=domain_id, trust_id=trust_id, federated_info=federated_info, access_token_id=access_token_id): break version = payload_class.version payload = payload_class.assemble( user_id, methods, project_id, domain_id, expires_at, audit_ids, trust_id, federated_info, access_token_id ) versioned_payload = (version,) + payload serialized_payload = msgpack.packb(versioned_payload) token = self.pack(serialized_payload) # NOTE(lbragstad): We should warn against Fernet tokens that are over # 255 characters in length. This is mostly due to persisting the tokens # in a backend store of some kind that might have a limit of 255 # characters. Even though Keystone isn't storing a Fernet token # anywhere, we can't say it isn't being stored somewhere else with # those kind of backend constraints. if len(token) > 255: LOG.info('Fernet token created with length of %d ' 'characters, which exceeds 255 characters', len(token)) return token
token里面塞入了payload,而这里payload里封装了一堆有用的东西,加一个version,self.pack()可以看做是加密起来,返回一个token
【pki】
这种format用devstack安装貌似中途有的地方没法安装,就直接列出官方说法,PKI的本质就是基于数字签名,Keystone用私钥对token进行数字签名,各个API server用公钥在本地验证该token
这种token感觉比较坑,网上查的有的说快2000字符长度,有的说4000度字符长度,我这里暂时不验证了,总之长度负载太重了,用起来是在不方便
【pkiz】
这种format实际上是在pkt的基础上做了压缩处理,压缩之后,长度负载大概是pki的90%左右,这个感觉可用性还是不太好,都不太人性化
总之,对于如何选择Token,网上有做过总结
Token类型 | UUID | PKI | PKIZ | Fernet |
大小 | 32Byte | KB级别 | KB级别 | 约255Byte |
本地认证 | 不支持 | 支持 | 支持 | 不支持 |
存储在数据库 | 是 | 是 | 是 | 否 |
携带信息 | 无 | user,catalog等 | user,catalog等 | user等 |
加密方式 | 无 | 非对称加密 | 非对称加密 | 对称加密 |
是否压缩 | 否 | 否 | 是 | 否 |
版本支持 | D | G | J | K |
Token 类型的选择涉及多个因素,包括 Keystone server 的负载、region 数量、安全因素、维护成本以及 token 本身的成熟度。region 的数量影响 PKI/PKIZ token 的大小,从安全的角度上看,UUID 无需维护密钥,PKI 需要妥善保管 Keystone server 上的私钥,Fernet 需要周期性的更换密钥,因此从安全、维护成本和成熟度上看,UUID > PKI/PKIZ > Fernet 如果:
Keystone server 负载低,region 少于 3 个,采用 UUID token。
Keystone server 负载高,region 少于 3 个,采用 PKI/PKIZ token。
Keystone server 负载低,region 大与或等于 3 个,采用 UUID token。
Keystone server 负载高,region 大于或等于 3 个,K 版本及以上可考虑采用 Fernet token。
目前我devstack环境里貌似就只有uuid和fernet两种format,其它两种可自行查询摸索