puppet-keystone 模块
0. 基础知识
puppet-keystone
是用于配置和管理 Keystone,其中包括:服务,软件包,Keystone user,role,service,endpoint 等等。其中 keystone user, role, service, endpoint 等资源的管理是通过自定义的 resource type 来实现。
在开始介绍 puppet-keystone 模块前,先来回顾一下 Keystone 中的基础概念。
Identity
Keystone 的 Identity: user
和 group
,用于标识用户的身份,数据可以存在 Keystone 数据库中,或者也可以使用 LDAP。
名称 | 说明 |
---|---|
user | user 表示独立的 API 消费者,user 非全局唯一,必须属于某个 domain,但在 domain 命名空间下唯一 |
group | group 表示汇总 user 集合的容器,和 user 一样,group 非全局唯一,必须属于某个 domain,在 domain 命名空间下唯一 |
Resources
Keystone 的 resources 部分提供了两类数据: Projects
和 Domains
,通常存储在 SQL 中。
名称 | 说明 |
---|---|
Project(Tenant) | Project(在 v2.0 时也称为 Tenant) 表示 Openstack 基本单位的所有权限。在 OpenStack 中的资源必须归属于某个特定 project。project 非全局唯一,必须归属于某个 domain,在 domain 命名空间下唯一。若一个 project 没有被指定 domain,那么其 domain 会被设置为 default |
Domain | domain 是 project,user 和 group 更高层级的容器。每个 domain 定义了一个命名空间,Keystone 默认提供了一个名为'Default'的默认 domain。Domain 是全局唯一的。 |
Assignment
Assignment 提供了 role 和 role assignment 的数据。
名称 | 说明 |
---|---|
Role | role 指定了 user 能获取的授权级别,roles 可以 domain 或 project 级别授予,role 可以被指定到单独的 user 或 group。注意噢,role 可是全局唯一的。 |
Role Assignments | 一个包含 Role, Resource, Identity 的三元组 |
Token
Token 服务用于验证和管理 token,在完成对用户正确的认证请求后,Keystone 会返回相应的 token,token 存在有效期。在用户与 Openstack 服务的交互中,会使用 token 作为验证信息,提高系统的安全性。
Catalog
Catalog 提供了各个 service 的 endpoint 注册入口,用于 endpoint 自动发现。
以下是 Keystone service catalog 的样例:
"catalog": [
{
"name": "Keystone",
"type": "identity",
"endpoints": [
{
"interface": "public",
"url": "https://identity.example.com:35357/"
}
]
}
]
通常,作为用户不需要关心这个列表,catalog 在以下情况下会作为返回值响应:
- token creation response (
POST /v3/auth/tokens
) - token validation response (
GET /v3/auth/tokens
) - standalone resource (
GET /v3/auth/catalog
)
Services
service catalog 本身是由一组 services 组成,service 的定义是:
Service 实体表示 Openstack 中的 web 服务。每个 service 可以有 0 个或以上的 endpoint,当然没有 endpoint 的 service 并没有什么实际用途。完整描述请参见: Identity API v3 spec
除了和 endpoint 相关以外,还有两个非常重要的属性:
- name (string)
面向用户的 service 名称
这表示该参数的值不是为了让程序去解析的,而是作为一个终端用户可读的字符串。例如 keystone 服务的 name,你可以设置为"Keystone"或者"New Public Cloud Indetity Service"。因此,使用者可以根据实际需求来设置。
- type (string)
描述 service 所实现的 API。该参数值只能在给定的列表中选择。目前 Openstack 支持的参数值有:
compute, image, ec2, identity, volume, network
等。
Endpoints
Endpoint 表示 API 服务的基础 URL,以及与其相关的 metadata。每个服务应该有 1 个及以上相关的 endpoint,例如:publicurl,adminurl,internalurl。
Endpoint 实体表示 Opestack web services 的 URL。
- interface(string)
根据设置的类型来决定 endpoint 的访问权限:
public
: 向终端用户提供可在公网上访问的网络接口internal
: 向终端用户提供近可在内部网络访问的网络接口admin
: 提供各个服务管理权限的访问,一般仅部署在内部并且加密的网络接口
多数服务在实际使用时,只需要设置 public
URL 即可。
- url (string)
service enpoint 的完整 URL。
这个完整 URL 应该由不带版本信息的基础 URL 加端口号组成。一个理想的 url 是: https://identity.example.com:35357/
相反, https://identity.example.com:35357/v2.0/
作为一个反例,它引导所有的 client 去连接指定的 v2.0 版本,不管这些客户端能否处理哪里版本。
我们通过图例来解释这些复杂的概念:
Keystone v2 model
- user 可以存在于不同的部门中(project),并且在各个部门中可以拥有不同的 role。
- SandraD 在 Aerospace 是个系统管理员,在 Comp Sci 就变身为客户支持。
Keystone v3 model: Domain
- v3 通过 domain 术语引入了多租户的概念。如上图,domain 相当于是 project 的容器。
- 通过 domain,一个云用户就可以创建属于自己的 user,groups 和 roles。
Keystone v3 model: Group
- 往常我们需要为 user/project 赋予 role,现在 domain owner 就可以把 role 赋予 group,然后把 user 添加到 group 里去。
- role 可以赋予到 domain 范围的 group 或者 project 范围的 group
在上图中:
- JohnB 属于"domain1 sysadmins" group,拥有 sysadmin role,并属于 Bio,Aero,Compsci project。
- LisaD 属于"Big Engineers"group,拥有 Engineer role,仅属于 compsci project。
Keystone 服务组件
组件 | 描述 |
---|---|
openstack-keystone | 对外提供认证和授权服务,同时支持 v2/v3 API |
keystone | 基于命令行的 keystone 客户端工具 |
1. 先睹为快
不想看下面大段的代码解析,已经跃跃欲试了?
OK,我们开始吧!
打开虚拟机终端并输入以下命令:
$ puppet apply -v keystone/examples/v3_basic.pp
等待命令执行完成,在终端下试试吧:
$ export OS_IDENTITY_API_VERSION=3
$ export OS_USERNAME=admin
$ export OS_USER_DOMAIN_NAME=admin_domain
$ export OS_PASSWORD=ChangeMe
$ export OS_PROJECT_NAME=admin
$ export OS_PROJECT_DOMAIN_NAME=admin_domain
$ export OS_AUTH_URL=http://keystone.local:35357/v3
$ openstack user list
$ openstack service list
这是如何做到的?下面来看 v3_basic.pp 代码
#设置了全局的 Exec 属性,当命令执行失败时,输出结果
Exec { logoutput => 'on_failure' }
# 安装 MySQL 服务
class { '::mysql::server': }
# 配置 keystone database
class { '::keystone::db::mysql':
password => 'keystone',
}
# 配置 keystone 服务
class { '::keystone':
verbose => true,
debug => true,
database_connection => 'mysql://keystone:keystone@127.0.0.1/keystone',
admin_token => 'admin_token',
enabled => true,
}
# 设置 admin role
class { '::keystone::roles::admin':
email => 'test@example.tld',
password => 'a_big_secret',
admin => 'admin', # username
admin_tenant => 'admin', # project name
admin_user_domain => 'admin', # domain for user
admin_tenant_domain => 'admin', # domain for project
}
# 创建 keystone endpoint
class { '::keystone::endpoint':
public_url => 'http://127.0.0.1:5000/',
admin_url => 'http://127.0.0.1:35357/',
}
2. 核心代码讲解
2.1 class keystone
class keystone
逻辑非常复杂,暂先抛开大量的判断逻辑和类调用,它主要完成了三个主要任务:
- 安装 Keystone 软件包
- 管理 Keystone.conf 中的主要配置项
- 管理 Keystone 服务
2.1.1 keystone 软件包管理
这里有一个重要参数$package_ensure,可以指定软件包的版本,或者将其标记为总是安装最新版本,本书将会在最佳实践部分再次提及它。
# keystone 软件包
package { 'keystone':
ensure => $package_ensure,
name => $::keystone::params::package_name,
tag => ['openstack', 'keystone-package'],
}
# keystone-client 软件包
if $client_package_ensure == 'present' {
include '::keystone::client'
} else {
class { '::keystone::client':
ensure => $client_package_ensure,
}
}
2.1.2 keystone.conf 核心参数管理
class keystone 管理了大量的配置项,比如 cache, token, db, endpoint 等相关参数,这里不一一列举。
那么对于这些选项是如何管理的呢?这里我们要提到 keystone_config
。
keystone_config
是一个自定义的 resource type,其代码路径是:
- lib/puppet/type/keystone_config.rb 定义了 keystone_config
- lib/puppet/provider/keystone_config/ini_setting.rb 实现了 keystone_config
在这里我们关注如何使用 keystone_config
。
keystone_config 有几种使用场景:
对指定参数赋值:
keystone_config { 'section_name/option_name': value => option_value}
对指定参数赋值,并设置为加密:
keystone_config { 'section_name/option_name': value => option_value, secret => true}
我们知道 puppet agent 的所有输出默认都会被 syslog 打到系统日志/var/log/messages 中,那么有心人只要用 grep 就能从中搜到许多敏感信息,例如:admin_token, user_password, keystone_db_password 等等。只要设置了 secret 为 true 后,那么就不会把该参数的相关日志打到系统日志中。
删除指定参数:
keystone_config { 'section_name/option_name': ensure => absent}
OK,讲解就到这里,下面看一段实际的代码。
keystone_config {
'DEFAULT/admin_token': value => $admin_token, secret => true;
'DEFAULT/public_bind_host': value => $public_bind_host;
'DEFAULT/admin_bind_host': value => $admin_bind_host;
'DEFAULT/public_port': value => $public_port;
'DEFAULT/admin_port': value => $admin_port;
}
与之对应的 keystone.conf 配置文件[DEFAULT]下的 admin_token 等配置项被 Puppet 修改为指定值。
2.1.3 keystone 服务管理
puppet 支持 keystone 以单进程模式运行或者跑在 Apache 上,请注意,如果需要将 keystone 运行在 Apache 上,那么需要添加 keystone::wsgi::apache,代码如下:
class { 'keystone':
...
service_name => 'httpd',
...
}
class { 'keystone::wsgi::apache':
...
}
我们来看一下管理 keystone 服务的逻辑:
if $service_name == $::keystone::params::service_name {
$service_name_real = $::keystone::params::service_name
...
#这里调用了 keystone::service 类,用于管理 keystone 服务的具体配置
class { '::keystone::service':
ensure => $service_ensure,
service_name => $service_name,
enable => $enabled,
hasstatus => true,
hasrestart => true,
validate => true,
admin_endpoint => $v_auth_url,
admin_token => $admin_token,
insecure => $validate_insecure,
cacert => $validate_cacert,
}
} else {
class { '::keystone::service':
ensure => $service_ensure,
service_name => $service_name,
enable => $enabled,
hasstatus => true,
hasrestart => true,
validate => false,
}
}
warning('Keystone under Eventlet has been deprecated during the Kilo cycle. Support for deploying under eventlet will be dropped as of the M-release of OpenStack.')
} elsif $service_name == 'httpd' {
# 在这里,我们可以看到当$service_name 为 httpd 时,将 keystone service 的状态设置为了 stopped。
include ::apache::params
class { '::keystone::service':
ensure => 'stopped',
service_name => $::keystone::params::service_name,
enable => false,
validate => false,
}
$service_name_real = $::apache::params::service_name
# leave this here because Ubuntu packages will start Keystone and we need it stopped
# before apache can run
Service['keystone'] -> Service[$service_name_real]
} else {
fail('Invalid service_name. Either keystone/openstack-keystone for running as a standalone service, or httpd for being run by a httpd server')
}
2.2 class keystone::service
在 class keystone
中就遇到了 keystone::service,从类的名称可以得知,该类用于管理 Keystone 服务。其中有两段代码需要注意:
第一段是管理 keystone 服务:
service { 'keystone':
ensure => $ensure,
name => $service_name,
enable => $enable,
hasstatus => $hasstatus,
hasrestart => $hasrestart,
tag => 'keystone-service',
}
第二段代码比较有意思,类似于 smoketest,简单调用 keystone 的 user list 接口来验证 keystone 服务是否正常运行:
if $validate and $admin_token and $admin_endpoint {
$cmd = "openstack --os-auth-url ${admin_endpoint} --os-token ${admin_token} ${insecure_s} ${cacert_s} user list"
$catch = 'name'
exec { 'validate_keystone_connection':
path => '/usr/bin:/bin:/usr/sbin:/sbin',
provider => shell,
command => $cmd,
subscribe => Service['keystone'],
refreshonly => true,
tries => $retries,
try_sleep => $delay,
notify => Anchor['keystone::service::end'],
}
}
2.3 class keystone::endpoint
顾名思义, class keystone::endpoint
用于创建和管理 Keystone 的 service 和 endpoint。
以下是使用样例:
class { 'keystone::endpoint':
public_url => 'https://154.10.10.23:5000',
internal_url => 'https://11.0.1.7:5000',
admin_url => 'https://10.0.1.7:35357',
}
那么它是如何实现的呢?继续往下看,它又调用了一个 define。
keystone::resource::service_identity { 'keystone':
configure_user => false,
configure_user_role => false,
service_type => 'identity',
service_description => 'OpenStack Identity Service',
public_url => $public_url_real,
admin_url => $admin_url_real,
internal_url => $internal_url_real,
region => $region,
user_domain => $user_domain,
project_domain => $project_domain,
default_domain => $default_domain,
}
接下来,需要跳转到 keystone::resource::service_identity
的定义了。
2.3.1 define keystone::resource::service_identity
莫慌,接着来看 keystone::resource::service_identity,终于到路的尽头了,来看看它是怎么实现的。
首先,它实现了管理 keystone user:
if $configure_user {
if $user_domain_real {
# We have to use ensure_resource here and hope for the best, because we have
# no way to know if the $user_domain is the same domain passed as the
# $default_domain parameter to class keystone.
ensure_resource('keystone_domain', $user_domain_real, {
'ensure' => 'present',
'enabled' => true,
})
}
ensure_resource('keystone_user', $auth_name, {
'ensure' => 'present',
'enabled' => true,
'password' => $password,
'email' => $email,
'domain' => $user_domain_real,
})
if ! $password {
warning("No password had been set for ${auth_name} user.")
}
}
这里的关键是 keystone_user 资源,这又是一个自定义 resource type,其源码路径为:
- lib/puppet/type/keystone_config.rb 定义
- lib/puppet/provider/keystone_config/ini_setting.rb 实现
通过 keystone_user,Puppet 完成了对 user 的管理(包括创建,修改,查询)。
同理,还有 keystone_domain,目的是完成对 domain 的管理。剩下的代码同理,就不一一解读了。
3.小结
在这里,我们介绍了 puppet-keystone 的核心代码,当然该 module 还有许多重要的 class 我们并没有涉及,例如:keystone::deps,keystone::policy 等等。这些就留给读者自己去阅读代码了。
4.动手练习
- 配置 token_flush 的 cron job,使得可以定期清理 Keystone 数据库的 token 表中 token 失效数据。
- 将 keystone 服务运行在 Apache 上
- 如何开启 keystone 的 debug 日志级别
- 接第 3 问,在 keystone 和 keystone::loging 里都存在$verbose 变量,这种代码冗余的原因是出于什么考虑?可以移除吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论