使用 AWS Lambda 整合您的身分提供者 - AWS Transfer Family

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

使用 AWS Lambda 整合您的身分提供者

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

對於大多數使用案例,設定自訂身分提供者的建議方法是使用 自訂身分提供者解決方案

注意

建立使用 Lambda 做為身分提供者的 Transfer Family 伺服器之前,您必須建立 函數。如需 Lambda 函數的範例,請參閱Lambda 函數範例。或者,您可以部署使用其中一個 的 CloudFormation 堆疊Lambda 函數範本。此外,請確定您的 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 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:lambda:region:account-id:function:my-lambda-auth-function", "Condition": { "ArnLike": { "AWS:SourceArn": "arn:aws:transfer:region:account-id:server/server-id" } } } ] }"
注意

在上述政策範例中,將每個使用者輸入預留位置取代為您自己的資訊。

事件訊息結構

從 SFTP 伺服器傳送至自訂 IDP 授權方 Lambda 函數的事件訊息結構如下所示。

{ "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 值

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

Value 描述 必要

Role

指定控制使用者存取 Amazon S3 儲存貯體或 Amazon EFS 檔案系統的 IAM 角色的 Amazon Resource Name (ARN)。連接到此角色的政策決定了在將檔案傳入和傳出 Amazon S3 或 Amazon EFS 檔案系統時,您要為使用者提供的存取層級。IAM 角色也應包含信任關係,允許伺服器在處理您使用者的傳輸請求時,存取您的資源。

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

必要

PosixProfile

完整的 POSIX 身分,包括使用者 ID (Uid)、群組 ID (Gid) 和任何次要群組 IDs (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 路徑。

如果 的值HomeDirectoryTypeLOGICAL

HomeDirectory

使用者使用 用戶端登入伺服器的登陸目錄。格式取決於您的儲存後端:

  • 對於 Amazon S3: /bucket-name/user-home-directory

    範例:/amzn-s3-demo-bucket/users/john

  • 對於 Amazon EFS: /fs-12345/user-home-directory

    範例:/fs-faa1a123/users/john

重要

路徑中必須包含儲存貯體名稱或 Amazon EFS 檔案系統 ID。省略此資訊會導致檔案傳輸期間發生「找不到檔案」錯誤。

選用

注意

HomeDirectoryDetails 是 JSON 映射的字串表示法。這與 相反PosixProfile,它是實際的 JSON 映射物件,PublicKeys也是字串的 JSON 陣列。如需特定語言的詳細資訊,請參閱程式碼範例。

HomeDirectory 格式需求

使用 HomeDirectory 參數時,請務必包含完整的路徑格式:

  • 對於 Amazon S3 儲存:一律以 格式包含儲存貯體名稱 /bucket-name/path

  • 對於 Amazon EFS 儲存:一律以 格式包含檔案系統 ID /fs-12345/path

「找不到檔案」錯誤的常見原因是從HomeDirectory路徑中省略儲存貯體名稱或 EFS 檔案系統 ID。將/沒有儲存識別符的 HomeDirectory 設定為 將導致身分驗證成功,但檔案操作失敗。

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 format depends on your storage backend: // For S3: '/bucket-name/user-home-directory' (e.g., '/my-transfer-bucket/users/john') // For EFS: '/fs-12345/user-home-directory' (e.g., '/fs-faa1a123/users/john') HomeDirectory: '/my-transfer-bucket/users/example-user' // S3 example - replace with your bucket name // HomeDirectory: '/fs-faa1a123/users/example-user' // EFS example - uncomment for EFS }; // 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 format depends on your storage backend: # For S3: '/bucket-name/user-home-directory' (e.g., '/my-transfer-bucket/users/john') # For EFS: '/fs-12345/user-home-directory' (e.g., '/fs-faa1a123/users/john') 'HomeDirectory': '/my-transfer-bucket/users/example-user', # S3 example - replace with your bucket name # 'HomeDirectory': '/fs-faa1a123/users/example-user', # EFS example - uncomment for EFS '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
使用 CLI AWS 測試您的組態
  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": "" }

Lambda 函數範本

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

注意

您可以在範本中指定啟用 FIPS 的安全政策 AWS CloudFormation ,透過 建立啟用 FIPS 的 AWS Transfer Family 伺服器。可用的安全政策說明於 AWS Transfer Family 伺服器的安全政策

建立用於身分驗證的 AWS CloudFormation 堆疊
  1. 在 https://https://console.aws.amazon.com/cloudformation 開啟 AWS CloudFormation 主控台。

  2. 遵循AWS CloudFormation 《 使用者指南》中的 AWS CloudFormation 從現有範本部署堆疊的指示。

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

    • Classic (Amazon Cognito) 堆疊範本

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

    • AWS Secrets Manager 堆疊範本

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

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

    • Okta-mfa 堆疊範本:使用 AWS Lambda 與 AWS Transfer Family 伺服器整合 Okta 的基本範本,並以多重要素驗證做為自訂身分提供者。

    • Azure Active Directory 範本:此堆疊的詳細資訊如使用 AWS Transfer Family Azure Active Directory 和 向 驗證 AWS Lambda的部落格文章所述。

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

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