使用外部 OAuth 2.0 授权服务器进行 Tacker API 访问

注意

本文档的内容已确认在使用 Tacker 2024.2 Dalmatian 和 Keycloak 26.0.2 时有效。

概述

有时用户可能会使用第三方授权服务器(例如 Keycloak)提供的 RFC6749 OAuth 2.0 授权框架的客户端凭据授权流程,用于用户身份验证。OAuth 2.0 客户端凭据授权流程在 ETSI NFV-SOL013 v3.4.1 的 API 规范中规定。

Tacker 使用 Keystonemiddleware 来支持外部 OAuth 2.0 授权服务器提供的 OAuth 2.0 客户端凭据授权。此外,Tacker 还可以访问使用外部 OAuth 2.0 授权服务器的其他 OpenStack 服务,使用客户端凭据授权流程进行用户身份验证。

准备工作

要使用外部 OAuth 2.0 授权服务器进行 Tacker API 身份验证,必须确认 Keystonemiddleware 中支持令牌过滤器工厂 external_oauth2_token。在本示例中,Keycloak 服务器用作外部 OAuth 2.0 授权服务器。 $keycloak_host_nameKeycloak 服务器使用的域名,而 Tacker 服务器使用的域名是 $tacker_host_name

设置外部授权服务器

在本指南中,Keycloak 服务器将通过运行脚本作为 docker 容器配置并启动。但是,如果用户属性映射器设置正确,可以使用以其他方式准备的 Keycloak 服务器。

以下是运行脚本以设置 Keycloak 服务器的示例。在运行脚本之前,您应该确认并记下至少 user_id、project_id,以便在配置 Keycloak 时使用。

$ openstack user show nfv_user
+---------------------+----------------------------------+
| Field               | Value                            |
+---------------------+----------------------------------+
| default_project_id  | None                             |
| domain_id           | default                          |
| email               | None                             |
| enabled             | True                             |
| id                  | 35c884d8a6544d38abce33c0003c8331 |
| name                | nfv_user                         |
| description         | None                             |
| password_expires_at | None                             |
+---------------------+----------------------------------+
$ openstack project show nfv
+-------------+----------------------------------+
| Field       | Value                            |
+-------------+----------------------------------+
| description |                                  |
| domain_id   | default                          |
| enabled     | True                             |
| id          | ce5b19a933354bdf87ac6d698494ad47 |
| is_domain   | False                            |
| name        | nfv                              |
| options     | {}                               |
| parent_id   | default                          |
| tags        | []                               |
+-------------+----------------------------------+

确认所需信息后,运行脚本文件以启动 Keycloak 服务器。该脚本是交互式的,会询问配置 Keycloak 服务器所需的必要信息。最后,它将输出配置 Tacker 服务器以将启动的 Keycloak 服务器用作授权服务器以及测试令牌端点的 curl 命令所需的信息。

当前,脚本和 Tacker API 支持以下 5 种客户端凭据授权流程

  • client_secret_post

  • client_secret_basic

  • private_key_jwt

  • client_secret_jwt

  • tls_client_auth

在本指南中,将使用 client_secret_basic 客户端作为示例来尝试外部身份验证。

注意

您可以使用 -h 或 --help 选项查看帮助。并且,通过提供 -n 或 --no-server 选项,您可以仅生成 Keycloak 配置和 realm json,而无需实际启动 Keycloak 服务器。

$ cd TACKER_ROOT/doc/tools/ext_oauth2_server
$ bash ./gen_keycloak.sh
Select the client credential grant flow type for keycloak server :
1. client_secret_post
2. client_secret_basic
3. private_key_jwt
4. client_secret_jwt
5. tls_client_auth
Enter your choice (1-5): 2
You selected: client_secret_basic
-------------------------

Enable Oauth2 certificate-bounded token? (y/n) [default:n] :
Oauth2 certificate-bounded token is disabled
-------------------------

Set auth type to client secret basic.
-------------------------

Enter OS username[default:nfv_user]:
Successfully set OS username with nfv_user
-------------------------

Enter OS user id: 35c884d8a6544d38abce33c0003c8331
Successfully set OS user id with 35c884d8a6544d38abce33c0003c8331
-------------------------

Enter OS user domain id[default:default]:
Successfully set OS user domain id with default
-------------------------

Enter OS user domain name[default:Default]:
Successfully set OS user domain name with default
-------------------------

Enter OS project name[default:nfv]:
Successfully set OS project name with nfv
-------------------------

Enter OS project id: ce5b19a933354bdf87ac6d698494ad47
Successfully set OS Project id with ce5b19a933354bdf87ac6d698494ad47
-------------------------

Enter OS project domain id[default:default]:
Successfully set OS project domain id with default
-------------------------

Enter OS project domain name[default:Default]:
Successfully set OS project domain name with default
-------------------------

Enter OS user roles[default:admin,member,reader]:
Successfully set OS user roles with 'admin,member,reader'
-------------------------

Generating Certificate
Certificate files already exist.
Overwrite? (y/n) [default:n]:
Existing file will be used
-------------------------

Creating Keycloak config
-------------------------

Starting Keycloak Server

keycloak
keycloak
64e2422c88cabfae6cb65a87f167a32bd73382bd4c9693e4af9ee23a4264aebc
Waiting Keycloak Server to start...[\]
Keycloak server is started successfully.
###################################
HTTP endpoint           : http://$keycloak_host_name:$keycloak_http_port/realms/testrealm/protocol/openid-connect/token
HTTPS endpoint          : https://$keycloak_host_name:$keycloak_https_port/realms/testrealm/protocol/openid-connect/token
client_id               : tacker_service, tacker_api_proj, tacker_api_domain
client_secret           : iIK6lARLzJgoQQyMyoymNYrGTDuR0733S
scope                   : project_scope, domain_scope
* If you want to use other Keycloak server, import this realm.json
realm JSON file         : SCRIPT_PATH/etc/keycloak/conf/base_realm.json
* Use the following keys and certificates for Tacker and client
RootCA certificate      : SCRIPT_PATH/etc/ssl/localcerts/ca.pem
Tacker certificate      : SCRIPT_PATH/etc/ssl/localcerts/tacker.pem
Tacker key              : SCRIPT_PATH/etc/ssl/localcerts/tacker.key
client certificate      : SCRIPT_PATH/etc/ssl/localcerts/client.pem
client key              : SCRIPT_PATH/etc/ssl/localcerts/client.pem
------------------------------------
You can try getting a token using following command

curl -s -X POST http://$keycloak_host_name:$keycloak_http_port/realms/testrealm/protocol/openid-connect/token \
-d 'scope=tacker_scope' -d 'client_id=tacker_service' -d 'grant_type=client_credentials' \
-u 'tacker_service:iIK6lARLzJgoQQyMyoymNYrGTDuR0733S'
###################################

运行脚本后,Keycloak 服务器将开始运行。为了确认服务器,您可以尝试使用脚本输出中的命令获取令牌,如下所示。

$ curl -s -X POST http://$keycloak_host_name:$keycloak_http_port/realms/testrealm/protocol/openid-connect/token \
-d 'scope=tacker_scope' -d 'client_id=tacker_service' -d 'grant_type=client_credentials' \
-u 'tacker_service:iIK6lARLzJgoQQyMyoymNYrGTDuR0733S'

{"access_token":"$access_token","expires_in":300,"refresh_expires_in":0,
"token_type":"Bearer","not-before-policy":0,"scope":""}

您可以在启动 keycloak 服务器后访问 http://$keycloak_host_name:$keycloak_http_port/admin/2025.2/console/#/testrealm,以查看作用域和 Client scope 页面中其他设置(如每个客户端详细信息)的用户属性映射设置。 realm json 文件也可以在 {SCRIPT_PATH}/etc/keycloak/conf/base_realm.json 中找到,如脚本输出所示。

通过使用脚本配置和启动的 Keycloak 服务器预配置了三个客户端。其中一个为 Tacker 准备,另外两个为用户准备。

名称

描述

它被用于哪里

使用方法

tacker_service

Tacker 用于调用 Keycloak 中的 OAuth2.0 令牌内省 API 的客户端

Tacker -> Keycloak

将凭据写入 tacker.conf

tacker_api_proj

用户使用项目范围的访问令牌调用 Tacker API 的客户端

User -> Tacker

将映射规则写入 tacker.conf,并在请求中设置凭据以从 Keycloak 获得访问令牌

tacker_api_domain

用户使用域范围的访问令牌调用 Tacker API 的客户端

User -> Tacker

将映射规则写入 tacker.conf,并在请求中设置凭据以从 Keycloak 获得访问令牌

tacker_service 客户端由 Tacker 用于执行 OAuth2.0 令牌内省。为此,您需要将客户端的凭据写入 tacker.conf

tacker_api_projtacker_api_domain 可用于调用 Tacker API。您可以根据所需的令牌范围选择其中一个。 tacker_api_proj 将获得项目范围的令牌,该令牌有权访问特定项目中的资源,而 tacker_api_domain 将获得域范围的令牌,该令牌有权访问特定域中的资源(参阅 项目详细信息域详细信息)。您还需要将正确的映射规则(如下所述)写入 tacker.conf,以便 Tacker 可以正确获取用户属性,例如 project_idproject_name 等,以便进行后续处理。

在本指南中,将使用 tacker_api_proj 客户端作为示例。

注意

如果您想手动配置这些客户端,请注意,如果令牌的项目 idname 声明存在于令牌中,Keystonemiddleware 将为特定项目授权客户端,否则,客户端将获得项目域的授权。您可以在 Keycloak 仪表板的客户端范围页面中的 Mappers 选项卡 中找到配置的声明。

指南

要使用外部 OAuth 2.0 授权服务器进行 Tacker,您应该按照以下步骤配置 Tacker 服务器和 Keystonemiddleware。

启用使用外部 OAuth 2.0 授权服务器

要使用外部 OAuth 2.0 授权服务器对 API 请求进行身份验证,您必须配置 Keystonemiddleware,该中间件接受来自客户端的 API 调用并验证客户端的身份。有关详细信息,请参阅 中间件架构

  1. keystonemiddleware.external_oauth2_token:filter_factory 添加到配置文件 api-paste.ini 以启用外部 OAuth 2.0 授权服务器身份验证。

    $ vi /etc/tacker/api-paste.ini
    [composite:tackerapi_v1_0]
    #keystone = request_id catch_errors authtoken keystonecontext extensions tackerapiapp_v1_0
    keystone = request_id catch_errors external_oauth2_token keystonecontext extensions tackerapiapp_v1_0
    
    [composite:vnfpkgmapi_v1]
    #keystone = request_id catch_errors authtoken keystonecontext vnfpkgmapp_v1
    keystone = request_id catch_errors external_oauth2_token keystonecontext vnfpkgmapp_v1
    
    [composite:vnflcm_v1]
    #keystone = request_id catch_errors authtoken keystonecontext vnflcmaapp_v1
    keystone = request_id catch_errors external_oauth2_token keystonecontext vnflcmaapp_v1
    
    [composite:vnflcm_v2]
    #keystone = request_id catch_errors authtoken keystonecontext vnflcmaapp_v2
    keystone = request_id catch_errors external_oauth2_token keystonecontext vnflcmaapp_v2
    
    [composite:vnfpm_v2]
    #keystone = request_id catch_errors authtoken keystonecontext vnfpmaapp_v2
    keystone = request_id catch_errors external_oauth2_token keystonecontext vnfpmaapp_v2
    
    [composite:vnflcm_versions]
    #keystone = request_id catch_errors authtoken keystonecontext vnflcm_api_versions
    keystone = request_id catch_errors external_oauth2_token keystonecontext vnflcm_api_versions
    
    [composite:vnffm_v1]
    #keystone = request_id catch_errors authtoken keystonecontext vnffmaapp_v1
    keystone = request_id catch_errors external_oauth2_token keystonecontext vnffmaapp_v1
    
    [filter:external_oauth2_token]
    paste.filter_factory = keystonemiddleware.external_oauth2_token:filter_factory
    
  2. 修改配置文件 tacker.conf 以启用 Keystonemiddleware 使用外部 OAuth 2.0 授权服务器来验证令牌,并启用 Tacker 从外部 OAuth 2.0 授权服务器获取访问令牌以访问其他 OpenStack 服务。

    如前所述,您需要更改 tacker.conf,如下所示

    1. 写入 tacker_service 客户端的凭据

    2. 写入 tacker_api_proj 客户端的映射规则

    您可以使用以下示例配置来使用上面部分启动的 keycloak 服务器。

    [DEFAULT]
    use_ssl=False
    
    [ext_oauth2_auth]
    use_ext_oauth2_auth=True
    token_endpoint=http://$keycloak_host_name:$keycloak_http_port/realms/testrealm/protocol/openid-connect/token
    scope=tacker_scope
    introspect_endpoint=http://$keycloak_host_name:$keycloak_http_port/realms/testrealm/protocol/openid-connect/token/introspect
    audience=http://$keycloak_host_name:$keycloak_http_port/realms/testrealm
    auth_method=client_secret_basic
    client_id=tacker_service
    client_secret=iIK6lARLzJgoQQyMyoymNYrGTDuR0733S
    insecure=True
    thumbprint_verify=False
    mapping_project_id=project_info.id
    mapping_project_name=project_info.name
    mapping_project_domain_id=project_info.domain.id
    mapping_project_domain_name=project_info.domain.name
    mapping_user_id=user.id
    mapping_user_name=user.name
    mapping_user_domain_id=user.domain.id
    mapping_user_domain_name=user.domain.name
    mapping_roles=role_info
    

    注意

    如果您只想尝试使用 Tacker 进行外部身份验证,可以跳过此步骤的其余部分,转到 验证对 Tacker API 的访问

    如果您想为您的特定用例配置 Tacker,则需要相应地更改上述参数。例如,如果外部 OAuth 2.0 授权服务器需要 HTTPS 通信,则需要将 CA 证书指定为配置参数 cafile 以验证外部 OAuth 2.0 授权服务器的证书。如果配置参数 insecure 设置为 True,则配置参数 cafile 指定的 CA 证书将不用于验证。

    如果外部 OAuth 2.0 授权服务器需要 mTLS 通信,则需要配置配置参数 certfilekeyfilecafile

    注意

    在本指南中,在所有示例中都使用 HTTP,除了 tls_client_auth,因为 mTLS 在 tls_client_auth 身份验证方法中是必需的。在生产环境中,强烈建议使用 HTTPS 以提高安全性。

    如果您需要启用 Tacker 服务器使用 mTLS 通信,则必须将配置参数 use_ssl 设置为 True,并且需要配置配置参数 ssl_cert_filessl_key_filessl_ca_file。有关设置 Tacker 中的 HTTPS 或 mTLS 的详细信息,您可以参考 配置 Tacker API 的 HTTPS/mTLS

    如果配置参数 thumbprint_verify 设置为 True,则令牌将根据用于获取访问令牌的客户端证书进行验证。因此,要访问 Tacker API,您需要使用与用于从外部 OAuth 2.0 授权服务器获取访问令牌相同的客户端证书。此外,必须确保外部 OAuth 2.0 授权服务器配置为在访问令牌中包含客户端证书指纹信息。对于用于存储客户端证书指纹的声明 cnf/x5t#S256,请参阅后续示例。

    为了使 Tacker 能够获取用户信息,例如项目 ID、用户 ID、角色等,必须设置以 mapping_ 开头的配置参数,并确保所有访问令牌都包含符合指定格式的数据。

    如果配置参数 use_ext_oauth2_auth 设置为 True,Tacker API 将从外部 OAuth 2.0 授权服务器获取访问令牌,然后访问其他 OpenStack 服务,例如 Barbican。如果配置参数 use_ext_oauth2_auth 设置为 False,Tacker API 将保留原始逻辑,从 Keystone 身份服务器获取 x-auth-token,然后访问其他 OpenStack 服务。

    以下是每个方法的示例配置。

    • client_secret_post

      这是 client_secret_post 身份验证方法的示例配置

      [DEFAULT]
      use_ssl=False
      
      [ext_oauth2_auth]
      use_ext_oauth2_auth=True
      token_endpoint=http://$keycloak_host_name:$keycloak_http_port/realms/testrealm/protocol/openid-connect/token
      scope=tacker_scope
      introspect_endpoint=http://$keycloak_host_name:$keycloak_http_port/realms/testrealm/protocol/openid-connect/token/introspect
      audience=http://$keycloak_host_name:$keycloak_http_port/realms/testrealm
      auth_method=client_secret_post
      client_id=tacker_service
      client_secret=iIK6lARLzJgoQQyMyoymNYrGTDuR0733S
      insecure=True
      thumbprint_verify=False
      mapping_project_id=project_info.id
      mapping_project_name=project_info.name
      mapping_project_domain_id=project_info.domain.id
      mapping_project_domain_name=project_info.domain.name
      mapping_user_id=user.id
      mapping_user_name=user.name
      mapping_user_domain_id=user.domain.id
      mapping_user_domain_name=user.domain.name
      mapping_roles=role_info
      
    • private_key_jwt

      这是 private_key_jwt 身份验证方法的示例配置

      注意

      在获取 private_key_jwtclient_secret_jwt 中的令牌时,请求主体的 client_assertion 是本地创建的已签名的 JWT,其中包含诸如 issaud 等声明。详细格式和声明可以在 openid-connect 1.0 核心规范RFC7523 中参考,以及如何在 RFC7519 中创建、加密和签名 JWT。

      [DEFAULT]
      use_ssl=False
      
      [ext_oauth2_auth]
      use_ext_oauth2_auth=True
      token_endpoint=http://$keycloak_host_name:$keycloak_http_port/realms/testrealm/protocol/openid-connect/token
      scope=tacker_scope
      introspect_endpoint=http://$keycloak_host_name:$keycloak_http_port/realms/testrealm/protocol/openid-connect/token/introspect
      audience=http://$keycloak_host_name:$keycloak_http_port/realms/testrealm
      auth_method=private_key_jwt
      client_id=tacker_service
      jwt_key_file=/etc/tacker/private_key.pem
      jwt_algorithm=RS256
      jwt_bearer_time_out=7200
      insecure=True
      thumbprint_verify=False
      mapping_project_id=project_info.id
      mapping_project_name=project_info.name
      mapping_project_domain_id=project_info.domain.id
      mapping_project_domain_name=project_info.domain.name
      mapping_user_id=user.id
      mapping_user_name=user.name
      mapping_user_domain_id=user.domain.id
      mapping_user_domain_name=user.domain.name
      mapping_roles=role_info
      
    • client_secret_jwt

      这是 client_secret_jwt 身份验证方法的示例配置

      [DEFAULT]
      use_ssl=False
      
      [ext_oauth2_auth]
      use_ext_oauth2_auth=True
      token_endpoint=http://$keycloak_host_name:$keycloak_http_port/realms/testrealm/protocol/openid-connect/token
      scope=tacker_scope
      introspect_endpoint=http://$keycloak_host_name:$keycloak_http_port/realms/testrealm/protocol/openid-connect/token/introspect
      audience=http://$keycloak_host_name:$keycloak_http_port/realms/testrealm
      auth_method=client_secret_jwt
      client_id=tacker_service
      client_secret=iIK6lARLzJgoQQyMyoymNYrGTDuR0733S
      jwt_algorithm=HS512
      jwt_bearer_time_out=7200
      insecure=True
      thumbprint_verify=False
      mapping_project_id=project_info.id
      mapping_project_name=project_info.name
      mapping_project_domain_id=project_info.domain.id
      mapping_project_domain_name=project_info.domain.name
      mapping_user_id=user.id
      mapping_user_name=user.name
      mapping_user_domain_id=user.domain.id
      mapping_user_domain_name=user.domain.name
      mapping_roles=role_info
      
    • tls_client_auth

      这是 tls_client_auth 身份验证方法的示例配置

      注意

      与 mTLS 在其他身份验证方法中是可选的不同,在使用 tls_client_auth 进行身份验证时,mTLS 是必需的。

      [DEFAULT]
      use_ssl=True
      ssl_ca_file=/etc/tacker/multi_ca.pem
      ssl_cert_file=/etc/tacker/tacker_api.pem
      ssl_key_file=/etc/tacker/tacker_api.key
      
      [ext_oauth2_auth]
      use_ext_oauth2_auth=True
      token_endpoint=https://$keycloak_host_name:$keycloak_https_port/realms/testrealm/protocol/openid-connect/token
      scope=tacker_scope
      introspect_endpoint=https://$keycloak_host_name:$keycloak_https_port/realms/testrealm/protocol/openid-connect/token/introspect
      audience=https://$keycloak_host_name:$keycloak_https_port/realms/testrealm
      auth_method=tls_client_auth
      client_id=tacker_service
      certfile=/etc/tacker/tacker_client.pem
      keyfile=/etc/tacker/tacker_client.key
      cafile=/etc/tacker/ca.pem
      insecure=False
      thumbprint_verify=True
      mapping_project_id=project_info.id
      mapping_project_name=project_info.name
      mapping_project_domain_id=project_info.domain.id
      mapping_project_domain_name=project_info.domain.name
      mapping_user_id=user.id
      mapping_user_name=user.name
      mapping_user_domain_id=user.domain.id
      mapping_user_domain_name=user.domain.name
      mapping_roles=role_info
      
  3. 重新启动 Tacker 服务,使修改后的配置信息生效。

    $ sudo systemctl restart devstack@tacker.service
    $ sudo systemctl restart devstack@tacker-conductor.service
    

验证对 Tacker API 的访问

使用 OAuth 2.0 访问令牌访问 Tacker API,以验证 OAuth 2.0 客户端凭据授权流程是否正常工作。

使用不同的外部 OAuth 2.0 授权服务器需要不同的获取访问令牌的方法。以下示例仅适用于将 Keycloak 用作外部授权服务器的情况。

使用 Keycloak 作为外部 OAuth 2.0 身份验证服务器来验证对 Tacker API 的访问权限,需要三个步骤

  1. 执行 Keycloak 提供的获取令牌 API (/realms/{realm_name}/protocol/openid-connect/token)。

  2. 使用从 Keycloak 获取的访问令牌执行 Tacker API。例如,本文档使用了 Tacker 提供的列出 VIM API (/v1.0/vims)。

  3. 从 Tacker 服务器日志中检查 Keycloak 提供的内省 API 的访问令牌。

验证请求取决于身份验证方法。由于在本指南中 Keycloak 配置为使用 client_secret_basic,因此可以使用以下示例。

$ curl -i -X POST http://$keycloak_host_name:$keycloak_http_port/realms/testrealm/protocol/openid-connect/token \
-u "tacker_api_proj:iIK6lARLzJgoQQyMyoymNYrGTDuR0733S" \
-d "scope=project_scope" -d "grant_type=client_credentials"
HTTP/1.1 200 OK
Cache-Control: no-store
Pragma: no-cache
content-length: 1773
Content-Type: application/json
Referrer-Policy: no-referrer
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block

{"access_token":"$access_token","expires_in":300,"refresh_expires_in":0,
"token_type":"Bearer","not-before-policy":0,"scope":"email profile"}


$ curl -i -X GET http://$tacker_host_name:9890/v1.0/vims \
-H "Authorization: Bearer $access_token"
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 743
X-Openstack-Request-Id: req-2f954ee7-e6fd-4d53-ac31-bf8c7d9245d7
Date: Tue, 17 Dec 2024 06:27:24 GMT

{"vims": [{"id": "a99189da-bf72-4af7-884c-36d157f00571",
"type": "openstack", "tenant_id": "2cc02f60acf34fdda7bc5e9af9a7032b",
"name": "openstack", "description": "", "placement_attr": {
"regions": ["RegionOne"]}, "is_default": true,
"created_at": "2024-11-07 02:04:46", "updated_at": "2024-11-07 02:10:18",
"extra": {}, "auth_url": "http://192.168.56.11/identity/v3",
"vim_project": {"name": "admin", "project_domain_name": "default"},
"auth_cred": {"username": "admin", "user_domain_name": "default",
"cert_verify": "True", "project_id": null, "project_name": "admin",
"project_domain_name": "default", "auth_url": "http://192.168.56.11/identity/v3",
"key_type": "barbican_key", "secret_uuid": "***", "password": "***"}, "status": "ACTIVE"}]}

$ tail -f /opt/stack/log/tacker-server.log
Dec 17 05:45:25 controller-tacker tacker-server[835]:
DEBUG keystonemiddleware.external_oauth2_token [-] The introspect API response: {
'exp': 1734414535, 'iat': 1734414235, 'jti': '49458c8f-507d-4d08-a81f-c3a6ee484190',
'iss': 'http://$keycloak_host_name:$keycloak_http_port/realms/testrealm', 'aud': 'account', 'sub': '7d3e5929-4bbe-4ef4-90e3-8fd4b445ed7f',
'typ': 'Bearer', 'azp': 'tacker_api_proj', 'acr': '1', 'allowed-origins': ['/*'],
'realm_access': {'roles': ['offline_access', 'uma_authorization', 'default-roles-testrealm']},
'resource_access': {'account': {'roles': ['manage-account', 'manage-account-links', 'view-profile']}},
'scope': 'email profile', 'email_verified': False, 'role_info': 'admin,member,reader',
'preferred_username': 'service-account-tacker_api_proj', 'user': {'domain': {'name': 'default', 'id': 'default'},
'name': 'nfv_user', 'id': '35c884d8a6544d38abce33c0003c8331'}, 'project_info': {'domain': {'id': 'default', 'name': 'Default'},
'id': 'ce5b19a933354bdf87ac6d698494ad47', 'name': 'nfv'}, 'client_id': 'tacker_api_proj',
'username': 'service-account-tacker_api_proj', 'token_type': 'Bearer', 'active': True}
{{(pid=835) _fetch_token /opt/stack/data/venv/lib/python3.10/site-packages/keystonemiddleware/external_oauth2_token.py:731}}

如果您使用另一种身份验证方法配置 Keycloak,请参阅以下示例。

  • client_secret_post

    $ curl -i -X POST http://$keycloak_host_name:$keycloak_http_port/realms/testrealm/protocol/openid-connect/token \
    -d "client_id=tacker_api_proj" -d "client_secret=iIK6lARLzJgoQQyMyoymNYrGTDuR0733S" \
    -d "scope=project_scope" -d "grant_type=client_credentials"
    HTTP/1.1 200 OK
    Cache-Control: no-store
    Pragma: no-cache
    content-length: 1773
    Content-Type: application/json
    Referrer-Policy: no-referrer
    Strict-Transport-Security: max-age=31536000; includeSubDomains
    X-Content-Type-Options: nosniff
    X-Frame-Options: SAMEORIGIN
    X-XSS-Protection: 1; mode=block
    
    {"access_token":"$access_token","expires_in":300,"refresh_expires_in":0,
    "token_type":"Bearer","not-before-policy":0,"scope":"email profile"}
    
    $ curl -i -X GET http://$tacker_host_name:9890/v1.0/vims \
    -H "Authorization: Bearer $access_token"
    HTTP/1.1 200 OK
    Content-Type: application/json
    Content-Length: 743
    X-Openstack-Request-Id: req-2cb42224-8293-4deb-b772-9aeff5354922
    Date: Fri, 06 Dec 2024 08:56:38 GMT
    
    {"vims": [{"id": "a99189da-bf72-4af7-884c-36d157f00571",
    "type": "openstack", "tenant_id": "2cc02f60acf34fdda7bc5e9af9a7032b",
    "name": "openstack", "description": "", "placement_attr": {
    "regions": ["RegionOne"]}, "is_default": true,
    "created_at": "2024-11-07 02:04:46", "updated_at": "2024-11-07 02:10:18",
    "extra": {}, "auth_url": "http://192.168.56.11/identity/v3",
    "vim_project": {"name": "admin", "project_domain_name": "default"},
    "auth_cred": {"username": "admin", "user_domain_name": "default",
    "cert_verify": "True", "project_id": null, "project_name": "admin",
    "project_domain_name": "default", "auth_url": "http://192.168.56.11/identity/v3",
    "key_type": "barbican_key", "secret_uuid": "***", "password": "***"}, "status": "ACTIVE"}]}
    
    $ tail -f /opt/stack/log/tacker-server.log
    Dec 06 08:56:38 controller-tacker tacker-server[20354]:
    DEBUG keystonemiddleware.external_oauth2_token [-] The introspect API response: {
    'exp': 1733475612, 'iat': 1733475312, 'jti': '946ef21c-17c2-4416-ac6a-37d9ef9f5739',
    'iss': 'http://$keycloak_host_name:$keycloak_http_port/realms/testrealm', 'aud': 'account', 'sub': '7d3e5929-4bbe-4ef4-90e3-8fd4b445ed7f',
    'typ': 'Bearer', 'azp': 'tacker_api_proj', 'acr': '1', 'allowed-origins': ['/*'],
    'realm_access': {'roles': ['offline_access', 'uma_authorization', 'default-roles-testrealm']},
    'resource_access': {'account': {'roles': ['manage-account', 'manage-account-links', 'view-profile']}},
    'scope': 'email profile', 'email_verified': False, 'role_info': 'admin,member,reader',
    'preferred_username': 'service-account-tacker_api_proj', 'user': {'domain': {'name': 'default', 'id': 'default'},
    'name': 'nfv_user', 'id': '35c884d8a6544d38abce33c0003c8331'}, 'project_info': {'domain': {
    'id': 'default', 'name': 'Default'}, 'id': 'ce5b19a933354bdf87ac6d698494ad47', 'name': 'nfv'},
    'client_id': 'tacker_api_proj', 'username': 'service-account-tacker_api_proj', 'token_type': 'Bearer', 'active': True}
    {{(pid=20354) _fetch_token /opt/stack/data/venv/lib/python3.10/site-packages/keystonemiddleware/external_oauth2_token.py:731}}
    
  • private_key_jwt

    $ curl -i -X POST http://$keycloak_host_name:$keycloak_http_port/realms/testrealm/protocol/openid-connect/token \
    -d "client_id=tacker_api_proj" -d "scope=project_scope" -d "grant_type=client_credentials" \
    -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
    -d "client_assertion=$client_assertion"
    HTTP/1.1 200 OK
    Cache-Control: no-store
    Pragma: no-cache
    content-length: 1774
    Content-Type: application/json
    Referrer-Policy: no-referrer
    Strict-Transport-Security: max-age=31536000; includeSubDomains
    X-Content-Type-Options: nosniff
    X-Frame-Options: SAMEORIGIN
    X-XSS-Protection: 1; mode=block
    
    {"access_token":"$access_token","expires_in":300,"refresh_expires_in":0,
    "token_type":"Bearer","not-before-policy":0,"scope":"email profile"}
    
    $ curl -i -X GET http://$tacker_host_name:9890/v1.0/vims \
    -H "Authorization: Bearer $access_token"
    HTTP/1.1 200 OK
    Content-Type: application/json
    Content-Length: 743
    X-Openstack-Request-Id: req-b4c07ca6-49c8-4c12-be51-248c507183b9
    Date: Fri, 06 Dec 2024 09:15:07 GMT
    
    {"vims": [{"id": "a99189da-bf72-4af7-884c-36d157f00571",
    "type": "openstack", "tenant_id": "2cc02f60acf34fdda7bc5e9af9a7032b",
    "name": "openstack", "description": "", "placement_attr": {
    "regions": ["RegionOne"]}, "is_default": true,
    "created_at": "2024-11-07 02:04:46", "updated_at": "2024-11-07 02:10:18",
    "extra": {}, "auth_url": "http://192.168.56.11/identity/v3",
    "vim_project": {"name": "admin", "project_domain_name": "default"},
    "auth_cred": {"username": "admin", "user_domain_name": "default",
    "cert_verify": "True", "project_id": null, "project_name": "admin",
    "project_domain_name": "default", "auth_url": "http://192.168.56.11/identity/v3",
    "key_type": "barbican_key", "secret_uuid": "***", "password": "***"}, "status": "ACTIVE"}]}
    
    $ tail -f /opt/stack/log/tacker-server.log
    Dec 06 09:15:07 controller-tacker tacker-server[21835]:
    DEBUG keystonemiddleware.external_oauth2_token [-] The introspect API response: {
    'exp': 1733476670, 'iat': 1733476370, 'jti': '8c9cf64f-f0f3-4d74-8b15-a6bda7036f07',
    'iss': 'http://$keycloak_host_name:$keycloak_http_port/realms/testrealm', 'aud': 'account', 'sub': '7d3e5929-4bbe-4ef4-90e3-8fd4b445ed7f',
    'typ': 'Bearer', 'azp': 'tacker_api_proj', 'acr': '1', 'allowed-origins': ['/*'], 'realm_access': {
    'roles': ['offline_access', 'uma_authorization', 'default-roles-testrealm']}, 'resource_access': {'account': {
    'roles': ['manage-account', 'manage-account-links', 'view-profile']}}, 'scope': 'email profile',
    'email_verified': False, 'role_info': 'admin,member,reader', 'preferred_username': 'service-account-tacker_api_proj',
    'user': {'domain': {'name': 'default', 'id': 'default'}, 'name': 'nfv_user', 'id': '35c884d8a6544d38abce33c0003c8331'},
    'project_info': {'domain': {'id': 'default', 'name': 'Default'}, 'id': 'ce5b19a933354bdf87ac6d698494ad47', 'name': 'nfv'},
    'client_id': 'tacker_api_proj', 'username': 'service-account-tacker_api_proj', 'token_type': 'Bearer', 'active': True}
    {{(pid=21835) _fetch_token /opt/stack/data/venv/lib/python3.10/site-packages/keystonemiddleware/external_oauth2_token.py:731}}
    
  • client_secret_jwt

    $ curl -i -X POST http://$keycloak_host_name:$keycloak_https_port/realms/testrealm/protocol/openid-connect/token \
    -d "client_id=tacker_api_proj" -d "scope=project_scope" -d "grant_type=client_credentials" \
    -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
    -d "client_assertion=$client_assertion"
    HTTP/1.1 200 OK
    Cache-Control: no-store
    Pragma: no-cache
    content-length: 1774
    Content-Type: application/json
    Referrer-Policy: no-referrer
    Strict-Transport-Security: max-age=31536000; includeSubDomains
    X-Content-Type-Options: nosniff
    X-Frame-Options: SAMEORIGIN
    X-XSS-Protection: 1; mode=block
    
    {"access_token":"$access_token","expires_in":300,"refresh_expires_in":0,
    "token_type":"Bearer","not-before-policy":0,"scope":"email profile"}
    
    $ curl -i -X GET http://$tacker_host_name:9890/v1.0/vims \
    -H "Authorization: Bearer $access_token"
    HTTP/1.1 200 OK
    Content-Type: application/json
    Content-Length: 743
    X-Openstack-Request-Id: req-4c2e2c76-672e-4517-ac2e-0a79a48b9122
    Date: Fri, 06 Dec 2024 09:28:24 GMT
    
    {"vims": [{"id": "a99189da-bf72-4af7-884c-36d157f00571",
    "type": "openstack", "tenant_id": "2cc02f60acf34fdda7bc5e9af9a7032b",
    "name": "openstack", "description": "", "placement_attr": {
    "regions": ["RegionOne"]}, "is_default": true,
    "created_at": "2024-11-07 02:04:46", "updated_at": "2024-11-07 02:10:18",
    "extra": {}, "auth_url": "http://192.168.56.11/identity/v3",
    "vim_project": {"name": "admin", "project_domain_name": "default"},
    "auth_cred": {"username": "admin", "user_domain_name": "default",
    "cert_verify": "True", "project_id": null, "project_name": "admin",
    "project_domain_name": "default", "auth_url": "http://192.168.56.11/identity/v3",
    "key_type": "barbican_key", "secret_uuid": "***", "password": "***"}, "status": "ACTIVE"}]}
    
    $ tail -f /opt/stack/log/tacker-server.log
    Dec 06 09:28:23 controller-tacker tacker-server[22701]:
    DEBUG keystonemiddleware.external_oauth2_token [-] The introspect API response: {
    'exp': 1733477523, 'iat': 1733477223, 'jti': '7c07a9ac-3af8-44c7-a243-f2faed10cac0',
    'iss': 'http://$keycloak_host_name:$keycloak_http_port/realms/testrealm', 'aud': 'account', 'sub': '7d3e5929-4bbe-4ef4-90e3-8fd4b445ed7f',
    'typ': 'Bearer', 'azp': 'tacker_api_proj', 'acr': '1', 'allowed-origins': ['/*'], 'realm_access': {
    'roles': ['offline_access', 'uma_authorization', 'default-roles-testrealm']}, 'resource_access': {'account': {
    'roles': ['manage-account', 'manage-account-links', 'view-profile']}}, 'scope': 'email profile',
    'email_verified': False, 'role_info': 'admin,member,reader', 'preferred_username': 'service-account-tacker_api_proj',
    'user': {'domain': {'name': 'default', 'id': 'default'}, 'name': 'nfv_user', 'id': '35c884d8a6544d38abce33c0003c8331'},
    'project_info': {'domain': {'id': 'default', 'name': 'Default'}, 'id': 'ce5b19a933354bdf87ac6d698494ad47', 'name': 'nfv'},
    'client_id': 'tacker_api_proj', 'username': 'service-account-tacker_api_proj', 'token_type': 'Bearer', 'active': True}
    {{(pid=22701) _fetch_token /opt/stack/data/venv/lib/python3.10/site-packages/keystonemiddleware/external_oauth2_token.py:731}}
    
  • tls_client_auth

    $ curl -i -X POST https://$keycloak_host_name:$keycloak_https_port/realms/testrealm/protocol/openid-connect/token \
    -d "client_id=tacker_api_proj" -d "scope=project_scope" -d "grant_type=client_credentials" \
    --cacert ca.pem \
    --cert client.pem \
    --key client.key
    HTTP/2 200
    cache-control: no-store
    pragma: no-cache
    content-length: 1861
    content-type: application/json
    referrer-policy: no-referrer
    strict-transport-security: max-age=31536000; includeSubDomains
    x-content-type-options: nosniff
    x-frame-options: SAMEORIGIN
    x-xss-protection: 1; mode=block
    
    {"access_token":"$access_token","expires_in":300,"refresh_expires_in":0,
    "token_type":"Bearer","not-before-policy":0,"scope":"email profile"}
    
    $ curl -i -X GET https://$tacker_host_name:9890/v1.0/vims \
    -H "Authorization: Bearer $access_token" \
    --cacert ca.pem \
    --cert client.pem \
    --key client.key
    HTTP/1.1 200 OK
    Content-Type: application/json
    Content-Length: 743
    X-Openstack-Request-Id: req-80b670f0-910f-4d53-9859-6d430f4ba7db
    Date: Fri, 06 Dec 2024 09:42:36 GMT
    
    {"vims": [{"id": "a99189da-bf72-4af7-884c-36d157f00571",
    "type": "openstack", "tenant_id": "2cc02f60acf34fdda7bc5e9af9a7032b",
    "name": "openstack", "description": "", "placement_attr": {
    "regions": ["RegionOne"]}, "is_default": true,
    "created_at": "2024-11-07 02:04:46", "updated_at": "2024-11-07 02:10:18",
    "extra": {}, "auth_url": "http://192.168.56.11/identity/v3",
    "vim_project": {"name": "admin", "project_domain_name": "default"},
    "auth_cred": {"username": "admin", "user_domain_name": "default",
    "cert_verify": "True", "project_id": null, "project_name": "admin",
    "project_domain_name": "default", "auth_url": "http://192.168.56.11/identity/v3",
    "key_type": "barbican_key", "secret_uuid": "***", "password": "***"}, "status": "ACTIVE"}]}
    
    $ tail -f /opt/stack/log/tacker-server.log
    Dec 06 09:42:36 controller-tacker tacker-server[25048]:
    DEBUG keystonemiddleware.external_oauth2_token [-] The introspect API response: {
    'exp': 1733478427, 'iat': 1733478127, 'jti': 'a51dbc9c-45b0-46ef-953e-71278b09c9d4',
    'iss': 'https://$keycloak_host_name:$keycloak_https_port/realms/testrealm', 'aud': 'account', 'sub': '7d3e5929-4bbe-4ef4-90e3-8fd4b445ed7f',
    'typ': 'Bearer', 'azp': 'tacker_api_proj', 'acr': '1', 'allowed-origins': ['/*'], 'realm_access': {
    'roles': ['offline_access', 'uma_authorization', 'default-roles-testrealm']}, 'resource_access': {'account': {
    'roles': ['manage-account', 'manage-account-links', 'view-profile']}},
    'cnf': {'x5t#S256': 'dAHdSS_CN-KNN9jgE8brrkFEDC2uWAKnMslE84CBB38'}, 'scope': 'email profile',
    'email_verified': False, 'role_info': 'admin,member,reader', 'preferred_username': 'service-account-tacker_api_proj',
    'user': {'domain': {'name': 'default', 'id': 'default'}, 'name': 'nfv_user', 'id': '35c884d8a6544d38abce33c0003c8331'},
    'project_info': {'domain': {'id': 'default', 'name': 'Default'}, 'id': 'ce5b19a933354bdf87ac6d698494ad47', 'name': 'nfv'},
    'client_id': 'tacker_api_proj', 'username': 'service-account-tacker_api_proj', 'token_type': 'Bearer', 'active': True}
    {{(pid=25048) _fetch_token /opt/stack/data/venv/lib/python3.10/site-packages/keystonemiddleware/external_oauth2_token.py:731}}
    

使用 Tacker API

在使用外部 OAuth 2.0 授权服务器时,当前版本的 OpenStack Command 不受支持。

相反,您可以使用 tacker_cli.sh,它是 curl 的封装器。例如,您可以按如下方式调用 v1.0/vims API。

$ export TACKER_AUTH_URL="http://$keycloak_host_name:$keycloak_http_port/realms/testrealm/protocol/openid-connect/token"
$ export TACKER_CLIENT_ID="tacker_api_proj"
$ export TACKER_CLIENT_SECRET="iIK6lARLzJgoQQyMyoymNYrGTDuR0733S"
$ export TACKER_AUTH_TYPE="client_secret_basic"
$ export TACKER_OAUTH2_SCOPE="tacker_scope"
$ export TACKER_URL=http://127.0.0.1:9890
$ ./tacker_cli.sh vim list

{"vims": [{"id": "a99189da-bf72-4af7-884c-36d157f00571",
"type": "openstack", "tenant_id": "2cc02f60acf34fdda7bc5e9af9a7032b",
"name": "openstack", "description": "", "placement_attr": {
"regions": ["RegionOne"]}, "is_default": true,
"created_at": "2024-11-07 02:04:46", "updated_at": "2024-11-07 02:10:18",
"extra": {}, "auth_url": "http://192.168.56.11/identity/v3",
"vim_project": {"name": "admin", "project_domain_name": "default"},
"auth_cred": {"username": "admin", "user_domain_name": "default",
"cert_verify": "True", "project_id": null, "project_name": "admin",
"project_domain_name": "default", "auth_url": "http://192.168.56.11/identity/v3",
"key_type": "barbican_key", "secret_uuid": "***", "password": "***"}, "status": "ACTIVE"}]}

您还可以找到其他对应于 Tacker API 的子命令。

$ ./tacker_cli.sh -h

Usage: tacker_cli.sh <command> [<args>]

Options:
  -h, --help      show this help message and exit
  -v, --version   print version

Commands:
  vim
  vnfpkgm
  vnflcm
  vnffm
  vnfpm

tacker_cli 使用与 OpenStack 项目 CLI 类似的身份验证方案,凭据信息作为以 TACKER 前缀开头的环境变量。以下提供了每种身份验证方法的完整示例。

# client_secret_basic
export TACKER_AUTH_URL="http://<keycloak_host>:<keycloak_port>/realms/testrealm/protocol/openid-connect/token"
export TACKER_CLIENT_ID="tacker_api_proj"
export TACKER_CLIENT_SECRET="<secret>"
export TACKER_AUTH_TYPE="client_secret_basic"
export TACKER_OAUTH2_SCOPE="tacker_scope"
export TACKER_URL=http://<tacker_host>:<tacker_port>

# client_secret_post
export TACKER_AUTH_URL="http://<keycloak_host>:<keycloak_port>/realms/testrealm/protocol/openid-connect/token"
export TACKER_CLIENT_ID="tacker_api_proj"
export TACKER_CLIENT_SECRET="<secret>"
export TACKER_AUTH_TYPE="client_secret_post"
export TACKER_OAUTH2_SCOPE="tacker_scope"
export TACKER_URL=http://<tacker_host>:<tacker_port>

# private_key_jwt
export TACKER_AUTH_URL="http://<keycloak_host>:<keycloak_port>/realms/testrealm/protocol/openid-connect/token"
export TACKER_CLIENT_ID="tacker_api_proj"
export TACKER_JWT_KEY="path/to/private_key.pem"
export TACKER_AUTH_TYPE="private_key_jwt"
export TACKER_OAUTH2_SCOPE="tacker_scope"
export TACKER_URL=http://<tacker_host>:<tacker_port>

# client_secret_jwt
export TACKER_AUTH_URL="http://<keycloak_host>:<keycloak_port>/realms/testrealm/protocol/openid-connect/token"
export TACKER_CLIENT_ID="tacker_api_proj"
export TACKER_CLIENT_SECRET="<secret>"
export TACKER_AUTH_TYPE="client_secret_jwt"
export TACKER_OAUTH2_SCOPE="tacker_scope"
export TACKER_URL=http://<tacker_host>:<tacker_port>

# tls_client_auth
export TACKER_AUTH_URL="https://<keycloak_host>:<keycloak_port>/realms/testrealm/protocol/openid-connect/token"
export TACKER_CLIENT_ID="tacker_api_proj"
export TACKER_AUTH_TYPE="tls_client_auth"
export TACKER_OAUTH2_SCOPE="tacker_scope"
export TACKER_CACERT="path/to/ca.pem"
export TACKER_CLIENT_CERT="path/to/client.pem"
export TACKER_CLIENT_KEY="path/to/client.key"
export TACKER_URL=https://<tacker_host>:<tacker_port>

注意

请注意,此脚本仅支持 版本 2 VNF LCM API

清理

当测试 Keycloak 环境不再需要时,测试完成后,可以通过运行以下命令简单地删除 Keycloak docker 容器。

$ docker stop keycloak && docker rm keycloak