📖

Transfer FamilyとCloudFormationを用いたSFTP環境の構築方法

2024/06/13に公開

はじめに

阿河です。

ここ1年の間業務の中で「閉域=インターネットアクセスを禁止したセキュアな環境下で安全にオペレーションを回したい」と相談を受ける機会が増えました。
ケースによって構成は変わりますが、閉域空間に安全にファイルを取り込む手段としてTransfer Familyの利用を検討しました。

実務上では検疫処理を別途入れていたりするものの、「SFTP処理のインフラストラクチャ構築の自動化を行うには?」というテーマで記事を書こうと思います。

誰かの参考になれば幸いです。

目次

  1. IaCでベースインフラストラクチャをデプロイ
  2. Transfer Familyユーザーのセット
  3. SFTP操作

1. IaCでベースインフラストラクチャをデプロイ

CloudFormationで以下のようなAWSリソースをデプロイします。

% tree
.
└── cfn
    ├── network.yml
    ├── s3.yml
    ├── transfer-user.yml
    └── transfer.yml

S3

AWSTemplateFormatVersion: '2010-09-09'

Parameters:
  Prefix:
    Type: String
    Default: test

Resources:
  SFTPBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${Prefix}-sftp-bucket
      PublicAccessBlockConfiguration:
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
        RestrictPublicBuckets: True

Outputs:
  SFTPBucket:
    Value: !Ref SFTPBucket
    Export:
      Name: !Sub SFTPBucket
  • ファイル保存先のS3バケット。
  • パラメータの「Prefix」は、環境に合わせて文字列を入れてください。リソース名の接頭文字として反映されます。
    BucketName: !Sub ${Prefix}-sftp-bucket

ネットワークリソース/その他Transfer Family関連リソース

AWSTemplateFormatVersion: "2010-09-09"

Parameters:

  Prefix:
    Type: String
    Default: test

  VPCCiderBlock:
    Type: String
    Default: 10.0.0.0/16
  PublicCiderBlock1A:
    Type: String
    Default: 10.0.1.0/24
  PublicCiderBlock1C:
    Type: String
    Default: 10.0.2.0/24
  PrivateCiderBlock1A:
    Type: String
    Default: 10.0.3.0/24
  PrivateCiderBlock1C:
    Type: String
    Default: 10.0.4.0/24

  Source:
    Type: "String"
    Default: "0.0.0.0/0"
    Description: "Source of Connection"


Resources:

  TransferVPC:
    Type: "AWS::EC2::VPC"
    Properties: 
      CidrBlock: !Ref VPCCiderBlock
      EnableDnsHostnames: "true"
      EnableDnsSupport: "true"

  TransferInternetGateway:
    Type: "AWS::EC2::InternetGateway"
    Properties: 
      Tags: 
        - Key: "Name"
          Value: "transfer-igw"

  AttachTransferIGW:
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties: 
      InternetGatewayId: !Ref "TransferInternetGateway"
      VpcId: !Ref "TransferVPC"

  TransferPublicSubnet1A:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PublicCiderBlock1A
      VpcId: !Ref TransferVPC
      Tags:
        - Key: "Name"
          Value: "TransferPublicSubnet1A"

  TransferPublicSubnet1C:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: !Ref PublicCiderBlock1C
      VpcId: !Ref TransferVPC
      Tags:
        - Key: "Name"
          Value: "TransferPublicSubnet1C"

  TransferPrivateSubnet1A:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PrivateCiderBlock1A
      VpcId: !Ref TransferVPC
      Tags:
        - Key: "Name"
          Value: "TransferPrivateSubnet1A"

  TransferPrivateSubnet1C:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: !Ref PrivateCiderBlock1C
      VpcId: !Ref TransferVPC
      Tags:
        - Key: "Name"
          Value: "TransferPrivateSubnet1C"

  TransferPublicRouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref TransferVPC
      Tags:
        - Key: "Name"
          Value: "TransferPublicRouteTable"

  TransferPrivateRouteTableA:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref TransferVPC
      Tags:
        - Key: "Name"
          Value: "TransferPrivateRouteTableA"

  TransferPrivateRouteTableC:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref TransferVPC
      Tags:
        - Key: "Name"
          Value: "TransferPrivateRouteTableC"

  AttachTransferPublicRouteIGW:
    Type: "AWS::EC2::Route"
    DependsOn: 
      - TransferInternetGateway
      - AttachTransferIGW
    Properties:
      RouteTableId: !Ref TransferPublicRouteTable
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref TransferInternetGateway

  AtachPublicSubnetRouteA:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      SubnetId: !Ref TransferPublicSubnet1A
      RouteTableId: !Ref TransferPublicRouteTable

  AtachPublicSubnetRouteC:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      SubnetId: !Ref TransferPublicSubnet1C
      RouteTableId: !Ref TransferPublicRouteTable

  AtachPrivateSubnetRouteA:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      SubnetId: !Ref TransferPrivateSubnet1A
      RouteTableId: !Ref TransferPrivateRouteTableA

  AtachPrivateSubnetRouteC:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      SubnetId: !Ref TransferPrivateSubnet1C
      RouteTableId: !Ref TransferPrivateRouteTableC

  ElasticIP1:
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: "vpc"

  TransferSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "SG of Upload"
      VpcId: !Ref TransferVPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref Source

  TransferIAMRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: "transferfamily-sftp-role"
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - transfer.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /

  InlinePolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - s3:ListBucket
              - s3:GetBucketLocation
            Resource: "arn:aws:s3:::*"
          - Effect: Allow
            Action:
              - s3:PutObject
              - s3:GetObject
              - s3:DeleteObjectVersion
              - s3:DeleteObject
              - s3:GetObjectVersion
              - s3:PutObjectACL
            Resource: "arn:aws:s3:::*"
      PolicyName: "trasfer-inline"
      Roles: 
        - !Ref TransferIAMRole

  TransferLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: "/aws/transfer/sftp-logs"

Outputs:
  TransferVPC:
    Value: !Ref TransferVPC
    Export:
      Name: !Sub TransferVPC

  TransferInternetGateway:
    Value: !Ref TransferInternetGateway
    Export:
      Name: !Sub TransferInternetGateway

  TransferPublicSubnet1A:
    Value: !Ref TransferPublicSubnet1A
    Export:
      Name: !Sub TransferPublicSubnet1A

  TransferPublicSubnet1C:
    Value: !Ref TransferPublicSubnet1C
    Export:
      Name: !Sub TransferPublicSubnet1C

  TransferPrivateSubnet1A:
    Value: !Ref TransferPrivateSubnet1A
    Export:
      Name: !Sub TransferPrivateSubnet1A

  TransferPrivateSubnet1C:
    Value: !Ref TransferPrivateSubnet1C
    Export:
      Name: !Sub TransferPrivateSubnet1C

  TransferIAMRole:
    Value: !GetAtt TransferIAMRole.Arn
    Export:
      Name: !Sub TransferIAMRole

  TransferLogGroup:
    Value: !GetAtt TransferLogGroup.Arn
    Export:
      Name: !Sub TransferLogGroup

  ElasticIP1:
    Value: !GetAtt ElasticIP1.AllocationId
    Export:
      Name: !Sub ElasticIP1

  TransferSecurityGroup:
    Value: !Ref TransferSecurityGroup
    Export:
      Name: !Sub TransferSecurityGroup
  • VPCやサブネットのネットワークレンジは、環境に合わせて変更下さい。

Transfer Familyのデプロイ

AWSTemplateFormatVersion: "2010-09-09"

Resources:

  SFTP:
    Type: AWS::Transfer::Server
    Properties: 
      Domain: S3
      Protocols: 
        - SFTP
      EndpointType: VPC
      EndpointDetails: 
        AddressAllocationIds:
          - !ImportValue ElasticIP1
        SecurityGroupIds: 
          - !ImportValue TransferSecurityGroup
        SubnetIds: 
          - !ImportValue TransferPublicSubnet1A
        VpcId: !ImportValue TransferVPC
      IdentityProviderType: SERVICE_MANAGED
      StructuredLogDestinations: 
        - !ImportValue TransferLogGroup

Outputs:
  SFTP:
    Value: !GetAtt SFTP.ServerId
    Export:
      Name: !Sub SFTP

2. Transfer Familyユーザーのセット

https://docs.aws.amazon.com/ja_jp/transfer/latest/userguide/create-user.html

ユーザーの作成

AWSTemplateFormatVersion: "2010-09-09"

Parameters:

  UserName:
    Type: String
    Default: tanaka

Resources:

  TransferUserIn:
    Type: AWS::Transfer::User
    Properties:
      ServerId: !ImportValue SFTP
      HomeDirectory: !Sub
        - "/${ImportedValue}/${UserName}"
        - ImportedValue: !ImportValue SFTPBucket
      HomeDirectoryType: "PATH"
      Role: !ImportValue TransferIAMRole
      UserName: !Sub "${UserName}"

SSHキーの登録

https://docs.aws.amazon.com/ja_jp/transfer/latest/userguide/key-management.html

  • AWSマネジメントコンソールでTransfer Familyの詳細ページに移動。作成したUserの「SSH public keys」に公開鍵を登録する。

3. SFTP操作

https://docs.aws.amazon.com/ja_jp/transfer/latest/userguide/transfer-file.html

% sftp -i [※transfer-key] [※sftp_user]@[※service_endpoint]

※transfer-key: SSHプライベートキー
※sftp_user: ユーザー名
※service_endpoint: 選択したサーバーの AWS Transfer Family コンソールに表示されるサーバーのエンドポイント

上記を環境に合わせて変更して、コマンドを実行ください。

sftp> put hello.txt
Uploading hello.txt to /xxxxx-bucket/xxxx/hello.txt
hello.txt

S3バケットの所定の場所に、ファイルがアップロードされました。

おわりに

以上CloudFormationを使ったTransfer Familyの構築フローと、簡単な操作を実施しました。
誰かの参考になれば幸いです。

MEGAZONE株式会社 Tech Blog

Discussion