こしあん
2025-01-21

AWS Transfer FamilyでSFTPサーバーを立ててみた


3{icon} {views}


AWS Transfer Familyを利用して、S3と連携できるマネージドSFTPサーバーをTerraformで簡単に構築する手順を紹介する。料金やWinSCP接続などの注意点も含め、一連の設計・運用のポイントを解説する。

はじめに

  • SAP勉強してたら結構出てきた、AWS Transfer Familyというサービス
  • SFTP、FTPS、FTP対応で
  • SFTPサーバーのエンドポイントだと、ほぼSFTPサーバーが立ってるイメージで、WinSCPみたいなSFTPアプリケーションから接続できる。
  • ただ、エンドポイントにアップロードすると、S3やEFSに勝手に同期してくれるというのが大きな売りで、AWSのワークロードにつなげやすいという特徴を持つ

価格

Transfer Familyの値段はちょっとお高め。東京リージョンの場合

  • サーバーエンドポイントで各プロトコルが有効になっている時間 プロトコルあたり USD 0.30/時間
  • データアップロード 転送されたギガバイト (GB) あたり USD 0.04
  • データダウンロード USD 0.04/転送されたギガバイト (GB)

エンドポイントが24時間30日立ってるとすると、エンドポイントだけで216ドル。SFTPへのファイル送受信がオンプレで結構大事なワークロードになっていて、それをAWSにシームレスに移行したいという場合ぐらいしか刺さらなそう。

注意点

  • EC2のSFTPサーバー
    • SFTPサーバー立てるだけなら、SSH対応のAmazon LinuxのEC2でもできる
    • デフォルトのAmazon Linux 2023でキーペアを設定すれば、ec2-userでWinSCPから接続すればそのままSFTP接続できる
    • ただアンマネージド(EC2)のSFTPだとS3やEFSの同期はつかない
    • 特にユーザーデータで初期化する場合だと、ユーザー管理や権限管理が結構面倒くさいec2-user以外でやろうとすると、非常にハマって挫折した
  • Transfer Familyについて
    • Transfer Family側には、オンプレのSFTPの取り込み機能はない。あくまでマネージドSFTPサーバー
    • オンプレからTransfer Familyでデータ連携する場合は、オンプレ側からSFTPの送信がいる

作るもの

  • Transfer Familyでもデフォルトのエンドポイント(ドメイン)は作成されるが、IPを固定したいため、エンドポイントにElastic IPを紐づける

SSHキーの準備

カレントディレクトリのssh_keys以下にSSHキーを準備しておく。これはSFTPに接続する際に必要。

mkdir -p ssh_keys
cd ssh_keys
ssh-keygen -t rsa -b 4096 -C "<input your text>" -f ./id_rsa

Terraformのコード

Transfer Familyをデプロイするためのコード。

Transfer Familyのセキュリティグループのインバウンドを絞ることでIP制限を実現し、Transfer FamilyのIPはElastic IPで固定する。

# S3バケットの作成
resource "aws_s3_bucket" "transfer_bucket" {
  bucket        = var.s3_bucket_name
  force_destroy = true
}

#----------------------------------------------------------------
# Transfer Family ログ出力用 IAM ロール
#    (CloudWatch Logs 等を利用したい場合)
#----------------------------------------------------------------
data "aws_iam_policy_document" "transfer_trust_policy" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["transfer.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "transfer_logging_role" {
  name               = "transfer-logging-role"
  assume_role_policy = data.aws_iam_policy_document.transfer_trust_policy.json
}

# CloudWatch Logs 等への書き込みポリシーをアタッチ
resource "aws_iam_role_policy_attachment" "transfer_attach_logging_policy" {
  role       = aws_iam_role.transfer_logging_role.name
  policy_arn = "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"
}

#----------------------------------------------------------------
# S3 へのアクセス権限用 IAM ロール
#    (Server Managed IDP を利用する場合、ユーザーごとにこのロールを指定)
#----------------------------------------------------------------
data "aws_iam_policy_document" "transfer_role_policy_doc" {
  statement {
    actions = [
      "s3:GetObject",
      "s3:PutObject",
      "s3:DeleteObject",
      "s3:ListBucket"
    ]
    resources = [
      aws_s3_bucket.transfer_bucket.arn,
      "${aws_s3_bucket.transfer_bucket.arn}/*"
    ]
  }
}

resource "aws_iam_role" "transfer_access_role" {
  name               = "transfer-s3-access-role"
  assume_role_policy = data.aws_iam_policy_document.transfer_trust_policy.json
}

resource "aws_iam_role_policy" "transfer_access_policy" {
  name   = "transfer-s3-access-policy"
  role   = aws_iam_role.transfer_access_role.id
  policy = data.aws_iam_policy_document.transfer_role_policy_doc.json
}

#----------------------------------------------------------------
# Transfer Family の SFTP サーバーを PUBLIC エンドポイントで作成
#    Elastic IP をアタッチ
#----------------------------------------------------------------
resource "aws_eip" "transfer_eip" {
  domain = "vpc"
  tags = {
    Name = "transfer-eip"
  }
}


# セキュリティグループ: SSH (SFTP) 用
resource "aws_security_group" "sftp_sg" {
  name        = "sftp_sg"
  description = "Allow SSH access for SFTP"
  vpc_id      = var.vpc_id

  ingress {
    description = "SSH"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = [var.my_api_cidr] # 必要に応じて制限
  }

  egress {
    description      = "Allow all outbound"
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }
}

resource "aws_transfer_server" "public_sftp_server" {
  endpoint_type          = "VPC"
  identity_provider_type = "SERVICE_MANAGED"
  protocols              = ["SFTP"]
  logging_role           = aws_iam_role.transfer_logging_role.arn

  endpoint_details {
    # Public endpoint では address_allocation_ids を設定できます
    # Elastic IP の id を割り当てることで固定 IP 化
    address_allocation_ids = [aws_eip.transfer_eip.id]
    subnet_ids             = [var.sftp_public_subnet_id]
    vpc_id                 = var.vpc_id
    security_group_ids     = [aws_security_group.sftp_sg.id]
  }

  # タグ付けは任意
  tags = {
    Name = "my-public-transfer-sftp"
  }
}

#----------------------------------------------------------------
# 6. Transfer Family ユーザーの作成
#    (Server Managed IDP + デフォルトホームディレクトリを指定)
#----------------------------------------------------------------
resource "aws_transfer_user" "sftp_user" {
  server_id      = aws_transfer_server.public_sftp_server.id
  user_name      = "sftp_user"
  role           = aws_iam_role.transfer_access_role.arn
  home_directory = "/${aws_s3_bucket.transfer_bucket.bucket}"

  # 必要に応じてポリシー (AWS Management Console 上でユーザーポリシーを設定する代わりに、ここで指定)
  policy = <<POLICY
{
  "Version":"2012-10-17",
  "Statement":[{
    "Effect":"Allow",
    "Action":[ "s3:*" ],
    "Resource":[
      "${aws_s3_bucket.transfer_bucket.arn}",
      "${aws_s3_bucket.transfer_bucket.arn}/*"
    ]
  }]
}
POLICY

  tags = {
    Name = "sftp_user"
  }
}

resource "aws_transfer_ssh_key" "ssh_key" {
  server_id = aws_transfer_server.public_sftp_server.id
  user_name = aws_transfer_user.sftp_user.user_name
  body      = file("${path.module}/ssh_keys/id_rsa.pub")
}

output "sftp_server_host" {
  value = aws_transfer_server.public_sftp_server.endpoint
}

output "sftp_public_ip" {
  value = aws_eip.transfer_eip.public_ip
}

WinSCPで接続する

terraform applyすると、以下のように表示される。

sftp_public_ip = "52.198.xx.xx"
sftp_server_host = "s-xxxxxx.server.transfer.ap-northeast-1.amazonaws.com"

今回はパブリックIPでWinSCPから接続してみる。Elastic IPなので、サーバーのホスト名が変わっても安定的に接続できるはず。接続はSSHキー経由で接続する。PuTTY形式に変換したSSHキーでも普通に接続できる。

必要ならSSHキーのパスフレーズを入れて普通につながる。

S3との連携

ドラッグアンドドロップでWinSCPでアップロード

S3に即時に同期されているのはちょっとかっこいい。ここからS3のイベント通知などを使ってLambdaなどと連携してフローを入れていくのがおそらく想定された使い方かと思われる。

所感

  • Transfer Family便利! ただ値段がちょっと高い
  • ユーザーが増えたり権限を管理したいだとTransfer Familyの強み出てきそう
  • 取り込む側の使い勝手(DataSyncだとSFTP対応してないし)がちょっと悪いのがうーんという感じ。SFTPはAWS側で持っておくのがベストプラクティスなのかもしれない


Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内

技術書コーナー

北海道の駅巡りコーナー


Add a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です