一、概念与关系
先上图,看user、credential、group、role、project/tenant、service、endpoint、domain、region;token、authentication、scope等彼此间的关系。
1. resource:project、domain
2. credential:可以是3个配对,username/password,username/API key,token
3. token:token是由keystone进行账户验证后返回给客户端的字符串,作为后续验证凭证,有3种,UUID、PKI/PKIZ(PKI的压缩版)、fernet token(有加密解密过程)
4. authentication:
a. keystone针对用户的请求进行授权验证,验证对象为credential的3个配对
b. scope:验证结果的token是有范围或者说是标的的,比如project范围还是domain范围
5. username/domain:用户名属于并在一个domain中唯一,userid全局唯一
6. group/project、group/domain:太常见了,不解释
7. role:可分配给user和group,全局唯一;权限地位的象征,VIP等级的高低,类似QQ的银钻黄钻
8. project/domain:project项目属于并在一个domain中唯一;资源集合;project在Identity API V3(Kilo)之前叫tenant
10. region:
a. OpenStack部署的地理位置,这个层面是为了识别多地域部署,比如阿里云虚机可选择深圳或者北京的IDC,默认为regionOne
b. 各个region具有独立的全部资源,regions之间共享一个Keystone和Horizon
11. service:比如计算的nova,块的cinder,网络的neutron等;service在keystone看来是个user
12. endpoint/service:service对外服务的窗口,就如同电信的营业厅,酒吧的吧台;endpoint有3个地址,public、private、admin分别对应不同权限用途
最后,为啥要用token,而不是直接用户名/密码对了事?因为,每个请求都指向一个对应的API比如nova-api,而API干事之前都需要验证你这个客户端,可能是Horizon,可能是Python-client,或者干脆赤裸裸的REST,如果每次都用对应的用户名密码对就不便于统一管理也不太安全。但是用token,因为token有过期这个么时间限制便于客户端缓存,只是一串统一的的字符。
# 客户端POST上去的请求:
{
"auth": {
"identity": {
"methods": [
"password"
],
"password": {
"user": {
"domain": {
"name": "acme"
}
"name": "userA",
"password": "secretsecret"
}
}
},
"scope": {
"project": {
"domain": {
"id": "1789d1"
},
"name": "project-x"
}
}
}
}
# 整成一个环境变量
$ export OS_PROJECT_DOMAIN_ID=1789d1
$ export OS_USER_DOMAIN_NAME=acme
$ export OS_USERNAME=userA
$ export OS_PASSWORD=secretsecret
$ export OS_PROJECT_NAME=project-x
二、Keystone的验证过程
1. UUID (通用唯一识别码,Universally Unique Identifier)
①. Keystone:
a. 生成UUID,存储UUID在后端比如Mysql,把这个UUID返回给客户端
b. 客户端发送过来的UUID endpoint API(service)都需要拿到后端去验证,包括过期日期
c. 验证成功就返回success,否则failure给API
②. 客户端:把Keystone返回的UUID给缓存起来,客户端的每个请求都要附带这个UUID
③. 如此,客户端的每次请求,Keystone都要参与验证(下发UUID时还需要存储,token过期时还要去删除),多用户多操作时对后端的压力着实有点大
# 查看Mysql中数据库keystone中的token表格
MariaDB [keystone]> select id,expires from token;
+----------------------------------+---------------------+
| id | expires |
+----------------------------------+---------------------+
| 7007133585f849f78b482546ebdc9fb6 | 2016-06-29 08:00:02 |
| 188575d329534b00822fae8f83040ced | 2016-06-29 08:00:03 |
| 4d0c793a331a4476bb85e7090ee059a4 | 2016-06-29 08:00:03 |
| 0f72278b68304d508ae0c9c64db29876 | 2016-06-29 08:00:04 |
| c059b199828f4df388907bd814cf79bc | 2016-06-29 08:00:04 |
| 5548b3bab4ca412e806841d907842d72 | 2016-06-29 08:00:05 |
| 9fe5ebbcbca74251bb2e8187c5af1bd0 | 2016-06-29 08:00:05 |
| 228d211c42444f1fb04c7435ad1a9434 | 2016-06-29 08:51:43 |
| a6f5f50f47294dea866fdcc2a46129a3 | 2016-06-29 08:51:43 |
+----------------------------------+---------------------+
①. Keystone: 做为CA,只管用自己专用签发私钥(对应有签发证书)去签发准备给客户端的token形成CMS格式的token,同时维护一份CRL(Certificate Revocation List)
②. 各个API:持有Keystone所有家当的一份copy,包括签发证书,CA自身的证书,以及CRL,就像它自己也是个CA
③. 如此,Keystone不必每次客户端请求都亲自处理了,各个API自己去验证,验证标的不是token本身,而是keystone签发的签名
④. 但是,这个CMS token太长了,服务越多越长,一般都超过1600个字节,如下
MIIDsAYJKoZIhvcNAQcCoIIDoTCCA50CAQExCTAHBgUrDgMCGjCCAokGCSqGSIb3DQEHAaCCAnoEggJ2ew0KICAgICJhY2Nlc3MiOiB7DQogICAgICAgICJtZ\
XRhZGF0YSI6IHsNCiAgICAgICAgICAgIC4uLi5tZXRhZGF0YSBnb2VzIGhlcmUuLi4uDQogICAgICAgIH0sDQogICAgICAgICJzZXJ2aWNlQ2F0YWxvZyI6IF\
sNCiAgICAgICAgICAgIC4uLi5lbmRwb2ludHMgZ29lcyBoZXJlLi4uLg0KICAgICAgICBdLA0KICAgICAgICAidG9rZW4iOiB7DQogICAgICAgICAgICAiZXh\
waXJlcyI6ICIyMDEzLTA1LTI2VDA4OjUyOjUzWiIsDQogICAgICAgICAgICAiaWQiOiAicGxhY2Vob2xkZXIiLA0KICAgICAgICAgICAgImlzc3VlZF9hdCI6\
ICIyMDEzLTA1LTI1VDE4OjU5OjMzLjg0MTgxMSIsDQogICAgICAgICAgICAidGVuYW50Ijogew0KICAgICAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6IG51b\
GwsDQogICAgICAgICAgICAgICAgImVuYWJsZWQiOiB0cnVlLA0KICAgICAgICAgICAgICAgICJpZCI6ICI5MjVjMjNlYWZlMWI0NzYzOTMzZTA4YTRjNDE0M2\
YwOCIsDQogICAgICAgICAgICAgICAgIm5hbWUiOiAidXNlciINCiAgICAgICAgICAgIH0NCiAgICAgICAgfSwNCiAgICAgICAgInVzZXIiOiB7DQogICAgICA\
gICAgICAuLi4udXNlcmRhdGEgZ29lcyBoZXJlLi4uLg0KICAgICAgICB9DQogICAgfQ0KfQ0KMYH/MIH8AgEBMFwwVzELMAkGA1UEBhMCVVMxDjAMBgNVBAgT\
BVVuc2V0MQ4wDAYDVQQHEwVVbnNldDEOMAwGA1UEChMFVW5zZXQxGDAWBgNVBAMTD3d3dy5leGFtcGxlLmNvbQIBATAHBgUrDgMCGjANBgkqhkiG9w0BAQEFA\
ASBgEh2P5cHMwelQyzB4dZ0FAjtp5ep4Id1RRs7oiD1lYrkahJwfuakBK7OGTwx26C+0IPPAGLEnin9Bx5Vm4cst/0+COTEh6qZfJFCLUDj5b4EF7r0iosFsc\
pnfCuc8jGMobyfApz/dZqJnsk4lt1ahlNTpXQeVFxNK/ydKL+tzEjg
⑤. 同时,这个PKI只是签名,和UUID一样都没有加密,用Firefox F12或者Wireshark等抓包就能看到明文的token,这时需要做下HTTPS
3. fernet token(Kilo)
①. Staged key:有且只有一个,命名为0,准备下一个rotation时变为Primary key,可以解密token
②. Primary key:有且只有一个,名为为x,当前用于加密解密token
③. Secondary key: 有x-1个,从Primary退役下来的,用于解密当初它加密过的token
④. AES256加密token,SHA256 HMAC验证完整性,只有Keystone具有访问这些key的权限,token不用存储,250字节以下大小
# vim /etc/keystone/keystone.conf
[fernet_tokens]
key_repository = /etc/keystone/fernet-keys/ /*基于文件的秘钥仓库*/
token_expiration=24
rotation_frequency=6
max_active_keys=(token_expiration/rotation_frequency)+2 /*2=1个staged key+1个将要删掉的secondary key*/
$ ls -la /etc/keystone/fernet-keys/
drwx------ 2 keystone keystone 4096 .
drwxr-xr-x 3 keystone keystone 4096 ..
-rw------- 1 keystone keystone 44 0 (staged key)
-rw------- 1 keystone keystone 44 2 (secondary key)
-rw------- 1 keystone keystone 44 3 (secondary key)
-rw------- 1 keystone keystone 44 4 (secondary key)
-rw------- 1 keystone keystone 44 5 (secondary key)
-rw------- 1 keystone keystone 44 6 (primary key)