Amazon Lambda 用于集成您的身份提供商 - Amazon Transfer Family
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

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

Amazon Lambda 用于集成您的身份提供商

创建连接到您的自定义身份提供商的 Amazon Lambda 函数。您可以使用任何自定义身份提供商,例如 Okta、Secrets Manager 或包含授权和身份验证逻辑的自定义数据存储。 OneLogin

注意

在创建使用 Lambda 作为身份提供程序的 Transfer Family 服务器之前,必须创建该函数。有关示例 Lambda 函数,请参阅 Lambda 函数示例。或者,您可以部署使用其中一个的 CloudFormation 堆栈Lambda 函数模板。此外,请确保您的 Lambda 函数使用信任 Transfer Family 的基于资源的策略。有关策略示例,请参阅Lambda 资源策略

  1. 打开Amazon Transfer Family 控制台

  2. 选择“创建服务器”以打开“创建服务器”页面。在“选择身份提供程序”中,选择“自定义身份提供程序”,如以下屏幕截图所示。

    选择身份提供程序控制台部分,其中已选中自定义身份提供程序。还选择了默认值,即用户可以使用其密码或密钥进行身份验证。
    注意

    只有在 Transfer Family 服务器上启用SFTP作为协议之一时,才能选择身份验证方法。

  3. 确保选择了默认值 “ Amazon Lambda 用于连接您的身份提供商”。

  4. 对于 Amazon Lambda 函数,选择 Lambda 函数名称。

  5. 填写其余的方框,然后选择“创建服务器”。有关创建服务器的其余步骤的详细信息,请参阅 配置SFTPFTPS、或FTP服务器端点

Lambda 资源策略

您必须有一个引用 Transfer Family 服务器和 Lambda ARNs 的策略。例如,您可以将以下策略与连接到您的身份提供程序的 Lambda 函数一起使用。该策略被转义JSON为字符串。

"Policy": "{ "Version": "2012-10-17", "Id": "default", "Statement": [ { "Sid": "AllowTransferInvocation", "Effect": "Allow", "Principal": { "Service": "transfer.amazonaws.com" }, "Action": "lambda:InvokeFunction", "Resource": "arn:aws:transfer:region:account-id:function:my-lambda-auth-function", "Condition": { "ArnLike": { "AWS:SourceArn": "arn:aws:transfer:region:account-id:server/server-id" } } } ] }"
注意

在上面的示例策略中,替换每个 user input placeholder 用你自己的信息。

事件消息结构

SFTP服务器发送给授权方 Lambda 函数的IDP自定义事件消息结构如下所示。

{ 'username': 'value', 'password': 'value', 'protocol': 'SFTP', 'serverId': 's-abcd123456', 'sourceIp': '192.168.0.100' }

其中 usernamepassword 是发送到服务器的登录凭证的值。

例如,您可输入以下连接命令。

sftp bobusa@server_hostname

系统会提示您输入密码:

Enter password: mysecretpassword

您可以在 Lambda 函数中进行检查,方法是在 Lambda 函数中打印传递的事件。此部分与以下文本块类似。

{ 'username': 'bobusa', 'password': 'mysecretpassword', 'protocol': 'SFTP', 'serverId': 's-abcd123456', 'sourceIp': '192.168.0.100' }

FTP和的事件结构类似FTPS:唯一的区别是这些值用于protocol参数,而不是SFTP。

用于身份验证的 Lambda 函数

要实现不同的身份验证策略,请编辑 Lambda 函数。为了帮助您满足应用程序的需求,您可以部署堆 CloudFormation 栈。有关更多信息,请参见 Amazon Lambda 开发人员指南 通过 Node.js 构建 Lambda 函数

Lambda 函数模板

您可以部署使用 Lambda 函数进行身份验证的 Amazon CloudFormation 堆栈。我们提供了多个模板,可使用登录凭证对您的用户进行身份验证和授权。您可以修改这些模板或 Amazon Lambda 代码以进一步自定义用户访问权限。

注意

您可以通过在模板FIPS中指定启用安全策略 Amazon CloudFormation 来创建FIPS启用了的 Amazon Transfer Family 服务器。有关可用安全策略的描述,请参见 的安全策略 Amazon Transfer Family

创建用于身份验证的 Amazon CloudFormation 堆栈
  1. https://console.aws.amazon.com/cloudformat ion 上打开 Amazon CloudFormation 控制台。

  2. 按照Amazon CloudFormation 用户指南中的选择 Amazon CloudFormation 堆栈模板中的现有模板部署堆栈的说明进行操作。

  3. 使用以下模板之一来创建在 Transfer Family 中进行身份验证的 Lambda 函数。

    • 经典 (Amazon Cognito) 堆栈模板

      用于在中创建用作 Amazon Lambda 自定义身份提供者的基本模板 Amazon Transfer Family。它会针对 Amazon Cognito 进行身份验证以进行基于密码的身份验证,如果使用基于公钥的身份验证,则会从 Amazon S3 存储桶返回公钥。部署后,您可以修改 Lambda 函数代码以执行不同的操作。

    • Amazon Secrets Manager 堆栈模板

      与 Amazon Transfer Family 服务器 Amazon Lambda 一起使用的基本模板,用于将 Secrets Manager 作为身份提供者进行集成。它根据格式aws/transfer/server-id/username的条目 Amazon Secrets Manager 进行身份验证。此外,该密钥必须包含返回给 Transfer Family 的所有用户属性的键值对。部署后,您可以修改 Lambda 函数代码以执行不同的操作。

    • Okta 堆栈模板:与 Amazon Transfer Family 服务器 Amazon Lambda 一起使用,将 Okta 作为自定义身份提供程序集成的基本模板。

    • Okta-MFA 堆栈模板:一种基本模板,可与 Amazon Transfer Family 服务器 Amazon Lambda 一起使用,将 Okta 与 MultiFactor 身份验证集成,作为自定义身份提供商。

    • Azure Active Directory 模板:博客文章使用 Azure 活动目录进行 Amazon Transfer Family 身份验证中描述了此堆栈的详细信息。 Amazon Lambda

    部署堆栈后,您可以在 CloudFormation 控制台的 Outputs 选项卡上查看有关堆栈的详细信息。

    部署其中一个堆栈是将自定义身份提供程序集成到 Transfer Family 工作流程的最简单方法。

有效的 Lambda 值

下表详细介绍了 Transfer Family 接受的用于自定义身份提供程序的 Lambda 函数的值。

描述 必填

Role

指定控制用户访问您的 Amazon S3 存储桶或亚马逊EFS文件系统的IAM角色的亚马逊资源名称 (ARN)。附加到此角色的策略决定了在将文件传入和传出您的 Amazon S3 或 Amazon EFS 文件系统时,您希望向用户提供的访问权限级别。该IAM角色还应包含信任关系,允许服务器在处理用户的转移请求时访问您的资源。

有关建立信任关系的详细信息,请参阅 建立信任关系

必需

PosixProfile

控制用户访问您的 Amazon EFS 文件系统的完整POSIX身份,包括用户 ID IDs (UidGidSecondaryGids)、群组 ID () 和任何辅助群组 ()。对文件系统中的文件和目录设置的POSIX权限决定了您的用户在将文件传入和传出您的 Amazon EFS 文件系统时获得的访问权限级别。

Amazon EFS 后备存储空间是必需的

PublicKeys

对此用户有效的SSH公钥值列表。空列表表示这不是有效的登录名。密码认证期间不得返回。

可选

Policy

适用于您的用户的会话策略,以便您可以跨多个用户使用同一个IAM角色。此策略将用户的访问范围缩小至 Amazon S3 存储桶的一部分。

可选

HomeDirectoryType

您希望用户在登录服务器时,用户主目录的登录目录(文件夹)的类型。

  • 如果您将其设置为PATH,则用户可以在其文件传输协议客户端中看到绝对的 Amazon S3 存储桶或 Amazon EFS 路径。

  • 如果您将其设置为LOGICAL,则必须在HomeDirectoryDetails参数中提供映射,这样您的用户才能看到 Amazon S3 或 Amazon EFS 路径。

可选

HomeDirectoryDetails

逻辑目录映射,用于指定哪些 Amazon S3 或 Amazon EFS 路径和密钥应该对您的用户可见,以及您希望如何让它们可见。您必须指定EntryTarget对,其中Entry显示路径是如何显示的,并且Target是实际的 Amazon S3 或 Amazon EFS 路径。

如果 HomeDirectoryType 值为 LOGICAL,则为必填项

HomeDirectory

用户使用客户端登录服务器时的登录目录。

可选

注意

HomeDirectoryDetails是JSON地图的字符串表示形式。这与之形成鲜明对比PosixProfile,后者是一个实际JSON的地图对象PublicKeys,也是一个字符串JSON数组。有关特定语言的详细信息,请参阅代码示例。

Lambda 函数示例

本节介绍了一些 NodeJS 和 Python 中的 Lambda 函数示例。

注意

在这些示例中,用户、角色、POSIX配置文件、密码和主目录详细信息均为示例,必须将其替换为实际值。

Logical home directory, NodeJS

以下 NodeJS 示例函数为拥有逻辑主目录的用户提供了详细信息。

// GetUserConfig Lambda exports.handler = (event, context, callback) => { console.log("Username:", event.username, "ServerId: ", event.serverId); var response; // Check if the username presented for authentication is correct. This doesn't check the value of the server ID, only that it is provided. if (event.serverId !== "" && event.username == 'example-user') { var homeDirectoryDetails = [ { Entry: "/", Target: "/fs-faa1a123" } ]; response = { Role: 'arn:aws:iam::123456789012:role/transfer-access-role', // The user is authenticated if and only if the Role field is not blank PosixProfile: {"Gid": 65534, "Uid": 65534}, // Required for EFS access, but not needed for S3 HomeDirectoryDetails: JSON.stringify(homeDirectoryDetails), HomeDirectoryType: "LOGICAL", }; // Check if password is provided if (!event.password) { // If no password provided, return the user's SSH public key response['PublicKeys'] = [ "ssh-rsa abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789" ]; // Check if password is correct } else if (event.password !== 'Password1234') { // Return HTTP status 200 but with no role in the response to indicate authentication failure response = {}; } } else { // Return HTTP status 200 but with no role in the response to indicate authentication failure response = {}; } callback(null, response); };
Path-based home directory, NodeJS

以下 NodeJS 示例函数为拥有基于路径的主目录的用户提供了详细信息。

// GetUserConfig Lambda exports.handler = (event, context, callback) => { console.log("Username:", event.username, "ServerId: ", event.serverId); var response; // Check if the username presented for authentication is correct. This doesn't check the value of the server ID, only that it is provided. // There is also event.protocol (one of "FTP", "FTPS", "SFTP") and event.sourceIp (e.g., "127.0.0.1") to further restrict logins. if (event.serverId !== "" && event.username == 'example-user') { response = { Role: 'arn:aws:iam::123456789012:role/transfer-access-role', // The user is authenticated if and only if the Role field is not blank Policy: '', // Optional, JSON stringified blob to further restrict this user's permissions HomeDirectory: '/fs-faa1a123' // Not required, defaults to '/' }; // Check if password is provided if (!event.password) { // If no password provided, return the user's SSH public key response['PublicKeys'] = [ "ssh-rsa abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789" ]; // Check if password is correct } else if (event.password !== 'Password1234') { // Return HTTP status 200 but with no role in the response to indicate authentication failure response = {}; } } else { // Return HTTP status 200 but with no role in the response to indicate authentication failure response = {}; } callback(null, response); };
Logical home directory, Python

以下 Python 示例函数为拥有逻辑主目录的用户提供了详细信息。

# GetUserConfig Python Lambda with LOGICAL HomeDirectoryDetails import json def lambda_handler(event, context): print("Username: {}, ServerId: {}".format(event['username'], event['serverId'])) response = {} # Check if the username presented for authentication is correct. This doesn't check the value of the server ID, only that it is provided. if event['serverId'] != '' and event['username'] == 'example-user': homeDirectoryDetails = [ { 'Entry': '/', 'Target': '/fs-faa1a123' } ] response = { 'Role': 'arn:aws:iam::123456789012:role/transfer-access-role', # The user will be authenticated if and only if the Role field is not blank 'PosixProfile': {"Gid": 65534, "Uid": 65534}, # Required for EFS access, but not needed for S3 'HomeDirectoryDetails': json.dumps(homeDirectoryDetails), 'HomeDirectoryType': "LOGICAL" } # Check if password is provided if event.get('password', '') == '': # If no password provided, return the user's SSH public key response['PublicKeys'] = [ "ssh-rsa abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789" ] # Check if password is correct elif event['password'] != 'Password1234': # Return HTTP status 200 but with no role in the response to indicate authentication failure response = {} else: # Return HTTP status 200 but with no role in the response to indicate authentication failure response = {} return response
Path-based home directory, Python

以下 Python 示例函数为拥有基于路径的主目录的用户提供了详细信息。

# GetUserConfig Python Lambda with PATH HomeDirectory def lambda_handler(event, context): print("Username: {}, ServerId: {}".format(event['username'], event['serverId'])) response = {} # Check if the username presented for authentication is correct. This doesn't check the value of the server ID, only that it is provided. # There is also event.protocol (one of "FTP", "FTPS", "SFTP") and event.sourceIp (e.g., "127.0.0.1") to further restrict logins. if event['serverId'] != '' and event['username'] == 'example-user': response = { 'Role': 'arn:aws:iam::123456789012:role/transfer-access-role', # The user will be authenticated if and only if the Role field is not blank 'Policy': '', # Optional, JSON stringified blob to further restrict this user's permissions 'HomeDirectory': '/fs-fs-faa1a123', 'HomeDirectoryType': "PATH" # Not strictly required, defaults to PATH } # Check if password is provided if event.get('password', '') == '': # If no password provided, return the user's SSH public key response['PublicKeys'] = [ "ssh-rsa abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789" ] # Check if password is correct elif event['password'] != 'Password1234': # Return HTTP status 200 but with no role in the response to indicate authentication failure response = {} else: # Return HTTP status 200 but with no role in the response to indicate authentication failure response = {} return response

测试您的配置

创建自定义身份提供程序后,应测试您的配置。

Console
使用 Amazon Transfer Family 控制台测试您的配置
  1. 打开Amazon Transfer Family 控制台

  2. 在“服务器”页面上,选择您的新服务器,选择“操作”,然后选择“测试”。

  3. 输入您在部署 Amazon CloudFormation 堆栈时设置的用户名密码文本。如果您保留默认选项,则用户名为 myuser,密码为 MySuperSecretPassword

  4. 如果在部署 Amazon CloudFormation 堆栈时设置了源 IP 地址,请选择服务器协议并输入源 IP 地址。

CLI
要测试您的配置,请使用 Amazon CLI
  1. 运行 test-identity-provider 命令。如后续步骤所述,将 user input placeholder 用您自己的信息进行替换。

    aws transfer test-identity-provider --server-id s-1234abcd5678efgh --user-name myuser --user-password MySuperSecretPassword --server-protocol FTP --source-ip 127.0.0.1
  2. 输入服务器 ID。

  3. 输入您在部署 Amazon CloudFormation 堆栈时设置的用户名和密码。如果您保留默认选项,则用户名为 myuser,密码为 MySuperSecretPassword

  4. 如果在部署 Amazon CloudFormation 堆栈时设置了服务器协议和源 IP 地址,请输入它们。

如果用户身份验证成功,则测试将返回一个StatusCode: 200HTTP响应、一个空字符串Message: ""(否则包含失败原因)和一个Response字段。

注意

在下面的响应示例中,该Response字段是一个已经 “字符串化”(转换为可在程序中使用的扁平JSON字符串)的JSON对象,其中包含用户角色和权限的详细信息。

{ "Response":"{\"Policy\":\"{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Sid\\\":\\\"ReadAndListAllBuckets\\\",\\\"Effect\\\":\\\"Allow\\\",\\\"Action\\\":[\\\"s3:ListAllMybuckets\\\",\\\"s3:GetBucketLocation\\\",\\\"s3:ListBucket\\\",\\\"s3:GetObjectVersion\\\",\\\"s3:GetObjectVersion\\\"],\\\"Resource\\\":\\\"*\\\"}]}\",\"Role\":\"arn:aws:iam::000000000000:role/MyUserS3AccessRole\",\"HomeDirectory\":\"/\"}", "StatusCode": 200, "Message": "" }