CloudFormation: Single Instance New Network
Overview
This article covers deploying a single instance of SFTP Gateway into a New VPC Network
.
SFTP Gateway on the AWS Marketplace lets you deploy the following CloudFormation templates:
- Single instance - Existing Network
- HA - New Network
- HA - Existing Network
There is no Single instance - New Network
template on the AWS Marketplace. So you will need to follow the steps below to deploy this configuration.
Existing vs New Network
We recommend deploying the Single instance - Existing Network
template for most use cases. You can find this template on the AWS marketplace.
If for some reason you need to deploy a New VPC Network
, continue reading.
Manually create a VPC, and use the Existing Network template
Instead of using the New Network
template, you can manually create the VPC. Then, you can deploy the Existing Network
template into the new VPC.
One of the benefits to this approach is that the VPC Wizard
gives you flexible options for IP ranges, subnet types, and more.
Here are the steps to configure a VPC:
- In the AWS console, go to the VPC service
- Click Create VPC
- Select VPC and more
- Replace the Name Tag with your desired name. Keep Auto-generate checked.
- Change the IPv4 CIDR block to
192.168.1.0/24
- Leave Tenancy as
Default
- Change the Number of Availability Zones to
1
- Leave the Number of Public Subnets as
1
- Set the Number of Private Subnets to
0
- Set NAT gateways to
None
- Set VPC endpoints to
None
- Leave the DNS options enabled
- Finally, click Create VPC
The options we suggested above will create a simple VPC with a single public subnet. Feel free to make adjustments to meet your requirements.
Once the VPC is created, you can deploy the Existing Network
template, and point it to your VPC and public subnet.
Deploy the New Network Template
If you need to deploy the New Network Template
, this section will walk you through the process.
Download the New Network Template
file here. You can also view this template at the bottom of this article.
There are special instructions for deploying this particular CloudFormation template:
1. Provide the AMI
This template requires the AMI ID as a parameter. Normally, the AWS Marketplace generates an AMI region map. But for this template, you will need to look up the AMI ID yourself.
To find the AMI ID for your region:
- Go to the EC2 dashboard
- Click Launch instance
- Under the AMI catalog, search for
SFTP Gateway
- Select
SFTP Gateway version 3.x
- Under the overview, click Continue
- Copy the AMI ID for your current region
- Cancel out of the Launch instance wizard
2. Set the admin credentials
This template includes a feature (in preview) where you can set the web admin username and password via the CloudFormation template. This way, you can skip the First Launch Experience
.
Follow the instructions in the template with regard to password complexity requirements.
The New Network Template
Here is the New Network Template
for reference:
AWSTemplateFormatVersion: 2010-09-09
Conditions:
S3IsOpen: !Equals
- "open"
- !Ref BucketAccess
S3IsRestricted: !Equals
- "restricted"
- !Ref BucketAccess
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Bucket
Parameters:
- BucketAccess
- Label:
default: Instance and storage options
Parameters:
- EC2Type
- DiskVolumeSize
- KeyPair
- AMIID
- Label:
default: Network
Parameters:
- VPCIPRange
- Label:
default: Access and authentication
Parameters:
- InputCIDR
- SFTPInputCIDR
- WebAdminUsername
- WebAdminPassword
Parameters:
AMIID:
Type: String
Description: AMI ID
EC2Type:
Description: SFTP Gateway instance type. You can use a t3.medium for testing, but m5.large is recommended for Production.
Type: String
Default: t3.medium
AllowedValues:
- t2.micro
- t2.small
- t2.medium
- t2.large
- t2.xlarge
- t2.2xlarge
- t3.micro
- t3.small
- t3.medium
- t3.large
- t3.xlarge
- t3.2xlarge
- m3.medium
- m3.large
- m3.xlarge
- m3.2xlarge
- m4.large
- m4.xlarge
- m4.2xlarge
- m4.4xlarge
- m4.10xlarge
- m4.16xlarge
- m5.large
- m5.xlarge
- m5.2xlarge
- m5.4xlarge
- m5.12xlarge
- m5.24xlarge
- c3.large
- c3.xlarge
- c3.2xlarge
- c3.4xlarge
- c3.8xlarge
- c4.large
- c4.xlarge
- c4.2xlarge
- c4.4xlarge
- c4.8xlarge
- c5.large
- c5.xlarge
- c5.2xlarge
- c5.4xlarge
- c5.9xlarge
- c5.18xlarge
- f1.2xlarge
- f1.16xlarge
- g2.2xlarge
- g2.8xlarge
- g3.4xlarge
- g3.8xlarge
- g3.16xlarge
- p2.xlarge
- p2.8xlarge
- p2.16xlarge
- p3.2xlarge
- p3.8xlarge
- p3.16xlarge
- cr1.8xlarge
- r3.large
- r3.xlarge
- r3.2xlarge
- r3.4xlarge
- r3.8xlarge
- r4.large
- r4.xlarge
- r4.2xlarge
- r4.4xlarge
- r4.8xlarge
- r4.16xlarge
- r5.large
- r5.xlarge
- r5.2xlarge
- r5.4xlarge
- r5.12xlarge
- r5.24xlarge
- r5d.large
- r5d.xlarge
- r5d.2xlarge
- r5d.4xlarge
- r5d.12xlarge
- r5d.24xlarge
- z1d.large
- z1d.xlarge
- z1d.2xlarge
- z1d.3xlarge
- z1d.6xlarge
- z1d.12xlarge
- x1.16xlarge
- x1.32xlarge
- x1e.xlarge
- x1e.2xlarge
- x1e.4xlarge
- x1e.8xlarge
- x1e.16xlarge
- x1e.32xlarge
- i2.xlarge
- i2.2xlarge
- i2.4xlarge
- i2.8xlarge
- i3.large
- i3.xlarge
- i3.2xlarge
- i3.4xlarge
- i3.8xlarge
- i3.16xlarge
- h1.2xlarge
- h1.4xlarge
- h1.8xlarge
- h1.16xlarge
- hs1.8xlarge
- d2.xlarge
- d2.2xlarge
- d2.4xlarge
- d2.8xlarge
- a1.medium
- a1.large
- a1.xlarge
- a1.2xlarge
- a1.4xlarge
- c5n.large
- c5n.xlarge
- c5n.2xlarge
- c5n.4xlarge
- c5n.9xlarge
- c5n.18xlarge
VPCIPRange:
Description: Class C IP range
AllowedValues:
- 192.168.1.0/24
- 192.168.2.0/24
- 192.168.3.0/24
- 192.168.4.0/24
- 192.168.5.0/24
- 192.168.6.0/24
- 192.168.7.0/24
- 192.168.8.0/24
- 192.168.9.0/24
- 192.168.10.0/24
- 192.168.11.0/24
- 192.168.12.0/24
- 192.168.13.0/24
- 192.168.14.0/24
- 192.168.15.0/24
- 192.168.16.0/24
- 192.168.17.0/24
- 192.168.18.0/24
- 192.168.19.0/24
- 192.168.20.0/24
Default: 192.168.1.0/24
Type: String
DiskVolumeSize:
Default: 32
Description: Disk volume size in GB. Must be at least 32.
ConstraintDescription: Must be a number greater or equal to 32
MinValue: 32
Type: Number
InputCIDR:
Description: Public IP address range for SSH and web access. Use a CIDR range to restrict access. To get your local machine's IP, see http://checkip.dyndns.org/. (Remember to append /32 for a single IP e.g. 12.34.56.78/32) For security reasons, do not use 0.0.0.0/0.
AllowedPattern: ([1-9]\d{0,2})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/([1-9]\d{0,1})
ConstraintDescription: Must be a valid IP CIDR range in the form of x.x.x.x/x. Do not use 0.0.0.0/0.
Type: String
SFTPInputCIDR:
Default: 0.0.0.0/0
Description: Public IP address range for SFTP access. Defaults to being open to the public (0.0.0.0/0), since the web admin portal lets you restrict IP ranges on a per-user basis.
AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,3})
ConstraintDescription: Must be a valid IP CIDR range in the form of x.x.x.x/x.
Type: String
WebAdminUsername:
Description: Optionally use this parameter and WebAdminPassword to initialize a username to log into web admin interface.
Type: String
WebAdminPassword:
Description:
Optionally use this parameter and WebAdminUsername to initialize a password to log into web admin interface. This password
could show in cloud-init configuration logs. We recommend changing the password after logging in with the admin account
to prevent a possible password leak.
NoEcho: true
MinLength: 8
Type: String
AllowedPattern: ^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[^0-9a-zA-Z])(?=\S+$).{8,}$
ConstraintDescription:
Password must contain 1 or more uppercase characters. Password must contain 1 or more digit characters.
Password must contain 1 or more special characters. Password must be 8 or more characters in length.
KeyPair:
Description: Make sure you have access to this EC2 key pair. Otherwise, create a new key pair before proceeding.
Type: AWS::EC2::KeyPair::KeyName
ConstraintDescription: Existing EC2 KeyPair.
BucketAccess:
Description: Choose "open" to allow full S3 access to support multiple buckets. Or "restrict" S3 permissions to our default bucket naming convention "sftpgw-<instance-id>".
Default: restricted
Type: String
AllowedValues:
- open
- restricted
Resources:
InternetGateway:
Type: AWS::EC2::InternetGateway
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
PublicRouteAssociationA:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnetA
PublicRouteAssociationB:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnetB
PublicRouteTable:
DependsOn: AttachGateway
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PublicDefaultRoute:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
RouteTableId: !Ref PublicRouteTable
PublicSubnetA:
DependsOn:
- AttachGateway
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select
- 0
- !GetAZs
Ref: AWS::Region
CidrBlock: !Select [ 0, !Cidr [ !Ref VPCIPRange, 16, 4 ]]
MapPublicIpOnLaunch: true
VpcId: !Ref VPC
PublicSubnetB:
DependsOn:
- AttachGateway
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select
- 1
- !GetAZs
Ref: AWS::Region
CidrBlock: !Select [ 1, !Cidr [ !Ref VPCIPRange, 16, 4 ]]
MapPublicIpOnLaunch: true
VpcId: !Ref VPC
PrivateRouteAssociationApp0:
Properties:
RouteTableId:
Ref: PrivateRoutingTable
SubnetId:
Ref: PrivateSubnetA
Type: AWS::EC2::SubnetRouteTableAssociation
PrivateRouteAssociationApp1:
Properties:
RouteTableId:
Ref: PrivateRoutingTable
SubnetId:
Ref: PrivateSubnetB
Type: AWS::EC2::SubnetRouteTableAssociation
PrivateRoutingTable:
Properties:
VpcId:
Ref: VPC
Type: AWS::EC2::RouteTable
PrivateSubnetA:
DependsOn:
- AttachGateway
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select
- 0
- !GetAZs
Ref: AWS::Region
CidrBlock: !Select [ 2, !Cidr [ !Ref VPCIPRange, 16, 4 ] ]
MapPublicIpOnLaunch: false
VpcId: !Ref VPC
PrivateSubnetB:
DependsOn:
- AttachGateway
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select
- 1
- !GetAZs
Ref: AWS::Region
CidrBlock: !Select [ 3, !Cidr [ !Ref VPCIPRange, 16, 4 ] ]
MapPublicIpOnLaunch: false
VpcId: !Ref VPC
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCIPRange
EnableDnsHostnames: true
SFTPGatewayInstance:
DependsOn:
- RolePolicies
Type: AWS::EC2::Instance
Properties:
IamInstanceProfile: !Ref EC2InstanceProfile
ImageId: !Ref AMIID
KeyName: !Ref KeyPair
InstanceType: !Ref EC2Type
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: !Ref DiskVolumeSize
VolumeType: gp2
Encrypted: 'true'
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: "0"
SubnetId: !Ref PublicSubnetA
GroupSet:
- !Ref SFTPGatewaySG
Tags:
- Key: Name
Value: SFTPGateway
UserData:
Fn::Base64: !Sub |
#cloud-config
repo_update: true
repo_upgrade: all
write_files:
- content : |
#!/bin/bash
export CLOUD_PROVIDER=aws
export ARCHITECTURE=Single
export LOG_GROUP_NAME=${LogGroup}
path: /opt/sftpgw/launch_config.env
runcmd:
- /opt/aws/bin/cfn-init --stack ${AWS::StackName} --resource SFTPGatewayInstance --region ${AWS::Region}
- /opt/aws/bin/cfn-signal -e 0 --stack ${AWS::StackName} --resource SFTPGatewayInstance --region ${AWS::Region}
- '[ -n "${WebAdminPassword}" ] && [ -n "${WebAdminUsername}" ] && curl -X "POST" "http://localhost:8080/3.0.0/admin/config" -H "accept: */*" -H "Content-Type: application/json" -d "{\"password\": \"${WebAdminPassword}\",\"username\": \"${WebAdminUsername}\"}"'
CreationPolicy:
ResourceSignal:
Count: 1
Timeout: PT15M
IPAddress:
Properties:
Domain: vpc
InstanceId: !Ref SFTPGatewayInstance
DeletionPolicy: Retain # Change this to "Delete" if you don't want to keep the IP after stack deletion
Type: AWS::EC2::EIP
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub sftpgw-${AWS::StackName}
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: /
Roles:
- !If [S3IsRestricted, !Ref RestrictedSFTPGWRole, !Ref OpenSFTPGWRole]
RestrictedSFTPGWRole:
Type: AWS::IAM::Role
Condition: S3IsRestricted
Properties:
Path: /
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
OpenSFTPGWRole:
Type: AWS::IAM::Role
Condition: S3IsOpen
Properties:
Path: /
ManagedPolicyArns:
- !Sub arn:${AWS::Partition}:iam::aws:policy/AmazonS3FullAccess
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
RolePolicies:
Type: AWS::IAM::Policy
Properties:
PolicyName: SFTPGatewayPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: 's3:*'
Resource: !Sub 'arn:${AWS::Partition}:s3:::sftpgw-i-*'
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
- logs:DescribeLogStreams
- logs:CreateLogGroup
Resource: '*'
- Effect: Allow
Action:
- ec2:DescribeAvailabilityZones
- ec2:DescribeInstances
- ec2:DescribeTags
- s3:ListAllMyBuckets
Resource: '*'
- Effect: Allow
Action:
- cloudformation:DescribeStacks
- cloudformation:ListStackResources
Resource: '*'
Roles:
- !If [S3IsRestricted, !Ref RestrictedSFTPGWRole, !Ref OpenSFTPGWRole]
SFTPGatewaySG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: SFTPGateway Security Group
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: !Ref SFTPInputCIDR
- IpProtocol: tcp
FromPort: 2222
ToPort: 2222
CidrIp: !Ref InputCIDR
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: !Ref InputCIDR
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: !Ref InputCIDR
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 0
ToPort: 65535
CidrIp: 0.0.0.0/0
- IpProtocol: udp
FromPort: 0
ToPort: 65535
CidrIp: 0.0.0.0/0
Outputs:
Hostname:
Value: !Ref IPAddress
Description: Elastic IP address
DefaultBucket:
Value: !Sub sftpgw-${SFTPGatewayInstance}
Description: Default S3 bucket
CloudWatchLogs:
Value: !Ref LogGroup
Description: CloudWatch logs