

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 使用 AWS Lambda 代币自动售货机对 Amazon S3 实施 SaaS 租户隔离
<a name="implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine"></a>

*Tabby Ward、Thomas Davis 和 Sravan Periyathambi，Amazon Web Services*

## Summary
<a name="implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine-summary"></a>

多租户 SaaS 应用程序必须实施系统，以确保保持租户隔离。当您将租户数据存储在同一个 AWS 资源上时，例如多个租户将数据存储在同一个亚马逊简单存储服务 (Amazon S3) Service 存储桶中时，您必须确保不会发生跨租户访问。代币自动售货机 (TVMs) 是提供租户数据隔离的一种方式。这些机器提供了一种获取令牌的机制，同时减少了这些令牌生成方式的复杂性。开发人员可以在不详细了解 TVM 如何生成令牌的情况下使用 TVM。

此模式通过使用 AWS Lambda实现 TVM。TVM 生成令牌，该令牌由临时安全令牌服务 (STS) 凭证组成，这些凭证限制对 S3 存储桶中单个 SaaS 租户数据的访问。

TVMs，以及此模式提供的代码，通常与派生自 JSON Web Tokens (JWTs) 的声明一起使用，以将 AWS 资源请求与租户范围 AWS Identity and Access Management (IAM) 策略关联起来。您可以使用此模式中的代码为基础实现 SaaS 应用程序，该应用程序根据 JWT 令牌中提供的语句生成范围内的临时 STS 凭证。

## 先决条件和限制
<a name="implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine-prereqs"></a>

**先决条件**
+ 活跃 AWS 账户的.
+ AWS Command Line Interface (AWS CLI) [版本 1.19.0 或更高版本](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv1.html)，已在 macOS、Linux 或 Windows 上安装和配置。或者，您可以使用 [2.1 或更高 AWS CLI 版本](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)。

**限制**
+ 此代码在 Java 中运行，当前不支持其他编程语言。 
+ 示例应用程序不包括 AWS 跨区域或灾难恢复 (DR) 支持。 
+ 此模式介绍了适用于 SaaS 应用程序的 Lambda TVM 如何提供限定范围的租户访问权限。如果没有额外的安全测试作为特定应用程序或使用案例的一部分，则并未预期在生产环境中使用此模式。

## 架构
<a name="implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine-architecture"></a>

**目标技术堆栈**
+ AWS Lambda
+ Amazon S3
+ IAM
+ AWS Security Token Service (AWS STS)

**目标架构 **

![\[生成令牌以获取临时 STS 凭证，从而访问 S3 存储桶中的数据。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/97a34c8e-d04e-40b6-acbf-1baa176d22a9/images/14d0508a-703b-4229-85e6-c5094de7fe01.png)


 

## 工具
<a name="implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine-tools"></a>

**AWS 服务**
+ [AWS Command Line Interface (AWS CLI)](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html) 是一个开源工具，可帮助您 AWS 服务 通过命令行外壳中的命令进行交互。
+ [AWS Identity and Access Management (IAM)](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html) 通过控制谁经过身份验证并有权使用 AWS 资源，从而帮助您安全地管理对资源的访问权限。
+ [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) 是一项计算服务，可帮助您运行代码，无需预调配或管理服务器。它只在需要时运行您的代码，并自动进行扩展，因此您只需为使用的计算时间付费。
+ [AWS Security Token Service (AWS STS)](https://docs.aws.amazon.com/STS/latest/APIReference/welcome.html) 可帮助您为用户申请临时的、有限权限的证书。
+ [Amazon Simple Storage Service（Amazon S3）](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html)是一项基于云的对象存储服务，可帮助您存储、保护和检索任意数量的数据。

**代码**

此模式的源代码以附件形式提供，包含以下文件：
+ `s3UploadSample.jar` 提供将 JSON 文档上传至 S3 存储桶的 Lambda 函数的源代码。
+ `tvm-layer.zip` 提供了一个可重复使用的 Java 库，该库为 Lambda 函数提供用于访问 S3 存储桶和上传 JSON 文档的令牌（STS 临时凭证）。
+ `token-vending-machine-sample-app.zip` 提供了用于创建这些构件的源代码和编译说明。

要使用这些文件，请按照下一节中的说明操作。

## 操作说明
<a name="implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine-epics"></a>

### 确定变量值
<a name="determine-variable-values"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 确定变量值。 | 此模式的实现包含几个必须一致使用的变量名。确定每个变量应使用的值，并在后续步骤请求时提供该值。[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine.html) | 云管理员 | 

### 创建 S3 存储桶
<a name="create-an-s3-bucket"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 为示例应用程序创建一个 S3 存储桶。 | 使用以下 AWS CLI 命令创建 S3 存储桶。在代码片段中提供 `<sample-app-bucket-name>`** **值：<pre>aws s3api create-bucket --bucket <sample-app-bucket-name></pre>Lambda 示例应用程序将 JSON 文件上传至此存储桶。 | 云管理员 | 

### 创建 IAM TVM 角色和策略
<a name="create-the-iam-tvm-role-and-policy"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建 TVM 角色。 | 使用以下 AWS CLI 命令之一创建 IAM 角色。在命令中提供 `<sample-tvm-role-name>`** **值。对于 macOS 或 Linux shell：<pre>aws iam create-role \<br />--role-name <sample-tvm-role-name> \<br />--assume-role-policy-document '{<br />    "Version": "2012-10-17",		 	 	 <br />    "Statement": [<br />        {<br />            "Effect": "Allow",<br />            "Action": [<br />                "sts:AssumeRole"<br />            ],<br />            "Principal": {<br />                "Service": [<br />                    "lambda.amazonaws.com"<br />                ]<br />            },<br />            "Condition": {<br />                "StringEquals": {<br />                    "aws:SourceAccount": "<AWS Account ID>"<br />                }<br />            }<br />        }<br />    ]<br />}'</pre>对于 Windows 命令行：<pre>aws iam create-role ^<br />--role-name <sample-tvm-role-name> ^<br />--assume-role-policy-document "{\"Version\": \"2012-10-17\", \"Statement\": [{\"Effect\": \"Allow\", \"Action\": [\"sts:AssumeRole\"], \"Principal\": {\"Service\": [\"lambda.amazonaws.com\"]}, \"Condition\": {\"StringEquals\": {\"aws:SourceAccount\": \"<AWS Account ID>\"}}}]}"</pre>调用应用程序时，Lambda 示例应用程序将代入此角色。通过范围策略代入应用程序角色的能力，为代码提供了更广泛的权限来访问 S3 存储桶。 | 云管理员 | 
| 创建内联 TVM 角色策略。 | 使用以下 AWS CLI 命令之一创建 IAM 策略。在命令中提供 `<sample-tvm-role-name>`、****`<AWS Account ID>` 和 `<sample-app-role-name>` 值。对于 macOS 或 Linux shell：<pre>aws iam put-role-policy \<br />--role-name <sample-tvm-role-name> \<br />--policy-name assume-app-role \<br />--policy-document '{<br />    "Version": "2012-10-17",		 	 	  <br />    "Statement": [<br />        {<br />            "Effect": "Allow", <br />            "Action": "sts:AssumeRole", <br />            "Resource": "arn:aws:iam::<AWS Account ID>:role/<sample-app-role-name>"<br />        }<br />    ]}'</pre>对于 Windows 命令行：<pre>aws iam put-role-policy ^<br />--role-name <sample-tvm-role-name> ^<br />--policy-name assume-app-role ^<br />--policy-document "{\"Version\": \"2012-10-17\", \"Statement\": [{\"Effect\": \"Allow\", \"Action\": \"sts:AssumeRole\", \"Resource\": \"arn:aws:iam::<AWS Account ID>:role/<sample-app-role-name>\"}]}"</pre>此策略附加在 TVM 角色上。它使代码能够代入应用程序角色，该角色具有更广泛的 S3 存储桶访问权限。 | 云管理员 | 
| 附加托管 Lambda 策略。 | 使用以下 AWS CLI 命令附加 `AWSLambdaBasicExecutionRole` IAM 策略。在命令中提供 `<sample-tvm-role-name>` 值：<pre>aws iam attach-role-policy \<br />--role-name <sample-tvm-role-name> \<br />--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole</pre>对于 Windows 命令行：<pre>aws iam attach-role-policy ^<br />--role-name <sample-tvm-role-name> ^<br />--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole</pre>此托管策略附加到 TVM 角色，允许 Lambda 向亚马逊发送日志。 CloudWatch | 云管理员 | 

### 创建 IAM 应用程序角色与策略
<a name="create-the-iam-application-role-and-policy"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建应用程序角色。 | 使用以下 AWS CLI 命令之一创建 IAM 角色。在命令中提供 `<sample-app-role-name>`、`<AWS Account ID>` 和 `<sample-tvm-role-name>` 值。对于 macOS 或 Linux shell：<pre>aws iam create-role \<br />--role-name <sample-app-role-name> \<br />--assume-role-policy-document '{<br />    "Version": "2012-10-17",		 	 	  <br />    "Statement": [<br />        {<br />            "Effect": <br />            "Allow",<br />            "Principal": {<br />                "AWS": "arn:aws:iam::<AWS Account ID>:role/<sample-tvm-role-name>"<br />            },<br />            "Action": "sts:AssumeRole"<br />        }<br />    ]}'</pre>对于 Windows 命令行：<pre>aws iam create-role ^<br />--role-name <sample-app-role-name> ^<br />--assume-role-policy-document "{\"Version\": \"2012-10-17\", \"Statement\": [{\"Effect\": \"Allow\",\"Principal\": {\"AWS\": \"arn:aws:iam::<AWS Account ID>:role/<sample-tvm-role-name>\"},\"Action\": \"sts:AssumeRole\"}]}"</pre>Lambda 示例应用程序通过限定范围的策略代入此角色，以获得基于租户的 S3 存储桶访问权限。 | 云管理员 | 
| 创建内联应用程序角色策略。 | 使用以下 AWS CLI 命令之一创建 IAM 策略。在命令中提供 `<sample-app-role-name>` 和 `<sample-app-bucket-name>`** **值。对于 macOS 或 Linux shell：<pre>aws iam put-role-policy \<br />--role-name <sample-app-role-name> \<br />--policy-name s3-bucket-access \<br />--policy-document '{<br />    "Version": "2012-10-17",		 	 	  <br />    "Statement": [<br />        {<br />            "Effect": "Allow", <br />            "Action": [<br />                "s3:PutObject", <br />                "s3:GetObject", <br />                "s3:DeleteObject"<br />            ], <br />            "Resource": "arn:aws:s3:::<sample-app-bucket-name>/*"<br />        }, <br />        {<br />            "Effect": "Allow", <br />            "Action": ["s3:ListBucket"], <br />            "Resource": "arn:aws:s3:::<sample-app-bucket-name>"<br />        }<br />    ]}'</pre>对于 Windows 命令行：<pre>aws iam put-role-policy ^<br />--role-name <sample-app-role-name> ^<br />--policy-name s3-bucket-access ^<br />--policy-document "{\"Version\": \"2012-10-17\", \"Statement\": [{\"Effect\": \"Allow\", \"Action\": [\"s3:PutObject\", \"s3:GetObject\", \"s3:DeleteObject\"], \"Resource\": \"arn:aws:s3:::<sample-app-bucket-name>/*\"}, {\"Effect\": \"Allow\", \"Action\": [\"s3:ListBucket\"], \"Resource\": \"arn:aws:s3:::<sample-app-bucket-name>\"}]}"</pre>此策略附加在应用程序角色上。提供了对 S3 存储桶中对象的广泛访问权限。当示例应用程序代入该角色时，这些权限将限定为使用 TVM 动态生成的策略的特定租户。 | 云管理员 | 

### 使用 TVM 创建 Lambda 示例应用程序
<a name="create-the-lam-sample-application-with-tvm"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 下载编译后源文件。 | 下载 `s3UploadSample.jar` 和 `tvm-layer.zip` ****文件，它们包含在附件内。`token-vending-machine-sample-app.zip` 中提供了用于创建这些构件的源代码和编译说明。 | 云管理员 | 
| 创建 Lambda 层。 | 使用以下 AWS CLI 命令创建 Lambda 层，这样 Lambda 就可以访问 TVM。 如果您不是从下载 ` tvm-layer.zip` 的位置运行此命令，请在 `--zip-file` 参数中提供正确的 `tvm-layer.zip` 路径。 <pre>aws lambda publish-layer-version \<br />--layer-name sample-token-vending-machine \<br />--compatible-runtimes java11 \<br />--zip-file fileb://tvm-layer.zip</pre>对于 Windows 命令行：<pre>aws lambda publish-layer-version ^<br />--layer-name sample-token-vending-machine ^<br />--compatible-runtimes java11 ^<br />--zip-file fileb://tvm-layer.zip</pre>此命令创建 Lambda 层，其中包含可重复使用的 TVM 库。 | 云管理员、应用程序开发人员 | 
| 创建 Lambda 函数。 | 使用以下 AWS CLI 命令创建 Lambda 函数。在命令中提供 `<sample-app-function-name>`、`<AWS Account ID>`、`<AWS Region>`、`<sample-tvm-role-name>`、`<sample-app-bucket-name>` 和 `<sample-app-role-name>` 值。 如果您不是从下载 `s3UploadSample.jar` 的位置运行此命令，请在 `--zip-file` 参数中提供正确的 `s3UploadSample.jar` 路径。 <pre>aws lambda create-function \<br />--function-name <sample-app-function-name>  \<br />--timeout 30 \<br />--memory-size 256 \<br />--runtime java11 \<br />--role arn:aws:iam::<AWS Account ID>:role/<sample-tvm-role-name> \<br />--handler com.amazon.aws.s3UploadSample.App \<br />--zip-file fileb://s3UploadSample.jar \<br />--layers arn:aws:lambda:<AWS Region>:<AWS Account ID>:layer:sample-token-vending-machine:1 \<br />--environment "Variables={S3_BUCKET=<sample-app-bucket-name>,<br />ROLE=arn:aws:iam::<AWS Account ID>:role/<sample-app-role-name>}"</pre>对于 Windows 命令行：<pre>aws lambda create-function ^<br />--function-name <sample-app-function-name>  ^<br />--timeout 30 ^<br />--memory-size 256 ^<br />--runtime java11 ^<br />--role arn:aws:iam::<AWS Account ID>:role/<sample-tvm-role-name> ^<br />--handler com.amazon.aws.s3UploadSample.App ^<br />--zip-file fileb://s3UploadSample.jar ^<br />--layers arn:aws:lambda:<AWS Region>:<AWS Account ID>:layer:sample-token-vending-machine:1 ^<br />--environment "Variables={S3_BUCKET=<sample-app-bucket-name>,ROLE=arn:aws:iam::<AWS Account ID>:role/<sample-app-role-name>}"</pre>此命令创建 Lambda 函数，其中包含示例应用程序代码和附加的 TVM 层。它还设置了两个环境变量：`S3_BUCKET` 和 `ROLE`。示例应用程序使用这些变量来确定要代入的角色以及要将 JSON 文档上传到的 S3 存储桶。 | 云管理员、应用程序开发人员 | 

### 测试示例应用程序和 TVM。
<a name="test-the-sample-application-and-tvm"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 调用 Lambda 示例应用程序。 | 使用以下 AWS CLI 命令之一以预期的有效负载启动 Lambda 示例应用程序。在命令中提供 `<sample-app-function-name>` 和 `<sample-tenant-name>` 值。对于 macOS 和 Linux shell：<pre>aws lambda invoke \<br />--function <sample-app-function-name> \<br />--invocation-type RequestResponse \<br />--payload '{"tenant": "<sample-tenant-name>"}' \<br />--cli-binary-format raw-in-base64-out response.json</pre>对于 Windows 命令行：<pre>aws lambda invoke ^<br />--function <sample-app-function-name> ^<br />--invocation-type RequestResponse ^<br />--payload "{\"tenant\": \"<sample-tenant-name>\"}" ^<br />--cli-binary-format raw-in-base64-out response.json</pre>此命令调用 Lambda 函数并在 `response.json` 文档中返回结果。在许多基于 Unix 的系统上，您可以将 `response.json` 更改为 `/dev/stdout`，将结果直接输出到 Shell，而无需创建其他文件。 在后续调用此 Lambda 函数时更改 `<sample-tenant-name>` 值，会改变 JSON 文档的位置和令牌提供的权限。 | 云管理员、应用程序开发人员 | 
| 查看 S3 存储桶，以查看创建的对象。 | 浏览到您之前创建的 S3 存储桶（`<sample-app-bucket-name>`）。此存储桶包含 S3 对象前缀，其值为 `<sample-tenant-name>`。在此前缀下，您将找到以 UUID 命名的 JSON 文档。多次调用示例应用程序将添加更多 JSON 文档。 | 云管理员 | 
| 在日志中查看示例应用程序的 CloudWatch 日志。 | 查看与日志中名为 Lambda 函数关联的日志`<sample-app-function-name>`。 CloudWatch 有关说明，请参阅 [Lambda 文档中的将 Lambda 函数 CloudWatch 日志发送到日志](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html)。您可在这些日志中查看 TVM 生成的租户范围策略。此租户范围策略向 Amazon S3 **PutObject**、、、和授予使用示例应用程序的权限 **GetObject**DeleteObject**ListBucket****** APIs，但仅限于与之关联的对象前缀。`<sample-tenant-name>`在后续调用示例应用程序时，如果您更改 `<sample-tenant-name>`，TVM 会更新范围内的策略，使其与调用有效载荷中提供的租户相对应。此动态生成的策略显示了如何在 SaaS 应用程序中使用 TVM 维护租户范围访问权限。 TVM 功能在 Lambda 层中提供，因此无需复制代码即可将其附加至应用程序使用的其他 Lambda 函数。有关动态生成的策略的说明，请参阅[其他信息](#implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine-additional)部分。 | 云管理员 | 

## 相关资源
<a name="implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine-resources"></a>
+ [使用动态生成的 IAM policy 隔离租户](https://aws.amazon.com/blogs/apn/isolating-saas-tenants-with-dynamically-generated-iam-policies/)（博客文章）
+ [在 SaaS 环境中应用动态生成的隔离策略](https://aws.amazon.com/blogs/apn/applying-dynamically-generated-isolation-policies-in-saas-environments/)（博客文章）
+ [SaaS 开启 AWS](https://aws.amazon.com/saas/)

## 附加信息
<a name="implement-saas-tenant-isolation-for-amazon-s3-by-using-an-aws-lambda-token-vending-machine-additional"></a>

以下日志显示了在这种模式下由 TVM 代码生成的动态生成的策略。在此屏幕截图中，`<sample-app-bucket-name>` 是 `DOC-EXAMPLE-BUCKET`，`<sample-tenant-name>` 是 `test-tenant-1`。此范围策略返回的 STS 凭证无法对 S3 存储桶中的对象执行任何操作，但与对象密钥前缀 `test-tenant-1` 关联的对象除外。

![\[显示了由 TVM 代码生成的动态生成的策略的日志。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/97a34c8e-d04e-40b6-acbf-1baa176d22a9/images/d4776ebe-fb8f-41ac-b8c5-b4f97a821c8c.png)


## 附件
<a name="attachments-97a34c8e-d04e-40b6-acbf-1baa176d22a9"></a>

要访问与此文档相关联的其他内容，请解压以下文件：[attachment.zip](samples/p-attach/97a34c8e-d04e-40b6-acbf-1baa176d22a9/attachments/attachment.zip)