用 AWS Lambda 於整合您的身分識別提供者 - AWS Transfer Family

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

用 AWS Lambda 於整合您的身分識別提供者

建立連線至您的自訂身分識別提供者的 AWS Lambda 函數。您可以使用任何自訂身分識別提供者,例如 Okta、Secrets Manager 或包含授權和驗證邏輯的自訂資料存放區。 OneLogin

注意

建立使用 Lambda 做為身分識別提供者的 Transfer Family 伺服器之前,您必須先建立函數。如需 Lambda 函數的範例,請參閱範例 Lambda 函數。或者,您可以部署使用Lambda 函數模板. CloudFormation 此外,請確保您的 Lambda 函數使用信任 Transfer Family 的資源型政策。如需政策範例,請參閱 以 Lambda 源為基礎的政策

  1. 開啟 AWS Transfer Family 主控台

  2. 選擇建立伺服器以開啟 [建立伺服器] 頁面。對於 [選擇身分識別提供者],選擇 [自訂識別提供者],如下列螢幕擷取畫面所示

    已選取自訂身分識別提供者的 [選擇身分識別提供者主控台] 也會選取預設值,也就是使用者可以使用其密碼或金鑰進行驗證。
    注意

    只有當您啟用 SFTP 作為 Transfer Family 伺服器的其中一個通訊協定時,才能選擇驗證方法。

  3. 確定已選取預設值「用 AWS Lambda 來連線您的身分識別提供者」。

  4. 對於AWS Lambda 函數,請選擇 Lambda 函數的名稱。

  5. 填入其餘的方塊,然後選擇 [建立伺服器]。如需建立伺服器的剩餘步驟的詳細資訊,請參閱設定 SFTP、FTPS 或 FTP 伺服器端點

以 Lambda 源為基礎的政策

您必須擁有參考 Transfer Family 伺服器和 Lambda ARN 的政策。例如,您可以將下列政策與連線至身分提供者的 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:lambda:region:account-id:function:my-lambda-auth-function", "Condition": { "ArnLike": { "AWS:SourceArn": "arn:aws:transfer:region:account-id:server/server-id" } } } ] }"
注意

在上面的示例策略中,用您自己的信息替換每個用戶輸入佔位符

事件訊息結構

針對自訂 IDP,傳送至授權者 Lambda 函數的 SFTP 伺服器的事件訊息結構如下。

{ "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 堆疊。如需有關 Lambda 的詳細資訊,請參閱AWS Lambda 開發人員指南使用 Node.js 建置 Lambda 函數

Lambda 函數模板

您可以部署使用 Lambda 函數進行驗證的 AWS CloudFormation 堆疊。我們提供數個範本,可使用登入認證來驗證和授權您的使用者。您可以修改這些範本或 AWS Lambda 程式碼,進一步自訂使用者存取權限。

注意

您可以通過在模板中指定啟用 FIPS 的安全策略 AWS CloudFormation 來創建啟用 FIPS 的 AWS Transfer Family 服務器。可用的安全策略說明,請參閱 AWS Transfer Family 伺服器的安全性原則

若要建立用於驗證的 AWS CloudFormation 堆疊
  1. 請在以下位置開啟 AWS CloudFormation 主控台。 https://console.aws.amazon.com/cloudformation

  2. 請遵循《使用指南》中的 「選取 AWS CloudFormation 堆疊範本」中,從現有範本部署堆疊AWS CloudFormation 指示。

  3. 使用下列其中一個範本建立 Lambda 函數,以便在 Transfer Family 中用於驗證。

    • 經典(Amazon Cognito)堆棧模板

      用於在中建立用作自訂身分識別提供者的基本範本 AWS Transfer Family。 AWS Lambda 如果使用以公開金鑰為基礎的身份驗證,則會對 Amazon Cognito 進行驗證,並從 Amazon S3 儲存貯體傳回公開金鑰。部署之後,您可以修改 Lambda 函數程式碼來執行不同的動作。

    • AWS Secrets Manager 堆疊範本

      與 AWS Transfer Family 伺服器 AWS Lambda 搭配使用的基本範本,可將 Secrets Manager 整合為身分識別提供者。它會針對格式 AWS Secrets Manager 的項目進行驗證。aws/transfer/server-id/username此外,密碼必須保留傳回至「Transfer Family」之所有使用者性質的鍵值對。部署之後,您可以修改 Lambda 函數程式碼來執行不同的動作。

    • Okta 堆疊範本:與 AWS Transfer Family 伺服器 AWS Lambda 搭配使用,將 Okta 整合為自訂身分識別提供者的基本範本。

    • Okta-MFA 堆疊範本:與 AWS Transfer Family 伺服器 AWS Lambda 搭配使用的基本範本,將 Okta 與 MultiFactor 驗證整合為自訂身分識別提供者。

    • Azure 作用中目錄範本:此堆疊的詳細資料會在部落格文章中描述AWS Transfer Family 使用 Azure 作用中目錄進行驗證,以及 AWS Lambda.

    部署堆疊之後,您可以在 CloudFormation 主控台的 [輸出] 索引標籤上檢視堆疊的詳細資料。

    部署其中一個堆疊是將自訂身分識別提供者整合至「Transfer Family」工作流程的最簡單方法。

有效的 Lambda 值

下表說明 Transfer Family 接受用於自訂身分識別提供者之 Lambda 函數之值的詳細資料。

Value 描述 必要

Role

指定 IAM 角色的亞馬遜資源名稱 (ARN),該角色可控制使用者對 Amazon S3 儲存貯體或 Amazon EFS 檔案系統的存取權。附加到此角色的政策決定了在將檔案傳入和傳出 Amazon S3 或 Amazon EFS 檔案系統時,您希望為使用者提供的存取層級。IAM 角色也應包含信任關係,允許伺服器在處理您使用者的傳輸請求時,存取您的資源。

如需建立信任關係的詳細資訊,請參閱建立信任關係

必要

PosixProfile

完整的 POSIX 身分,包括使用者 ID (Uid)、群組 ID (Gid) 和任何次要群組 ID (SecondaryGids),可控制使用者對 Amazon EFS 檔案系統的存取。對檔案系統中的檔案和目錄設定的 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 函數

本節介紹了一些示例 Lambda 函數,在這兩個 NodeJS 和 Python。

注意

在這些範例中,使用者、角色、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
使用 AWS Transfer Family 控制台測試您的配置
  1. 開啟 AWS Transfer Family 主控台

  2. 在 [伺服器] 頁面上,選擇新伺服器,選擇 [動作],然後選擇 [測試]。

  3. 輸入部署 AWS CloudFormation 堆疊時設定的「使用者名稱」和「密碼」文字。如果您保留預設選項,則使用者名稱為myuser,密碼為MySuperSecretPassword

  4. 如果您在部署 AWS CloudFormation 堆疊時設定來源 IP,請選擇伺服器通訊協定並輸入 IP 位址。

CLI
使用 AWS 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. 輸入您在部署 AWS CloudFormation 堆疊時設定的使用者名稱和密碼。如果您保留預設選項,則使用者名稱為myuser,密碼為MySuperSecretPassword

  4. 如果您在部署 AWS CloudFormation 堆疊時進行設定,請輸入伺服器通訊協定和來源 IP 位址。

如果使用者驗證成功,則測試會傳回 StatusCode: 200 HTTP 回應、空字串 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": "" }