

# 使用 AWS Config 对 Lambda 实施预防性控制
<a name="governance-config"></a>

在开发过程中尽早确保无服务器应用程序的合规性至关重要。在本主题中，我们将介绍如何使用 [AWS Config](https://docs.aws.amazon.com/config/latest/developerguide/WhatIsConfig.html) 实现预防性控制。这样，您就能够在开发过程的早期阶段实施合规性检查，并在 CI/CD 管道中实施相同的控制。这还可以在集中管理的规则存储库中实现标准控制化，以便在 AWS 账户中一致地应用控制。

例如，假设您的合规性管理员定义了一项要求，用于确保所有 Lambda 函数都包含 AWS X-Ray 跟踪。借助 AWS Config 的主动模式，您可以在部署前对 Lambda 函数资源进行合规性检查，从而降低部署配置不当的 Lambda 函数的风险，并通过向开发人员就基础设施即代码模板提供更快的反馈来节省其时间。以下是使用 AWS Config 进行预防性控制的可视化流程：

 ![\[CloudFormation requests must pass AWS Config rules before provisioning.\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/governance-config-1.png) 

考虑要求所有 Lambda 函数都必须启用跟踪。对此，平台团队确定需要在所有账户中主动运行的特定 AWS Config 规则。此规则将缺乏已配置的 X-Ray 跟踪配置的任何 Lambda 函数标记为不合规资源。该团队制定规则，将其打包到[一致性包](https://docs.aws.amazon.com/config/latest/developerguide/conformance-packs.html)中，然后在所有 AWS 账户中部署一致性包，以确保组织中的所有账户统一应用这些控制措施。您可以用 AWS CloudFormation Guard 2.x.x 语法编写规则，其形式如下：

```
rule name when condition { assertion }
```

以下是一个 Guard 规则示例，用于检查以确保 Lambda 函数已启用跟踪：

```
rule lambda_tracing_check {
  when configuration.tracingConfig exists {
      configuration.tracingConfig.mode == "Active"
  }
}
```

 平台团队采取进一步行动，要求每个 AWS CloudFormation 部署必须调用预创建/更新[钩子](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/hooks-structure.html)。他们承担开发此钩子和配置管道的全部责任，加强对合规性规则的集中控制，并在所有部署中保持应用程序的一致性。要开发、打包和注册钩子，请参阅 CloudFormation Command Line Interface（CFN-CLI）文档中的 [Developing AWS CloudFormation Hooks](https://docs.aws.amazon.com/cloudformation-cli/latest/hooks-userguide/hooks-develop.html)。您可以使用 [CloudFormation CLI](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/initiating-hooks-project-python.html) 来创建钩子项目：

```
cfn init
```

此命令要求您提供有关钩子项目的一些基本信息，并创建一个包含以下文件的项目：

```
README.md
<hook-name>.json
rpdk.log
src/handler.py
template.yml
hook-role.yaml
```

作为钩子开发者，您需要在 `<hook-name>.json` 配置文件中添加所需的目标资源类型。在下面的配置中，将钩子配置为在使用 CloudFormation 创建任何 Lambda 函数之前执行。您也可以为 `preUpdate` 和 `preDelete` 操作添加类似的处理程序。

```
    "handlers": {
        "preCreate": {
            "targetNames": [
                "AWS::Lambda::Function"
            ],
            "permissions": []
        }
    }
```

您还需要确保 CloudFormation 钩子具有调用 AWS Config API 的相应权限。为此，您可以更新名为 `hook-role.yaml` 的角色定义文件。默认情况下，角色定义文件具有以下信任策略，该策略允许 CloudFormation 代入该角色。

```
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - hooks.cloudformation.amazonaws.com
                - resources.cloudformation.amazonaws.com
```

要允许此钩子调用配置 API，您必须在策略语句中添加以下权限。然后，使用 `cfn submit` 命令提交钩子项目，CloudFormation 会在其中为您创建一个具有所需权限的角色。

```
      Policies:
        - PolicyName: HookTypePolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - "config:Describe*"
                  - "config:Get*"
                  - "config:List*"
                  - "config:SelectResourceConfig"
                Resource: "*
```

接下来，您需要在 `src/handler.py` 文件中编写一个 Lambda 函数。在此文件中，您可以找到在启动项目时就已创建的名为 `preCreate`、`preUpdate`、和 `preDelete` 的方法。您的目标是编写一个通用、可重复使用的函数，该函数可以主动模式使用 适用于 Python (Boto3) 的 AWS SDK 调用 AWS Config `StartResourceEvaluation` API。此 API 调用将资源属性作为输入，并根据规则定义评估资源。

```
def validate_lambda_tracing_config(resource_type, function_properties: MutableMapping[str, Any]) -> ProgressEvent:
  LOG.info("Fetching proactive data")
  config_client = boto3.client('config')
  resource_specs = {
      'ResourceId': 'MyFunction',
      'ResourceType': resource_type,
      'ResourceConfiguration': json.dumps(function_properties),
      'ResourceConfigurationSchemaType': 'CFN_RESOURCE_SCHEMA'
  }
  LOG.info("Resource Specifications:", resource_specs)
  eval_response = config_client.start_resource_evaluation(EvaluationMode='PROACTIVE', ResourceDetails=resource_specs, EvaluationTimeout=60)
  ResourceEvaluationId = eval_response.ResourceEvaluationId
  compliance_response = config_client.get_compliance_details_by_resource(ResourceEvaluationId=ResourceEvaluationId)
  LOG.info("Compliance Verification:", compliance_response.EvaluationResults[0].ComplianceType)
  if "NON_COMPLIANT" == compliance_response.EvaluationResults[0].ComplianceType:
      return ProgressEvent(status=OperationStatus.FAILED, message="Lambda function found with no tracing enabled : FAILED", errorCode=HandlerErrorCode.NonCompliant)
  else:
      return ProgressEvent(status=OperationStatus.SUCCESS, message="Lambda function found with tracing enabled : PASS.")
```

现在，您可以从预创建钩子的处理程序中调用通用函数。下面是处理程序的示例：

```
@hook.handler(HookInvocationPoint.CREATE_PRE_PROVISION)
def pre_create_handler(
        session: Optional[SessionProxy],
        request: HookHandlerRequest,
        callback_context: MutableMapping[str, Any],
        type_configuration: TypeConfigurationModel
) -> ProgressEvent:
    LOG.info("Starting execution of the hook")
    target_name = request.hookContext.targetName
    LOG.info("Target Name:", target_name)
    if "AWS::Lambda::Function" == target_name:
        return validate_lambda_tracing_config(target_name,
            request.hookContext.targetModel.get("resourceProperties")
        )
    else:
        raise exceptions.InvalidRequest(f"Unknown target type: {target_name}")
```

完成此步骤后，您可以注册钩子并将其配置为侦听所有 AWS Lambda 函数创建事件。

 开发人员使用 Lambda 为无服务器微服务准备基础设施即代码（IaC）模板。准备工作包括遵守内部标准，然后进行本地测试并将模板提交到存储库。IaC 模板示例如下：

```
  MyLambdaFunction:
  Type: 'AWS::Lambda::Function'
  Properties:
    Handler: index.handler
    Role: !GetAtt LambdaExecutionRole.Arn
    FunctionName: MyLambdaFunction
    Code:
      ZipFile: |
        import json

        def handler(event, context):
            return {
                'statusCode': 200,
                'body': json.dumps('Hello World!')
            }
    Runtime: python3.14
    TracingConfig:
        Mode: PassThrough
    MemorySize: 256
    Timeout: 10
```

作为 CI/CD 流程的一部分，在部署 CloudFormation 模板时，CloudFormation 服务会在预置 `AWS::Lambda::Function` 资源类型之前调用预创建/更新钩子。该钩子利用在主动模式下运行的 AWS Config 规则来验证 Lambda 函数配置是否包含规定的跟踪配置。来自钩子的响应决定了下一步行动。如果合规，则钩子会发出成功响应，CloudFormation 将继续预置资源。否则，CloudFormation 堆栈部署将失败，管道会立即停止，系统会记录详细信息以供后续审查。合规性通知将被发送到相关利益方。

您可以在 CloudFormation 控制台中找到钩子成功/失败信息：

 ![\[Hook success/fail information in the CloudFormation console\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/governance-config-2.png) 

如果您为 CloudFormation 钩子启用了日志，则可以捕获钩子评估结果。以下是状态为失败的钩子的示例日志，表明 Lambda 函数未启用 X-Ray：

 ![\[Sample log for a hook with a failed status\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/governance-config-3.png) 

如果开发者选择更改 IaC，将 `TracingConfig Mode` 值更新为 `Active` 并重新部署，则钩子将成功执行，堆栈会继续创建 Lambda 资源。

 ![\[CloudFormation console shows successful resource deployment\]](http://docs.aws.amazon.com/zh_cn/lambda/latest/dg/images/governance-config-4.png) 

这样，在开发和部署 AWS 账户中的无服务器资源时，您就可以通过 AWS Config 以主动模式实施预防性控制。通过将 AWS Config 规则集成到 CI/CD 管道中，您可以识别并选择阻止不合规的资源部署，例如缺乏活动跟踪配置的 Lambda 函数。这样可以确保只有符合最新治理策略的资源才会部署到您的 AWS 环境中。