AWS Transfer for SFTP
User Guide

Working with Identity Providers

By default, a new AWS Transfer for SFTP server uses a directory managed by AWS SFTP for key-based authentication with Secure Shell (SSH). To integrate your existing identity provider into AWS SFTP, provide a RESTful interface with a single Amazon API Gateway method. AWS SFTP calls this method to authenticate your SFTP users.

This single method authenticates and authorizes your users for access to Amazon S3. After you configure the method, attach it to your SFTP server when you create a new server. You can create a new server using the AWS SFTP Management Console or CreateServer API operation.

For more information about working with API Gateway, see the API Gateway Developer Guide.

Authenticating Using Custom Identity Providers

To create a custom identity provider for AWS SFTP, use Amazon API Gateway, which provides a highly secure way for you to create and provide APIs. With API Gateway, you can create an HTTPS endpoint so that all incoming API calls are transmitted with greater security. For more details on the API Gateway service, see the API Gateway Developer Guide.

API Gateway offers an authentication method named AWS_IAM, which gives you the same authentication based on AWS Identity and Access Management (IAM) that AWS uses internally. If you enable authentication with AWS_IAM, only callers with explicit permissions to call an API can reach that API's API Gateway method.

To use API Gateway as a custom identity provider for AWS SFTP, enable AWS Identity and Access Management (IAM) for your API Gateway gateway. As part of this process, you provide an IAM role with permissions for AWS SFTP to use your gateway.

To use API Gateway for custom authentication with AWS SFTP

  1. Create an AWS CloudFormation stack from the sample template found at the following Amazon S3 URL:

    https://s3.amazonaws.com/aws-transfer-resources/custom-idp-templates/aws-transfer-custom-idp-basic-apig.template.yml

    This template generates a basic API Gateway and AWS Lambda function. For instructions on deploying an AWS CloudFormation stack from an existing template, see Selecting a Stack Template in the AWS CloudFormation User Guide.

    Deploying this stack is the easiest way to integrate a custom identity provider into the AWS SFTP workflow. The stack uses the AWS Lambda function to support a gateway based on API Gateway. You can then use this gateway as a custom identity provider in AWS SFTP. By default, the Lambda function authenticates a single user called myuser with a password of MySuperSecretPassword. You can edit these credentials or update the Lambda function code to do something different after deployment.

    After the stack has been deployed, you can view details about it on the Outputs tab. These details include the stack Amazon Resource Name (ARN), the ARN of the IAM role that the stack created, and the URL for your new gateway.

    Note

    If you are using the custom identity provider option to enable password based authentication for your users, and you enable API-Gateway's request and response logging, it will log your users passwords to your Amazon CloudWatch Logs. We don't recommend using this log in your production environment. For more information, see Set Up CloudWatch API Logging in API Gateway in the API Gateway Developer Guide.

  2. Check the API Gateway configuration for your SFTP server.

    To do this, open the API Gateway console at https://console.aws.amazon.com/apigateway and view the Transfer Custom Identity Provider basic template API that the AWS CloudFormation template generated. The following screenshot shows the correct method configuration.

    The following screenshot shows the complete API configuration. In this example, the method is backed by a Lambda function, but many other integration types are also possible. At this point, your gateway is ready to be deployed.

  3. For Actions, choose Deploy API. For Deployment stage, choose prod, and then choose Deploy.

    After the API is successfully deployed, view its performance in the Stage Editor section, as shown following. Note the Invoke URL value that appears at the top of the screen. You use this URL when you create your AWS SFTP server.

  4. Open the AWS SFTP console at https://console.aws.amazon.com/transfer/ and choose Create server. In the configuration screen that appears, choose Custom for Identity provider, as shown following.

  5. For Custom provider, enter the URL of the API Gateway endpoint that you just created. For Invocation role, choose the IAM role that was created by the AWS CloudFormation template. This role allows AWS SFTP to invoke your gateway and has the following format: CloudFormation-stack-name-TransferIdentityProviderRole-ABC123DEF456GHI.

    Fill in the remaining boxes and choose Create server.

  6. Test your configuration in the console or AWS CLI:

    • In the AWS SFTP console, choose your new server for Servers, and then for Actions choose Test.

      Enter the user name and password that you set when you deployed the AWS CloudFormation stack. If you kept the default options, the user name is myuser and the password is MySuperSecretPassword.

      If user authentication succeeds, Test return a status code 200 HTML response and a JSON object containing the details of the user's roles and permissions, as shown following.

    • From the AWS CLI, run the test-identity-provider command, substituting your own server, user name, and password. Enter the user name and password that you set when you deployed the AWS CloudFormation stack. If you kept the default options, the user name is myuser and the password is MySuperSecretPassword.

      aws transfer test-identity-provider --server-id s-1234abcd5678efgh --user-name myuser --password MySuperSecretPassword

      If user authentication succeeds, the command returns a status code 200 HTML response and a JSON object containing the details of the user's roles and permissions.

Implementing Your API Gateway Method

To create a custom identity provider for AWS SFTP, your API Gateway gateway must implement a single method that has a resource path of /servers/serverId/users/username/config. The serverId and username values come from the RESTful resource path.

If AWS SFTP attempts password authentication for your user, the service supplies a Password: header field. In the absence of a Password: header, AWS SFTP attempts public key authentication to authenticate your user.

This method should always return HTTP status 200. Any other HTTP status code means an error accessing the API.

The response body is a JSON document of the following form.

{ "Role": "IAM role with configured S3 permissions", "PublicKeys": [ "ssh-rsa public-key1", "ssh-rsa public-key2" ], "Policy": "STS Assume role scope down policy", "HomeDirectory": "User's home directory" }

The Role field shows that successful authentication occurred. When doing password authentication (when you supply a Password: header), you don't need to provide SSH public keys. Also, the Policy and HomeDirectory fields are optional. When no home directory is provided, AWS SFTP defaults to the parent Amazon S3 bucket. If a user can't be authenticated, for example if the password is incorrect, your method should return a response without Role set. An example is an empty JSON object.

Include user policies in the Lambda function in JSON format. For more information about configuring user policies in AWS SFTP, see Editing User Configuration.

Example Lambda Functions for Authentication

To implement different authentication strategies, edit the Lambda function that your gateway uses. To help you meet your application's needs, you can find example Lambda functions in Python following. For more information on Lambda, see the AWS Lambda Developer Guide.

Default Lambda Function

The following Lambda function takes your user name, password, IAM access role, and public SSH key. It authenticates them, and, if successful, returns the relevant IAM access role and policy. This Lambda function is the same one is used in the example AWS CloudFormation template following.

The commented-out section of the following example demonstrates how to use logical directories in your AWS SFTP architecture. By using logical directories, you can delegate access to specific pathways within your AWS setup on a user-by-user basis. For example, you can delegate access to different sections of an Amazon S3 bucket. To use logical directories, set your function's HomeDirectoryType parameter to LOGICAL. Also, in the HomeDirectoryDetails parameter, provide a list of Entry and Target pairs.

// GetUserConfig Lambda exports.handler = (event, context, callback) => { // Print out the event parameters for debugging console.log("Event:", JSON.stringify(event)); var response; // Check if the user name presented for authentication is correct. This doesn't check the value of the serverId, only that it is provided. if (event.serverId !== "" && event.username == 'myuser') { response = { Role: 'arn:aws:iam::000000000000:role/MyUserS3AccessRole', // The user is authenticated only if the Role field is not blank Policy: `{ "Version": "2012-10-17", "Statement": [ { "Sid": "ReadAndListAllBuckets", "Effect": "Allow", "Action": [ "s3:ListAllMyBuckets", "s3:GetBucketLocation", "s3:ListBucket", "s3:GetObjectVersion", "s3:GetObjectVersion" ], "Resource": "*" } ] }`, // Not required HomeDirectory: '/' // Not required, defaults to '/' // Use the following HomeDirectory section instead if you want to use a logical directory architecture. // { // 'HomeDirectoryDetails': '[{"Entry": "/", "Target": "/<my-bucket-name>/<real-path-in-my-bucket>/ "}]' // , 'HomeDirectoryType': 'LOGICAL' // , 'Role': 'arn:aws:iam::XXXXX:role/RoleThatProvidesAccessToS3Buckets // } }; // Check if password is provided if (event.password == "") { // If no password provided, return the user's SSH public key response['PublicKeys'] = [ "ssh-rsa myrsapubkey" ]; // Check if password is correct } else if (event.password !== 'MySuperSecretPassword') { // 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); };

Lambda Function for Use with AWS Secrets Manager

To use AWS Secrets Manager as your identity provider, work with the following Lambda function. It queries the Secrets Manager service with your credentials and, if successful, returns a designated secret. For more information on Secrets Manager, see the AWS Secrets Manager User Guide.

To download a sample AWS CloudFormation template that uses this Lambda function, go to the Amazon S3 bucket provided by AWS SFTP.

import os import json import boto3 import base64 from botocore.exceptions import ClientError def lambda_handler(event, context): # Begin constructing response response_data = {} if 'username' not in event or 'serverId' not in event: return response_data # We recommend to verify server ID against some value; this template does not verify server ID input_server_id = event['serverId'] input_username = event['username'] print("Username: {}, ServerId: {}".format(input_username, input_server_id)) if 'password' in event: input_password = event['password'] else: print("No password, checking for SSH public key") input_password = '' # Look up user's secret, which can contain the password or SSH public keys secrets_manager_response = get_secret("SFTP/" + input_username) if secrets_manager_response != None: user_info_secret_dict = json.loads(secrets_manager_response) else: # There was an error when calling Secrets Manager; most likely the username doesn't exist return response_data # If we have an incoming password then we are performing password auth and need to validate it agains the Secrets entry if input_password != '': # Password Auth Flow - Check if we have a Password Key/Value pair in our Secrets Entry if 'Password' in user_info_secret_dict: response_password = user_info_secret_dict['Password'] else: print("Unable to authenticate user - No field match in Secret for password") return response_data # Password Auth Flow - Check for password mismatch if response_password != input_password: print("Unable to authenticate user - Incoming password does not match stored") return response_data else: # SSH Public Key Auth Flow - The incoming password was empty so we are trying SSH auth and need to return the public key data if we have it if 'PublicKey' in user_info_secret_dict: response_data['PublicKeys'] = [ user_info_secret_dict['PublicKey'] ] else: print("Unable to authenticate user - No public keys found") return response_data # If we've got this far then we've either authenticated the user by password or we're using SSH public key auth and # we've begun constructing the data response. Check for each key value pair. # These are required so set to empty string if missing if 'Role' in user_info_secret_dict: response_data['Role'] = user_info_secret_dict['Role'] else: print("No field match for role - Set empty string in response") rhttps://us-west-2.console.aws.amazon.com/lambda/home?region=us-west-2esponse_data['Role'] = '' # These are optional so ignore if not present if 'Policy' in user_info_secret_dict: response_data['Policy'] = user_info_secret_dict['Policy'] if 'HomeDirectory' in user_info_secret_dict: response_data['HomeDirectory'] = user_info_secret_dict['HomeDirectory'] return response_data # This function calls out to Secrets Manager to lookup user and returns None if there is an error. def get_secret(secret_name): # Create a Secrets Manager client client = boto3.session.Session().client( service_name='secretsmanager', region_name=os.environ['AWS_REGION'] ) try: get_secret_value_response = client.get_secret_value( SecretId=secret_name ) # Decrypts secret using the associated KMS CMK. # Depending on whether the secret is a string or binary, one of these fields will be populated. if 'SecretString' in get_secret_value_response: secret = get_secret_value_response['SecretString'] return secret else: decoded_binary_secret = base64.b64decode(get_secret_value_response['SecretBinary']) return decoded_binary_secret except ClientError as e: print('Error Talking to SecretsManager: ' + e.response['Error']['Code']) return None

Identity Provider Examples

For examples of how to integrate different identity providers with AWS SFTP, see the following posts on the AWS Blog: