从Open Policy Agent迁移到Amazon Verified Permissions的完整指南

本文详细介绍了如何从开源策略代理Open Policy Agent迁移到AWS全托管授权服务Amazon Verified Permissions,涵盖策略语言转换、架构调整、集成实现和最佳实践,帮助组织降低运维负担并提升授权性能。

从Open Policy Agent迁移到Amazon Verified Permissions

应用授权是现代软件系统的关键组件,决定用户能在特定资源上执行哪些操作。许多组织采用Open Policy Agent(OPA)及其Rego策略语言,在应用和基础设施中实施细粒度授权控制。虽然OPA在策略即代码实现中证明有效,但组织越来越寻求更高性能的托管服务,在保持基于策略授权的灵活性和能力的同时减少运维开销。

Amazon Verified Permissions是一项全托管授权服务,使用Cedar策略语言帮助您为应用实现细粒度权限。Cedar是AWS开发的开源策略语言,提供与Rego相同的许多能力,同时提供改进的性能(比Rego快42-60倍)、直观的策略编写和形式验证能力。通过从OPA迁移到Verified Permissions,组织可以减少管理授权基础设施的运维负担,同时获得专为可扩展安全授权设计的服务。

此迁移提供几个关键优势:减少基础设施管理开销、改进策略性能和验证、通过AWS托管服务模型增强安全性,以及与其他AWS服务的无缝集成。此外,Cedar的语法设计比Rego更直观,减少编写、阅读和维护策略所需的工作量。

在本文中,我们探讨从OPA和Rego迁移到Verified Permissions和Cedar的过程,包括策略转换策略、软件开发和测试方法,以及部署考虑因素。我们通过实际示例演示如何将常见Rego策略转换为Cedar策略,并将Verified Permissions集成到现有应用中。

解决方案概述

从OPA到Verified Permissions的迁移代表从自管理授权基础设施向全托管服务的转变。在典型OPA设置中,客户运行OPA服务器作为边车、独立服务或嵌入式库,针对传入授权请求评估Rego策略。这些服务器从存储系统拉取策略包,并维护自身性能和可用性。

使用Verified Permissions时,AWS管理整个授权基础设施。应用向Verified Permissions服务进行API调用,该服务评估存储在托管策略存储中的Cedar策略。这消除了操作和维护OPA服务器、管理策略分发或处理服务扩展和可用性的需求。这种转变意味着您的团队可以专注于授权逻辑而非基础设施管理,同时获得AWS提供的规模和可靠性优势。

理解差异:比较Rego与Cedar

在开始迁移前,理解Rego和Cedar策略语言的基本差异很重要。这些差异将塑造您转换现有策略的方法。

策略结构和理念

Rego策略围绕规则构建,可评估以产生结果集。Rego使用逻辑编程方法,您定义规则必须满足的条件。策略通常涉及复杂查询、循环和推导式来检查数据结构。

示例Rego策略

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package authz
default allow = false

# 规则1:允许具有查看者角色的用户读取文档
allow {
    input.action == "read"
    input.resource.type == "document"
    input.user.role == "viewer"
}
# 规则2:允许具有编辑者角色的用户写入文档
allow {
    input.action == "write"
    input.resource.type == "document"
    input.user.role == "editor"
}

Cedar采用更具声明性的方法,具有明确的permit和forbid语句。每个Cedar策略都是独立的授权决策,清楚说明允许或拒绝的内容。Cedar策略设计为人类可读且易于审计。

等效Cedar策略

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 策略1:允许具有查看者角色的主体读取文档
permit (
    principal in UserRole::"viewer",
    action == Action::"read",
    resource in ResourceType::"document"
);
// 策略2:允许具有编辑者角色的主体写入文档
permit (
    principal in UserRole::"editor",
    action == Action::"write",
    resource in ResourceType::"document"
);

数据模型差异

两个评估引擎之间最显著的差异之一是它们处理数据的方式。Rego使用任意JSON输入数据,给用户在如何构建授权请求方面提供完全灵活性。用户可以使用Rego的路径符号访问输入数据中的任何字段。

Cedar允许使用类型化实体创建定义的模式。这意味着用户需要将授权数据建模为具有特定类型、属性和关系的实体。虽然这需要更多前期规划,但提供卓越的验证、运行时性能和工具支持。

策略评估

Rego和Cedar在策略评估方法上存在根本差异。Rego使用逻辑编程模型,因此策略评估功能类似于逻辑谜题求解器。它从问题开始,通过链接规则向后搜索以找到答案。这种方法允许灵活的策略组合,但通常更慢、更不可预测且更难审计。

另一方面,Cedar使用更简单的功能评估方法。它使用直接的评估模型,每个策略针对授权请求独立检查。策略使用基本条件逻辑产生快速、确定的允许或拒绝决策。策略要么完全匹配授权请求(主体、操作、资源和所有条件),要么不适用。这对于高性能授权场景至关重要,其中可预测的评估时间和清晰的审计跟踪是必不可少的。Cedar策略评估遵循四个核心原则:

  • 默认拒绝未明确授予的访问
  • Forbid覆盖permit以处理策略冲突
  • 顺序无关评估以防止错误
  • 确定性结果以获得可靠结果

设置Verified Permissions

在开始迁移授权策略前,您需要在Verified Permissions中建立基础基础设施。

创建策略存储

为说明迁移过程,您将使用使用OPA和Rego进行授权的虚构文档管理应用。迁移到Verified Permissions的第一步是创建策略存储。策略存储是Cedar策略和模式的容器。您可以为不同应用或环境创建多个策略存储。

创建策略存储时,您需要在两种验证模式之间选择:

  • STRICT模式:需要模式,策略根据该模式进行验证
  • OFF模式:允许无模式的策略(对初始测试有用)

对于生产迁移,推荐STRICT模式,因为它提供比OFF模式更好的验证,并能启用减少授权请求所需实体数据的优化。您可以通过AWS管理控制台、AWS命令行界面(AWS CLI)或使用AWS SDK以编程方式创建策略存储。以下示例使用AWS CLI:

1
2
3
4
aws verifiedpermissions create-policy-store \
    --region us-east-1 \
    --validation-settings mode=STRICT \
    --description "Migration from OPA to Amazon Verified Permissions"

如果请求成功,您应该看到类似以下的JSON编码响应:

1
2
3
4
5
6
{
    "policyStoreId": "PSEXAMPLEabcdefg012345",
    "arn": "arn:aws:verifiedpermissions:us-east-1:123456789012:policy-store/PSEXAMPLEabcdefg012345",
    "createdDate": "2025-09-15T10:30:45.123456+00:00",
    "lastUpdatedDate": "2025-09-15T10:30:45.123456+00:00"
}

记下响应中的policyStoreId——您将在后续操作中需要它。

定义模式

在STRICT模式下,Verified Permissions需要定义授权系统中实体类型的Cedar模式。此模式服务于几个重要目的,包括在创建时验证策略、启用实体切片性能优化、启用更好的工具和IDE支持,以及记录授权模型。模式应定义:

  • 实体类型:系统中对象的种类(例如,用户、角色、文档等)
  • 属性:实体可以具有的属性(例如,部门、分类、创建日期)
  • 操作:可以执行的操作(例如,读取、写入、删除)
  • 关系:实体如何相互关联(例如,用户属于角色、文档由用户拥有)

设计模式时,您应考虑当前OPA输入数据如何映射到Cedar实体。例如,如果您的Rego策略访问input.user.department,您将需要具有department属性的User实体类型。以下是文档管理应用的示例Cedar模式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
    "MyApp": {
        "entityTypes": {
            "User": {
                "shape": {
                    "type": "Record",
                    "attributes": {
                        "department": {"type": "String"},
                        "jobLevel": {"type": "Long"},
                        "email": {"type": "String"}
                    }
                }
            },
            "Role": {
                "shape": {
                    "type": "Record",
                    "attributes": {"name": {"type": "String"}}
                }
            },
            "Document": {
                "shape": {
                    "type": "Record",
                    "attributes": {
                        "owner": {"type": "Entity", "name": "User"},
                        "classification": {"type": "String"},
                        "createdDate": {"type": "String"}
                    }
                }
            }
        },
        "actions": {
            "read": {"appliesTo": {"principalTypes": ["User"], "resourceTypes": ["Document"]}},
            "write": {"appliesTo": {"principalTypes": ["User"], "resourceTypes": ["Document"]}},
            "delete": {"appliesTo": {"principalTypes": ["User"], "resourceTypes": ["Document"]}}
        }
    }
}

要使用AWS CLI将此模式应用到您之前创建的策略存储,您可以运行以下命令:

1
2
3
4
aws verifiedpermissions put-schema \
    --region us-east-1 \
    --policy-store-id YOUR_POLICY_STORE_ID \
    --definition file://schema.json

确保将YOUR_POLICY_STORE_ID替换为创建策略存储时返回的policyStoreId。

您可以在Verified Permissions控制台中查看可视化的策略模式(如图1所示),方法是转到策略存储并选择模式。

图1:Verified Permissions策略模式可视化

策略迁移模式

策略存储和模式就位后,您现在可以开始将Rego策略转换为Cedar策略,遵循常见授权模式。

模式1:基于角色的访问控制

基于角色的访问控制(RBAC)是最常用的授权模式之一。在RBAC系统中,用户被分配角色,角色被授予对资源执行操作的权限。

在您当前的Rego实现中,您可能检查用户在其roles数组中是否具有特定角色,然后基于该角色允许某些操作。您的Rego策略可能如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package rbac

import future.keywords.if
import future.keywords.in

default allow := false

allow if {
    input.user.roles[_] == "admin"
}

allow if {
    input.user.roles[_] == "editor"
    input.action in ["read", "write"]
}

allow if {
    input.user.roles[_] == "viewer"
    input.action == "read"
}

迁移到Cedar时,您将使用实体关系建模,其中用户属于角色实体。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 管理员用户可以执行任何资源的任何操作
permit (
    principal in MyApp::Role::"admin",
    action,
    resource
);

// 编辑者用户可以读取和写入每个资源
permit (
    principal in MyApp::Role::"editor",
    action in [MyApp::Action::"read", MyApp::Action::"write"],
    resource
);

// 查看者用户只能读取每个资源
permit (
    principal in MyApp::Role::"viewer",
    action == MyApp::Action::"read",
    resource
);

迁移方法 要成功将RBAC策略从Rego迁移到Cedar,请遵循以下步骤:

  1. 在模式中定义User和Role实体类型
  2. 为每个角色-操作组合创建permit策略
  3. 使用Cedar的in运算符检查角色成员资格
  4. 如果您有嵌套角色,考虑创建角色层次结构

关键差异 理解Rego和Cedar在RBAC方法上的根本差异将帮助您设计更有效的策略:

  • Cedar使用实体关系而不是检查数组成员资格
  • 每个权限成为单独的显式策略
  • 角色层次结构通过实体父子关系建模

模式2:基于属性的访问控制

基于属性的访问控制(ABAC)基于用户、资源、操作和环境的属性做出授权决策。这通常比RBAC更灵活,但实现可能更复杂。

在Rego中,您将从输入数据访问各种属性并在策略条件中使用它们:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package abac

default allow := false
// 任何人都可以读取公共文档
allow if {
    input.action == "read"
    input.resource.classification == "public"
}

// 用户可以读取其部门的内部文档
allow if {
    input.action == "read"
    input.resource.classification == "internal"
    input.user.department == input.resource.department
}

// 用户可以写入他们拥有的文档
allow if {
    input.action == "write"
    input.resource.owner == input.user.id
}

Cedar通过实体属性和策略条件处理此问题,使用when和unless子句。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 任何人都可以读取公共文档。空'principal'和'resource'实体是匹配一切的通配符
permit (
    principal,
    action == MyApp::Action::"read",
    resource
) when {
    resource.classification == "public"
};

// 用户可以读取其部门的内部文档
permit (
    principal,
    action == MyApp::Action::"read",
    resource
) when {
    resource.classification == "internal" &&
    principal.department == resource.department
};

// 用户可以写入他们拥有的文档
permit (
    principal,
    action == MyApp::Action::"write",
    resource
) when {
    resource.owner == principal
};

迁移方法 迁移ABAC策略需要仔细将属性从Rego输入结构映射到Cedar的实体模型:

  1. 识别当前策略中使用的属性
  2. 将这些属性映射到Cedar模式中的实体属性
  3. 在Cedar策略中使用when子句实现基于属性的条件
  4. 考虑使用上下文获取环境特定属性(时间、IP地址等)

关键差异 Cedar的基于模式的属性方法比Rego的动态属性访问提供几个优势:

  • Cedar要求属性在模式中定义
  • Cedar模式验证帮助在策略创建时捕获属性访问错误
  • 复杂的属性逻辑可能需要跨多个策略拆分

模式3:基于关系的访问控制

基于关系的访问控制(ReBAC)基于被访问资源的属性或用户与资源之间的关系(如所有权)授予权限。在Rego中,这可能表示如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package rebac

import future.keywords.if
import future.keywords.in

// 允许文档所有者执行任何操作
allow if {
    input.resource.type == "document"
    input.resource.owner_id == input.user.id
}

// 替代方案:通过单独的所有权数据结构检查所有权
allow if {
    input.resource.type == "document"
    ownership := data.ownerships[input.resource.id]
    ownership.owner_id == input.user.id
}

在前面的示例中,通过比较资源上的owner_id属性与用户的ID来检查所有权。您可能直接从输入数据或从单独的数据源访问此信息。在Cedar中,关系是一流概念。resource.owner == principal语法直接检查主体是否是资源引用的所有者实体。这比字符串比较更自然和类型安全:

1
2
3
4
5
6
7
permit (
    principal,
    action,
    resource is MyApp::Document
) when {
    resource.owner == principal
};

迁移方法 转换基于关系的策略需要将数据关系建模为Cedar实体引用:

  1. 将资源建模为具有相关属性的Cedar实体
  2. 在策略条件中使用资源属性
  3. 通过实体引用建模所有权和其他关系
  4. 使用Cedar的属性访问语法获取资源属性

模式4:基于时间和上下文的访问

许多授权系统需要考虑上下文信息,如时间、用户位置或请求特征(IP地址、用户代理等)。在Rego中表达此信息如下例所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package temporal

import future.keywords.if

default allow := false
// 允许在业务时间内(UTC时间上午9点到下午5点)读取访问
allow if {
    input.action == "read"
    current_hour := time.clock([time.now_ns(), "UTC"])[0]
    current_hour >= 9
    current_hour <= 17
}

在Cedar中,相同的策略逻辑可以表示如下:

1
2
3
4
5
6
7
8
9
// 允许在业务时间内(UTC时间上午9点到下午5点)读取访问
permit (
    principal,
    action == MyApp::Action::"read",
    resource
) when {
    context.currentTime.hour >= 9 &&
    context.currentTime.hour <= 17
};

迁移方法 Cedar中基于上下文的策略使用与每个授权请求一起传递的context参数:

  1. 使用Cedar的context功能获取环境信息
  2. 在授权请求上下文中传递基于时间的信息
  3. 使用上下文属性创建基于时间的条件策略
  4. 考虑对时间敏感策略的缓存影响

应用集成更改

将策略迁移到Cedar后,您需要更新应用代码以与Verified Permissions集成。

更新授权调用

应用代码中最显著的变化是将OPA API调用替换为Verified Permissions API调用。理解这些系统之间的差异将帮助您有效规划集成工作。本节中的示例代码使用Python编写。

请求结构更改

调用OPA时,您通常发送包含授权数据的单个JSON负载。例如,您当前的OPA请求可能如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
opa_request = {
    "input": {
        "user": {
            "id": "user123",
            "department": "engineering",
            "role": "editor"
        },
        "resource": {
            "id": "doc456",
            "type": "document",
            "owner": "user123"
        },
        "action": "read"
    }
}

response = requests.post(
    "http://opa-server:8181/v1/data/authz/allow",
    json=opa_request
)
authorized = response.json()["result"]

Verified Permissions需要更结构化的方法,其中主体、资源和操作是显式类型化的实体。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import boto3
import json
from typing import Dict, Any, List

class AuthorizationService:
    def __init__(self, policy_store_id: str, region: str = 'us-east-1'):
        self.client = boto3.client('verifiedpermissions', region_name=region)
        self.policy_store_id = policy_store_id
    
    # 检查主体是否被授权对资源执行操作
    def is_authorized(self, principal: Dict[str, Any], action: str,
                resource: Dict[str, Any], context: Dict[str, Any] = None) -> bool:
        try:
            # 转换为Cedar实体格式
            principal_entity = self._to_cedar_entity(principal, "User")
            resource_entity = self._to_cedar_entity(resource, "Document")
            action_entity = {"actionType": "MyApp::Action", "actionId": action}

            request = {
                'policyStoreId': self.policy_store_id,
                'principal': principal_entity,
                'action': action_entity,
                'resource': resource_entity
            }

            if context:
                request['context'] = {'contextMap': context}
                
            response = self.client.is_authorized(**request)
            return response['decision'] == 'ALLOW'
        except Exception as e:
            print(f"Authorization error: {e}")
            return False

    def _to_cedar_entity(self, entity_data: Dict[str, Any], entity_type: str) -> Dict[str, Any]:
        # 将应用数据转换为Cedar实体格式
        return {
            'entityType': f'MyApp::{entity_type}',
            'entityId': str(entity_data.get('id', '')),
            'attributes': entity_data
        }

此新结构中的关键差异是:

  • 实体类型声明:每个实体(主体、资源)必须包括与Cedar模式匹配的entityType
  • 实体ID:每个实体需要唯一的entityId用于标识
  • 操作格式:操作使用actionType和actionId指定,而不是简单字符串
  • 独立上下文:环境信息(如时间、IP地址或用户代理)在独立的context参数中传递

响应处理更改

OPA返回Rego策略输出的任何内容,可能是布尔值、允许操作集或复杂的嵌套数据结构。无论策略输出如何,Verified Permissions返回一致的授权决策结构:

1
2
3
4
5
6
# Amazon Verified Permissions响应结构
{
    'decision': 'ALLOW', # 或'DENY'
    'determiningPolicies': [...], # 哪些策略决定了决策
    'errors': [...] # 评估期间发生的错误
}

您的应用逻辑变得更简单,因为您只需要检查ALLOW或DENY:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 示例用法

def check_document_access():
    auth_service = AuthorizationService('YOUR_POLICY_STORE_ID')

    # 示例主体(用户)
    user = {
        'id': 'user123',
        'department': 'engineering',
        'jobLevel': 5,
        'email': 'user@company.com'
    }

    # 示例资源(文档)
    document = {
        'id': 'doc456',
        'owner': 'user123',
        'classification': 'internal',
        'department': 'engineering'
    }

    # 示例上下文
    context = {
        'currentHour': 14, # 下午2点
        'userAgent': 'MyApp/1.0'
    }

    # 检查授权
    can_read = auth_service.is_authorized(user, 'read', document, context)
    can_write = auth_service.is_authorized(user, 'write', document, context)

    print(f"用户可以读取文档: {can_read}")
    print(f"用户可以写入文档: {can_write}")

错误处理更改

OPA错误通常与策略评估问题或服务器连接问题相关。使用Verified Permissions时,您会遇到AWS特定的错误类型,如下例所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def is_authorized_with_error_handling(self, principal, action, resource, context=None):
    try:
        principal_entity = self._to_cedar_entity(principal, "User")
        resource_entity = self._to_cedar_entity(resource, "Document")
        action_entity = {"actionType": "MyApp::Action", "actionId": action}

        request = {
            'policyStoreId': self.policy_store_id,
            'principal': principal_entity,
            'action': action_entity,
            'resource': resource_entity
        }

        if context:
            request['context'] = {'contextMap': context}

        response = self.client.is_authorized(**request)
        return response['decision'] == 'ALLOW'
    except ClientError as e:
        error_code = e.response['Error']['Code']

        if error_code == 'ResourceNotFoundException':
            print(f"策略存储未找到: {self.policy_store_id}")
        elif error_code == 'ValidationException':
            print(f"无效请求: {e.response['Error']['Message']}")
        elif error_code == 'ThrottlingException':
            print("请求被限制 - 考虑实现指数退避")
        else:
            print(f"AWS错误: {error_code}")

        # 故障关闭 - 出错时拒绝访问
        return False

    except BotoCoreError as e:
        print(f"SDK错误: {e}")
        return False

    except Exception as e:
        print(f"意外错误: {e}")
        return False

重要的是要注意,AWS SDK为瞬时故障提供内置重试逻辑。以下是您可以启用此功能的示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 配置重试行为
config = Config(
    retries={
        'max_attempts': 3,
        'mode': 'adaptive' # 自动调整重试行为
    },
    connect_timeout=5,
    read_timeout=10
)

self.client = boto3.client(
    'verifiedpermissions',
    region_name=region,
    config=config
)

数据转换

您当前的授权数据需要转换为Cedar的实体格式。此转换发生在错误处理更改示例中显示的_to_cedar_entity方法中,但让我们分解涉及的内容。

提取实体信息 识别当前OPA输入的哪些部分代表主体、资源和操作。在大多数OPA实现中,此映射很简单:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 当前OPA结构
opa_input = {
    "user": {...}, # 这成为主体
    "resource": {...}, # 这成为资源
    "action": "read" # 这成为操作
}

# 映射到Cedar结构
principal = opa_input["user"]
resource = opa_input["resource"]
action = opa_input["action"]

添加类型信息 Cedar要求所有实体具有显式类型声明。您需要根据模式确定适当的实体类型:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
def _determine_entity_type(self, entity_data: Dict[str, Any]) -> str:
    # 基于实体数据确定Cedar实体类型。此逻辑将特定于您的应用。
    # 示例:基于实体结构或类型字段确定类型
    if 'role' in entity_data:
        return 'User'
    elif 'document_type' in entity_data:
        return 'Document'
    elif 'name' in entity_data and 'member_count' in entity_data:
        return 'Team'
    else:
        raise ValueError(f"无法确定实体类型: {entity_data}")

def _to_cedar_entity(self, entity_data: Dict[str, Any], entity_type: str = None) -> Dict[str, Any]:
    # 将应用数据转换为Cedar实体格式。
    if entity_type is None:
        entity_type = self._determine_entity_type(entity_data)

    return {
        'entityType': f'MyApp::{entity_type}',
        'entityId': str(entity_data.get('id', '')),
        'attributes': entity_data
    }

结构化属性 Cedar属性必须匹配模式定义,因此您可能需要转换属性名称或值。这也是迭代和改进命名的机会。以下示例演示了在代码中转换属性名称和值的代码模式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
def _prepare_attributes(self, entity_data: Dict[str, Any], entity_type: str) -> Dict[str, Any]:
    # 根据Cedar模式要求准备实体属性。
    attributes = {}

    if entity_type == 'User':
        # 将OPA字段名称映射到Cedar模式字段名称
        attributes = {
            'department': entity_data.get('dept', entity_data.get('department')),
            'jobLevel': int(entity_data.get('job_level', entity_data.get('jobLevel', 0))),
            'email': entity_data.get('email', entity_data.get('email_address'))
        }
    elif entity_type == 'Document':
        attributes = {
            'classification': entity_data.get('classification','internal'),
            'department': entity_data.get('department'),
            'owner': entity_data.get('owner', entity_data.get('owner_id'))
        }

    # 移除None值
    return {k: v for k, v in attributes.items() if v is not None}

处理上下文 从实体数据中分离环境信息。上下文信息不应是实体属性的一部分。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def prepare_authorization_request(self, user_data, resource_data, action,
                        request_metadata=None):

    # 实体数据仅包括固有属性
    principal = {
        'id': user_data['id'],
        'department': user_data['department'],
        'jobLevel': user_data['job_level']
    }

    resource = {
        'id': resource_data['id'],
        'classification': resource_data['classification'],
        'owner': resource_data['owner']
    }

    # 上下文包括环境和请求特定数据
    context = {}
    if request_metadata:
        context = {
            'currentHour': request_metadata.get('hour'),
            'ipAddress': request_metadata.get('ip_address'),
            'userAgent': request_metadata.get('user_agent'),
            'requestTime': request_metadata.get('timestamp')
        }
    return self.is_authorized(principal, action, resource, context)

测试迁移

迁移测试的最关键方面是验证您已正确将授权逻辑从Rego迁移到Cedar。这需要使用全面测试用例进行系统测试。

测试用例开发

  1. 清点当前策略:记录您当前的Rego策略,包括其决策逻辑、输入数据要求和关键测试场景的预期结果
  2. 创建测试场景:开发覆盖所有策略分支和边缘情况的测试用例
  3. 捕获当前行为:针对OPA运行测试用例以建立基线结果
  4. 测试Cedar策略:针对Cedar策略运行相同的测试用例
  5. 分析差异:调查不匹配并相应调整策略

测试策略时,首先处理基本直接策略,然后再处理复杂策略。测试正面案例(应允许)和负面案例(应拒绝),并包括边缘情况和边界条件。此外,使用真实生产数据(如有必要进行匿名化)进行测试,以验证策略在生产中实施时的有效性。

比较OPA设置与Verified Permissions在几个关键指标上的性能特征也很重要。这些指标应包括授权请求的平均响应时间、吞吐量(每秒请求数),以及正常和压力条件下的错误率。测试期间,从应用使用的实际部署环境进行测试,并考虑AWS服务的网络延迟。

最后,您应在几个关键领域测试应用与Verified Permissions的完整集成。您的集成测试应覆盖身份验证和AWS凭据处理、请求/响应数据转换、错误处理和回退场景、连接池和资源管理,以及日志记录和监控集成,以帮助确保组件无缝协同工作。

部署策略

从OPA到Verified Permissions的成功迁移需要仔细规划和风险管理的部署方法,以最小化对生产系统的中断。

分阶段迁移方法

不要尝试在单一步骤中完全切换到Verified Permissions,而是实施分阶段迁移以降低风险。

  1. 并行部署:将Verified Permissions与现有OPA基础设施一起部署,并将小部分授权请求路由到新系统。记录并比较两个系统之间的结果,最初专注于非关键操作,以在过渡过程中最小化风险。
  2. 逐步流量转移:逐渐增加路由到Verified Permissions的请求百分比,同时监控系统性能、错误率和授权准确性。实施断路器模式以便在需要时回退到OPA,并随着对新系统可靠性和性能的信心增长,扩展到更关键的操作。
  3. 完全迁移:将所有流量路由到Verified Permissions,但暂时保持OPA基础设施运行。在完整生产负载下监控系统行为,并在确认稳定性并对新系统性能有信心后停用OPA基础设施。

功能标志实现

使用功能标志通过各种标志类型控制迁移过程。这些包括基于百分比的推出(将特定百分比的请求路由到新系统)、基于用户的推出(将特定用户或用户组路由到新系统)、基于操作的推出(将特定类型的操作路由到新系统),以及基于环境的推出(在不同环境中使用不同系统)。功能标志提供几个优势,包括如果出现问题能够即时回滚、对迁移范围的精细控制、授权决策的A/B测试,以及新策略的安全实验。

故障排除常见迁移问题

从Rego迁移到Cedar时,您可能会遇到几个常见问题。在本节中,您将找到故障排除指南。

复杂Rego逻辑转换

一些Rego策略使用不直接转换为Cedar的复杂逻辑。例如:

1
2
3
4
5
6
# 具有循环和推导式的复杂Rego策略
allow {
    some i # i变量用于迭代input.user.permissions数组中的项目
        input.user.permissions[i].resource == input.resource.id
        input.user.permissions[i].actions[_] == input.action # 通配符_用于迭代actions数组中的项目
}

在这些场景中,您应重构数据模型以更好地与Cedar的基于实体的方法配合。例如,Cedar提供in运算符以改进性能和可读性,如下例所示:

1
2
3
4
5
6
7
8
9
permit (
    principal,
    action,
    resource
) when {
    principal has permission &&
    resource in principal.permission.resources &&
    action in principal.permission.actions
};

模式验证错误

Cedar要求严格模式合规性。常见错误包括:

  • 未定义的实体类型
  • 缺少必需属性
  • 类型不匹配

您可以使用Verified Permissions提供的模式验证工具来分类这些问题。

最佳实践和建议

遵循以下建议和最佳实践将帮助您使用Verified Permissions构建可维护、安全和性能良好的授权系统。

策略设计最佳实践

精心设计的策略是可靠授权系统的基础,直接影响可维护性和安全性:

  • 模式优先设计:在编写策略前从全面模式设计开始。精心设计的模式使策略编写更可维护。
  • 基本、显式策略:偏好多个基本策略而不是复杂的单体策略。Cedar的显式permit/forbid模型与清晰直接的策略语句配合最佳。
  • 有意义的命名:对实体类型、属性和策略描述使用描述性名称。这提高了策略的可理解性和可维护性。
  • 文档:记录您的授权模型,包括实体关系、策略意图和业务规则。

迁移策略建议

成功迁移授权系统需要通过增量步骤平衡速度与安全性:

  • 增量方法:不要尝试一次迁移所有内容。从基本、低风险策略开始,逐渐转向更复杂场景。
  • 在审计模式下开始:计算并记录两个系统的策略决策。这将帮助您比较结果而不影响运行时授权。
  • 全面测试:在迁移期间大力投资测试。全面测试的成本远低于生产中授权失败的成本。
  • 并行操作:在迁移期间并行运行两个系统以验证策略行为并建立对新系统的信心。
  • 团队培训:确保您的团队理解Cedar的策略模型和语法。与Rego的概念差异需要学习投入。

运营卓越性

维护生产授权系统需要持续关注超出初始迁移的运营问题:

  • 版本控制:将策略视为代码,具有适当的版本控制、代码审查和部署过程。
  • 监控和警报:从第一天开始实施全面监控。授权问题可能具有重大业务影响。
  • 定期审计:定期审查和审计策略以验证它们是否仍满足业务要求和安全标准。
  • 性能优化:持续监控和优化性能,特别是围绕缓存策略和策略效率。

结论

从Open Policy Agent迁移到Amazon Verified Permissions代表在减少运维开销、改进运行时授权性能和增强治理同时保持强大授权能力方面的重要一步。从OPA到Verified Permissions的迁移旅程不仅是关于改变技术,也是改进授权架构、增强安全实践和为应用访问控制需求构建更可扩展基础的机会。

感谢您阅读本文。如果您对从OPA迁移到Verified Permissions有评论或问题,请在下面的评论部分留下它们。

其他资源

以下链接提供本文涵盖主题的进一步阅读资源:

  • Amazon Verified Permissions用户指南
  • Cedar策略语言规范
  • 使用Amazon Verified Permissions简化应用中的授权管理
  • GitHub上的Cedar策略语言
  • Amazon Verified Permissions API参考

如果您对本文有反馈,请在评论部分提交评论。如果您对本文有疑问,请联系AWS支持。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计