Keystone Federation Identity
转自 http://wsfdl.com/openstack/2016/01/14/Keystone-Federation-Identity.html
Keystone federation identity 涉及很多概念,安装配置复杂,官网的文档又不够清晰,下面 4 篇文章在安装配置方面阐述的非常详细。
- Configure Keystone to Keystone Federation
- Configure Keystone to Testshib Federation with SAML
- Configure Keystone federation with Kerberos
- Configure Keystone federation with multi-IDP
1. Federation Identity 简介
关于 federation identity,维基百科的定义如下:
A federated identity is the means of linking a person's electronic identity and attributes, stored across multiple distinct identity management systems(IDM).
在多个认证管理系统互相信任的基础上,federation identity 允许多个认证管理系统联邦认证各个系统的用户身份。它最重要的一个功能就是实现单点登录(Single Sign On),用户仅需认证一次,便可访问这些互相授信的资源。比如 A 公司员工需使用 AWS 公有云,出于安全考虑,不希望在 AWS 的 IAM 创建员工账户信息,通过 federation identity 打通二者之间的用户授权和认证,A 公司员工只需在本公司完成身份认证即可访问 AWS 资源。我们把 A 公司称之为 Identity Provider(IDP), AWS 称之为 Service Provider(SP)。
- Service Provider: 服务提供方,它只提供服务,依赖 Identity Provider 认证用户身份
- Identity Provider: 断言(assertion)方,用于认证用户身份
- Assertion Protocol: 认证(断言)协议,Service Provider 和 Identity Provider 完成认证用户身份所用的协议,常用有 SAML, OpenID, Oauth 等
以 SAML 协议为例,典型的认证流程分为 Redirect Bindings 和 Artifact/POST Bindings 两种。
Federation identity 具有以下优点:
- 支持 SSO 单点登录
- 避免向 Service Provider 暴露用户信息,提高安全性
- 避免用户注册多个账号,增加用户负担
Keystone Federation 的原理
Federation identity 为 hybrid cloud 在用户管理层面提供了良好的解决方案。Keystone 从 Icehouse 开始逐步增加 federation identity 的功能,Icehouse 支持 Keystone 作为 Service Provider,Juno 版本新增了 Identity Provider,支持 SAML 和 OpenID 两种认证协议。OpenStack 作为云服务的解决方案,对外提供计算、存储和网络等服务,多数场景下 Keystone 常常作为服务端,对接其它的 Identity Provider,所以本节着重阐述 Service Provider 的原理和流程。首先先介绍 3 类重要的 API。
- Identity Provider API: /OS-FEDERATION/identity_providers
管理 Keystone 信任的 Identity Providers。 - Protocol API: /OS-FEDERATION/identity_providers/{idp_id}/protocols
管理 Keystone 和某个 Identity Provider 之间认证的协议,通常为 oidc(OpenID) 或 saml2(SAML)。 Mapping API: /OS-FEDERATION/mappings
管理 Identity Provider 里的用户和 Keystone 里的用户之间的映射规则,通过该 API,管理员可以管理 IDP 中用户访问 Service 的权限。比如 IDP 有用户 A,B,通过配置 mapping rule,可以允许 A 有权限而 B 无权限访问。
为了支持 Service Provider,Keystone 必须运行在 Apache HTTPD 上,mod-shibboleth 作为 apache plugin 支持 SAML 认证协议,完成了 Keystone 和 IDP 之间用户的身份认证,流程如下。
- 用户访问 /OS-FEDERATION/identity_providers/{identity_provider}/protocols/{protocol}/auth,Apache 捕获该 URL 并触发 mod-shibboleth 重定向至外部的 Identity Provider。
- 外部的 Identity Provider 认证用户的身份并把用户的某些身份信息返回给 Apache,Apache 再把信息传给 Keystone。
- Keystone 根据 mapping rule 把判断用户是否有访问权限,如果有访问权限,返回一个 unscoped token。用户可拿 unscoped token 查看可用的 project 并生成 scoped token,进而访问 OpenStack 的 API。
Configure Keystone as a Service Provider
本节开始介绍如何安装配置 Keystone to Keystone Federation,重点参考了 it-is-time-to-play-with-keystone-to-keystone-federation-in-kilo(原文存在 2 处配置错误,本文已给予纠正)。 我们有两个服务器,分别作为 SP 和 IDP,二者均需按照官网的手册安装 Keystone。
- Linux: Ubuntu 14.04 LTS
- OpenStack: Kilo
更新 keystone.conf 如下配置:
[auth]
methods = external,password,token,oauth1,saml2
saml2 = keystone.auth.plugins.mapped.Mapped
Apache 新增如下配置:
Listen 5000
Listen 35357
<VirtualHost *:5000>
WSGIScriptAliasMatch ^(/v3/OS-FEDERATION/identity_providers/.*?/protocols/.*?/auth)$ /var/www/cgi-bin/keystone/main/$1
......
</VirtualHost>
<VirtualHost *:35357>
WSGIScriptAliasMatch ^(/v3/OS-FEDERATION/identity_providers/.*?/protocols/.*?/auth)$ /var/www/cgi-bin/keystone/admin/$1
......
</VirtualHost>
<Location /Shibboleth.sso>
SetHandler shib
</Location>
<LocationMatch /v3/OS-FEDERATION/identity_providers/.*?/protocols/saml2/auth>
ShibRequestSetting requireSession 1
AuthType shibboleth
ShibExportAssertion Off
Require valid-user
</LocationMatch>
安装 Shibboleth:
apt-get install libapache2-mod-shib2
更新 /etc/shibboleth/attribute-map.xml 的以下配置项:
<Attribute name="openstack_user" id="openstack_user"/>
<Attribute name="openstack_roles" id="openstack_roles"/>
<Attribute name="openstack_project" id="openstack_project"/>
<Attribute name="openstack_user_domain" id="openstack_user_domain"/>
<Attribute name="openstack_project_domain" id="openstack_project_domain"/>
更新 /etc/shibboleth/shibboleth2.xml 的以下配置项:
<SSO entityID="http://idp:5000/v3/OS-FEDERATION/saml2/idp">
SAML2 SAML1
</SSO>
<MetadataProvider type="XML" uri="http://idp:5000/v3/OS-FEDERATION/saml2/metadata"/>
启动 shibboleth 并重启 apache:
shib-keygen
service apache2 restart
查看 shibboleth 是否正常运行
unbuntu@SP:~# a2enmod shib2
Module shib2 already enabled
Configure Keystone as an Identity Provider
安装 xmlsec1 和 pysaml2:
sudo apt-get install xmlsec1
sudo pip install pysaml2
更新 keystone.conf 的如下配置:
[saml]
certfile=/etc/keystone/ssl/certs/ca.pem
keyfile=/etc/keystone/ssl/private/cakey.pem
idp_entity_id=http://idp:5000/v3/OS-FEDERATION/saml2/idp
idp_sso_endpoint=http://idp:5000/v3/OS-FEDERATION/saml2/sso
idp_metadata_path=/etc/keystone/keystone_idp_metadata.xml
生成 DIP 的 metadata 并重启 apache HTTPD:
keystone-manage saml_idp_metadata > /etc/keystone/keystone_idp_metadata.xml
service apache2 restart
Test Keystone to Keystone federation
- 在 Service Provider 端执行以下脚本,创建 domain, group, mapping, idp, protocol 等。其中 idp 指向另外一个作为 Identity Provider 的 Keystone,protocol 采用了 saml2 协议,mapping 的规则为只要 IDP 中名为 bob 或者 acme 的用户都可通过认证,并且映射到 Service 端的 federated_user 用户上。
import os
from keystoneclient import session as ksc_session
from keystoneclient.auth.identity import v3
from keystoneclient.v3 import client as keystone_v3
try:
# Used for creating the ADMIN user
OS_PASSWORD = '123456'
OS_USERNAME = 'admin'
# This will vary according to the entity:
# the IdP or the SP
OS_AUTH_URL = 'http://sp:35357/v3'
OS_PROJECT_NAME = 'admin'
OS_DOMAIN_NAME = 'default'
except KeyError as e:
raise SystemExit('%s environment variable not set.' % e)
def client_for_admin_user():
auth = v3.Password(auth_url=OS_AUTH_URL,
username=OS_USERNAME,
password=OS_PASSWORD,
user_domain_name=OS_DOMAIN_NAME,
project_name=OS_PROJECT_NAME,
project_domain_name=OS_DOMAIN_NAME)
session = ksc_session.Session(auth=auth)
return keystone_v3.Client(session=session)
# Used to execute all admin actions
client = client_for_admin_user()
def create_domain(client, name):
try:
d = client.domains.create(name=name)
except:
d = client.domains.find(name=name)
return d
def create_group(client, name, domain):
try:
g = client.groups.create(name=name, domain=domain)
except:
g = client.groups.find(name=name)
return g
def create_role(client, name):
try:
r = client.roles.create(name=name)
except:
r = client.roles.find(name=name)
return r
print('\nCreating domain1')
domain1 = create_domain(client, 'domain1')
print('\nCreating group1')
group1 = create_group(client, 'group1', domain1)
print('\nCreating role Member')
role1 = create_role(client, 'Member')
print('\nGrant role Member to group1 in domain1')
client.roles.grant(role1, group=group1, domain=domain1)
print('\nList group1 role assignments')
client.role_assignments.list(group=group1)
def create_mapping(client, mapping_id, rules):
try:
m = client.federation.mappings.create(
mapping_id=mapping_id, rules=rules)
except:
m = client.federation.mappings.find(
mapping_id=mapping_id)
return m
print('\nCreating mapping')
rules = [
{
"local": [
{
"user": {
"name": "federated_user"
},
"group": {
"id": group1.id
}
}
],
"remote": [
{
"type": "openstack_user",
"any_one_of": [
"bob",
"acme"
]
}
]
}
]
mapping1 = create_mapping(client, mapping_id='keystone-idp-mapping', rules=rules)
def create_idp(client, id, remote_id):
idp_ref = {'id': id,
'remote_ids': [remote_id],
'enabled': True}
try:
i = client.federation.identity_providers.create(**idp_ref)
except:
i = client.federation.identity_providers.find(id=id)
return i
def create_protocol(client, protocol_id, idp, mapping):
try:
p = client.federation.protocols.create(protocol_id=protocol_id,
identity_provider=idp,
mapping=mapping)
except:
p = client.federation.protocols.find(protocol_id=protocol_id)
return p
print('\nRegister keystone-idp')
idp1 = create_idp(client, id='keystone-idp',
remote_id='http://idp:5000/v3/OS-FEDERATION/saml2/idp')
print('\nRegister protocol')
protocol1 = create_protocol(client, protocol_id='saml2', idp=idp1,
mapping=mapping1)
在 IDP 端执行以下脚本,用户 bob 获得一个 unscoped token,可拿该 token 向 SP 获取 scope token 后访问 Service 端的资源。
import json
import os
import requests
from keystoneclient import session as ksc_session
from keystoneclient.auth.identity import v3
from keystoneclient.v3 import client as keystone_v3
class K2KClient(object):
def __init__(self):
self.sp_id = 'keystone-sp'
self.auth_url = 'http://idp:35357/v3'
self.project_id = '582df27a0db14149a6da375b31fce3df'
self.username = 'bob'
self.password = '123456'
self.domain_id = 'default'
def v3_authenticate(self):
auth = v3.Password(auth_url=self.auth_url,
username=self.username,
password=self.password,
user_domain_id=self.domain_id,
project_id=self.project_id)
self.session = ksc_session.Session(auth=auth, verify=False)
self.session.auth.get_auth_ref(self.session)
self.token = self.session.auth.get_token(self.session)
def _generate_token_json(self):
return {
"auth": {
"identity": {
"methods": [
"token"
],
"token": {
"id": self.token
}
},
"scope": {
"service_provider": {
"id": self.sp_id
}
}
}
}
def _check_response(self, response):
if not response.ok:
raise Exception("Something went wrong, %s" % response.__dict__)
def get_saml2_ecp_assertion(self):
token = json.dumps(self._generate_token_json())
url = self.auth_url + '/auth/OS-FEDERATION/saml2/ecp'
r = self.session.post(url=url, data=token, verify=False)
self._check_response(r)
self.assertion = str(r.text)
def _get_sp(self):
url = self.auth_url + '/OS-FEDERATION/service_providers/' + self.sp_id
r = self.session.get(url=url, verify=False)
self._check_response(r)
sp = json.loads(r.text)[u'service_provider']
return sp
def _handle_http_302_ecp_redirect(self, response, location, **kwargs):
return self.session.get(location, authenticated=False, **kwargs)
def exchange_assertion(self):
"""Send assertion to a Keystone SP and get token."""
sp = self._get_sp()
r = self.session.post(
sp[u'sp_url'],
headers={'Content-Type': 'application/vnd.paos+xml'},
data=self.assertion,
authenticated=False,
redirect=False)
self._check_response(r)
r = self._handle_http_302_ecp_redirect(r, sp[u'auth_url'],
headers={'Content-Type':
'application/vnd.paos+xml'})
self.fed_token_id = r.headers['X-Subject-Token']
self.fed_token = r.text
def list_federated_projects(self):
url = 'http://sp:5000/v3/OS-FEDERATION/projects'
headers = {'X-Auth-Token': self.fed_token_id}
r = requests.get(url=url, headers=headers)
self._check_response(r)
return json.loads(str(r.text))
def _get_scoped_token_json(self, project_id):
return {
"auth": {
"identity": {
"methods": [
"token"
],
"token": {
"id": self.fed_token_id
}
},
"scope": {
"project": {
"id": project_id
}
}
}
}
def scope_token(self, project_id):
# project_id can be select from the list in the previous step
token = json.dumps(self._get_scoped_token_json(project_id))
url = 'http://sp:5000/v3/auth/tokens'
headers = {'X-Auth-Token': self.fed_token_id,
'Content-Type': 'application/json'}
r = requests.post(url=url, headers=headers, data=token,
verify=False)
self._check_response(r)
self.scoped_token_id = r.headers['X-Subject-Token']
self.scoped_token = str(r.text)
def main():
client = K2KClient()
client.v3_authenticate()
client.get_saml2_ecp_assertion()
print('ECP wrapped SAML assertion: %s' % client.assertion)
client.exchange_assertion()
print('Unscoped token id: %s' % client.fed_token_id)
# If you want to get a scope token, please ensure federated_user has a project
# and uncommen below codes.
'''
projects = client.list_federated_projects()
print('Federated projects: %s' % projects['projects'])
project_id = projects['projects'][0]['id']
project_name = projects['projects'][0]['name']
client.scope_token(project_id)
print('Scoped token of ' + project_name + ' : ' + client.scoped_token_id)
'''
if __name__ == "__main__":
main()
Reference
- https://developer.rackspace.com/blog/keystone-to-keystone-federation-with-openstack-ansible/
- https://specs.openstack.org/openstack/keystone-specs/api/v3/identity-api-v3-os-federation-ext.html
- http://blog.rodrigods.com/it-is-time-to-play-with-keystone-to-keystone-federation-in-kilo/
- https://bigjools.wordpress.com/2015/05/22/saml-federation-with-openstack/
Keystone Federation Identity的更多相关文章
- OpenStack (1) - Keystone OpenStack Identity Service
echo deb http://ubuntu-cloud.archive.canonical.com/ubuntu precise-updates/grizzly main >> /etc ...
- 使用openstackclient调用Keystone v3 API
本文内容属于个人原创,转载务必注明出处: http://www.cnblogs.com/Security-Darren/p/4138945.html 考虑到Keystone社区逐渐弃用第二版身份AP ...
- Openstack组件部署 — Keystone Install & Create service entity and API endpoints
目录 目录 前文列表 Install and configure Prerequisites 先决条件 Create the database for identity service 生成一个随机数 ...
- OpenStack Identity API v3 extensions (CURRENT)
Table Of Contents Identity API v3 extensions (CURRENT) OS-ENDPOINT-POLICY API Associate policy and e ...
- OpenStack Identity API v3
Table Of Contents OpenStack Identity API v3 What’s New in Version 3.7 What’s New in Version 3.6 What ...
- 云计算管理平台之OpenStack认证服务Keystone
一.keystone简介 keystone是openstack中的核心服务,它主要作用是实现用户认证和授权以及服务目录:所谓服务目录指所有可用服务的信息库,包含所有可用服务及其API endport路 ...
- 【OpenStack】OpenStack系列2之KeyStone详解
源码下载.依赖安装 参考:http://www.oschina.net/question/565065_66271 https://github.com/yongluo2013/osf-opensta ...
- 3. Configure the Identity Service
Controller Node: 安装认证服务: 1. sudo apt-get install keystone 2. sudo vi /etc/keystone/keystone.conf [ ...
- OpenStack:安装Keystone
>安装Keystone1. 安装# apt-get install keystone2. 创建dbcreate database keystone;grant all privileges on ...
随机推荐
- base64文件上传的问题
package com.zhicall.media.util; import java.io.FileInputStream; import java.io.FileOutputStream; imp ...
- redis集群报错,(error) MOVED 15495 127.0.0.1:7003
节点会对命令请求进行分析和key的slot计算,并且会查找这个命令所要处理的键所在的槽.如果要查找的哈希槽正好就由接收到命令的节点负责处理, 那么节点就直接执行这个命令. 另一方面, 如果所查 ...
- jquery.fileDownload plugin: Success msg alert before actual pdf download completed
Currently , I use jquery fileDownload plugin to download multiple pdf that in a list page, which eve ...
- 【题解】Journeys(线段树优化连边)
[#3073. Pa2011]Journeys (线段树优化连边) 这张图太直观了,直接讲透了线段树优化连边的原理和正确性. 考虑建立两颗线段树,一颗是外向树,一颗是内向树,相当于网络流建模一样,我们 ...
- 小程序发送 request请求失败 提示不在合法域名列表中的解决方法
可以在小程序开发工具中设置不校验域名.
- Android之四大组件、六大布局、五大存储
[-] Android六大界面布局方式 1 LinearLayout线性布局 LinearLayout的常用XML属性及相关方法 LinearLayout子元素支持的常用XML属性及方法 2 Tabl ...
- mysql数据库补充知识6 完整性约束
一 介绍 约束条件与数据类型的宽度一样,都是可选参数 作用:用于保证数据的完整性和一致性主要分为: PRIMARY KEY (PK) 标识该字段为该表的主键,可以唯一的标识记录 FOREIGN KEY ...
- django路由系统之反向生成url
from niubin.service import v1 from django.urls import reverse from django.shortcuts import HttpRespo ...
- settings配置 文件操作
设置文件路径 import os BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 这里用到了python中一个神奇的变量 __file__ ...
- JDK1.8(JRE)和eclipse-jee不匹配解决放
想要用eclipse-jee的话,需要jdk1.8一下版本才能用. 1.需要下载jdk1.7 2.把jdk1.7安装(不需要设置环境变量). 3.在项目上右击选择properties 4.选择Java ...