AWS Partner Network (APN) Blog

How Multi-Tenancy with AWS Transfer Family is a Cost-Effective Solution

By Ben Bridts, Principal AWS Technologist and AWS Ambassador – Cloudar

Cloudar-AWS-Partners-2022
Cloudar
Connect with Cloudar-1

AWS Transfer Family provides SFTP, FTPS, and/or FTP access to Amazon Simple Storage Service (Amazon S3) or Amazon Elastic File System (Amazon EFS).

It does that by providing a secure, highly available, and scalable server endpoint. You pay for the time this endpoint is enabled, and for data transfer (upload and download). This makes a multi-tenant setup where an endpoint is shared between different users (or applications), a more cost-efficient solution than having dedicated endpoints.

In this post, I will look at a few strategies to have a multi-tenant setup, while keeping security and flexibility in mind.

In my role as AWS Technologist at Cloudar, I work with our customers to build well-architected applications on Amazon Web Services (AWS). I have been designing and operating workloads for both startups and enterprises, which allows me to dive deep on a wide spectrum of services.

I have been an AWS Ambassador since 2018 and an AWS Hero since 2021.

Cloudar is an AWS Premier Tier Services Partner and Managed Cloud Services Provider (MSP). Cloudar holds AWS Competencies in Government, Migration, and DevOps consulting, and is a member of the AWS Well-Architected Partner Program.

Our Setup

AWS Transfer Family supports multiple (client-side) protocols and (cloud-side) storage backends. In this post, I will be using the SFTP endpoint with an Amazon S3 bucket.

This means we need the following components:

We can create these resources with AWS CloudFormation, so we can easily manage their lifecycle. Note that you need to empty an S3 bucket before you can delete it. A template with those three resources is available on GitHub.

Additionally, we need to create users that can connect to our endpoint. In our multi-tenant setup, we have a few requirements:

  • Users cannot access files from other users.
  • Users can either share an S3 bucket (if they’re part of the same application), or use completely separate buckets.

One property of a Transfer Family user is the IAM role that will be used to access S3. You can meet those requirements by creating an IAM role per user and have that only contain the exact rights the user needs.

This can be complex to manage, however, especially if the number of users would grow. Not only would we have to make sure to write a bespoke least-privilege policy, but the number of IAM roles would grow 1-to-1 with the number of Transfer Family users.

Luckily, there are two other ways to do this: logical directories and session policies.

Logical Directories

With logical directories, we’ll hide some of the underlying storage (bucket name and object prefix), from the SFTP user. This way, the Transfer Family server prevents them from accessing folders we didn’t “map” into their directory structure.

This AWS blog post explains how you can set this up when you’re using password-based authentication. We are going to use key-based authentication here, and use a CloudFormation template to make it repeatable. The template that creates a Transfer Family user can also be found on GitHub.

The user is created in our Transfer Family (SFTP) server with a given username and public key. That user uses the IAM role that can access the whole bucket, but because we define a logical home directory, mapped to s3://${BucketName}/home/${UserName} they are prevented from doing anything outside of that path.

We can already see advantages here:

  • We can easily reuse this template multiple times, to have multiple users.
  • We are reusing the same IAM role for each user. If we wanted to use multiple buckets, we could create a role per bucket (if users always access exactly one bucket), or expand the permissions of the role.
  • Users don’t see the underlying paths in S3, so there is no way for them to accidently try to read/write to an object they don’t have access to.

For a lot of use cases, this will be the preferred solution. However, for more advanced IAM configurations we can also rely on session policies

Session Polices

With session policies, we can achieve the same result as with logical directories, but we can also have more fine-grained control over the access rights.

In contrast to logical directories, we rely on an IAM feature to prevent unauthorized access. This means we can see what’s happening in AWS CloudTrail and not hide the underlying bucket and object names.

A session policy is an IAM policy that Transfer Family attaches to the (assume-role) session that gets created when a user connects to the endpoint. This way, access rights are limited to the intersection of the policy of the role and session policy, meaning the user can only execute the actions that are allowed by both.

AWS Transfer Family allows us to use transfer-specific variables in the session policy, so we don’t have to hard-code any names. We get the same reusability as with logical directories.

Let’s look at a few examples of these policies.

Per User Folders

This policy achieves the same result as the logical directories example. In this case, it’s a bit more complex to write, but from an audit-perspective it’s nice to see what’s happening in CloudTrail.

Note that this policy assumes the home folder does not end in a /, as we add those in the JSON document.

{
  "Version": "2012-10-17",
  "Statement": [
      {
          "Sid": "AllowListingOfUserFolder",
          "Action": ["s3:ListBucket"],
          "Effect": "Allow",
          "Resource": ["arn:aws:s3:::${transfer:HomeBucket}"],
          "Condition": {
              "StringLike": {
                  "s3:prefix": [
                      "${transfer:HomeFolder}/*",
                      "${transfer:HomeFolder}"
                  ]
              }
          }
      },
      {
          "Sid": "HomeDirObjectAccess",
          "Effect": "Allow",
          "Action": [
              "s3:PutObject",
              "s3:GetObject",
              "s3:DeleteObject",
              "s3:DeleteObjectVersion",
              "s3:GetObjectVersion",
              "s3:GetObjectACL",
              "s3:PutObjectACL"
          ],
          "Resource": "arn:aws:s3:::${transfer:HomeDirectory}/*"
      }
    ]
  }

If we take a look in CloudTrail, we can see the ${transfer:*} variables are replaced with values that are related to the user and their home folder. We can see the actual session policy by searching for the IAM role we’re using. We can do this in the AWS Management Console, or by using the AWS Command Line Interface (CLI).

# assuming the assume-role call is the most recent one
~$ aws cloudtrail lookup-events \
    --lookup-attributes AttributeKey=ResourceName,AttributeValue=arn:aws:iam::123456789012:role/server-TransferSharedRole-AABBCCDDEEFF \
    --output json \
  | jq ".Events[0].CloudTrailEvent | fromjson | .requestParameters.policy | fromjson"

For a user with the home directory /home/ben, this would return:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowListingOfUserFolder",
      "Action": ["s3:ListBucket"],
      "Effect": "Allow",
      "Resource": ["arn:aws:s3:::server-bucket-aabbccddee123"],
      "Condition": {
        "StringLike": {
          "s3:prefix": [
            "home/ben/*",
            "home/ben"
          ]
        }
      }
    },
    {
      "Sid": "HomeDirObjectAccess",
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject",
        "s3:DeleteObjectVersion",
        "s3:GetObjectVersion",
        "s3:GetObjectACL",
        "s3:PutObjectACL"
      ],
      "Resource": "arn:aws:s3:::server-bucket-aabbccddee123/home/ben/*"
    }
  ]
}

Looking in CloudTrail is also a good way to debug your policies.

Write Only

We could also use session policies to create a “drop off” folder, where users can only write files but cannot read them. This can be useful for writing logs to a central location, for example.

Note there are other ways to achieve this—you could use a similar policy in the (shared) IAM role, or if reads are permissible you could take advantage of the Object Lock feature of S3.

This policy won’t hide which objects are present because list operations are still allowed. It does prevent overwriting existing files, because AWS Transfer Family will try do a (denied) HeadObject, before trying to overwrite a file.

To be sure files are never overwritten, it’s better to enable S3 Object Lock, instead of relying on this implementation detail.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowListingOfUserFolder",
      "Action": ["s3:ListBucket"],
      "Effect": "Allow",
      "Resource": ["arn:aws:s3:::${transfer:HomeBucket}"],
      "Condition": {
        "StringLike": {
          "s3:prefix": [
            "${transfer:HomeFolder}/*",
            "${transfer:HomeFolder}"
          ]
        }
      }
    },
    {
      "Sid": "HomeDirFolderAccess",
      "Effect": "Allow",
      "Action": ["s3:GetObject"],
      "Resource": [
        "arn:aws:s3:::${transfer:HomeDirectory}/",
        "arn:aws:s3:::${transfer:HomeDirectory}/*/"
      ]
    },
    {
      "Sid": "HomeDirObjectAccess",
      "Effect": "Allow",
      "Action": ["s3:PutObject"],
      "Resource": "arn:aws:s3:::${transfer:HomeDirectory}/*"
    }
  ]
}

Conclusion

In this post, we looked at two different ways to allow multiple users to use the same AWS Transfer Family server endpoint. Logical directories are a bit simpler, and session policies are more powerful.

Both can be used to have multiple storage buckets, by editing the IAM role and/or session policy. Session polices will show the bucket names to the user, whereas logical directories will hide that.

Either solution allows you to use the AWS Transfer server in a cost-effective way without compromising security.

The content and opinions in this blog are those of the third-party author and AWS is not responsible for the content or accuracy of this post.

.
Cloudar-APN-Blog-Connect-1
.


Cloudar – AWS Partner Spotlight

Cloudar is an AWS Premier Tier Services Partner and MSP. Cloudar holds AWS Competencies in Government, Migration, and DevOps consulting, and is a member of the AWS Well-Architected Partner Program.

Contact Cloudar | Partner Overview