Deploy Gateways Via AWS Organizations With CloudFormation StackSets
Last modified on October 9, 2024
Overview
This guide describes how to deploy StrongDM gateways across multiple AWS accounts within an AWS organization using CloudFormation StackSets.
Prerequisites
- StrongDM Administrator account
- StrongDM admin token with the ability to list and create gateways
- AWS organization set up with multiple member accounts
- AWS Identity and Access Management (IAM) role with permissions to create and manage StackSets
- Basic knowledge of AWS CloudFormation and StackSets
Procedure
Prepare CloudFormation templates
You may leverage this YAML template as the basis for one that would function in your AWS environment:
AWSTemplateFormatVersion: '2010-09-09'
Description: StrongDM self-registering gateway with VPC creation
Parameters:
# StrongDM variables
SDMListenPort:
Type: Number
Default: 5000
MinValue: 1024
MaxValue: 65535
Description: The TCP port that will be exposed to the internet
SDMAdminToken:
AllowedPattern: (.+)
Type: String
Description: Paste your StrongDM admin token to create the gateway
LatestAmiId:
Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
CommonVpcCIDR:
Type: String
MinLength: 9
MaxLength: 18
AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})"
ConstraintDescription: Must be a valid CIDR range in the form x.x.x.x/x
Default: 10.112.0.0/16
PublicACIDR:
Type: String
MinLength: 9
MaxLength: 18
AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})"
ConstraintDescription: Must be a valid CIDR range in the form x.x.x.x/x
Default: 10.112.0.0/22
PublicBCIDR:
Type: String
MinLength: 9
MaxLength: 18
AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})"
ConstraintDescription: Must be a valid CIDR range in the form x.x.x.x/x
Default: 10.112.4.0/22
PrivateACIDR:
Type: String
MinLength: 9
MaxLength: 18
AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})"
ConstraintDescription: Must be a valid CIDR range in the form x.x.x.x/x
Default: 10.112.12.0/22
PrivateBCIDR:
Type: String
MinLength: 9
MaxLength: 18
AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})"
ConstraintDescription: Must be a valid CIDR range in the form x.x.x.x/x
Default: 10.112.16.0/22
# Organization structure for parameters
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
-
Label:
default: "StrongDM Configuration"
Parameters:
- SDMAdminToken
- SDMListenPort
Resources:
VPC:
Type: "AWS::EC2::VPC"
Properties:
EnableDnsSupport: true
EnableDnsHostnames: true
CidrBlock: !Ref CommonVpcCIDR
Tags:
- Key: Name
Value: ACBL VPC
IGW:
Type: "AWS::EC2::InternetGateway"
GatewayAttach:
Type: "AWS::EC2::VPCGatewayAttachment"
Properties:
InternetGatewayId: !Ref IGW
VpcId: !Ref VPC
SubnetPublicSharedA:
Type: "AWS::EC2::Subnet"
Properties:
AvailabilityZone: !Select [0, !GetAZs ]
CidrBlock: !Ref PublicACIDR
MapPublicIpOnLaunch: true
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "Public A - ${PublicACIDR}"
SubnetPublicSharedB:
Type: "AWS::EC2::Subnet"
Properties:
AvailabilityZone: !Select [1, !GetAZs ]
CidrBlock: !Ref PublicBCIDR
MapPublicIpOnLaunch: true
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "Public B - ${PublicBCIDR}"
SubnetPrivateSharedA:
Type: "AWS::EC2::Subnet"
Properties:
AvailabilityZone: !Select [0, !GetAZs ]
CidrBlock: !Ref PrivateACIDR
MapPublicIpOnLaunch: false
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "Private A - ${PrivateACIDR}"
SubnetPrivateSharedB:
Type: "AWS::EC2::Subnet"
Properties:
AvailabilityZone: !Select [1, !GetAZs ]
CidrBlock: !Ref PrivateBCIDR
MapPublicIpOnLaunch: false
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "Private B - ${PrivateBCIDR}"
SubnetRouteTableAssociatePublicA:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
RouteTableId: !Ref RouteTablePublic
SubnetId: !Ref SubnetPublicSharedA
SubnetRouteTableAssociatePublicB:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
RouteTableId: !Ref RouteTablePublic
SubnetId: !Ref SubnetPublicSharedB
SubnetRouteTableAssociatePrivateA:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
RouteTableId: !Ref RouteTablePrivate
SubnetId: !Ref SubnetPrivateSharedA
SubnetRouteTableAssociatePrivateB:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
RouteTableId: !Ref RouteTablePrivate
SubnetId: !Ref SubnetPrivateSharedB
RouteDefaultPublic:
Type: "AWS::EC2::Route"
DependsOn: GatewayAttach
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref IGW
RouteTableId: !Ref RouteTablePublic
RouteDefaultPrivate:
Type: "AWS::EC2::Route"
Properties:
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
RouteTableId: !Ref RouteTablePrivate
RouteTablePublic:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref VPC
RouteTablePrivate:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref VPC
EIPNatGW:
DependsOn: GatewayAttach
Type: "AWS::EC2::EIP"
Properties:
Domain: vpc
NatGateway:
DependsOn: GatewayAttach
Type: "AWS::EC2::NatGateway"
Properties:
AllocationId: !GetAtt EIPNatGW.AllocationId
SubnetId: !Ref SubnetPublicSharedB
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Expose StrongDM listening port"
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: !Ref SDMListenPort
ToPort: !Ref SDMListenPort
CidrIp: 0.0.0.0/0
SDMGWONE:
Type: AWS::EC2::Instance
Properties:
InstanceType: t3.medium
Tags:
- Key: "Name"
Value: "StrongDM Gateway One"
NetworkInterfaces:
- DeviceIndex: '0'
SubnetId: !Ref SubnetPublicSharedA
AssociatePublicIpAddress: 'true'
DeleteOnTermination: 'true'
GroupSet: [!Ref EC2SecurityGroup]
ImageId: !Ref LatestAmiId
UserData:
Fn::Base64:
!Sub |
#!/bin/bash -xe
# set environment variables
mkdir -p /home/ec2-user/.sdm
touch /home/ec2-user/.sdm/sdm.log
export TARGET_USER=ec2-user
export SDM_LISTEN_PORT=${SDMListenPort}
export SDM_GATEWAY_NAME=AWS-CloudFormation-$(date +%s)
export SDM_HOSTNAME="$(curl http://169.254.169.254/latest/meta-data/public-hostname)"
export SDM_HOME="/home/ec2-user/.sdm"
# downloads sdm binary
yum update -y && yum install -y unzip curl
curl -J -O -L https://app.strongdm.com/releases/cli/linux && unzip sdmcli* && rm sdmcli*
# Generate a gateway token
export SDM_RELAY_TOKEN="$(./sdm --admin-token=${SDMAdminToken} relay create-gateway --name $SDM_GATEWAY_NAME $SDM_HOSTNAME:$SDM_LISTEN_PORT 0.0.0.0:$SDM_LISTEN_PORT)"
chown -R ec2-user:ec2-user /home/ec2-user/.sdm
# Install SDM
./sdm install --relay --token=$SDM_RELAY_TOKEN --user $TARGET_USER
SDMGWTWO:
Type: AWS::EC2::Instance
Properties:
InstanceType: t3.medium
Tags:
- Key: "Name"
Value: "StrongDM Gateway TWO"
NetworkInterfaces:
- DeviceIndex: '0'
SubnetId: !Ref SubnetPublicSharedB
AssociatePublicIpAddress: 'true'
DeleteOnTermination: 'true'
GroupSet: [!Ref EC2SecurityGroup]
ImageId: !Ref LatestAmiId
UserData:
Fn::Base64:
!Sub |
#!/bin/bash -xe
# set environment variables
mkdir -p /home/ec2-user/.sdm
touch /home/ec2-user/.sdm/sdm.log
export TARGET_USER=ec2-user
export SDM_LISTEN_PORT=${SDMListenPort}
export SDM_GATEWAY_NAME=AWS-CloudFormation-$(date +%s)
export SDM_HOSTNAME="$(curl http://169.254.169.254/latest/meta-data/public-hostname)"
export SDM_HOME="/home/ec2-user/.sdm"
# downloads sdm binary
yum update -y && yum install -y unzip curl
curl -J -O -L https://app.strongdm.com/releases/cli/linux && unzip sdmcli* && rm sdmcli*
# Generate a gateway token
export SDM_RELAY_TOKEN="$(./sdm --admin-token=${SDMAdminToken} relay create-gateway --name $SDM_GATEWAY_NAME $SDM_HOSTNAME:$SDM_LISTEN_PORT 0.0.0.0:$SDM_LISTEN_PORT)"
chown -R ec2-user:ec2-user /home/ec2-user/.sdm
# Install SDM
./sdm install --relay --token=$SDM_RELAY_TOKEN --user $TARGET_USER
Outputs:
VpcId:
Description: ID of the created VPC
Value: !Ref VPC
SecurityGroupId:
Description: Security Group ID for StrongDM gateway
Value: !GetAtt [ EC2SecurityGroup, GroupId ]
The above YAML template creates the following infrastructure components in each target account and region specified during StackSet deployment:
- New VPC
- Two public subnets
- Two private subnets
- Publicly accessible StrongDM gateway in each of the public subnets
- Associated route tables, security groups, NAT gateways, and IGWs for the above
Every organization’s environment and architecture is unique, and the template should be modified to suit each organization as necessary.
If your organization uses a control plane located in a region other than the default, add a --region yourdomain
flag to the install commands, such as:
./sdm install --region app.uk.strongdm.com --relay --token=$SDM_RELAY_TOKEN --user $TARGET_USER
Gather necessary parameters and input to deploy
Gather necessary information to deploy, such as the StrongDM admin token. If using the included template, you will need the following as input parameters:
- StrongDM admin token
- Port gateways listen on (default is
5000
) - Private CIDR range for the new VPC (default is
10.112.0.0/16
) - StrongDM Gateway AMI (default is the latest provided by StrongDM)
- Private CIDR range for both private subnets
Prepare IAM permissions
Create an IAM role with permissions required for deploying resources defined in your CloudFormation templates. This role should have sufficient permissions to create EC2 instances, IAM roles, security groups, network components, and so forth, across all member accounts within the AWS organization. Assuming into child accounts via an administrator role may be an option if starting from the master root account as documented on Amazon.
Create StackSets
- Navigate to AWS CloudFormation service in the AWS Management Console.
- Select StackSets from the menu.
- Click on Create StackSet and choose the CloudFormation template prepared in Prepare CloudFormation templates.
- Specify the IAM role created in Prepare IAM Permissions as the execution role for StackSets.
- Configure parameters that are required in the template.
- Choose the AWS organization as the deployment target.
Configure deployment options
Specify deployment options, such as region availability, deployment schedule, and rollback options as per your requirements.
Deploy StackSets
Review the configuration settings and initiate the deployment of StackSets. Monitor the deployment progress in the StackSets dashboard.
Validate deployment
Once the deployment is complete, validate that StrongDM gateways are provisioned across all member accounts and target regions as configured/desired within the AWS organization.
Ensure that the gateways are functioning correctly, are shown as healthy in StrongDM, and are accessible as expected.