📖
Transfer FamilyとCloudFormationを用いたSFTP環境の構築方法
はじめに
阿河です。
ここ1年の間業務の中で「閉域=インターネットアクセスを禁止したセキュアな環境下で安全にオペレーションを回したい」と相談を受ける機会が増えました。
ケースによって構成は変わりますが、閉域空間に安全にファイルを取り込む手段としてTransfer Familyの利用を検討しました。
実務上では検疫処理を別途入れていたりするものの、「SFTP処理のインフラストラクチャ構築の自動化を行うには?」というテーマで記事を書こうと思います。
誰かの参考になれば幸いです。
目次
- IaCでベースインフラストラクチャをデプロイ
- Transfer Familyユーザーのセット
- 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ユーザーのセット
ユーザーの作成
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キーの登録
- AWSマネジメントコンソールでTransfer Familyの詳細ページに移動。作成したUserの「SSH public keys」に公開鍵を登録する。
3. SFTP操作
% 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の構築フローと、簡単な操作を実施しました。
誰かの参考になれば幸いです。
Discussion