

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

# 使用 AWS Lambda 整合您的身分提供者
<a name="custom-lambda-idp"></a>

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

對於大多數使用案例，設定自訂身分提供者的建議方法是使用 [自訂身分提供者解決方案](custom-idp-toolkit.md)。

**注意**  
建立使用 Lambda 做為身分提供者的 Transfer Family 伺服器之前，您必須建立 函數。如需 Lambda 函數的範例，請參閱[Lambda 函數範例](#lambda-auth-examples)。或者，您可以部署使用其中一個 的 CloudFormation 堆疊[Lambda 函數範本](#lambda-idp-templates)。此外，請確定您的 Lambda 函數使用信任 Transfer Family 的資源型政策。如需政策範例，請參閱 [Lambda 資源型政策](#lambda-resource-policy)。

1. 開啟 [AWS Transfer Family 主控台](https://console.aws.amazon.com/transfer/)。

1. 選擇**建立伺服器**以開啟**建立伺服器**頁面。針對**選擇身分提供者**，選擇**自訂身分提供者**，如下列螢幕擷取畫面所示。  
![\[選擇已選取自訂身分提供者的身分提供者主控台區段。也會選取預設值，也就是使用者可以使用其密碼或金鑰進行身分驗證。\]](http://docs.aws.amazon.com/zh_tw/transfer/latest/userguide/images/custom-lambda-console.png)
**注意**  
只有在您將 SFTP 啟用為 Transfer Family 伺服器的其中一個通訊協定時，才可選擇身分驗證方法。

1. 請確定已選取預設值，**即 AWS Lambda 使用 來連接您的身分提供者**。

1. 針對**AWS Lambda 函數**，選擇 Lambda 函數的名稱。

1. 填寫剩餘的方塊，然後選擇**建立伺服器**。如需建立伺服器之其餘步驟的詳細資訊，請參閱 [設定 SFTP、FTPS 或 FTP 伺服器端點](tf-server-endpoint.md)。

## Lambda 資源型政策
<a name="lambda-resource-policy"></a>

您必須擁有參考 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:123456789012:function:my-lambda-auth-function\",
  \"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:transfer:region:123456789012:server/server-id\"}}}
]}"
```

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

## 事件訊息結構
<a name="event-message-structure"></a>

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

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

其中 `username`和 `password`是傳送至伺服器的登入憑證值。

例如，您可以輸入下列命令來連線：

```
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 函數
<a name="authentication-lambda-examples"></a>

若要實作不同的身分驗證策略，請編輯 Lambda 函數。為了協助您滿足應用程式的需求，您可以部署 CloudFormation 堆疊。如需 Lambda 的詳細資訊，請參閱[AWS Lambda 開發人員指南](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html)或使用 [ Node.js 建置 Lambda 函數。](https://docs.aws.amazon.com/lambda/latest/dg/lambda-nodejs.html)

**Topics**
+ [有效的 Lambda 值](#lambda-valid-values)
+ [Lambda 函數範例](#lambda-auth-examples)
+ [測試您的組態](#authentication-test-configuration)
+ [Lambda 函數範本](#lambda-idp-templates)

### 有效的 Lambda 值
<a name="lambda-valid-values"></a>

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


|  Value  |  Description  |  必要  | 
| --- | --- | --- | 
|  `Role`  |  指定控制使用者存取 Amazon S3 儲存貯體或 Amazon EFS 檔案系統的 IAM 角色的 Amazon Resource Name (ARN)。連接到此角色的政策會決定在將檔案傳入和傳出 Amazon S3 或 Amazon EFS 檔案系統時，您要為使用者提供的存取層級。IAM 角色也應包含信任關係，允許伺服器在處理您使用者的傳輸請求時，存取您的資源。 如需建立信任關係的詳細資訊，請參閱 [建立信任關係](requirements-roles.md#establish-trust-transfer)。  |  必要  | 
|  `PosixProfile`  |  完整的 POSIX 身分，包括使用者 ID (`Uid`)、群組 ID (`Gid`) 和控制使用者存取 Amazon EFS 檔案系統的任何次要群組 IDs (`SecondaryGids`)。對檔案系統中的檔案和目錄設定的 POSIX 許可，會決定使用者在 Amazon EFS 檔案系統中傳入和傳出檔案時所取得的存取等級。  |  Amazon EFS 備份儲存的必要項目  | 
|  `PublicKeys`  |  對此使用者有效的 SSH 公有金鑰值清單。空白清單表示這不是有效的登入。密碼身分驗證期間不得傳回 。  |  選用  | 
|  `Policy`  |  您使用者的工作階段政策，讓您可以在多個使用者之間使用相同的 IAM 角色。此政策會將使用者存取的範圍縮小到他們 Amazon S3 儲存貯體的部分。如需搭配自訂身分提供者使用工作階段政策的詳細資訊，請參閱本主題中的工作階段政策範例。  |  選用  | 
|  `HomeDirectoryType`  |  使用者登入伺服器時，您希望的使用者主目錄之登陸目錄 (資料夾) 類型。 [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/transfer/latest/userguide/custom-lambda-idp.html)  |  選用  | 
|  `HomeDirectoryDetails`  |  邏輯目錄映射，指定哪些 Amazon S3 或 Amazon EFS 路徑和金鑰應該可供您的使用者查看，以及如何讓它們可見。您必須指定 `Entry`和 `Target`對，其中 `Entry`顯示如何顯示路徑，而 `Target`是實際的 Amazon S3 或 Amazon EFS 路徑。  |  如果 的值`HomeDirectoryType`為 ，則為必要 `LOGICAL`  | 
|  `HomeDirectory`  |  使用者使用 用戶端登入伺服器的登陸目錄。格式取決於您的儲存後端： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/transfer/latest/userguide/custom-lambda-idp.html)  路徑中必須包含儲存貯體名稱或 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 函數範例
<a name="lambda-auth-examples"></a>

本節提供一些範例 Lambda 函數，包括 NodeJS 和 Python。

**注意**  
在這些範例中，使用者、角色、POSIX 設定檔、密碼和主目錄詳細資訊都是範例，必須以您的實際值取代。

------
#### [ Logical home directory, NodeJS ]

下列 NodeJS 範例函數提供具有[邏輯主目錄](https://docs.aws.amazon.com/transfer/latest/userguide/logical-dir-mappings.html)之使用者的詳細資訊。

```
// 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 範例函數提供具有[邏輯主目錄](https://docs.aws.amazon.com/transfer/latest/userguide/logical-dir-mappings.html)之使用者的詳細資訊。

```
# 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
```

------

### 測試您的組態
<a name="authentication-test-configuration"></a>

建立自訂身分提供者之後，您應該測試您的組態。

------
#### [ Console ]

**使用 AWS Transfer Family 主控台測試您的組態**

1. 開啟 [AWS Transfer Family 主控台](https://console.aws.amazon.com/transfer/)。

1. 在**伺服器**頁面上，選擇您的新伺服器，選擇**動作**，然後選擇**測試**。

1. 輸入****您在部署 CloudFormation 堆疊時設定的**使用者名稱和密碼**文字。如果您保留預設選項，使用者名稱為 `myuser`，密碼為 `MySuperSecretPassword`。

1. 如果您在部署 CloudFormation 堆疊時設定來源 IP，請選擇**伺服器通訊協定**並輸入其 **IP** 地址。

------
#### [ CLI ]

**使用 CLI AWS 測試您的組態**

1. 執行 [test-identity-provider](https://docs.aws.amazon.com/cli/latest/reference/transfer/test-identity-provider.html) 命令。將每個 取代`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
   ```

1. 輸入伺服器 ID。

1. 輸入您在部署 CloudFormation 堆疊時設定的使用者名稱和密碼。如果您保留預設選項，使用者名稱為 `myuser`，密碼為 `MySuperSecretPassword`。

1. 如果您在部署 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 函數範本
<a name="lambda-idp-templates"></a>

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

**注意**  
您可以在範本中指定啟用 FIPS 的安全政策 CloudFormation ，透過 建立啟用 FIPS 的 AWS Transfer Family 伺服器。可用的安全政策說明於 [AWS Transfer Family 伺服器的安全政策](security-policies.md) 

**建立用於身分驗證的 CloudFormation 堆疊**

1. 在 https：//[https://console.aws.amazon.com/cloudformation](https://console.aws.amazon.com/cloudformation/) 開啟 CloudFormation 主控台。

1. 遵循*AWS CloudFormation 《 使用者指南*》中選取 CloudFormation 堆疊範本中從現有範本部署堆疊的指示。 [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-using-console-create-stack-template.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-using-console-create-stack-template.html)

1. 使用下列其中一個範本來建立 Lambda 函數，以用於 Transfer Family 中的身分驗證。
   + [Classic (Amazon Cognito) 堆疊範本](https://s3.amazonaws.com/aws-transfer-resources/custom-idp-templates/aws-transfer-custom-idp-basic-lambda-cognito-s3.template.yml)

     用於建立 AWS Lambda 以用作 中自訂身分提供者的基本範本 AWS Transfer Family。它會對 Amazon Cognito 進行身分驗證，如果使用以公有金鑰為基礎的身分驗證，則會從 Amazon S3 儲存貯體傳回公有金鑰。部署之後，您可以修改 Lambda 函數程式碼，以執行不同的動作。
   + [AWS Secrets Manager 堆疊範本](https://s3.amazonaws.com/aws-transfer-resources/custom-idp-templates/aws-transfer-custom-idp-secrets-manager-lambda.template.yml)

     使用 AWS Lambda 與 AWS Transfer Family 伺服器整合 Secrets Manager 做為身分提供者的基本範本。它會對 格式 AWS Secrets Manager 中的項目進行身分驗證`aws/transfer/server-id/username`。此外，秘密必須保留傳回 Transfer Family 的所有使用者屬性的鍵/值對。部署之後，您可以修改 Lambda 函數程式碼，以執行不同的動作。
   + [Okta 堆疊範本](https://s3.amazonaws.com/aws-transfer-resources/custom-idp-templates/aws-transfer-custom-idp-okta-lambda.template.yml)：使用 AWS Lambda 與 AWS Transfer Family 伺服器整合 Okta 做為自訂身分提供者的基本範本。
   + [Okta-mfa 堆疊範本](https://s3.amazonaws.com/aws-transfer-resources/custom-idp-templates/aws-transfer-custom-idp-okta-mfa-lambda.template.yml)：使用 AWS Lambda 與 AWS Transfer Family 伺服器整合 Okta 的基本範本，以及多重要素驗證，做為自訂身分提供者。
   + [ Azure Active Directory 範本](https://s3.amazonaws.com/aws-transfer-resources/custom-idp-templates/aws-transfer-custom-idp-basic-lambda-azure-ad.template.yml)：此堆疊的詳細資訊如[AWS Transfer Family 使用 Azure Active Directory 和 驗證 AWS Lambda](https://aws.amazon.com/blogs/storage/authenticating-to-aws-transfer-family-with-azure-active-directory-and-aws-lambda/)的部落格文章所述。

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

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