Keystone 深度解析:OpenStack 统一认证与授权体系
定位与职责
Keystone 是 OpenStack 的身份认证服务,所有其他组件都依赖它完成:
- 认证(Authentication):你是谁?验证用户名/密码或 Token
- 授权(Authorization):你能做什么?基于 RBAC 的权限控制
- 服务目录(Service Catalog):各组件的 API Endpoint 注册中心
没有 Keystone,OpenStack 的任何 API 都无法调用。
核心概念模型
1 2 3 4 5
| Domain(域) └── Project(项目/租户) └── User(用户) └── Role Assignment(角色绑定) └── Role(角色)→ Policy Rules(权限规则)
|
Domain
- 最高级别的隔离单元,对应一个组织或部门
- 默认域:
Default
- 不同 Domain 下的用户名可以重复
Project(Tenant)
- 资源隔离的基本单位,所有资源(VM、网络、卷)都归属于某个 Project
- 旧版本叫 Tenant,新版本统一叫 Project
User & Group
- User:具体的操作主体
- Group:用户集合,可以批量绑定角色
Role
- 角色是权限的载体,通过
policy.yaml 定义每个角色能执行哪些 API 操作
- 内置角色:
admin、member、reader(Keystone 默认三级角色体系)
Token 机制
Fernet Token(当前主流)
1
| Fernet Token = Base64(IV + Timestamp + UserID + ProjectID + ... ) + HMAC签名
|
特点:
- 无状态:Token 本身携带所有信息,不需要数据库查询验证
- 轻量:约 255 字节
- 有时效:默认 1 小时过期
- 密钥轮转:通过
fernet_keys/ 目录管理密钥,支持滚动更新
1 2 3 4 5
| /etc/keystone/fernet-keys/ ├── 0 ├── 1 └── 2
|
Token 获取流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| POST /v3/auth/tokens { "auth": { "identity": { "methods": ["password"], "password": { "user": {"name": "admin", "password": "xxx", "domain": {"name": "Default"}} } }, "scope": {"project": {"name": "myproject", "domain": {"name": "Default"}}} } }
响应 Header: X-Subject-Token: gAAAAABh...(Fernet Token)
|
源码关键路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| keystone/ ├── api/ │ └── v3/ │ ├── auth/ │ ├── users.py │ └── projects.py ├── token/ │ ├── provider.py │ └── providers/ │ └── fernet/ ├── identity/ ├── assignment/ ├── resource/ └── middleware/ └── auth_token.py
|
核心认证流程源码追踪
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Tokens(ks_flask.ResourceBase): def post(self): auth_data = self.request_body_json.get('auth') token = authentication.authenticate(auth_data) return token
class Provider(common.BaseProvider): def generate_id_and_issued_at(self, token): serialized = self._token_formatter.create_token(...) return serialized, issued_at
|
RBAC 权限模型
policy.yaml 结构
1 2 3 4 5 6 7 8 9
|
"context_is_admin": "role:admin" "project_member": "role:member and project_id:%(project_id)s"
"os_compute_api:servers:create": "rule:project_member" "os_compute_api:servers:delete": "rule:project_member" "os_compute_api:os-admin-actions:reset_state": "rule:context_is_admin"
|
Keystone 默认三级角色
1 2 3
| admin(系统管理员) └── member(项目成员,可操作资源) └── reader(只读,只能查看)
|
从 Wallaby 版本开始推行 Secure RBAC,引入 system scope 和 project scope 的区分,避免 admin 权限过于宽泛。
服务目录(Service Catalog)
每个 OpenStack 服务启动时需要在 Keystone 注册自己的 Endpoint:
1 2 3 4 5 6 7
| openstack service create --name nova --description "Compute" compute
openstack endpoint create --region RegionOne compute public http://controller:8774/v2.1 openstack endpoint create --region RegionOne compute internal http://controller:8774/v2.1 openstack endpoint create --region RegionOne compute admin http://controller:8774/v2.1
|
Token 中携带 Service Catalog,客户端通过它找到各服务的 API 地址,无需硬编码。
高可用部署
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| VIP: 10.0.0.10 │ ┌──────────┴──────────┐ │ HAProxy │ └──────────┬──────────┘ │ ┌──────────────┼──────────────┐ │ │ │ keystone-1 keystone-2 keystone-3 (uwsgi) (uwsgi) (uwsgi) │ │ │ └──────────────┼──────────────┘ │ MySQL Galera Cluster
|
Keystone 是无状态服务(Fernet Token),天然支持水平扩展,HA 部署只需在前面加负载均衡即可。
二次开发要点
自定义认证插件
1 2 3 4 5 6 7 8 9 10 11 12
| from keystone.auth import plugins as auth_plugins
class SMSAuth(auth_plugins.AuthMethodHandler): def authenticate(self, auth_payload): phone = auth_payload.get('phone') code = auth_payload.get('code') if verify_sms_code(phone, code): user = get_user_by_phone(phone) return None, user, None raise exception.Unauthorized()
|
常见运维问题
| 问题 |
原因 |
解决 |
| Token 验证失败 |
Fernet 密钥不同步 |
同步所有节点的 /etc/keystone/fernet-keys/ |
| 服务无法调用其他组件 |
Endpoint 注册错误 |
检查 openstack endpoint list |
| 权限拒绝 |
policy.yaml 规则错误 |
检查对应服务的 policy 文件 |
| Token 过期时间太短 |
默认 3600s |
修改 [token] expiration 配置 |