Site icon Mr. 沙先生

AWS Transfer Family with SFTP Service by AWS CDK

前陣子同事在詢問怎麼讓外部使用者有授權的訪問 Amazon S3,當下第一時間想到的就是 AWS Transfer family 這個 Service,Transfer family 支援 FTP、FTPS、SFTP 三種協定,以安全性來說都建議至少採用 FTPS (SSL) 或 SFTP (SSH) 加密協定,在這篇也是紀錄一下如何透過 AWS CDK 建立一套 Transfer family。

這篇所有的 CDK 程式碼都放在 Github:shazi7804/cdk-samples 有興趣的可以看完整版。

Transfer family Server

首先必須要有一個 Amazon S3 bucket 存放使用者上傳下載的位置:

const bucket = new s3.Bucket(this, 'BucketOfHome', {
    bucketName: 'transfer-family-sftp-' + this.region + '-' + this.account,
    encryption: s3.BucketEncryption.S3_MANAGED,
    versioned: true
});

建議 encryption 跟 versioned 都打開,防止意外刪除或覆蓋造成的杯具 …

再來是 Transfer Family 所使用的 Service-Role

const role = new iam.Role(this, 'AWSTransferFamilyServiceRole', {
    roleName: 'AWSTransferLoggingAccess',
    assumedBy: new iam.ServicePrincipal('transfer.amazonaws.com'),
})
        role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSTransferLoggingAccess'))

AWSTransferLoggingAccess 這個 Managed Policy 提供了 Transfer family 有權限將使用者訪問紀錄寫到 Cloudwatch logs,而預設 Cloudwatch logs 預設保存 Never expire 如果不需要長期保存記得將時間設定到適當的時間。

每一個 User 也必須定義 IAM Role 的訪問權限

const userRole = new iam.Role(this, 'AWSTransferUsersAccessRole', {
    roleName: 'AWSTransferUsersAccess',
    assumedBy: new iam.ServicePrincipal("transfer.amazonaws.com"),
});

userRole.addToPolicy(
    new iam.PolicyStatement({
        effect: iam.Effect.ALLOW,
        actions: ['s3:ListBucket'],
        resources: [bucket.bucketArn],
    })
);

userRole.addToPolicy(
    new iam.PolicyStatement({
        effect: iam.Effect.ALLOW,
        actions: [
            's3:GetObject',
            's3:GetObjectAcl',
            's3:GetObjectVersion',
            's3:PutObject',
            's3:PutObjectACL',
            's3:DeleteObject',
            's3:DeleteObjectVersion'
        ],
        resources: [`${bucket.bucketArn}/*`],
    })
);

產生出來的 IAM Policy 會長的像這樣:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::${BUCKET}",
            "Effect": "Allow"
        },
        {
            "Action": [
                "s3:GetObject",
                "s3:GetObjectAcl",
                "s3:GetObjectVersion",
                "s3:PutObject",
                "s3:PutObjectACL",
                "s3:DeleteObject",
                "s3:DeleteObjectVersion"
            ],
            "Resource": "arn:aws:s3:::${BUCKET}/*",
            "Effect": "Allow"
        }
    ]
}

最後就是 Transfer family Server,由於作者在寫 AWS CDK 的時候 Transfer family 還沒有 Layer 2 resource,所以會用 Cfn 撰寫:

const server = new tf.CfnServer(this, 'TransferFamilyServer', {
    protocols: ['SFTP'],
    identityProviderType: 'SERVICE_MANAGED',
    loggingRole: role.roleArn
});

指定 SFTP Protocol 和驗證方式後,也是很簡單的幾行就搞定。

cdk deploy 後會建立幾個 resources:

接著畫面跳轉到 AWS Transfer family 的 Management Console 就可以找到剛剛建立的 Transfer server

當狀態變成 Online 後就可以 Add user 訪問權限啦:

Role 記得選擇 AWSTransferUsersAccess,而 Home directory 也要選到與 Role Policy 相同的 S3 bucket,否則即便登入也沒辦法訪問 Home 目錄。

Restricted 打勾時使用者會被鎖在自己的家目錄下而無法訪問其他層級的目錄,這是在 FTP 上很常見的權限處理方式。

由於 Transfer family SFTP 的驗證是採用 SSH public/private keys 進行驗證,如果是 macOS/Linux 可以用 ssh-keygen 來產生 ssh key (Windows 也有 OpenSSH 或是 puttygen 可以用)

$ ssh-keygen -P "" -m PEM -f shazi7804

拿到兩隻檔案分別是 shazi7804 (private key) 和 shazi7804.pub (public key),將 public key 填上 Add user 欄位內。

Transfer family users

在使用者端可以選擇有支援 SFTP Client 的 FileZilla 並且了解以下資訊:

建立站台後就可以成功連上 Transfer family server 啦!!

而在 Cloudwatch logs 上每一個 user 都會有獨立的 Log stream 紀錄 login, read, write 等資料:

shazi7804.2291717934357386 CONNECTED SourceIP=36.231.106.61 User=shazi7804 HomeDir=LOGICAL Client=SSH-2.0-FileZilla_3.54.1 Role=arn:aws:iam::0123456789:role/AWSTransferUsersAccess Kex=curve25519-sha256@libssh.org Ciphers=aes256-gcm@openssh.com,aes256-gcm@openssh.com
shazi7804.2291717934357386 OPEN Path=/transfer-family-sftp-us-east-1-0123456789/shazi7804/.zshrc Mode=CREATE|TRUNCATE|WRITE
shazi7804.2291717934357386 CLOSE Path=/transfer-family-sftp-us-east-1-0123456789/shazi7804/.zshrc BytesIn=3208
shazi7804.2291717934357386 OPEN Path=/transfer-family-sftp-us-east-1-0123456789/shazi7804/.zshrc Mode=READ
shazi7804.2291717934357386 CLOSE Path=/transfer-family-sftp-us-east-1-0123456789/shazi7804/.zshrc BytesOut=3208

Exit mobile version