OpenStack身份管理

理解Keystone身份服务

Keystone简介

Keystone 是OpenStack项目中的一个重要组件,是OpenStack Identity服务。主要提供身份认证(Authentication)、授权(Authorization)、用户管理。

  • 身份认证(Authentication): Keystone负责验证用户的身份。它支持多种认证方法,包括用户名/密码、令牌(Token)、证书等。用户可以使用这些认证方法之一进行身份验证,以访问OpenStack中的资源。

  • 授权(Authorization): 一旦用户通过身份认证,Keystone会根据用户的身份和权限规则控制用户可以访问的资源和操作。这种授权是基于角色的,用户被分配到特定角色,而角色具有特定的权限。

  • 用户和角色管理: Keystone允许管理员创建、管理用户、角色和项目(Project),并分配特定权限给用户或角色。

Keystone相关名词解释

  1. User(用户): 在 Keystone 中,用户是系统中的实体,代表可以访问 OpenStack 服务的个体。用户通过身份认证来验证其身份。

  2. Role(角色): 角色是一组权限的集合,可以分配给用户或项目。角色决定了用户或项目可以执行的操作。

  3. Project(项目): 项目是 OpenStack 中的逻辑隔离单元,用于组织资源和权限。可以将用户分配到项目中,并管理项目的资源。

  4. Domain(域): 域是一组相关的用户、角色和项目的集合。域可以用于组织和隔离 OpenStack 环境中的用户和项目。

  5. Endpoint(终端): 终端是服务的访问地址,包括 URL 和访问协议。每个服务可以有多个终端,用于访问不同的服务节点。

  6. Service(服务): 服务代表 OpenStack 中的一个功能单元,如计算服务(Nova)、网络服务(Neutron)、镜像服务(Glance)等。每个服务有一个唯一的标识符。

  7. Token(令牌): 令牌是用于身份认证和授权的临时凭证。用户经过身份认证后,会获得一个令牌,该令牌允许他们在一定时间内访问特定资源。

Keystone的层次结构

4324542b834a4aecb25d739fac41150e (1).png

Keystone的认证流程

1694789108795.png

Keystone服务安装

作业与实验环境

超星网址
快照管理:控制节点选择快照5.3.13/项目5-9

安装与配置

  1. Keystone是运行在Web服务器上的Web应用,需要安装以下软件包:

    [root@controller ~]# yum -y install openstack-keystone httpd mod_wsgi
    • 在安装Keystone的过程中系统会自动创建名为keystone的用户和用户组,通过以下命令查看:

      [root@controller ~]# cat /etc/passwd |grep keystone     # 查看用户名
      keystone:x:163:163:OpenStack Keystone Daemons:/var/lib/keystone:/sbin/nologin
      [root@controller ~]# cat /etc/group |grep keystone      # 查看用户组
      keystone:x:163:
  2. OpenStack Keystone作为OpenStack项目的身份认证和授权服务,需要存储大量的用户、角色、项目、域等身份信息以及相应的权限信息,因此需要为Keystone创建数据库。

    [root@controller ~]# mysql -uroot -p000000      # 用root用户登录数据库
    Welcome to the MariaDB monitor.  Commands end with ; or \g.
    Your MariaDB connection id is 8
    Server version: 10.3.20-MariaDB MariaDB Server
    
    Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
    
    Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
    
    MariaDB [(none)]> CREATE DATABASE keystone;     # 创建名为keystone的数据库
    Query OK, 1 row affected (0.000 sec)MariaDB [(none)]>
    // 把数据库的所有表的权限授予本地登录的keystone用户,密码为000000
    MariaDB [(none)]> GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'localhost' IDENTIFIED BY '000000';
    Query OK, 0 rows affected (0.001 sec)
    
    // 把数据库的所有表的权限授予远程登录的keystone用户,密码为000000
    MariaDB [(none)]> GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%' IDENTIFIED BY '000000';
    Query OK, 0 rows affected (0.000 sec)
    
    // 退出数据库
    MariaDB [(none)]> quit
  3. 修改Keystone配置文件关联数据库

    打开/etc/keystone/keystone.conf后查找[database]字段 (600行)并修改连接主机controller的数据库keystone

    connection = mysql+pymysql://keystone:000000@controller/keystone
    • 配置令牌加密方式:查找[token](2475行)并删除provider = fernet前的#注释符
  4. 初始化Keystone数据库

    • 同步数据库

      # 通过keystone用户执行数据库初始化命令
      [root@controller ~]# su keystone -s /bin/sh -c "keystone-manage db_sync"
    • 查看数据库是否同步,打印出数据表

      [root@controller ~]# mysql -uroot -p000000
      Welcome to the MariaDB monitor.  Commands end with ; or \g.
      Your MariaDB connection id is 10
      Server version: 10.3.20-MariaDB MariaDB Server
      
      Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
      
      Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
      
      MariaDB [(none)]> use keystone;
      Reading table information for completion of table and column names
      You can turn off this feature to get a quicker startup with -A
      
      Database changed
      MariaDB [keystone]> show tables;        # 打印出数据表
      +------------------------------------+
      | Tables_in_keystone                 |
      +------------------------------------+
      | access_rule                        |
      | access_token                       |
      | application_credential             |
      | application_credential_access_rule |
      | application_credential_role        |
      | assignment                         |

      作业1:完成数据库同步后显示数据表并截图

Keystone服务初始化

  1. 初始化fernet密钥库

    # 生成用来加密解密令牌的fernet密钥
    [root@controller ~]# keystone-manage fernet_setup --keystone-user keystone --keystone-group keystone
    # 生成用来加密解密用户凭证的fernet密钥
    [root@controller ~]# keystone-manage credential_setup --keystone-user keystone --keystone-group keystone
  2. 初始化admin用户身份认证信息

    [root@controller ~]# keystone-manage bootstrap --bootstrap-password 000000 --bootstrap-admin-url http://controller:5000/v3 --bootstrap-internal-url http://contoller:5000/v3 --bootstrap-public-url http://controller:5000/v3 --bootstrap-region-id RegionOne

    此时keystone数据库内已经存放了admin用户登录所需要验证的信息。

  3. 配置web服务

    • 在Apache服务器的配置目录中生成WSGI的配置文件用来支持WSGI

      [root@controller ~]# ln -s /usr/share/keystone/wsgi-keystone.conf /etc/httpd/conf.d/
    • 修改/etc/httpd/conf/httpd.conf文件95行为ServerName controller设置Apache服务的地址为控制节点主机名,重启服务、设置开机自启

      [root@controller b]# systemctl enable httpd
      Created symlink from /etc/systemd/system/multi-user.target.wants/httpd.service to /usr/lib/systemd/system/httpd.service.
      [root@controller b]# systemctl restart httpd

Keystone服务认证、身份管理

通过OpenStack 管理命令进行身份管理

  1. vi admin-login创建文件添加以下身份凭证:

    export OS_USERNAME=admin
    export OS_PASSWORD=000000
    export OS_PROJECT_NAME=admin
    export OS_USER_DOMAIN_NAME=Default
    export OS_PROJECT_DOMAIN_NAME=Default
    export OS_AUTH_URL=http://controller:5000/v3
    export OS_IDENTITY_API_VERSION=3
    export OS_IMAGE_API_VERSION=2
    • 导入环境变量

      [root@controller ~]# source admin-login 
  2. 通过管理命令创建项目:

    [root@controller ~]# openstack project create --domain default project
    +-------------+----------------------------------+
    | Field       | Value                            |
    +-------------+----------------------------------+
    | description |                                  |
    | domain_id   | default                          |
    | enabled     | True                             |
    | id          | 1ddf073425f44982b81884a6cd897616 |
    | is_domain   | False                            |
    | name        | project                          |
    | options     | {}                               |
    | parent_id   | default                          |
    | tags        | []                               |
    +-------------+----------------------------------+

    作业2:项目创建完成后截图

  3. 常用管理命令:

    1694795372656.png 1694795400751.png

通过API进行身份管理

OpenStack所提供的API允许开发人员通过编程方式与OpenStack的各种服务进行通信,以实现自动化、集成和定制化的功能。

监控和报警: OpenStack API允许开发人员编写监控应用程序,以监测OpenStack环境的状态并实时响应,包括生成警报和报告。
数据分析和报告: 通过API,开发人员可以获取OpenStack环境中的数据,并进行分析和报告,以支持决策制定和优化资源分配。

  1. 使用curl向openstack请求一个令牌:

    curl -v -X POST $OS_AUTH_URL/auth/tokens \
     -H "Content-Type: application/json" \
     -d '{
         "auth": {
             "identity": {
                 "methods": ["password"],
                 "password": {
                     "user": {
                         "domain": {"name": "'"$OS_USER_DOMAIN_NAME"'"},
                         "name": "'"$OS_USERNAME"'",
                         "password": "'"$OS_PASSWORD"'"
                     }
                 }
             },
             "scope": {
                 "project": {
                     "domain": {"name": "'"$OS_PROJECT_DOMAIN_NAME"'"},
                     "name": "'"$OS_PROJECT_NAME"'"
                 }
             }
         }
     }' \
     |python -m json.tool
    • 从返回的响应中提取字段X-Subject-Token的值即获取到的token

      * About to connect() to controller port 5000 (#0)
      *   Trying 192.168.10.10...
      * Connected to controller (192.168.10.10) port 5000 (#0)
      > POST /v3/auth/tokens?nocatalog HTTP/1.1
      > User-Agent: curl/7.29.0
      > Host: controller:5000
      > Accept: */*
      > Content-Type: application/json
      > Content-Length: 472
      >
      } [data not shown]
      * upload completely sent off: 472 out of 472 bytes
      < HTTP/1.1 201 CREATED
      < Date: Fri, 15 Sep 2023 17:29:38 GMT
      < Server: Apache/2.4.6 (CentOS) mod_wsgi/3.4 Python/2.7.5
      < X-Subject-Token: gAAAAABlBJSCUlkAxmXcgaMCjAQznrea88WgOxL5VP8PifbX-Q-boOzZUVDb3sc0cTOVUegjeNSRKdii1ZtYwBDbZ1WRTTki_2dRlrE1d_D5erFMml-3YAY5BL2vBWXSimVAXIG9jUycTLkqbcavEf79i3Z0xfPHUOaxzC9eA5dvDbS1eRA_Y2c
      < Vary: X-Auth-Token
      < x-openstack-request-id: req-ecb79fdc-f4e7-4ca8-8c02-f2d69a28f453
      < Content-Length: 648
      < Content-Type: application/json
      <
      { [data not shown]
      * Connection #0 to host controller left intact
  2. 导出环境变量OS_TOKEN、通过API读取用户列表:

    • 将上一步提取的token导出环境变量,每个人的token不一样,自行替换

      [root@controller ~]# export OS_TOKEN=gAAAAABlBJSCUlkAxmXcgaMCjAQznrea88WgOxL5VP8PifbX-Q-boOzZUVDb3sc0cTOVUegjeNSRKdii1ZtYwBDbZ1WRTTki_2dRlrE1d_D5erFMml-3YAY5BL2vBWXSimVAXIG9jUycTLkqbcavEf79i3Z0xfPHUOaxzC9eA5dvDbS1eRA_Y2c
    • 使用相关的API获取用户列表

      [root@controller ~]# curl -s -X GET $OS_AUTH_URL/users -H "X-Auth-Token:$OS_TOKEN" -H "Content-Type: application/json" |python -m json.tool
      {
          "links": {
              "next": null,
              "previous": null,
              "self": "http://controller:5000/v3/users"
          },
          "users": [
              {
                  "domain_id": "default",
                  "enabled": true,
                  "id": "d21e719687444563a40a2528eb22996b",
                  "links": {
                      "self": "http://controller:5000/v3/users/d21e719687444563a40a2528eb22996b"
                  },
                  "name": "admin",
                  "options": {},
                  "password_expires_at": null
              }
          ]
      }

    作业3:通过api获取用户列表后截图

使用Python requests库进行身份管理

除了使用curl命令与Keystone API进行交互外,我们还可以使用Python的requests库来编写更灵活的自动化脚本。这种方式特别适合需要进行批量操作或集成到其他应用程序中的场景。

环境准备

首先确保已安装Python的requests库:

# 配置dns
[root@controller ~]# echo "nameserver 8.8.8.8" >/etc/resolv.conf
# 同步时间
[root@controller ~]# systemctl restart chronyd                  
# 安装python3
[root@controller ~]# yum install -y python3
# 安装requests库
[root@controller ~]# pip3 install requests 

基本的令牌申请

以下是使用Python requests库申请Keystone令牌的基本示例:

import requests
import json

# Keystone认证信息配置
KEYSTONE_URL = "http://controller:5000/v3"
USERNAME = "admin"
PASSWORD = "000000"
PROJECT_NAME = "admin"
DOMAIN_NAME = "Default"

def get_keystone_token():
    """获取Keystone认证令牌"""
    auth_url = f"{KEYSTONE_URL}/auth/tokens"
    
    # 构建认证请求体
    auth_data = {
        "auth": {
            "identity": {
                "methods": ["password"],
                "password": {
                    "user": {
                        "domain": {"name": DOMAIN_NAME},
                        "name": USERNAME,
                        "password": PASSWORD
                    }
                }
            },
            "scope": {
                "project": {
                    "domain": {"name": DOMAIN_NAME},
                    "name": PROJECT_NAME
                }
            }
        }
    }
    
    # 发送认证请求
    headers = {"Content-Type": "application/json"}
    response = requests.post(auth_url, 
                           data=json.dumps(auth_data), 
                           headers=headers)
    
    if response.status_code == 201:
        # 从响应头中获取令牌
        token = response.headers.get('X-Subject-Token')
        print(f"认证成功,获取到令牌: {token[:50]}...")
        return token, response.json()
    else:
        print(f"认证失败: {response.status_code} - {response.text}")
        return None, None

# 使用示例
token, token_info = get_keystone_token()

用户管理操作

获取令牌后,可以进行各种身份管理操作:

def keystone_api_request(token, method, endpoint, data=None):
    """通用的Keystone API请求函数"""
    url = f"{KEYSTONE_URL}{endpoint}"
    headers = {
        "X-Auth-Token": token,
        "Content-Type": "application/json"
    }
    
    if method.upper() == "GET":
        response = requests.get(url, headers=headers)
    elif method.upper() == "POST":
        response = requests.post(url, data=json.dumps(data), headers=headers)
    elif method.upper() == "PUT":
        response = requests.put(url, data=json.dumps(data), headers=headers)
    elif method.upper() == "DELETE":
        response = requests.delete(url, headers=headers)
    else:
        raise ValueError("不支持的HTTP方法")
    
    return response

def list_users(token):
    """获取用户列表"""
    response = keystone_api_request(token, "GET", "/users")
    if response.status_code == 200:
        users = response.json()["users"]
        print(f"共找到 {len(users)} 个用户:")
        for user in users:
            print(f"- ID: {user['id']}, 名称: {user['name']}, 启用状态: {user['enabled']}")
        return users
    else:
        print(f"获取用户列表失败: {response.status_code} - {response.text}")
        return []

def create_user(token, username, password, email=None, description=None):
    """创建新用户"""
    user_data = {
        "user": {
            "name": username,
            "password": password,
            "enabled": True,
            "domain_id": "default"
        }
    }
    
    if email:
        user_data["user"]["email"] = email
    if description:
        user_data["user"]["description"] = description
    
    response = keystone_api_request(token, "POST", "/users", user_data)
    if response.status_code == 201:
        user = response.json()["user"]
        print(f"用户创建成功: {user['name']} (ID: {user['id']})")
        return user
    else:
        print(f"用户创建失败: {response.status_code} - {response.text}")
        return None

def list_projects(token):
    """获取项目列表"""
    response = keystone_api_request(token, "GET", "/projects")
    if response.status_code == 200:
        projects = response.json()["projects"]
        print(f"共找到 {len(projects)} 个项目:")
        for project in projects:
            print(f"- ID: {project['id']}, 名称: {project['name']}, 启用状态: {project['enabled']}")
        return projects
    else:
        print(f"获取项目列表失败: {response.status_code} - {response.text}")
        return []

def create_project(token, project_name, description=None):
    """创建新项目"""
    project_data = {
        "project": {
            "name": project_name,
            "enabled": True,
            "domain_id": "default"
        }
    }
    
    if description:
        project_data["project"]["description"] = description
    
    response = keystone_api_request(token, "POST", "/projects", project_data)
    if response.status_code == 201:
        project = response.json()["project"]
        print(f"项目创建成功: {project['name']} (ID: {project['id']})")
        return project
    else:
        print(f"项目创建失败: {response.status_code} - {response.text}")
        return None

实际应用示例

以下是一个实际应用示例,演示如何创建用户、项目:

def setup_new_tenant(token, tenant_name, admin_username, admin_password):
    """为新租户设置完整的环境"""
    print(f"=== 为租户 '{tenant_name}' 设置环境 ===")
    
    # 1. 创建项目
    project = create_project(token, tenant_name, f"{tenant_name} 项目")
    if not project:
        return False
    
    # 2. 创建用户
    user = create_user(token, admin_username, admin_password, 
                      description=f"{tenant_name} 管理员")
    if not user:
        return False
    
# 使用示例
if __name__ == "__main__":
    # 获取管理员令牌
    token, _ = get_keystone_token()
    if token:
        # 为新租户设置环境
        setup_new_tenant(token, "demo_company", "demo_admin", "demo_password")

作业4:运行Python脚本获取令牌并进行用户管理操作后截图