OpenStack Keystone 详解

86 阅读13分钟

Keystone 是 OpenStack 的身份认证服务,提供统一的身份验证、授权和服务发现功能。它是 OpenStack 生态系统的核心组件,所有其他服务都依赖它进行身份验证。


1. Keystone 架构概述

1.1 核心架构图

┌─────────────────────────────────────────────────────────────────┐
│                      Keystone Architecture                      │
├─────────────────────────────────────────────────────────────────┤
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐          │
│  │   Client    │    │   Horizon   │    │    CLI      │          │
│  │Applications │    │  Dashboard  │    │   Tools     │          │
│  └─────────────┘    └─────────────┘    └─────────────┘          │
│         │                   │                   │               │
│         └───────────────────┼───────────────────┘               │
│                             │                                   │
│  ┌─────────────────────────────────────────────────────────────┐ │
│  │                 Keystone API (WSGI)                        │ │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐          │ │
│  │  │ Identity API│ │Assignment API│ │Catalog API  │          │ │
│  │  │    (v3)     │ │    (v3)     │ │   (v3)      │          │ │
│  │  └─────────────┘ └─────────────┘ └─────────────┘          │ │
│  └─────────────────────────────────────────────────────────────┘ │
│                             │                                   │
│  ┌─────────────────────────────────────────────────────────────┐ │
│  │                  Core Services                              │ │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐          │ │
│  │  │  Identity   │ │ Assignment  │ │   Catalog   │          │ │
│  │  │  Service    │ │   Service   │ │   Service   │          │ │
│  │  └─────────────┘ └─────────────┘ └─────────────┘          │ │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐          │ │
│  │  │   Token     │ │   Policy    │ │   Trust     │          │ │
│  │  │  Service    │ │  Service    │ │  Service    │          │ │
│  │  └─────────────┘ └─────────────┘ └─────────────┘          │ │
│  └─────────────────────────────────────────────────────────────┘ │
│                             │                                   │
│  ┌─────────────────────────────────────────────────────────────┐ │
│  │                    Backends                                 │ │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐          │ │
│  │  │   SQL DB    │ │    LDAP     │ │   External  │          │ │
│  │  │  (MySQL)    │ │  Directory  │ │   Systems   │          │ │
│  │  └─────────────┘ └─────────────┘ └─────────────┘          │ │
│  └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

1.2 核心概念

# keystone/server/wsgi.py
def initialize_application():
    """初始化 Keystone WSGI 应用"""
    
    # 加载配置
    config.configure()
    
    # 初始化各种服务
    services = [
        'identity',      # 身份服务
        'assignment',    # 分配服务
        'catalog',       # 服务目录
        'token',         # 令牌服务
        'policy',        # 策略服务
        'trust',         # 信任服务
        'credential',    # 凭证服务
    ]
    
    # 创建 WSGI 应用
    application = keystone_service.loadapp('config:keystone.conf')
    
    return application

2. 核心概念和模型

2.1 身份 (Identity) 模型

2.1.1 用户 (User)

# keystone/identity/backends/sql.py
class User(sql.ModelBase, sql.ModelDictMixin):
    """用户模型"""
    
    __tablename__ = 'user'
    
    id = sql.Column(sql.String(64), primary_key=True)
    name = sql.Column(sql.String(255), nullable=False)
    domain_id = sql.Column(sql.String(64), sql.ForeignKey('domain.id'), 
                          nullable=False)
    password = sql.Column(sql.String(128))
    enabled = sql.Column(sql.Boolean, default=True)
    
    # 额外属性
    default_project_id = sql.Column(sql.String(64))
    description = sql.Column(sql.Text())
    
    # 密码过期
    password_expires_at = sql.Column(sql.DateTimeInt())
    
    # 多因子认证
    multi_factor_auth_enabled = sql.Column(sql.Boolean, default=False)
    multi_factor_auth_rules = sql.Column(sql.JsonBlob())

用户操作示例

# 创建域
openstack domain create --description "Company Domain" company

# 创建用户
openstack user create \
    --domain company \
    --description "John Doe" \
    --email john@company.com \
    --password secretpass \
    john

# 列出用户
openstack user list --domain company

# 更新用户
openstack user set \
    --email newemail@company.com \
    --enable \
    john

# 设置用户密码
openstack user set --password newpassword john

2.1.2 组 (Group)

# keystone/identity/backends/sql.py
class Group(sql.ModelBase, sql.ModelDictMixin):
    """组模型"""
    
    __tablename__ = 'group'
    
    id = sql.Column(sql.String(64), primary_key=True)
    name = sql.Column(sql.String(255), nullable=False)
    domain_id = sql.Column(sql.String(64), sql.ForeignKey('domain.id'),
                          nullable=False)
    description = sql.Column(sql.Text())
    
    # 组成员关系
    users = relationship('User', secondary='user_group_membership')

class UserGroupMembership(sql.ModelBase, sql.ModelDictMixin):
    """用户组成员关系"""
    
    __tablename__ = 'user_group_membership'
    
    user_id = sql.Column(sql.String(64), sql.ForeignKey('user.id'),
                        primary_key=True)
    group_id = sql.Column(sql.String(64), sql.ForeignKey('group.id'),
                         primary_key=True)

组操作示例

# 创建组
openstack group create \
    --domain company \
    --description "Developers Group" \
    developers

# 将用户添加到组
openstack group add user developers john

# 列出组成员
openstack group show developers

# 从组中移除用户
openstack group remove user developers john

2.2 项目和域 (Project & Domain)

2.2.1 域 (Domain)

# keystone/resource/backends/sql.py
class Domain(sql.ModelBase, sql.ModelDictMixin):
    """域模型"""
    
    __tablename__ = 'domain'
    
    id = sql.Column(sql.String(64), primary_key=True)
    name = sql.Column(sql.String(255), nullable=False)
    enabled = sql.Column(sql.Boolean, default=True, nullable=False)
    description = sql.Column(sql.Text())
    
    # 域配置
    extra = sql.Column(sql.JsonBlob())
    
    # 关联的项目
    projects = relationship('Project', backref='domain')
    
    # 关联的用户
    users = relationship('User', backref='domain')

2.2.2 项目 (Project)

# keystone/resource/backends/sql.py
class Project(sql.ModelBase, sql.ModelDictMixin):
    """项目模型"""
    
    __tablename__ = 'project'
    
    id = sql.Column(sql.String(64), primary_key=True)
    name = sql.Column(sql.String(255), nullable=False)
    domain_id = sql.Column(sql.String(64), sql.ForeignKey('domain.id'),
                          nullable=False)
    description = sql.Column(sql.Text())
    enabled = sql.Column(sql.Boolean, default=True)
    
    # 父项目支持 (项目层次结构)
    parent_id = sql.Column(sql.String(64), sql.ForeignKey('project.id'))
    is_domain = sql.Column(sql.Boolean, default=False)
    
    # 项目标签
    tags = sql.Column(sql.JsonBlob())
    
    # 项目选项
    options = sql.Column(sql.JsonBlob())

项目操作示例

# 创建项目
openstack project create \
    --domain company \
    --description "Development Project" \
    --enable \
    dev-project

# 创建子项目
openstack project create \
    --domain company \
    --parent dev-project \
    --description "Frontend Development" \
    frontend-dev

# 列出项目
openstack project list --domain company

# 显示项目层次结构
openstack project show dev-project --parents
openstack project show dev-project --children

2.3 角色和分配 (Role & Assignment)

2.3.1 角色 (Role)

# keystone/assignment/backends/sql.py
class Role(sql.ModelBase, sql.ModelDictMixin):
    """角色模型"""
    
    __tablename__ = 'role'
    
    id = sql.Column(sql.String(64), primary_key=True)
    name = sql.Column(sql.String(255), nullable=False, unique=True)
    domain_id = sql.Column(sql.String(64), sql.ForeignKey('domain.id'))
    description = sql.Column(sql.Text())
    
    # 角色选项
    options = sql.Column(sql.JsonBlob())

class Assignment(sql.ModelBase, sql.ModelDictMixin):
    """角色分配模型"""
    
    __tablename__ = 'assignment'
    
    type = sql.Column(sql.Enum(AssignmentType.USER_PROJECT,
                              AssignmentType.GROUP_PROJECT,
                              AssignmentType.USER_DOMAIN,
                              AssignmentType.GROUP_DOMAIN),
                     nullable=False)
    
    actor_id = sql.Column(sql.String(64), nullable=False)  # 用户或组ID
    target_id = sql.Column(sql.String(64), nullable=False) # 项目或域ID
    role_id = sql.Column(sql.String(64), sql.ForeignKey('role.id'),
                        nullable=False)
    inherited = sql.Column(sql.Boolean, default=False)

角色分配示例

# 创建角色
openstack role create developer
openstack role create project-manager

# 在项目中分配角色给用户
openstack role add \
    --project dev-project \
    --user john \
    developer

# 在域中分配角色给组
openstack role add \
    --domain company \
    --group developers \
    member

# 列出角色分配
openstack role assignment list \
    --user john \
    --project dev-project

# 继承角色分配
openstack role add \
    --project parent-project \
    --user john \
    --inherited \
    admin

3. 令牌 (Token) 系统

3.1 令牌类型

# keystone/token/providers/base.py
class Provider(object):
    """令牌提供者基类"""
    
    def issue_token(self, user_id, method_names, expires_at=None,
                   project_id=None, domain_id=None, auth_context=None,
                   trust_id=None, include_catalog=True,
                   parent_audit_id=None):
        """签发令牌"""
        
        # 构建令牌数据
        token_data = self._get_token_data(
            user_id=user_id,
            method_names=method_names,
            expires_at=expires_at,
            project_id=project_id,
            domain_id=domain_id,
            auth_context=auth_context,
            trust_id=trust_id,
            include_catalog=include_catalog
        )
        
        # 生成令牌ID
        token_id = self._generate_token_id(token_data)
        
        return token_id, token_data

# keystone/token/providers/fernet/core.py
class Provider(base.Provider):
    """Fernet 令牌提供者"""
    
    def __init__(self, *args, **kwargs):
        super(Provider, self).__init__(*args, **kwargs)
        self.token_formatter = TokenFormatter()
    
    def _generate_token_id(self, token_data):
        """生成 Fernet 令牌ID"""
        
        # 提取关键信息
        user_id = token_data['token']['user']['id']
        expires_at = token_data['token']['expires_at']
        audit_ids = token_data['token']['audit_ids']
        
        # 创建令牌载荷
        payload = (user_id, expires_at, audit_ids)
        
        # 使用 Fernet 加密
        versioned_payload = (payload, self.token_formatter.creation_time)
        token_id = self.token_formatter.pack(versioned_payload)
        
        return token_id
    
    def validate_token(self, token_id):
        """验证令牌"""
        try:
            # 解密令牌
            payload, creation_time = self.token_formatter.unpack(token_id)
            user_id, expires_at, audit_ids = payload
            
            # 检查过期时间
            if expires_at and expires_at < timeutils.utcnow():
                raise exception.TokenNotFound(token_id=token_id)
            
            # 重建令牌数据
            return self._rebuild_token_data(user_id, expires_at, audit_ids)
            
        except (ValueError, fernet.InvalidToken):
            raise exception.TokenNotFound(token_id=token_id)

3.2 令牌生命周期

# keystone/token/core.py
class Manager(object):
    """令牌管理器"""
    
    def authenticate(self, auth=None):
        """认证并签发令牌"""
        
        # 验证认证方法
        method_names = auth.get('identity', {}).keys()
        
        # 执行认证
        user_info = self._authenticate(auth)
        user_id = user_info['user_id']
        
        # 获取作用域信息
        project_id = None
        domain_id = None
        
        if 'scope' in auth:
            if 'project' in auth['scope']:
                project_id = auth['scope']['project']['id']
            elif 'domain' in auth['scope']:
                domain_id = auth['scope']['domain']['id']
        
        # 设置过期时间
        expires_at = self._get_token_expiry_time(
            method_names=method_names,
            project_id=project_id,
            domain_id=domain_id
        )
        
        # 签发令牌
        token_id, token_data = self.provider.issue_token(
            user_id=user_id,
            method_names=method_names,
            expires_at=expires_at,
            project_id=project_id,
            domain_id=domain_id,
            include_catalog=True
        )
        
        return token_id, token_data
    
    def revoke_token(self, token_id, revoke_chain=False):
        """撤销令牌"""
        
        # 记录撤销事件
        revoke_event = {
            'token_id': token_id,
            'revoked_at': timeutils.utcnow(),
            'user_id': token_data['token']['user']['id']
        }
        
        if revoke_chain:
            # 撤销整个令牌链
            self._revoke_token_chain(token_id)
        
        # 添加到撤销列表
        self.revoke_api.revoke_by_event(revoke_event)

4. 服务目录 (Service Catalog)

4.1 服务和端点模型

# keystone/catalog/backends/sql.py
class Service(sql.ModelBase, sql.ModelDictMixin):
    """服务模型"""
    
    __tablename__ = 'service'
    
    id = sql.Column(sql.String(64), primary_key=True)
    type = sql.Column(sql.String(255), nullable=False)  # compute, network, etc.
    name = sql.Column(sql.String(255), nullable=False)
    description = sql.Column(sql.Text())
    enabled = sql.Column(sql.Boolean, default=True)
    
    # 关联的端点
    endpoints = relationship('Endpoint', backref='service')

class Endpoint(sql.ModelBase, sql.ModelDictMixin):
    """端点模型"""
    
    __tablename__ = 'endpoint'
    
    id = sql.Column(sql.String(64), primary_key=True)
    service_id = sql.Column(sql.String(64), sql.ForeignKey('service.id'),
                           nullable=False)
    interface = sql.Column(sql.Enum('public', 'internal', 'admin'),
                          nullable=False)
    url = sql.Column(sql.Text(), nullable=False)
    region_id = sql.Column(sql.String(255))
    enabled = sql.Column(sql.Boolean, default=True)

class Region(sql.ModelBase, sql.ModelDictMixin):
    """区域模型"""
    
    __tablename__ = 'region'
    
    id = sql.Column(sql.String(255), primary_key=True)
    description = sql.Column(sql.String(255))
    parent_region_id = sql.Column(sql.String(255), sql.ForeignKey('region.id'))
    
    # 区域层次结构
    sub_regions = relationship('Region')

4.2 服务目录操作

# 创建服务
openstack service create \
    --name nova \
    --description "OpenStack Compute Service" \
    compute

openstack service create \
    --name neutron \
    --description "OpenStack Networking Service" \
    network

# 创建端点
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

# 列出服务目录
openstack catalog list
openstack catalog show compute

# 查看令牌包含的服务目录
openstack token issue

5. 认证机制

5.1 密码认证

# keystone/auth/plugins/password.py
class Password(base.AuthMethodHandler):
    """密码认证插件"""
    
    method = 'password'
    
    def authenticate(self, auth_payload):
        """执行密码认证"""
        
        response_data = {}
        
        # 提取用户信息
        user_info = auth_payload['user']
        user_id = user_info.get('id')
        user_name = user_info.get('name')
        domain_info = user_info.get('domain', {})
        password = user_info['password']
        
        # 获取用户对象
        if user_id:
            user_ref = PROVIDERS.identity_api.get_user(user_id)
        else:
            domain_id = self._lookup_domain(domain_info)
            user_ref = PROVIDERS.identity_api.get_user_by_name(
                user_name, domain_id)
        
        # 验证密码
        PROVIDERS.identity_api.authenticate(
            user_id=user_ref['id'],
            password=password)
        
        response_data['user_id'] = user_ref['id']
        
        return base.AuthHandlerResponse(
            status=True,
            response_body=None,
            response_data=response_data)

5.2 令牌认证

# keystone/auth/plugins/token.py
class Token(base.AuthMethodHandler):
    """令牌认证插件"""
    
    method = 'token'
    
    def authenticate(self, auth_payload):
        """执行令牌认证"""
        
        response_data = {}
        
        # 获取令牌ID
        token_id = auth_payload['id']
        
        # 验证令牌
        token_data = PROVIDERS.token_provider_api.validate_token(token_id)
        
        # 提取用户信息
        user_id = token_data['token']['user']['id']
        
        response_data['user_id'] = user_id
        response_data['auth_token'] = token_data
        
        return base.AuthHandlerResponse(
            status=True,
            response_body=None,
            response_data=response_data)

5.3 多因子认证 (MFA)

# keystone/auth/plugins/totp.py
class TOTP(base.AuthMethodHandler):
    """TOTP (时间戳动态密码) 认证插件"""
    
    method = 'totp'
    
    def authenticate(self, auth_payload):
        """执行 TOTP 认证"""
        
        response_data = {}
        
        # 获取用户信息
        user_info = auth_payload['user']
        user_id = user_info['id']
        passcode = auth_payload['passcode']
        
        # 获取用户的 TOTP 凭证
        credential_list = PROVIDERS.credential_api.list_credentials_for_user(
            user_id, type='totp')
        
        if not credential_list:
            raise exception.Unauthorized('No TOTP credentials found')
        
        # 验证 TOTP 代码
        for credential in credential_list:
            if self._validate_totp_passcode(credential, passcode):
                response_data['user_id'] = user_id
                return base.AuthHandlerResponse(
                    status=True,
                    response_body=None,
                    response_data=response_data)
        
        raise exception.Unauthorized('Invalid TOTP passcode')
    
    def _validate_totp_passcode(self, credential, passcode):
        """验证 TOTP 密码"""
        import pyotp
        
        secret = credential['blob']
        totp = pyotp.TOTP(secret)
        
        # 允许时间窗口偏差
        return totp.verify(passcode, valid_window=1)

6. 策略 (Policy) 系统

6.1 策略引擎

# keystone/policy/backends/rules.py
class Rules(base.PolicyDriverBase):
    """基于规则的策略驱动"""
    
    def __init__(self):
        super(Rules, self).__init__()
        self.enforcer = policy.Enforcer(CONF)
    
    def enforce(self, credentials, action, target):
        """执行策略检查"""
        
        # 构建上下文
        context = {
            'user_id': credentials.get('user_id'),
            'project_id': credentials.get('project_id'),
            'domain_id': credentials.get('domain_id'),
            'roles': credentials.get('roles', []),
            'is_admin': credentials.get('is_admin', False),
        }
        
        # 执行策略检查
        try:
            self.enforcer.enforce(action, target, context,
                                do_raise=True,
                                exc=exception.ForbiddenAction,
                                action=action)
        except policy.PolicyNotAuthorized:
            raise exception.Forbidden(action=action)

# keystone/common/policies/base.py
def build_enforcement_kwargs(func):
    """构建策略执行参数的装饰器"""
    
    def wrapper(self, request, *args, **kwargs):
        context = request.context
        target = {}
        
        # 从请求中提取目标信息
        if hasattr(request, 'context_dict'):
            target.update(request.context_dict)
        
        # 执行策略检查
        self.policy_api.enforce(
            context.auth_context,
            action=func.__name__,
            target=target)
        
        return func(self, request, *args, **kwargs)
    
    return wrapper

6.2 策略配置示例

# /etc/keystone/policy.yaml
"admin_required": "role:admin"
"service_role": "role:service"
"service_or_admin": "rule:admin_required or rule:service_role"
"owner": "user_id:%(user_id)s"
"admin_or_owner": "rule:admin_required or rule:owner"
"token_subject": "user_id:%(target.token.user_id)s"
"admin_or_token_subject": "rule:admin_required or rule:token_subject"

# Identity API
"identity:get_user": "rule:admin_or_owner"
"identity:list_users": "rule:admin_required"
"identity:create_user": "rule:admin_required"
"identity:update_user": "rule:admin_required"
"identity:delete_user": "rule:admin_required"

"identity:get_group": "rule:admin_required"
"identity:list_groups": "rule:admin_required"
"identity:create_group": "rule:admin_required"
"identity:update_group": "rule:admin_required"
"identity:delete_group": "rule:admin_required"

# Resource API
"resource:get_project": "rule:admin_required or project_id:%(target.project.id)s"
"resource:list_projects": "rule:admin_required"
"resource:create_project": "rule:admin_required"
"resource:update_project": "rule:admin_required"
"resource:delete_project": "rule:admin_required"

# Assignment API
"assignment:list_role_assignments": "rule:admin_required"
"assignment:create_grant": "rule:admin_required"
"assignment:check_grant": "rule:admin_required"
"assignment:revoke_grant": "rule:admin_required"

7. 联邦认证 (Federation)

7.1 身份提供者 (Identity Provider)

# keystone/federation/backends/sql.py
class IdentityProvider(sql.ModelBase, sql.ModelDictMixin):
    """身份提供者模型"""
    
    __tablename__ = 'identity_provider'
    
    id = sql.Column(sql.String(64), primary_key=True)
    description = sql.Column(sql.Text())
    enabled = sql.Column(sql.Boolean, nullable=False)
    remote_ids = sql.Column(sql.JsonBlob(), nullable=False)
    
    # 域ID
    domain_id = sql.Column(sql.String(64), sql.ForeignKey('domain.id'))

class FederationProtocol(sql.ModelBase, sql.ModelDictMixin):
    """联邦协议模型"""
    
    __tablename__ = 'federation_protocol'
    
    id = sql.Column(sql.String(64), primary_key=True)
    idp_id = sql.Column(sql.String(64), sql.ForeignKey('identity_provider.id'),
                       primary_key=True)
    mapping_id = sql.Column(sql.String(64), sql.ForeignKey('mapping.id'),
                           nullable=False)

7.2 映射规则

# keystone/federation/backends/sql.py
class Mapping(sql.ModelBase, sql.ModelDictMixin):
    """映射模型"""
    
    __tablename__ = 'mapping'
    
    id = sql.Column(sql.String(64), primary_key=True)
    rules = sql.Column(sql.JsonBlob(), nullable=False)

# 映射规则示例
mapping_rules = [
    {
        "local": [
            {
                "user": {
                    "name": "{0}",
                    "domain": {
                        "name": "federated_domain"
                    }
                }
            },
            {
                "group": {
                    "name": "federated_users",
                    "domain": {
                        "name": "federated_domain"
                    }
                }
            }
        ],
        "remote": [
            {
                "type": "REMOTE_USER"
            },
            {
                "type": "HTTP_SHIB_IDENTITY_PROVIDER",
                "any_one_of": ["university_idp"]
            }
        ]
    }
]

7.3 SAML 配置

# 创建身份提供者
openstack identity provider create \
    --description "University SAML IdP" \
    --remote-id https://university.edu/saml/metadata \
    university_idp

# 创建映射
openstack mapping create \
    --rules mapping_rules.json \
    university_mapping

# 创建协议
openstack federation protocol create \
    --identity-provider university_idp \
    --mapping university_mapping \
    saml2

# 配置 Apache mod_shib
# /etc/httpd/conf.d/keystone.conf
<Location /Shibboleth.sso>
    SetHandler shib
</Location>

<Location /v3/OS-FEDERATION/identity_providers/university_idp/protocols/saml2/auth>
    ShibRequestSetting requireSession 1
    AuthType shibboleth
    ShibExportAssertion Off
    Require valid-user
</Location>

8. 配置和部署

8.1 主配置文件

# /etc/keystone/keystone.conf
[DEFAULT]
# 日志配置
debug = False
verbose = True
log_file = /var/log/keystone/keystone.log

# 数据库连接
[database]
connection = mysql+pymysql://keystone:password@controller/keystone
connection_recycle_time = 10
max_pool_size = 1
max_retries = -1

# 令牌配置
[token]
provider = fernet
expiration = 3600
revoke_by_id = False

# Fernet 令牌配置
[fernet_tokens]
key_repository = /etc/keystone/fernet-keys/
max_active_keys = 3

# 缓存配置
[cache]
enabled = True
backend = oslo_cache.memcache_pool
memcache_servers = controller:11211

# 认证配置
[auth]
methods = external,password,token,oauth1,mapped,application_credential
password = keystone.auth.plugins.password.Password
token = keystone.auth.plugins.token.Token

# 身份后端
[identity]
driver = keystone.identity.backends.sql.Identity
default_domain_id = default

# 分配后端
[assignment]
driver = keystone.assignment.backends.sql.Assignment

# 资源后端
[resource]
driver = keystone.resource.backends.sql.Resource

# 角色后端
[role]
driver = keystone.assignment.backends.sql.Role

# 服务目录后端
[catalog]
driver = keystone.catalog.backends.sql.Catalog

# 策略后端
[policy]
driver = keystone.policy.backends.sql.Policy

# 信任后端
[trust]
driver = keystone.trust.backends.sql.Trust
enabled = True
allow_redelegation = False
max_redelegation_count = 3

# 联邦认证
[federation]
driver = keystone.federation.backends.sql.Federation

8.2 WSGI 配置

# /etc/httpd/conf.d/wsgi-keystone.conf
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
    LimitRequestBody 114688
    
    <IfVersion >= 2.4>
      ErrorLogFormat "%{cu}t %M"
    </IfVersion>
    
    ErrorLog /var/log/httpd/keystone.log
    CustomLog /var/log/httpd/keystone_access.log combined

    <Directory /usr/bin>
        <IfVersion >= 2.4>
            Require all granted
        </IfVersion>
        <IfVersion < 2.4>
            Order allow,deny
            Allow from all
        </IfVersion>
    </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
    LimitRequestBody 114688
    
    ErrorLog /var/log/httpd/keystone.log
    CustomLog /var/log/httpd/keystone_access.log combined

    <Directory /usr/bin>
        <IfVersion >= 2.4>
            Require all granted
        </IfVersion>
        <IfVersion < 2.4>
            Order allow,deny
            Allow from all
        </IfVersion>
    </Directory>
</VirtualHost>

9. 高可用和性能优化

9.1 高可用部署

# HAProxy 配置
# /etc/haproxy/haproxy.cfg
global
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

frontend keystone_public
    bind *:5000
    default_backend keystone_public_servers

frontend keystone_admin
    bind *:35357
    default_backend keystone_admin_servers

backend keystone_public_servers
    balance roundrobin
    option httpchk GET /v3
    server controller1 10.0.1.11:5000 check
    server controller2 10.0.1.12:5000 check
    server controller3 10.0.1.13:5000 check

backend keystone_admin_servers
    balance roundrobin
    option httpchk GET /v3
    server controller1 10.0.1.11:35357 check
    server controller2 10.0.1.12:35357 check
    server controller3 10.0.1.13:35357 check

9.2 性能调优

# 数据库连接池优化
[database]
connection = mysql+pymysql://keystone:password@controller/keystone
max_pool_size = 20
max_overflow = 30
pool_timeout = 30
pool_recycle = 300

# 缓存优化
[cache]
enabled = True
backend = oslo_cache.memcache_pool
memcache_servers = controller1:11211,controller2:11211,controller3:11211
expiration_time = 600

# 令牌优化
[token]
provider = fernet
expiration = 3600
cache_time = 300

# WSGI 进程优化
# WSGIDaemonProcess keystone-public processes=10 threads=5

10. 监控和故障排除

10.1 健康检查

# keystone/common/health.py
class HealthController(flask_restful.Resource):
    """健康检查控制器"""
    
    def get(self):
        """获取健康状态"""
        
        status = {
            'status': 'UP',
            'components': {}
        }
        
        # 检查数据库连接
        try:
            PROVIDERS.identity_api.list_users()
            status['components']['database'] = {'status': 'UP'}
        except Exception as e:
            status['components']['database'] = {
                'status': 'DOWN',
                'error': str(e)
            }
            status['status'] = 'DOWN'
        
        # 检查缓存
        try:
            cache.get_cache_region().get('health_check')
            status['components']['cache'] = {'status': 'UP'}
        except Exception as e:
            status['components']['cache'] = {
                'status': 'DOWN', 
                'error': str(e)
            }
        
        return status

10.2 日志分析

# 查看认证失败
grep "UNAUTHORIZED" /var/log/keystone/keystone.log

# 查看令牌操作
grep "token" /var/log/keystone/keystone.log | grep -E "(issue|validate|revoke)"

# 查看数据库连接问题
grep "Lost connection" /var/log/keystone/keystone.log

# 查看性能问题
grep "Slow query" /var/log/keystone/keystone.log

# 实时监控
tail -f /var/log/keystone/keystone.log | grep -E "(ERROR|WARNING)"

10.3 性能监控

# 自定义监控脚本
#!/usr/bin/env python3
import time
import requests
import json

def check_keystone_performance():
    """检查 Keystone 性能"""
    
    auth_data = {
        "auth": {
            "identity": {
                "methods": ["password"],
                "password": {
                    "user": {
                        "name": "admin",
                        "domain": {"name": "Default"},
                        "password": "password"
                    }
                }
            },
            "scope": {
                "project": {
                    "name": "admin",
                    "domain": {"name": "Default"}
                }
            }
        }
    }
    
    # 测试认证性能
    start_time = time.time()
    response = requests.post(
        'http://controller:5000/v3/auth/tokens',
        json=auth_data,
        headers={'Content-Type': 'application/json'}
    )
    auth_time = time.time() - start_time
    
    if response.status_code == 201:
        token = response.headers['X-Subject-Token']
        
        # 测试令牌验证性能
        start_time = time.time()
        validate_response = requests.get(
            'http://controller:5000/v3/auth/tokens',
            headers={
                'X-Auth-Token': token,
                'X-Subject-Token': token
            }
        )
        validate_time = time.time() - start_time
        
        print(f"Auth time: {auth_time:.3f}s")
        print(f"Validate time: {validate_time:.3f}s")
        
        # 警告阈值
        if auth_time > 2.0:
            print("WARNING: Authentication is slow!")
        if validate_time > 1.0:
            print("WARNING: Token validation is slow!")
    
    else:
        print(f"Authentication failed: {response.status_code}")

if __name__ == '__main__':
    check_keystone_performance()

11. 安全最佳实践

11.1 密码策略

# /etc/keystone/keystone.conf
[security_compliance]
# 密码强度要求
password_regex = ^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=]).{8,}$
password_regex_description = Password must contain at least 8 characters including one lowercase letter, one uppercase letter, one digit, and one special character

# 密码历史
unique_last_password_count = 5

# 密码过期
password_expires_days = 90
password_expire_warn_days = 30

# 账户锁定
lockout_failure_attempts = 5
lockout_duration = 300

# 会话管理
minimum_password_age = 1
change_password_upon_first_use = True

11.2 令牌安全

# Fernet 密钥轮换
keystone-manage fernet_setup --keystone-user keystone --keystone-group keystone
keystone-manage fernet_rotate --keystone-user keystone --keystone-group keystone

# 定期轮换脚本
#!/bin/bash
# /usr/local/bin/rotate_fernet_keys.sh

# 备份当前密钥
cp -r /etc/keystone/fernet-keys /etc/keystone/fernet-keys.backup.$(date +%Y%m%d)

# 轮换密钥
keystone-manage fernet_rotate --keystone-user keystone --keystone-group keystone

# 重启服务
systemctl reload httpd

# 验证
sleep 5
curl -s http://controller:5000/v3 > /dev/null && echo "Keystone is healthy after key rotation"

# 添加到 crontab
# 0 2 * * 0 /usr/local/bin/rotate_fernet_keys.sh

11.3 审计日志

# /etc/keystone/keystone.conf
[audit]
enabled = True
audit_map_file = /etc/keystone/api_audit_map.conf

# /etc/keystone/api_audit_map.conf
[resource_type_map]
users = user
projects = project
groups = group
domains = domain
roles = role

[path_keyword_map]
users = user_id
projects = project_id
groups = group_id
domains = domain_id
roles = role_id

[service_endpoints]
identity = /v3/users, /v3/projects, /v3/groups, /v3/domains, /v3/roles

总结

Keystone 作为 OpenStack 的身份认证服务,提供了:

核心功能:

  1. 身份管理:用户、组、域的生命周期管理
  2. 认证服务:多种认证方法支持
  3. 授权管理:基于角色的访问控制
  4. 服务发现:统一的服务目录
  5. 令牌管理:安全的令牌签发和验证

架构优势:

  1. 模块化设计:可插拔的后端驱动
  2. 可扩展性:支持联邦认证和外部身份源
  3. 高可用性:无状态设计,易于水平扩展
  4. 安全性:Fernet 令牌,策略引擎,审计日志

部署考虑:

  1. 性能优化:合理配置缓存和数据库连接池
  2. 安全加固:密码策略、密钥轮换、审计日志
  3. 高可用部署:负载均衡、数据库集群
  4. 监控运维:健康检查、性能监控、日志分析

Keystone 是构建安全、可靠的 OpenStack 云平台的基础,正确的配置和运维对整个云平台的安全性和稳定性至关重要。

Similar code found with 3 license types