与 Identity Service 联邦


Red Hat OpenStack Platform 16.2

使用 Red Hat Single Sign-On 与 Identity Service 相结合

OpenStack Documentation Team

摘要

使用 Red Hat Single Sign-On 与 Identity Service 相结合

对红帽文档提供反馈

我们感谢您对文档提供反馈信息。与我们分享您的成功秘诀。

在 JIRA 中提供文档反馈

使用 Create Issue 表单对文档提供反馈。JIRA 问题将在 Red Hat OpenStack Platform Jira 项目中创建,您可以在其中跟踪您的反馈进度。

  1. 确保您已登录到 JIRA。如果您没有 JIRA 帐户,请创建一个帐户来提交反馈。
  2. 点击以下链接打开 Create Issue 页面: Create Issue
  3. 完成 SummaryDescription 字段。在 Description 字段中,包含文档 URL、章节或章节号以及问题的详细描述。不要修改表单中的任何其他字段。
  4. Create

第 1 章 简介

警告

红帽目前不支持联邦。此功能只应用于测试,不应在生产环境中部署。

要在高可用性 Red Hat OpenStack Platform director 环境中配置联邦,您必须配置以下内容:

  • Red Hat Identity Management
  • Red Hat Single Sign-on (RH-SSO)
  • Red Hat OpenStack Platform overcloud

1.1. 概述

联邦身份验证是一种在不同服务间提供身份验证的方法。此身份验证解决方案依赖于身份提供程序(IdP),它是一个服务提供商(SP),并基于安全断言标记语言(SAML)。

当 OpenStack 是联邦身份验证解决方案中的服务提供商时,Red Hat Identity Management (IdM)组的成员将映射到具有项目访问权限的 Member 角色的 OpenStack Keystone 组 federated_users 中。因此,您可以通过将用户添加到 IdM 组 openstack-users 来授予用户对 OpenStack 的访问权限。

1.2. 先决条件

部署联邦身份验证前,您需要完成以下内容:

  • 您已使用以下属性部署了 Red Hat OpenStack Platform director 和 overcloud:

    • 您可以使用 SSH 连接到 Red Hat OpenStack Platform director 和每个 overcloud 节点。
    • 所有节点都具有完全限定域名(FQDN)。
    • TLS 加密用于所有外部通信。
    • HAProxy 终止 TLS 前端连接,在 HAProxy 之后运行的服务器不使用 TLS。
  • 存在 RH-SSO 服务器,您对服务器上具有管理特权,或者 RH-SSO 管理员已为您创建了一个域,并为您提供该域的管理特权。由于联邦 IdP 通过定义外部,因此 RH-SSO 服务器假定 Red Hat OpenStack Platform director overcloud 外部。如需更多信息,请参阅 安装和配置 RH-SSO 和创建域和用户
  • IdM 服务器存在,以及管理用户和组的 Red Hat OpenStack Platform director overcloud 外部。RH-SSO 使用 IdM 作为其用户联邦后备存储。
  • 按照 Keystone Federation 配置指南中所述的示例进行操作。
  • undercloud-0 节点上,您将帮助程序文件安装到 stack 用户的主目录中,并在 stack 用户主目录中工作。
  • controller-0 节点上,您将帮助程序文件安装到 heat-admin 用户的主目录中,并在 heat-admin 用户主目录中工作。
  • 如果之前已在控制器节点上安装了 mod_auth_mellon,您必须重新安装它,因为 Puppet Apache 类会删除不属于 Puppet 控制的任何 Apache 配置文件。
注意

只有 Red Hat OpenStack overcloud 启用联邦。director 不是联邦。

1.3. 访问 Red Hat OpenStack Platform 节点

默认情况下,您必须登录到 Red Hat OpenStack Platform director 才能访问 overcloud 节点。

  1. 使用 SSH 连接到 Red Hat OpenStack director:

    # ssh undercloud-0
    Copy to Clipboard Toggle word wrap
  2. 成为 stack 用户:

    $ su - stack
    Copy to Clipboard Toggle word wrap
  3. 查找 stackrc 配置,以启用所需的 OpenStack 环境变量:

    $ source stackrc
    Copy to Clipboard Toggle word wrap
  4. 在进行 source stackrc 后,您可以使用 openstack 命令行工具发出命令,该工具针对 Red Hat OpenStack Platform director 运行。要直接访问其中一个 overcloud 节点,请使用 openstack server list 检索 ip 地址,然后使用 SSH 连接:

    (undercloud) [stack@director ~]$ openstack server list -c Name -c Networks
    +----------------------+-----------------------+
    | Name                 | Networks              |
    +----------------------+-----------------------+
    | rhosp-controller-0   | ctlplane=10.94.101.11 |
    | rhosp-controller-1   | ctlplane=10.94.101.14 |
    | rhosp-controller-2   | ctlplane=10.94.101.17 |
    | rhosp-hypervisor-0   | ctlplane=10.94.101.18 |
    | rhosp-hypervisor-1   | ctlplane=10.94.101.20 |
    +----------------------+-----------------------+
    
    $ ssh heat-admin@10.94.101.11
    Copy to Clipboard Toggle word wrap

1.4. 技术概述

以下技术是 Red Hat OpenStack Platform 的一部分。

1.4.1. 高可用性

Red Hat OpenStack Platform director 在 overcloud 部署中分发各种 OpenStack 服务的冗余副本。这些冗余服务部署在 overcloud 控制器节点上,director 将这些节点命名为 controller-0controller-1controller-2 等等,具体取决于已经配置了控制器节点 director 的数量。

Controller 节点的 IP 地址不是外部可见,因为 Controller 节点上运行的服务是 HAProxy 后端服务器。一组控制器节点有一个公开可见的 IP 地址;这是 HAProxy 的前端。当请求到达公共 IP 地址上的服务时,HAProxy 选择一个后端服务器来为请求提供服务。

overcloud 被组织为高可用性集群。Pacemaker 管理集群,执行健康检查,并在资源停止工作时切换到另一个集群资源。您可以使用 Pacemaker 启动和停止这些资源。

有关高可用性的更多信息,请参阅 高可用性部署和用法指南

1.4.1.1. 管理 Pacemaker 服务

不要在 Controller 节点上使用 podman 命令来管理 Pacemaker 管理的包含的服务。使用 Pacemaker pcs 命令:

sudo pcs resource restart haproxy-bundle
Copy to Clipboard Toggle word wrap

要确定资源名称,请使用 Pacemaker status 命令:

sudo pcs status
...
* Container bundle set: haproxy-bundle [cluster.common.tag/openstack-haproxy:pcmklatest]:
    * haproxy-bundle-podman-0   (ocf::heartbeat:podman):        Started rhosp13-controller-0
    * haproxy-bundle-podman-1   (ocf::heartbeat:podman):        Started rhosp13-controller-1
    * haproxy-bundle-podman-2   (ocf::heartbeat:podman):        Started rhosp13-controller-2
...
Copy to Clipboard Toggle word wrap

1.4.2. HAProxy 概述

HAProxy 提供与 Pacemaker 类似的角色。它对后端服务器执行健康检查,并将请求转发到正常工作的后端服务器。在所有 Controller 节点上运行了一个 HAProxy。

虽然运行 HAProxy 的 N 个副本,但任何给定时间只有一个是字段请求;此活跃的 HAProxy 实例由 Pacemaker 管理。这种方法可防止发生冲突,并允许 HAProxy 的多个副本协调在多个后端间分发请求。如果 Pacemaker 检测到 HAProxy 失败,它会将前端 IP 地址重新分配给不同的 HAProxy 实例。然后,这个 HAProxy 实例成为控制 HAProxy 实例。

1.5. 使用配置脚本

要配置联邦身份验证,您需要运行长且复杂的命令。要使该任务更容易,并允许可重复使用,命令被保存到名为 configure-federation 的 shell 脚本中。如果您将步骤名称传递到 configure-federation,则可以执行特定的步骤。要查看可能的命令列表,请使用 help 选项(-h 或 --help)。

注意

有关脚本内容的更多信息,请参阅 第 6 章 configure-federation 文件

要查看变量替换后执行的命令,请使用以下选项:

-n
此选项提供了一个空运行模式,可在不修改系统的情况下将其操作写入 stdout。
-v
此选项提供了一个详细模式,它会在执行前将其操作写入 stdout。这对于日志记录非常有用。

1.6. 使用代理或 SSL 术语

对于代理后面的环境,请考虑以下主要功能:

  • 后端服务器可能具有不同的主机名、侦听不同端口,或使用与代理前端中看到的不同协议。

    当服务器生成自我引用 URL 时可能会出现问题,例如,如果服务器将客户端重定向到同一服务器上的不同 URL。服务器生成的 URL 必须与客户端显示的公共地址和端口匹配。

  • 身份验证协议(如 HTTP 和 HTTPS)对主机、端口和协议敏感,因为它们通常需要确保请求针对特定的服务器、端口和安全传输。代理可能会干扰此信息。

    • 代理会在将其发送到后端中的非公共服务器前转换其公共前端上收到的请求。
    • 来自非公共后端服务器的响应有时需要调整,以便其显示为来自代理的公共前端的响应。

      有多种方法可以解决这个问题。由于 SAML 对主机、端口和协议信息敏感,因此由于您要在高可用性代理(HAProxy)后配置 SAML,所以您必须处理这些问题,否则您的配置可能会失败。

第 2 章 配置红帽身份管理

您可以使用以下功能使用联邦用户管理配置 Red Hat OpenStack Platform:

  • Red Hat Identity Management (IdM)是 Red Hat OpenStack Platform 外部
  • Red Hat IdM 是所有用户和组群信息的来源
  • Red Hat Single Signon (RH-SSO)被配置为使用 Red Hat IdM 进行用户联邦

2.1. 为 RH-SSO 创建 IdM 服务帐户

如果您使用匿名绑定,出于安全原因,Red Hat Single Sign-On (RH-SSO)非常重要的一些信息。因此,您需要在查询专用帐户中的 RH-SSO 中为 RH-SSO 提供适当的权限,以查询 IdM LDAP 服务器以获取此信息:

LDAP_URL="ldaps://$FED_IPA_HOST"
DIR_MGR_DN="cn=Directory Manager"
SERVICE_NAME="rhsso"
SERVICE_DN="uid=$service_name,cn=sysaccounts,cn=etc,$FED_IPA_BASE_DN"

$ ldapmodify -H "${LDAP_URL}" -x -D "${DIR_MGR_DN}" -w <_FED_IPA_ADMIN_PASSWD_> <<EOF
dn: ${SERVICE_DN}
changetype: add
objectclass: account
objectclass: simplesecurityobject
uid: ${SERVICE_NAME}
userPassword: <_FED_IPA_RHSSO_SERVICE_PASSWD_>
passwordExpirationTime: 20380119031407Z
nsIdleTimeout: 0
EOF
Copy to Clipboard Toggle word wrap
注意

您可以使用 configure-federation 脚本执行上述步骤: $ ./configure-federation create-ipa-service-account

2.2. 创建测试用户

在 IdM 中创建用户帐户以进行测试:

流程

  1. 在 IdM 中创建 jdoe 用户:

    $ipa user-add --first John --last Doe --email jdoe@example.com jdoe
    Copy to Clipboard Toggle word wrap
  2. 为用户分配密码:

    $ipa passwd jdoe
    Copy to Clipboard Toggle word wrap

2.3. 为 OpenStack 用户创建 IdM 组

您必须有一个 IdM 组 openstack-users 来映射到 Keystone 组 federated_users。将 test 用户映射到此组。

在 Red Hat Identity Management (IdM)中创建 openstack-users 组:

流程

  1. 确保 openstack-users 组不存在:

    $ ipa group-show openstack-users
    ipa: ERROR: openstack-users: group not found
    Copy to Clipboard Toggle word wrap
  2. 将 openstack-users 组添加到 IdM 中:

    ipa group-add openstack-users
    Copy to Clipboard Toggle word wrap
  3. 将 test 用户添加到 openstack-users 组中:

    ipa group-add-member --users jdoe openstack-users
    Copy to Clipboard Toggle word wrap
  4. 验证 openstack-users 组是否存在,并将 test 用户作为成员:

    $ ipa group-show openstack-users
      Group name: openstack-users
      GID: 331400001
      Member users: jdoe
    Copy to Clipboard Toggle word wrap

第 3 章 配置红帽单点登录

Red Hat Single Sign-On (RH-SSO)支持多租户,并使用 realm 在租户之间分离。因此,RH-SSO 操作总是在域上下文中发生。如果 RH-SSO 服务器上有管理特权,您可以手动创建域,或使用 keycloak-httpd-client-install 工具创建域。

先决条件

您必须有一个完全安装的 RH-SSO 服务器。有关安装 RH-SSO 的更多信息,请参阅 服务器安装和配置指南

在出现如下时,您需要定义以下变量:

Expand

<_RH_RHSSO_URL_>

Red Hat Single Sign-On URL

<_FED_RHSSO_REALM_>

标识正在使用的 RH-SSO 域

3.1. 配置 RH-SSO 域

当 Red Hat Single Sign-On (RH-SSO)域可用时,请使用 RH-SSO Web 控制台配置针对 IdM 的用户联邦的域:

流程

  1. 从左角的下拉列表中,选择 RH-SSO 域。
  2. Configure 面板中,选择 User Federation
  3. User Federation 面板的 Add provider 下拉列表中选择 ldap
  4. 为以下参数提供值。将所有特定于站点的值替换为与您环境相关的值。

    Expand
    属性

    控制台显示名称

    Red Hat IDM

    编辑模式

    READ_ONLY

    同步注册

    Vendor

    Red Hat Directory Server

    用户名 LDAP 属性

    uid

    RDN LDAP 属性

    uid

    UUID LDAP 属性

    ipaUniqueID

    用户对象类

    inetOrgPerson, organizationalPerson

    连接 URL

    LDAPS://<_FED_IPA_HOST_>

    用户 DN

    cn=users,cn=accounts,<_FED_IPA_BASE_DN_>

    身份验证类型

    simple

    绑定 DN

    uid=rhsso,cn=sysaccounts,cn=etc,<_FED_IPA_BASE_DN_>

    绑定凭证

    <_FED_IPA_RHSSO_SERVICE_PASSWD_>

  5. 使用 Test connection and Test authentication 按钮来确保用户联邦正常工作。
  6. Save 保存新用户联邦提供程序。
  7. 点您创建的 Red Hat IdM 用户联邦页面顶部的 Mappers 选项卡。
  8. 创建映射器以检索用户组信息。用户的组成员资格返回 SAM 断言。稍后使用组成员资格在 OpenStack 中提供授权。
  9. 在 Mappers 页面中点 Create
  10. Add user federation mapper 页面中,从 Mapper Type 下拉列表中选择 group-ldap-mapper,并将其命名为 Group Mapper。为以下参数提供值。将所有特定于站点的值替换为与您环境相关的值。

    Expand
    属性

    LDAP 组 DN

    cn=groups,cn=accounts„<_FED_IPA_BASE_DN_>

    组名称 LDAP 属性

    cn

    组对象类

    groupOfNames

    成员资格 LDAP 属性

    成员

    成员资格属性类型

    DN

    模式

    READ_ONLY

    用户组检索策略

    GET_GROUPS_FROM_USER_MEMBEROF_ATTRIBUTE

  11. 点击 Save

3.2. 使用 SAML 断言添加用户属性

安全断言标记语言(SAML)是一种开放的标准,它允许身份提供程序(IdP)和服务提供商(SP)之间的用户属性和授权凭证通信。

您可以配置 Red Hat Single Sign-On (RH-SSO),以返回断言中所需的属性。当 OpenStack Identity 服务收到 SAML 断言时,它会将这些属性映射到 OpenStack 用户。将 IdP 属性映射到 Identity Service 数据的过程称为 Federated Mapping。更多信息请参阅 第 4.20 节 “创建映射文件和上传到 Keystone”

使用以下流程为 SAML 添加属性:

流程

  1. 在 RH-SSO 管理 web 控制台中,从左上角的下拉列表中选择 <_FED_RHSSO_REALM_>。
  2. Configure 面板中选择 Clients
  3. 选择 keycloak-httpd-client-install 配置的服务提供商客户端。您可以使用 SAML EntityId 识别客户端。
  4. 从水平标签页列表中选择 mappers 选项卡。
  5. 在映射器面板中,选择 CreateAdd Builtin 将协议映射程序添加到客户端。

您可以添加额外的属性,但只需要用户所属的组列表。组成员资格是您如何授权用户。

3.3. 在 SAML 断言中添加组信息

流程

  1. 点映射面板中的 Create 按钮。
  2. Create Protocol Mapper 面板中,从 Mapper tpe 下拉列表中选择 Group 列表。
  3. Name 字段中输入 Group List 作为名称。
  4. 在 Group 属性 Name 字段中输入 groups 作为 SAML 属性的名称。

    注意

    这是出现在 SAML 断言中的属性的名称。当 keystone mapper 在映射声明的 Remote 部分搜索名称时,它会搜索 SAML 属性名称。当您在 RH-SSO 中添加要传递的属性时,请指定 SAML 属性名称。您可以在 RH-SSO 协议映射器中定义名称。

  5. 在 SAML Attribute NameFormat 参数中,选择 Basic
  6. 在 Single Group Attribute 下拉菜单中,选择 On
  7. 点击 Save
注意

运行 keycloak-httpd-client-install 工具时,该过程会添加一个组映射程序。

第 4 章 为联邦配置 Red Hat OpenStack Platform

以下节点需要分配的完全限定域名(FQDN):

  • 运行 Dashboard (horizon) 的主机。
  • 运行 Identity Service (keystone)的主机,在本指南中称为 $FED_KEYSTONE_HOST。请注意,多个主机将在高可用性环境中运行服务,因此 IP 地址不是主机地址,而不是绑定到该服务的 IP 地址。
  • 运行 RH-SSO 的主机。
  • 运行 IdM 的主机。

Red Hat OpenStack Platform director 部署不配置 DNS,或为节点分配 FQDN,但身份验证协议(和 TLS)需要使用 FQDN。

4.1. 检索 IP 地址

在 Red Hat OpenStack Platform 中,所有 OpenStack 服务都有一个通用的公共 IP 地址,用端口号分隔。要确定 overcloud 服务的公共 IP 地址,请使用 openstack endpoint list 命令:

(overcloud) [stack@director ~]$ openstack endpoint list -c "Service Name" -c Interface -c URL | grep public

| swift        | public    | http://10.0.0.101:8080/v1/AUTH_%(tenant_id)s |
| panko        | public    | http://10.0.0.101:8977                       |
| nova         | public    | http://10.0.0.101:8774/v2.1                  |
| glance       | public    | http://10.0.0.101:9292                       |
| neutron      | public    | http://10.0.0.101:9696                       |
| keystone     | public    | http://10.0.0.101:5000                       |
| cinderv2     | public    | http://10.0.0.101:8776/v2/%(tenant_id)s      |
| placement    | public    | http://10.0.0.101:8778/placement             |
| cinderv3     | public    | http://10.0.0.101:8776/v3/%(tenant_id)s      |
| heat         | public    | http://10.0.0.101:8004/v1/%(tenant_id)s      |
| heat-cfn     | public    | http://10.0.0.101:8000/v1                    |
| gnocchi      | public    | http://10.0.0.101:8041                       |
| aodh         | public    | http://10.0.0.101:8042                       |
| cinderv3     | public    | http://10.0.0.101:8776/v3/%(tenant_id)s      |
Copy to Clipboard Toggle word wrap

4.2. 设置主机变量并命名主机

您必须确定要使用的 IP 地址和端口。在本例中,IP 地址为 10.0.0.101,端口为 13000。

  1. 在 overcloudrc 中确认这个值:

    export OS_AUTH_URL=https://10.0.0.101:13000/v2.0
    Copy to Clipboard Toggle word wrap
  2. 将 IP 地址分配给完全限定域名(FQDN),并将它写入 /etc/hosts 文件。本例使用 overcloud.localdomain:

    10.0.0.101 overcloud.localdomain # FQDN of the external VIP
    Copy to Clipboard Toggle word wrap
    注意

    虽然 Red Hat OpenStack Platform director 在 overcloud 节点上配置 hosts 文件,但您可能需要在参与的任何外部主机上添加主机条目。

  3. 在 fed_variables 文件中设置 $FED_KEYSTONE_HOST 和 $FED_KEYSTONE_HTTPS_PORT。这个示例使用相同的值:

    FED_KEYSTONE_HOST="overcloud.localdomain"
    FED_KEYSTONE_HTTPS_PORT=13000
    Copy to Clipboard Toggle word wrap

由于 Mellon 在托管 Identity 服务(keystone)的 Apache 服务器上运行,因此 Mellon host:port 和 keystone host:port 值必须匹配。

注意

如果您在其中一个 Controller 节点上运行 hostname 命令,则输出类似于 controller-0.localdomain。这是一个内部集群名称,而不是其公共名称。改为使用公共 IP 地址。

4.3. 安装帮助程序文件

您必须安装帮助程序文件,作为配置的一部分。

4.4. 设置部署变量

文件 fed_variables 包含特定于您的联邦部署的变量。本指南及 configure-federation 帮助脚本都会引用这些变量。每个特定于站点的联邦变量都前缀为 FED_。确保 fed_variables 中的每个 FED_ 变量都提供了一个值。

4.5. 复制帮助程序文件

您必须在 controller-0 上具有配置文件和变量文件才能继续。

  • 将 configure-federation 和编辑的 fed_variables 从 undercloud-0 上的 ~/stack 主目录复制到 controller-0 上的 ~/heat-admin 主目录:
$ scp configure-federation fed_variables heat-admin@controller-0:/home/heat-admin
Copy to Clipboard Toggle word wrap
注意

您可以使用 configure-federation 脚本执行上述步骤: $ ./configure-federation copy-helper-to-controller

4.6. 初始化工作环境

  1. 在 undercloud 节点上,以 stack 用户身份创建 fed_deployment 目录。这个位置是文件 stash :

    $ su - stack
    $ mkdir fed_deployment
    Copy to Clipboard Toggle word wrap
    注意

    您可以使用 configure-federation 脚本执行上一步:

    $ ./configure-federation initialize
    Copy to Clipboard Toggle word wrap
  2. 使用 SSH 连接到 controller-0,并以 head-admin 用户身份创建 ~/fed_deployment 目录。这个位置是文件 stash :

    $ ssh heat-admin@controller-0
    $ mkdir fed_deployment
    Copy to Clipboard Toggle word wrap
    注意

    您可以使用 configure-federation 脚本来执行上一步。从 controller-0 节点:

    $ ./configure-federation initialize
    Copy to Clipboard Toggle word wrap

4.7. 安装 mod_auth_mellon

您必须在环境中的每个控制器上安装 mod_auth_mellon

  • 在每个控制器上,运行以下命令:

    $ ssh heat-admin@controller-n # replace n with controller number
    $ sudo dnf install mod_auth_mellon
    Copy to Clipboard Toggle word wrap

4.8. 将 RH-SSO FQDN 添加到每个 Controller

确保每个控制器都可以通过其 完全限定域名(FQDN)访问。

  • mellon 服务在每个 Controller 节点上运行,并连接到 RH-SSO IdP。如果 RH-SSO IdP 的 FQDN 无法通过 DNS 解析,请在 Heat Hosts 部分后手动将 FQDN 添加到所有控制器节点上的 /etc/hosts 文件中:

    $ ssh heat-admin@controller-n
    $ sudo vi /etc/hosts
    
    # Add this line (substituting the variables) before this line:
    # HEAT_HOSTS_START - Do not edit manually within this section!
    ...
    # HEAT_HOSTS_END
    $FED_RHSSO_IP_ADDR $FED_RHSSO_FQDN
    Copy to Clipboard Toggle word wrap

4.9. 在 Controller 节点上安装和配置 Mellon

keycloak-httpd-client-install 工具执行配置 mod_auth_mellon 所需的许多步骤,并使其对 RH-SSO IdP 进行身份验证。在运行 mellon 的节点上运行 keycloak-httpd-client-install 工具。在本例中,mellon 在 overcloud 控制器上运行,保护 Identity 服务(keystone)。

注意

Red Hat OpenStack Platform 是具有多个 overcloud Controller 节点的高可用性部署,每个节点都运行相同的副本。因此,您必须在每个 Controller 节点上复制 mellon 配置。要做到这一点,在 controller-0 上安装和配置 mellon,并收集在 tar 文件中创建的 keycloak-httpd-client-install 工具的配置文件。使用 Object Storage (swift)将存档复制到每个 Controller,并在那里解压缩文件。

  • 运行 RH-SSO 客户端安装:

      $ ssh heat-admin@controller-0
      $ dnf -y install keycloak-httpd-client-install
      $ sudo keycloak-httpd-client-install \
       --client-originate-method registration \
       --mellon-https-port $FED_KEYSTONE_HTTPS_PORT \
       --mellon-hostname $FED_KEYSTONE_HOST  \
       --mellon-root /v3 \
       --keycloak-server-url $FED_RHSSO_URL  \
       --keycloak-admin-password  $FED_RHSSO_ADMIN_PASSWORD \
       --app-name v3 \
       --keycloak-realm $FED_RHSSO_REALM \
       -l "/v3/auth/OS-FEDERATION/websso/mapped" \
       -l "/v3/auth/OS-FEDERATION/identity_providers/rhsso/protocols/mapped/websso" \
       -l "/v3/OS-FEDERATION/identity_providers/rhsso/protocols/mapped/auth"
    Copy to Clipboard Toggle word wrap
    注意

    您可以使用 configure-federation 脚本执行上述步骤: $ ./configure-federation client-install

客户端 RPM 安装后,您应该看到类似如下的输出:

  [Step  1] Connect to Keycloak Server
  [Step  2] Create Directories
  [Step  3] Set up template environment
  [Step  4] Set up Service Provider X509 Certificates
  [Step  5] Build Mellon httpd config file
  [Step  6] Build Mellon SP metadata file
  [Step  7] Query realms from Keycloak server
  [Step  8] Create realm on Keycloak server
  [Step  9] Query realm clients from Keycloak server
  [Step 10] Get new initial access token
  [Step 11] Creating new client using registration service
  [Step 12] Enable saml.force.post.binding
  [Step 13] Add group attribute mapper to client
  [Step 14] Add Redirect URIs to client
  [Step 15] Retrieve IdP metadata from Keycloak server
  [Step 16] Completed Successfully
Copy to Clipboard Toggle word wrap

4.10. 编辑 Mellon 配置

在 IdP-assertion-to-Keystone 映射阶段,您的组必须处于分号分开的列表中。使用以下步骤配置 mellon,以便在它收到属性的多个值时,它会将它们合并到分号的单个值中。

流程

  1. 打开 v3_mellon_keycloak_openstack.conf 配置文件以进行编辑:
$ vi /var/lib/config-data/puppet-generated/keystone/etc/httpd/conf.d/v3_mellon_keycloak_openstack.conf
Copy to Clipboard Toggle word wrap
  1. MellonMergeEnvVars 参数添加到 <Location /v3> 块中:

      <Location /v3>
          ...
          MellonMergeEnvVars On ";"
      </Location>
    Copy to Clipboard Toggle word wrap

4.11. 创建生成的配置文件的存档

要在所有 Controller 节点上复制 mellon 配置,请创建要在每个 Controller 节点上安装的文件存档。将存档存储在 ~/fed_deployment 子目录中。

  1. 创建压缩的归档:

    mkdir fed_deployment && cd fed_deployment
    tar -czvf rhsso_config.tar.gz \
      --exclude '*.orig' \
      --exclude '*~' \
      /var/lib/config-data/puppet-generated/keystone/etc/httpd/federation \
      /var/lib/config-data/puppet-generated/keystone/etc/httpd/conf.d/v3_mellon_keycloak_openstack.conf
    Copy to Clipboard Toggle word wrap
注意

您可以使用 configure-federation 脚本执行上一步:

$ ./configure-federation create-sp-archive
Copy to Clipboard Toggle word wrap

4.12. 检索 Mellon 配置存档

  • undercloud-0 节点上,检索您创建的存档并提取文件,以便在以后的步骤中根据需要访问数据。

    $ scp heat-admin@controller-0:/home/heat-admin/fed_deployment/rhsso_config.tar.gz ~/fed_deployment
    $ tar -C fed_deployment -xvf fed_deployment/rhsso_config.tar.gz
    Copy to Clipboard Toggle word wrap
注意

您可以使用 configure-federation 脚本执行上述步骤: $ ./configure-federation fetch-sp-archive

4.13. 防止 Puppet 删除非受管 HTTPD 文件

默认情况下,Puppet Apache 模块会清除它不管理的 Apache 配置目录中的任何文件。这可防止 Apache 针对 Puppet 强制的配置进行操作。但是,这与 HTTPD 配置目录中的手动配置 mellon 冲突。默认情况下启用 Apache Puppet apache::purge_configs 标志,该标记指示 Puppet 删除属于 mod_auth_mellon RPM 的文件。puppet 也会删除 keycloak-httpd-client-install 生成的配置文件。在 Puppet 控制 mellon 文件之前,禁用 apache::purge_configs 标志。

注意

禁用 apache::purge_configs 标志会打开 Controller 节点漏洞。当 Puppet 添加对 mellon 的支持时,重新启用它。

要覆盖 apache::purge_configs 标志,请创建一个包含覆盖的 Puppet 文件,并将覆盖文件添加到您运行 overcloud_deploy.sh 脚本时使用的 Puppet 文件列表中。

  1. 创建 fed_deployment/puppet_override_apache.yaml 环境文件并添加以下内容:

      parameter_defaults:
        ControllerExtraConfig:
          apache::purge_configs: false
    Copy to Clipboard Toggle word wrap
  2. 添加 puppet_override_apache.yaml 作为 overcloud_deploy.sh 脚本中的最后一个环境文件:

    ...
      -e /home/stack/fed_deployment/puppet_override_apache.yaml \
      --log-file overcloud_deployment_14.log &> overcloud_install.log
    Copy to Clipboard Toggle word wrap
注意

您可以使用 configure-federation 脚本执行上述步骤: $ ./configure-federation puppet-override-apache

4.14. 为联邦配置 Identity 服务(keystone)

Keystone 域需要额外的配置。但是,如果启用了 keystone Puppet 模块,它可以执行此额外的配置步骤。

  • 在 Puppet YAML 文件中添加以下内容:

    keystone::using_domain_config: true
    Copy to Clipboard Toggle word wrap

/etc/keystone/keystone.conf 中设置以下值以启用联邦:

auth:methods
允许的身份验证方法列表。默认情况下,列表为: ['external', 'password', 'token', 'oauth1']。您必须使用 映射 的方法启用 SAML。另外,外部方法 还必须被排除。将值设为以下内容: password、token、oauth1、mapped
federation:trusted_dashboard
可信仪表板主机列表。在接受单点登录请求返回令牌之前,原始主机必须是此列表的成员。您可以多次使用此配置选项作为不同的值。您必须将其设置为使用基于 Web 的 SSO 流。对于这个部署,值为: https://$FED_KEYSTONE_HOST/dashboard/auth/websso/ the host is $FED_KEYSTONE_HOST,因为 Red Hat OpenStack Platform director 在同一主机上并置 keystone 和 horizon。如果 horizon 在另一个主机上运行到 keystone,您必须相应地调整。
federation:sso_callback_template
用作单点登录回调处理器的 HTML 文件的绝对路径,本页将通过在 POST 请求中格式化令牌将来自 Identity 服务的用户重定向到可信仪表板主机。默认值足以满足大多数部署的需要。
federation:remote_id_attribute

用于获取身份提供程序的实体 ID 的值。对于 mod_auth_mellon,请使用 Mellon_IDP。使用 Mellon IDP 指令在 mellon 配置文件中设置这个值。

  • 使用以下内容创建 fed_deployment/puppet_override_keystone.yaml 文件:

    parameter_defaults:
      controllerExtraConfig:
        keystone::using_domain_config: true
        keystone::config::keystone_config:
          identity/domain_configurations_from_database:
            value: true
          auth/methods:
            value: external,password,token,oauth1,mapped
          federation/trusted_dashboard:
            value: https://$FED_KEYSTONE_HOST/dashboard/auth/websso/
          federation/sso_callback_template:
            value: /etc/keystone/sso_callback_template.html
          federation/remote_id_attribute:
            value: MELLON_IDP
    Copy to Clipboard Toggle word wrap
  • overcloud_deploy.sh 脚本末尾附加创建的环境文件。

    ...
    -e /home/stack/fed_deployment/puppet_override_keystone.yaml \
    --log-file overcloud_deployment_14.log &> overcloud_install.log
    Copy to Clipboard Toggle word wrap
注意

您可以使用 configure-federation 脚本执行上述步骤: $ ./configure-federation puppet-override-keystone

4.15. 部署 Mellon 配置存档

  • 使用 Object Storage (swift)工件在每个 Controller 节点上安装 mellon 配置文件。

    $ source ~/stackrc
    $ upload-swift-artifacts -f fed_deployment/rhsso_config.tar.gz
    Copy to Clipboard Toggle word wrap
注意

您可以使用 configure-federation 脚本执行上述步骤: './configure-federation deploy-mellon-configuration '

4.16. 重新部署 overcloud

  • 要应用 Puppet YAML 配置文件和对象存储工件的更改,请运行 deploy 命令:

    ./overcloud_deploy.sh
    Copy to Clipboard Toggle word wrap

重要: 当您通过重新运行 Puppet 对 Controller 节点进行额外的更改时,overcloud_deploy.sh 脚本可能会覆盖以前的配置。此流程后不要应用 Puppet 配置,以避免丢失您对 overcloud Controller 节点上的配置文件进行的手动编辑。

mod_auth_mellon 建立会话时,它无法在多个服务器间共享其状态信息。由于 SAML 使用的重定向数量涉及状态信息,因此同一服务器必须处理所有事务。因此,您必须配置 HAProxy,每次将每个客户端请求定向到同一服务器。

HAProxy 可以通过两种方式将客户端绑定到同一服务器:

关联性
当使用应用层下的层信息将客户端请求固定到单个服务器时,请使用关联性。
Persistence
当应用层信息将客户端绑定到单个服务器粘性会话时,请使用持久性。持久性比关联性更准确。使用以下步骤实现持久性。

HAProxy cookie 指令命名 Cookie 及其参数来持久性。HAProxy 服务器指令有一个 Cookie 选项,它将 Cookie 的值设置为服务器的名称。如果传入请求没有识别后端服务器的 Cookie,则 HAProxy 会根据配置的平衡算法选择服务器。

流程

  1. 要在 /var/lib/config-data/puppet-generated/haproxy/etc/haproxy/haproxy.cfg 配置文件的 keystone_public 块中启用持久性,请添加以下行:

    cookie SERVERID insert indirect nocache
    Copy to Clipboard Toggle word wrap

    此设置指出 SERVERID 是持久性 Cookie 的名称。

  2. 编辑每个 server 行并添加 cookie <server-name> 作为附加选项:

    server controller-0 cookie controller-0
    server controller-1 cookie controller-1
    Copy to Clipboard Toggle word wrap

4.18. 创建联邦资源

创建 Identity 服务(keystone)目标、用户和组,供身份提供程序(IdP)使用。

流程

  1. 以 stack 用户身份在 undercloud 上提供 overcloudrc 文件,并运行以下命令:

    $ openstack domain create federated_domain
    $ openstack project create  --domain federated_domain federated_project
    $ openstack group create federated_users --domain federated_domain
    $ openstack role add --group federated_users --group-domain federated_domain --domain federated_domain _member_
    $ openstack role add --group federated_users --group-domain federated_domain --project federated_project _member_
    Copy to Clipboard Toggle word wrap
注意

您可以使用 configure-federation 脚本执行上述步骤: $ ./configure-federation create-federated-resources

IdP 必须在身份服务 (keystone) 中注册,这将在 SAML 断言中的 entityID 以及身份服务中的名称之间创建一个绑定。

流程

  1. 找到 RH-SSO IdP 的 entityID,它位于 IdP 元数据中。IdP 元数据存储在 /var/lib/config-data/puppet-generated/keystone/etc/httpd/federation/v3_keycloak_$FED_RHSSO_REALM_idp_metadata.xml 文件中。您还可以在 fed_deployment/var/lib/config-data/puppet-generated/keystone/etc/httpd/federation/v3_keycloak_$FED_RHSSO_REALM_idp_metadata.xml 文件中找到 IdP 元数据。
  2. 请注意 entityID 属性的值,它位于 < EntityDescriptor > 元素中的 IdP 元数据文件中。分配 $FED_IDP_ENTITY_ID 变量。
  3. 为您的 IdP rhsso 命名,它被分配给变量 $FED_IANA_IDP_NAME

    $ openstack identity provider create --remote-id $FED_IDP_ENTITY_ID $FED_OPENSTACK_IDP_NAME
    Copy to Clipboard Toggle word wrap
注意

您可以使用 configure-federation 脚本执行上述步骤: $ ./configure-federation openstack-create-idp

4.20. 创建映射文件和上传到 Keystone

Keystone 执行映射,将 IdP 的 SAML 断言与 keystone 可以理解的格式匹配。映射由 keystone 的映射规则执行,它基于绑定到 IdP 的映射规则。

  1. 这些是本示例中使用的映射规则(如简介所述):

    [
        {
            "local": [
                {
                    "user": {
                        "name": "{0}"
                    },
                    "group": {
                        "domain": {
                            "name": "federated_domain"
                        },
                        "name": "federated_users"
                    }
                }
            ],
            "remote": [
                {
                    "type": "MELLON_NAME_ID"
                },
                {
                    "type": "MELLON_groups",
                    "any_one_of": ["openstack-users"]
                }
            ]
        }
    ]
    Copy to Clipboard Toggle word wrap

此映射文件仅包含一条规则。规则被分为两个部分:localremote。映射引擎的工作原理是迭代规则列表,直到匹配为止,然后执行它。只有在规则 的远程 部分的 所有条件都匹配时才会考虑规则。在本例中,远程 条件指定:

  1. 断言必须包含名为 MELLON_NAME_ID 的值。
  2. 断言必须包含名为 MELLON_groups 的值,组列表中至少有一个组必须是 openstack-users

如果规则匹配,则:

  1. keystone 用户名 将被分配来自 MELLON_NAME_ID 的值。
  2. 该用户将被分配给 federated_domain 域中的 keystone 组 federated_users

在摘要中,如果 IdP 成功验证用户,并且 IdP 属于组 openstack-users,那么 keystone 将允许该用户使用绑定到 keystone 中的 federated_users 组的权限访问 OpenStack。

4.20.1. 创建映射

  1. 要在 keystone 中创建映射,请创建一个包含映射规则的文件,然后将其上传到 keystone,为其提供参考名称。在 fed_deployment 目录中创建映射文件(例如,在 fed_deployment/mapping_${FED_IDP_IDP_NAME}_saml2.json)中,并将名称 $FED_114_MAPPING_NAME 分配给映射规则。例如:

    $ openstack mapping create --rules fed_deployment/mapping_rhsso_saml2.json $FED_OPENSTACK_MAPPING_NAME
    Copy to Clipboard Toggle word wrap
注意

您可以使用 configure-federation 脚本作为两个步骤执行上述步骤:

$ ./configure-federation create-mapping
$ ./configure-federation openstack-create-mapping
Copy to Clipboard Toggle word wrap
  • create-mapping - 创建映射文件。
  • openstack-create-mapping - 执行文件的上传。

4.21. 创建 Keystone Federation 协议

  1. Keystone 使用 Mapped 协议将 IdP 绑定到映射。建立此绑定:

    $ openstack federation protocol create \
    --identity-provider $FED_OPENSTACK_IDP_NAME \
    --mapping $FED_OPENSTACK_MAPPING_NAME \
    mapped"
    Copy to Clipboard Toggle word wrap
注意

您可以使用 configure-federation 脚本执行上述步骤: $ ./configure-federation openstack-create-protocol

4.22. 全面化 Keystone 设置

  1. 在每个节点中,编辑 /var/lib/config-data/puppet-generated/keystone/etc/httpd/conf.d/10-keystone_wsgi_main.conf,以确认 VirtualHost 块中的 ServerName 指令包含 HTTPS 方案、公共主机名和公共端口。您还必须启用 UseCanonicalName 指令。例如:

    <VirtualHost>
      ServerName https:$FED_KEYSTONE_HOST:$FED_KEYSTONE_HTTPS_PORT
      UseCanonicalName On
      ...
    </VirtualHost>
    Copy to Clipboard Toggle word wrap
注意

务必将 $FED_ 变量替换为特定于部署的值。

4.23. 将 Horizon 配置为使用联邦

  1. 在每个节点中,编辑 /var/lib/config-data/puppet-generated/horizon/etc/openstack-dashboard/local_settings,并确保设置了以下配置值:

    OPENSTACK_KEYSTONE_URL = "https://$FED_KEYSTONE_HOST:$FED_KEYSTONE_HTTPS_PORT/v3"
    OPENSTACK_KEYSTONE_DEFAULT_ROLE = "_member_"
    WEBSSO_ENABLED = True
    WEBSSO_INITIAL_CHOICE = "mapped"
    WEBSSO_CHOICES = (
        ("mapped", _("RH-SSO")),
        ("credentials", _("Keystone Credentials")),
    )
    Copy to Clipboard Toggle word wrap
注意

务必将 $FED_ 变量替换为特定于部署的值。

  1. 在每个节点中,编辑 /var/lib/config-data/puppet-generated/horizon/etc/openstack-dashboard/local_settings 并取消注释行:

    #SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
    Copy to Clipboard Toggle word wrap
注意

您必须重启容器才能使配置更改生效。

第 5 章 故障排除

5.1. 测试 Keystone 映射规则

建议您验证映射规则是否按预期工作。keystone-manage 命令行工具允许您对从文件中读取的断言数据执行一组映射规则(从文件读取)。例如:

  1. 文件 mapping_rules.json 具有此内容:

    [
        {
            "local": [
                {
                    "user": {
                        "name": "{0}"
                    },
                    "group": {
                        "domain": {
                            "name": "Default"
                        },
                        "name": "federated_users"
                    }
                }
            ],
            "remote": [
                {
                    "type": "MELLON_NAME_ID"
                },
                {
                    "type": "MELLON_groups",
                    "any_one_of": ["openstack-users"]
                }
            ]
        }
    ]
    Copy to Clipboard Toggle word wrap
  2. 文件 assertion_data.txt 具有此内容:

    MELLON_NAME_ID: 'G-90eb44bc-06dc-4a90-aa6e-fb2aa5d5b0de
    MELLON_groups: openstack-users;ipausers
    Copy to Clipboard Toggle word wrap
  3. 如果运行此命令,请运行以下命令:

    $ keystone-manage mapping_engine --rules mapping_rules.json --input assertion_data.txt
    Copy to Clipboard Toggle word wrap
  4. 您应该获得此映射的结果:

    {
      "group_ids": [],
      "user": {
        "domain": {
          "id": "Federated"
        },
        "type": "ephemeral",
        "name": "'G-90eb44bc-06dc-4a90-aa6e-fb2aa5d5b0de"
      },
      "group_names": [
        {
          "domain": {
            "name": "Default"
          },
          "name": "federated_users"
        }
      ]
    }
    Copy to Clipboard Toggle word wrap
注意

您还可以包含 --engine-debug 命令行参数,该参数将输出描述如何评估映射规则的诊断信息。

5.2. 确定 Keystone 接收的实际评估值

Keystone 将使用的 mapped 的断言值作为 CGI 环境变量传递。检索这些环境变量的转储:

  1. /var/www/cgi-bin/keystone/test 中创建以下测试脚本,其内容如下:

    import pprint
    import webob
    import webob.dec
    
    
    @webob.dec.wsgify
    def application(req):
        return webob.Response(pprint.pformat(req.environ),
                              content_type='application/json')
    Copy to Clipboard Toggle word wrap
  2. 编辑 /var/lib/config-data/puppet-generated/keystone/etc/httpd/conf.d/10-keystone_wsgi_main.conf 文件,通过临时修改 WSGIScriptAlias directive 来使它运行 test 脚本:

    WSGIScriptAlias "/v3/auth/OS-FEDERATION/websso/mapped" "/var/www/cgi-bin/keystone/test"
    Copy to Clipboard Toggle word wrap
  3. 重启容器:

    podman restart keystone
    Copy to Clipboard Toggle word wrap
  4. 尝试登录,并查看脚本转储的信息。完成后,请记住恢复 WSGIScriptAlias 指令,然后再次重启 HTTPD 服务。

5.3. 查看 SP 和 IdP 之间交换的 SAML 消息

SAML Tracer Firefox 附加组件是捕获和显示 SP 和 IdP 之间交换的 SAML 消息的一个有用工具。

  1. 从这个 URL 安装 SAML Tracer: https://addons.mozilla.org/en-US/firefox/addon/saml-tracer/
  2. 从 Firefox 菜单启用 SAML Tracer。这时将显示 SAML Tracer 弹出窗口,其中会显示所有浏览器请求。如果请求被检测到为 SAML 消息,则会在请求中添加特殊的 SAML 图标。
  3. 从 Firefox 浏览器启动 SSO 登录。
  4. SAML Tracer 窗口中,找到第一个 SAML 消息并单击它。使用窗口中的 SAML 选项卡查看已解码的 SAML 消息(注意,工具无法在邮件正文中解密加密内容,如果您需要看到必须禁用元数据中的加密内容)。第一个 SAML 消息应该是 SP 向 IdP 发送的 AuthnRequest。第二个 SAML 消息应当是 IdP 发送的断言响应。由于 SAML HTTP-Redirect 配置集正在使用断言响应,因此将嵌套在 POST 中。点 SAML 选项卡查看断言的内容。

第 6 章 configure-federation 文件

#!/bin/sh


prog_name=`basename $0`
action=
dry_run=0
verbose=0

base_dir=$(pwd)
stage_dir="${base_dir}/fed_deployment"

mellon_root="/v3"
mellon_endpoint="mellon"
mellon_app_name="v3"

overcloud_deploy_script="overcloud_deploy.sh"
overcloudrc_file="./overcloudrc"

function cmd_template {
    local status=0
    local cmd="$1"
    if [ $verbose -ne 0 -o $dry_run -ne 0 ]; then
        echo $cmd
    fi
    if [ $dry_run -ne 0 ]; then
        return $status
    fi

    $cmd
    status=$?
    if [ $status -ne 0 ]; then
        (>&2 echo -e "ERROR cmd \"$cmd\" failed\nstatus = $status")
    fi
    return $status
}

function cmds_template {
    local return_status=0
    declare -a cmds=(
        "date"
        "ls xxx"
        "head $0"
    )

    if [ $dry_run -ne 0 ]; then
        for cmd in "${cmds[@]}"; do
            echo $cmd
        done
    else
        for cmd in "${cmds[@]}"; do
            if [ $verbose -ne 0 ]; then
                echo $cmd
            fi
            $cmd
            status=$?
            if [ $status -ne 0 ]; then
                (>&2 echo -e "ERROR cmd \"$cmd\" failed\nstatus = $status")
                return_status=$status
            fi
        done
    fi
    return $return_status
}

function show_variables {
    echo "base_dir: $base_dir"
    echo "stage_dir: $stage_dir"
    echo "config_tar_filename: $config_tar_filename"
    echo "config_tar_pathname: $config_tar_pathname"
    echo "overcloud_deploy_script: $overcloud_deploy_script"
    echo "overcloudrc_file: $overcloudrc_file"

    echo "puppet_override_apache_pathname: $puppet_override_apache_pathname"
    echo "puppet_override_keystone_pathname: $puppet_override_keystone_pathname"

    echo

    echo "FED_RHSSO_URL: $FED_RHSSO_URL"
    echo "FED_RHSSO_ADMIN_PASSWORD: $FED_RHSSO_ADMIN_PASSWORD"
    echo "FED_RHSSO_REALM: $FED_RHSSO_REALM"

    echo

    echo "FED_KEYSTONE_HOST: $FED_KEYSTONE_HOST"
    echo "FED_KEYSTONE_HTTPS_PORT: $FED_KEYSTONE_HTTPS_PORT"
    echo "mellon_http_url: $mellon_http_url"
    echo "mellon_root: $mellon_root"
    echo "mellon_endpoint: $mellon_endpoint"
    echo "mellon_app_name: $mellon_app_name"
    echo "mellon_endpoint_path: $mellon_endpoint_path"
    echo "mellon_entity_id: $mellon_entity_id"

    echo

    echo "FED_OPENSTACK_IDP_NAME: $FED_OPENSTACK_IDP_NAME"
    echo "openstack_mapping_pathname: $openstack_mapping_pathname"
    echo "FED_OPENSTACK_MAPPING_NAME: $FED_OPENSTACK_MAPPING_NAME"

    echo

    echo "idp_metadata_filename: $idp_metadata_filename"
    echo "mellon_httpd_config_filename: $mellon_httpd_config_filename"
}

function initialize {
    local return_status=0
    declare -a cmds=(
        "mkdir -p $stage_dir"
    )

    if [ $dry_run -ne 0 ]; then
        for cmd in "${cmds[@]}"; do
            echo $cmd
        done
    else
        for cmd in "${cmds[@]}"; do
            if [ $verbose -ne 0 ]; then
                echo $cmd
            fi
            $cmd
            status=$?
            if [ $status -ne 0 ]; then
                (>&2 echo -e "ERROR cmd \"$cmd\" failed\nstatus = $status")
                return_status=$status
            fi
        done
    fi
    return $return_status
}

function copy_helper_to_controller {
    local status=0
    local controller=${1:-"controller-0"}
    local cmd="scp configure-federation fed_variables heat-admin@${controller}:/home/heat-admin"
    if [ $verbose -ne 0 -o $dry_run -ne 0 ]; then
        echo $cmd
    fi
    if [ $dry_run -ne 0 ]; then
        return $status
    fi

    $cmd
    status=$?
    if [ $status -ne 0 ]; then
        (>&2 echo -e "ERROR cmd \"$cmd\" failed\nstatus = $status")
    fi
    return $status
}

function install_mod_auth_mellon {
    local status=0
    local cmd="sudo dnf -y install mod_auth_mellon"

    if [ $verbose -ne 0 -o $dry_run -ne 0 ]; then
        echo $cmd
    fi
    if [ $dry_run -ne 0 ]; then
        return $status
    fi

    $cmd
    status=$?
    if [ $status -ne 0 ]; then
        (>&2 echo -e "ERROR cmd \"$cmd\" failed\nstatus = $status")
    fi
    return $status
}

function create_ipa_service_account {
    # Note, after setting up the service account it can be tested
    # by performing a user search like this:
    # ldapsearch -H $ldap_url -x -D "$service_dn" -w "$FED_IPA_RHSSO_SERVICE_PASSWD" -b "cn=users,cn=accounts,$FED_IPA_BASE_DN"

    local status=0
    local ldap_url="ldaps://$FED_IPA_HOST"
    local dir_mgr_dn="cn=Directory Manager"
    local service_name="rhsso"
    local service_dn="uid=$service_name,cn=sysaccounts,cn=etc,$FED_IPA_BASE_DN"
    local cmd="ldapmodify -H \"$ldap_url\" -x -D \"$dir_mgr_dn\" -w \"$FED_IPA_ADMIN_PASSWD\""

    read -r -d '' contents <<EOF
dn: $service_dn
changetype: add
objectclass: account
objectclass: simplesecurityobject
uid: $service_name
userPassword: $FED_IPA_RHSSO_SERVICE_PASSWD
passwordExpirationTime: 20380119031407Z
nsIdleTimeout: 0

EOF

    if [ $verbose -ne 0 -o $dry_run -ne 0 ]; then
        echo $cmd
        echo -e "$contents"
    fi
    if [ $dry_run -ne 0 ]; then
        return $status
    fi

    sh <<< "$cmd <<< \"$contents\""
    status=$?
    if [ $status -ne 0 ]; then
        (>&2 echo -e "ERROR cmd \"$cmd\" failed\nstatus = $status")
    fi

    return $status
}



function client_install {
    local status=0
    local cmd_client_install="sudo dnf -y install keycloak-httpd-client-install"
    local cmd="sudo keycloak-httpd-client-install \
   --client-originate-method registration \
   --mellon-https-port $FED_KEYSTONE_HTTPS_PORT \
   --mellon-hostname $FED_KEYSTONE_HOST  \
   --mellon-root $mellon_root \
   --keycloak-server-url $FED_RHSSO_URL  \
   --keycloak-admin-password  $FED_RHSSO_ADMIN_PASSWORD \
   --app-name $mellon_app_name \
   --keycloak-realm $FED_RHSSO_REALM \
   -l "/v3/auth/OS-FEDERATION/websso/mapped" \
   -l "/v3/auth/OS-FEDERATION/identity_providers/rhsso/protocols/mapped/websso" \
   -l "/v3/OS-FEDERATION/identity_providers/rhsso/protocols/mapped/auth"
"
    if [ $verbose -ne 0 -o $dry_run -ne 0 ]; then
        echo $cmd_client_install
        echo $cmd
    fi
    if [ $dry_run -ne 0 ]; then
        return $status
    fi

    $cmd_client_install
    status=$?
    if [ $status -ne 0 ]; then
        (>&2 echo -e "ERROR cmd \"$cmd_client_install\" failed\nstatus = $status")
    else
        $cmd
        status=$?
        if [ $status -ne 0 ]; then
            (>&2 echo -e "ERROR cmd \"$cmd\" failed\nstatus = $status")
        fi
    fi
    return $status
}

function create_sp_archive {
    # Note, we put the exclude patterns in a file because it is
    # insanely difficult to put --exclude patttern in the $cmd shell
    # variable and get the final quoting correct.

    local status=0
    local cmd="tar -cvzf $config_tar_pathname --exclude-from $stage_dir/tar_excludes /var/lib/config-data/puppet-generated/keystone/etc/httpd/federation /var/lib/config-data/puppet-generated/keystone/etc/httpd/conf.d/$mellon_httpd_config_filename"
    if [ $verbose -ne 0 -o $dry_run -ne 0 ]; then
        echo $cmd
    fi
    if [ $dry_run -ne 0 ]; then
        return $status
    fi

    cat <<'EOF' > $stage_dir/tar_excludes
*.orig
*~
EOF

    $cmd
    status=$?
    if [ $status -ne 0 ]; then
        (>&2 echo -e "ERROR cmd \"$cmd\" failed\nstatus = $status")
    fi
    return $status
}

function fetch_sp_archive {
    local return_status=0
    declare -a cmds=(
        "scp heat-admin@controller-0:/home/heat-admin/fed_deployment/$config_tar_filename $stage_dir"
        "tar -C $stage_dir -xvf $config_tar_pathname"
    )

    if [ $dry_run -ne 0 ]; then
        for cmd in "${cmds[@]}"; do
            echo $cmd
        done
    else
        for cmd in "${cmds[@]}"; do
            if [ $verbose -ne 0 ]; then
                echo $cmd
            fi
            $cmd
            status=$?
            if [ $status -ne 0 ]; then
                (>&2 echo -e "ERROR cmd \"$cmd\" failed\nstatus = $status")
                return_status=$status
            fi
        done
    fi
    return $return_status
}

function deploy_mellon_configuration {
    local status=0
    local cmd="upload-swift-artifacts -f $config_tar_pathname"
    if [ $verbose -ne 0 -o $dry_run -ne 0 ]; then
        echo $cmd
    fi
    if [ $dry_run -ne 0 ]; then
        return $status
    fi

    $cmd
    status=$?
    if [ $status -ne 0 ]; then
        (>&2 echo -e "ERROR cmd \"$cmd\" failed\nstatus = $status")
    fi
    return $status
}

function idp_entity_id {
    local metadata_file=${1:-$idp_metadata_filename}

    # Extract the entitID from the metadata file, should really be parsed
    # with an XML xpath but a simple string match is probably OK

    entity_id=`sed -rne 's/^.*entityID="([^"]*)".*$/\1/p' ${metadata_file}`
    status=$?
    if [ $status -ne 0 -o "$entity_id"x = "x" ]; then
        (>&2 echo -e "ERROR search for entityID in ${metadata_file} failed\nstatus = $status")
        return 1
    fi
    echo $entity_id
    return 0
}

function append_deploy_script {
    local status=0
    local deploy_script=$1
    local extra_line=$2
    local count

    count=$(grep -c -e "$extra_line" $deploy_script)
    if [ $count -eq 1 ]; then
        echo -e "SKIP appending:\n$extra_line"
        echo "already present in $deploy_script"
        return $status
    elif [ $count -gt 1 ]; then
        status=1
        (>&2 echo -e "ERROR multiple copies of line in  ${deploy_script}\nstatus = $status\nline=$extra_line")
        return $status
    fi

    if [ $verbose -ne 0 -o $dry_run -ne 0 ]; then
        echo "appending $deploy_script with:"
        echo -e $extra_line
    fi
    if [ $dry_run -ne 0 ]; then
        return $status
    fi

    # insert line after last -e line already in script
    #
    # This is not easy with sed, we'll use tac and awk instead.  Here
    # is how this works: The logic is easier if you insert before the
    # first line rather than trying to find the last line and insert
    # after it. We use tac to reverse the lines in the file. Then the
    # awk script looks for the candidate line. If found it outputs the
    # line we're adding, sets a flag (p) to indicate it's already been
    # printed. The "; 1" pattern always output the input line. Then we
    # run the output through tac again to set things back in the
    # original order.

    local tmp_file=$(mktemp)

    tac $deploy_script | awk "!p && /^-e/{print \"${extra_line} \\\\\"; p=1}; 1" | tac > $tmp_file

    count=$(grep -c -e "${extra_line}" $tmp_file)
    if [ $count -ne 1 ]; then
        status=1
    fi
    if [ $status -ne 0 ]; then
        rm $tmp_file
        (>&2 echo -e "ERROR failed to append ${deploy_script}\nstatus = $status\nline=$extra_line")
    else
        mv $tmp_file $deploy_script
    fi


    return $status
}

function puppet_override_apache {
    local status=0
    local pathname=${1:-$puppet_override_apache_pathname}
    local deploy_cmd="-e $pathname"

    read -r -d '' contents <<'EOF'
parameter_defaults:
  ControllerExtraConfig:
    apache::purge_configs: false
EOF

    if [ $verbose -ne 0 -o $dry_run -ne 0 ]; then
        echo "writing pathname = $pathname with contents"
        echo -e "$contents"
    fi
    if [ $dry_run -ne 0 ]; then
        return $status
    fi

    echo -e "$contents" > $pathname
    status=$?
    if [ $status -ne 0 ]; then
        (>&2 echo -e "ERROR failed to write ${pathname}\nstatus = $status")
    fi

    append_deploy_script $overcloud_deploy_script "$deploy_cmd"
    status=$?

    return $status
}

function puppet_override_keystone {
    local status=0
    local pathname=${1:-$puppet_override_keystone_pathname}
    local deploy_cmd="-e $pathname"

    read -r -d '' contents <<EOF
parameter_defaults:
  controllerExtraConfig:
    keystone::using_domain_config: true
    keystone::config::keystone_config:
      identity/domain_configurations_from_database:
        value: true
      auth/methods:
        value: external,password,token,oauth1,mapped
      federation/trusted_dashboard:
        value: https://$FED_KEYSTONE_HOST/dashboard/auth/websso/
      federation/sso_callback_template:
        value: /etc/keystone/sso_callback_template.html
      federation/remote_id_attribute:
        value: MELLON_IDP

EOF

    if [ $verbose -ne 0 -o $dry_run -ne 0 ]; then
        echo "writing pathname = $pathname with contents"
        echo -e "$contents"
    fi
    if [ $dry_run -ne 0 ]; then
        return $status
    fi

    echo -e "$contents" > $pathname
    status=$?
    if [ $status -ne 0 ]; then
        (>&2 echo -e "ERROR failed to write ${pathname}\nstatus = $status")
    fi

    append_deploy_script $overcloud_deploy_script "$deploy_cmd"
    status=$?

    return $status
}

function create_federated_resources {
    # follow example in Keystone federation documentation
    # http://docs.openstack.org/developer/keystone/federation/federated_identity.html#create-keystone-groups-and-assign-roles
    local return_status=0
    declare -a cmds=(
    "openstack domain create federated_domain"
    "openstack project create  --domain federated_domain federated_project"
    "openstack group create federated_users --domain federated_domain"
    "openstack role add --group federated_users --group-domain federated_domain --domain federated_domain _member_"
    "openstack role add --group federated_users --project federated_project Member"
    )

    if [ $dry_run -ne 0 ]; then
        for cmd in "${cmds[@]}"; do
            echo $cmd
        done
    else
        for cmd in "${cmds[@]}"; do
            if [ $verbose -ne 0 ]; then
                echo $cmd
            fi
            $cmd
            status=$?
            if [ $status -ne 0 ]; then
                (>&2 echo -e "ERROR cmd \"$cmd\" failed\nstatus = $status")
                return_status=$status
            fi
        done
    fi
    return $return_status
}

function create_mapping {
    # Matches documentation
    # http://docs.openstack.org/developer/keystone/federation/federated_identity.html#create-keystone-groups-and-assign-roles
    local status=0
    local pathname=${1:-$openstack_mapping_pathname}

    read -r -d '' contents <<'EOF'
[
    {
        "local": [
            {
                "user": {
                    "name": "{0}"
                },
                "group": {
                    "domain": {
                        "name": "federated_domain"
                    },
                    "name": "federated_users"
                }
            }
        ],
        "remote": [
            {
                "type": "MELLON_NAME_ID"
            },
            {
                "type": "MELLON_groups",
                "any_one_of": ["openstack-users"]
            }
        ]
    }
]
EOF

    if [ $verbose -ne 0 -o $dry_run -ne 0 ]; then
        echo "writing pathname = $pathname with contents"
        echo -e "$contents"
    fi
    if [ $dry_run -ne 0 ]; then
        return $status
    fi


    echo -e "$contents" > $pathname
    status=$?
    if [ $status -ne 0 ]; then
        (>&2 echo -e "ERROR failed to write ${pathname}\nstatus = $status")
    fi

    return $status
}

function create_v3_rcfile {
    local status=0
    local input_file=${1:-$overcloudrc_file}
    local output_file="${input_file}.v3"

    source $input_file
    #clear the old environment
    NEW_OS_AUTH_URL=`echo $OS_AUTH_URL | sed 's!v2.0!v3!'`

    read -r -d '' contents <<EOF
for key in \$( set | sed 's!=.*!!g'  | grep -E '^OS_') ; do unset $key ; done
export OS_AUTH_URL=$NEW_OS_AUTH_URL
export OS_USERNAME=$OS_USERNAME
export OS_PASSWORD=$OS_PASSWORD
export OS_USER_DOMAIN_NAME=Default
export OS_PROJECT_DOMAIN_NAME=Default
export OS_PROJECT_NAME=$OS_TENANT_NAME
export OS_IDENTITY_API_VERSION=3
EOF

    if [ $verbose -ne 0 -o $dry_run -ne 0 ]; then
        echo "writing output_file = $output_file with contents:"
        echo -e "$contents"
    fi
    if [ $dry_run -ne 0 ]; then
        return $status
    fi

    echo -e "$contents" > $output_file
    status=$?
    if [ $status -ne 0 ]; then
        (>&2 echo -e "ERROR failed to write ${output_file}\nstatus = $status")
    fi

    return $status
}

function openstack_create_idp {
    local status=0
    local metadata_file="$stage_dir/var/lib/config-data/puppet-generated/keystone/etc/httpd/federation/$idp_metadata_filename"
    local entity_id
    entity_id=$(idp_entity_id $metadata_file)
    status=$?
    if [ $status -ne 0 ]; then
        return $status
    fi

    local cmd="openstack identity provider create --remote-id $entity_id $FED_OPENSTACK_IDP_NAME"

    if [ $verbose -ne 0 -o $dry_run -ne 0 ]; then
        echo $cmd
    fi
    if [ $dry_run -ne 0 ]; then
        return $status
    fi

    $cmd
    status=$?
    if [ $status -ne 0 ]; then
        (>&2 echo -e "ERROR cmd \"$cmd\" failed\nstatus = $status")
    fi
    return $status
}

function openstack_create_mapping {
    local status=0
    local mapping_file=${1:-$openstack_mapping_pathname}
    local mapping_name=${2:-$FED_OPENSTACK_MAPPING_NAME}
    cmd="openstack mapping create --rules $mapping_file $mapping_name"

    if [ $verbose -ne 0 -o $dry_run -ne 0 ]; then
        echo $cmd
    fi
    if [ $dry_run -ne 0 ]; then
        return $status
    fi

    $cmd
    status=$?
    if [ $status -ne 0 ]; then
        (>&2 echo -e "ERROR cmd \"$cmd\" failed\nstatus = $status")
    fi
    return $status
}

function openstack_create_protocol {
    local status=0
    local idp_name=${1:-$FED_OPENSTACK_IDP_NAME}
    local mapping_name=${2:-$FED_OPENSTACK_MAPPING_NAME}
    cmd="openstack federation protocol create --identity-provider $idp_name --mapping $mapping_name mapped"

    if [ $verbose -ne 0 -o $dry_run -ne 0 ]; then
        echo $cmd
    fi
    if [ $dry_run -ne 0 ]; then
        return $status
    fi

    $cmd
    status=$?
    if [ $status -ne 0 ]; then
        (>&2 echo -e "ERROR cmd \"$cmd\" failed\nstatus = $status")
    fi
    return $status
}

function usage {
cat <<EOF
$prog_name action

-h --help        print usage
-n --dry-run     dry run, just print computed command
-v --verbose     be chatty

action may be one of:

show-variables
initialize
copy-helper-to-controller
install-mod-auth-mellon
create-ipa-service-account
client-install
create-sp-archive
fetch-sp-archive
deploy-mellon-configuration
puppet-override-apache
puppet-override-keystone
create-federated-resources
create-mapping
create-v3-rcfile
openstack-create-idp
openstack-create-mapping
openstack-create-protocol

EOF
}

#-----------------------------------------------------------------------------
# options may be followed by one colon to indicate they have a required argument
if ! options=$(getopt -o hnv -l help,dry-run,verbose -- "$@")
then
    # something went wrong, getopt will put out an error message for us
    exit 1
fi

eval set -- "$options"

while [ $# -gt 0 ]
do
    case $1 in
    -h|--help) usage; exit 1 ;;
    -n|--dry-run) dry_run=1 ;;
    -v|--verbose) verbose=1 ;;
    # for options with required arguments, an additional shift is required
    (--) shift; break;;
    (-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
    (*) break;;
    esac
    shift
done
#-----------------------------------------------------------------------------
source ./fed_variables


# Strip leading and trailing space and slash from these variables
mellon_root=`echo ${mellon_root} | perl -pe 's!^[ /]*(.*?)[ /]*$!\1!'`
mellon_endpoint=`echo ${mellon_endpoint} | perl -pe 's!^[ /]*(.*?)[ /]*$!\1!'`

mellon_root="/${mellon_root}"

mellon_endpoint_path="${mellon_root}/${mellon_endpoint}"
mellon_http_url="https://${FED_KEYSTONE_HOST}:${FED_KEYSTONE_HTTPS_PORT}"
mellon_entity_id="${mellon_http_url}${mellon_endpoint_path}/metadata"

openstack_mapping_pathname="${stage_dir}/mapping_${FED_OPENSTACK_IDP_NAME}_saml2.json"
idp_metadata_filename="${mellon_app_name}_keycloak_${FED_RHSSO_REALM}_idp_metadata.xml"
mellon_httpd_config_filename="${mellon_app_name}_mellon_keycloak_${FED_RHSSO_REALM}.conf"
config_tar_filename="rhsso_config.tar.gz"
config_tar_pathname="${stage_dir}/${config_tar_filename}"
puppet_override_apache_pathname="${stage_dir}/puppet_override_apache.yaml"
puppet_override_keystone_pathname="${stage_dir}/puppet_override_keystone.yaml"

#-----------------------------------------------------------------------------

if [ $# -lt 1 ]; then
  echo "ERROR: no action specified"
  exit 1
fi
action="$1"; shift

if [ $dry_run -ne 0 ]; then
    echo "Dry Run Enabled!"
fi

case $action in
    show-var*)
        show_variables ;;
    initialize)
        initialize ;;
    copy-helper-to-controller)
        copy_helper_to_controller "$1" ;;
    install-mod-auth-mellon)
        install_mod_auth_mellon ;;
    create-ipa-service-account)
        create_ipa_service_account ;;
    client-install)
        client_install ;;
    create-sp-archive)
        create_sp_archive ;;
    fetch-sp-archive)
        fetch_sp_archive ;;
    deploy-mellon-configuration)
        deploy_mellon_configuration ;;
    create-v3-rcfile)
        create_v3_rcfile "$1" ;;
    puppet-override-apache)
        puppet_override_apache "$1" ;;
    puppet-override-keystone)
        puppet_override_keystone "$1" ;;
    create-federated-resources)
        create_federated_resources ;;
    create-mapping)
        create_mapping "$1" ;;
    openstack-create-idp)
        openstack_create_idp "$1" ;;
    openstack-create-mapping)
        openstack_create_mapping "$1" "$2" ;;
    openstack-create-protocol)
        openstack_create_protocol "$1" "$2" ;;
    *)
        echo "unknown action: $action"
        usage
        exit 1
        ;;
esac
Copy to Clipboard Toggle word wrap

第 7 章 fed_variables 文件

# FQDN of IPA server
FED_IPA_HOST="jdennis-ipa.example.com"

# Base DN of IPA server
FED_IPA_BASE_DN="dc=example,dc=com"

# IPA administrator password
FED_IPA_ADMIN_PASSWD="FreeIPA4All"

# Password used by RH-SSO service to authenticate to IPA
# when RH-SSO obtains user/group information from IPA as part of
# RH-SSO's User Federation.
FED_IPA_RHSSO_SERVICE_PASSWD="rhsso-passwd"

# RH-SSO server IP address
FED_RHSSO_IP_ADDR="10.0.0.12"

# RH-SSO server FQDN
FED_RHSSO_FQDN="jdennis-rhsso-7"

# URL used to access the RH-SSO server
FED_RHSSO_URL="https://$FED_RHSSO_FQDN"

# Administrator password for RH-SSO server
FED_RHSSO_ADMIN_PASSWORD=FreeIPA4All

# Name of the RH-SSO realm
FED_RHSSO_REALM="openstack"

# Host name of the mellon server
# Note, this is identical to the Keystone server since Keystone is
# being front by Apache which is protecting it's resources with mellon.
FED_KEYSTONE_HOST="overcloud.localdomain"

# Port number mellon is running on the FED_KEYSTONE_HOST
# Note, this is identical to the Keystone server port
FED_KEYSTONE_HTTPS_PORT=13000

# Name assigned in OpenStack to our IdP
FED_OPENSTACK_IDP_NAME="rhsso"

# Name of our Keystone mapping rules
FED_OPENSTACK_MAPPING_NAME="${FED_OPENSTACK_IDP_NAME}_mapping"
Copy to Clipboard Toggle word wrap

法律通告

Copyright © 2025 Red Hat, Inc.
The text of and illustrations in this document are licensed by Red Hat under a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). An explanation of CC-BY-SA is available at http://creativecommons.org/licenses/by-sa/3.0/. In accordance with CC-BY-SA, if you distribute this document or an adaptation of it, you must provide the URL for the original version.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, the Red Hat logo, JBoss, OpenShift, Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.
返回顶部
Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2025 Red Hat