Creating your entire WordPress Stack using AWS Cloudformation – Part 2 – Security Groups (Firewalls)

By | February 4, 2019

In case you missed my last post, I am starting a series on creating/developing an entire WordPress stack from scratch. In Part 1, we discussed creating the underlying Network VPC infrastructure that will host your cluster. The VPC was unique from most templates I’ve seen, since it also included the ability to run dual stack IPv4/IPv6 which is rare in the US but extremely important. In this post, I will discuss Security Group creation which act as your firewall(s) and a way to group together servers with similar functions.

Before I post the code, lets discuss how things will be laid out. The template will create the following Security Groups:

  • Bastion – Will contain the grouping for the Bastion hosts or Jump boxes. This is used for secure access in the event you don’t use VPN, Direct Connect, or require it for PCI Compliancy.
  • Database – Will contain our RDS instances or Database servers.
  • ElasticCache – This will contain the managed caching instances such as memcached or Redis.
  • EFS (Elastic File System/NAS) – This will contain the EFS service which acts as NAS for a shared file system between servers.
  • ALB (Application Load Balancer) – The grouping for the front end Load Balancer.
  • App – This will be used for generic app servers and will serve no purpose in this series. If you have dependencies such as Lambda that will run out of this same VPC, you could throw them in this Security Group.
  • Web – The Security Group for the web servers.

The Security Groups will be able to access each other as follows:

  • The Bastion servers will allow SSH access from the IP range you specify. The Web security group allows SSH from Bastion and the Database security group allows MySQL port 3306 access from the Bastion. This is purely for management purposes.
  • The Database security group will also allow MySQL access from the Web security group since they will need to read/write to MySQL. You will notice that we also allow access from the App security group on the Redshift default port, but it is not used in this series.
  • The ElasticCache security group allows access to port 11211 from the Web security group for memcached access.
  • The EFS security group allows access to port 2049 from the web servers so they can write to the EFS share via NFS. We also allow SSH access from Bastion.
  • The Public ALB security group will allow access via HTTP/HTTPS from the internet. All web traffic should come into the Load Balancer directly and never direct to the web servers.
  • Ignore App security group.
  • The Web security group allows HTTP/HTTPS from the Load Balancer only. They are not accessible directly from the internet. Again, it allows SSH from the Bastion host.

So, now that we have covered how the whole thing will work, it’s time to see the code.

---
AWSTemplateFormatVersion: 2010-09-09

Description: Creates VPC security groups for web/database applications.

Metadata:

  AWS::CloudFormation::Interface:
    ParameterGroups:
    - Label:
        default: AWS Parameters
      Parameters:
        - SshAccessCidr
        - Vpc
    ParameterLabels:
      SshAccessCidr:
        default: SSH Access From
      Vpc:
        default: Vpc Id

Parameters:
  
  SshAccessCidr:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
    Description: The CIDR IP range that is permitted to SSH to bastion instance. Note - a value of 0.0.0.0/0 will allow access from ANY IP address.
    Type: String
    Default: 4.14.226.211/32
  Vpc:
    AllowedPattern: ^(vpc-)([a-z0-9]{8}|[a-z0-9]{17})$
    Description: The Vpc Id of an existing Vpc.
    Type: AWS::EC2::VPC::Id

Resources:

  BastionSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for Bastion instances
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref SshAccessCidr
      VpcId:
        !Ref Vpc

  DatabaseSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for Amazon RDS cluster
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          SourceSecurityGroupId: !Ref WebSecurityGroup
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          SourceSecurityGroupId: !Ref BastionSecurityGroup
        - IpProtocol: tcp
          FromPort: 5439
          ToPort: 5439
          SourceSecurityGroupId: !Ref AppSecurityGroup
      VpcId:
        !Ref Vpc

  ElastiCacheSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for ElastiCache cache cluster
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 11211
          ToPort: 11211
          SourceSecurityGroupId: !Ref WebSecurityGroup
      VpcId:
        !Ref Vpc
  EfsSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for EFS mount targets
      VpcId: !Ref Vpc
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 2049
          ToPort: 2049
          SourceSecurityGroupId: !Ref WebSecurityGroup
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          SourceSecurityGroupId: !Ref BastionSecurityGroup
  EfsSecurityGroupIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      IpProtocol: tcp
      FromPort: 2049
      ToPort: 2049
      SourceSecurityGroupId: !GetAtt EfsSecurityGroup.GroupId
      GroupId: !GetAtt EfsSecurityGroup.GroupId

  PublicAlbSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for ALB
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      VpcId:
        !Ref Vpc       

  AppSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for App Subnets
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
      VpcId:
        !Ref Vpc

  WebSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for web instances
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Ref PublicAlbSecurityGroup
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          SourceSecurityGroupId: !Ref PublicAlbSecurityGroup
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          SourceSecurityGroupId: !Ref BastionSecurityGroup
      VpcId:
        !Ref Vpc

Outputs:
  BastionSecurityGroup:
    Value: !Ref BastionSecurityGroup
  DatabaseSecurityGroup:
    Value: !Ref DatabaseSecurityGroup
  EfsSecurityGroup:
    Value: !Ref EfsSecurityGroup
  ElastiCacheSecurityGroup:
    Value: !Ref ElastiCacheSecurityGroup
  PublicAlbSecurityGroup:
    Value: !Ref PublicAlbSecurityGroup
  WebSecurityGroup:
    Value: !Ref WebSecurityGroup

The template itself is pretty short and should be clear since we discussed how it works previously. The Parameters section simply asks you to input what the SSH CIDR range is, which will typically be your IP as a /32 (e.g. 69.63.119.113/32) and inserts it into the Bastion Security Group which permits SSH access. It then asks you to specify the VPC to create these Security Groups which will be in the VPC we created in Part 1.

After providing those parameters, the template will create everything we discussed previously. Once it’s done, it will output the security group IDs it created to be easily referenced at a later time. The outputs section plays an important role when we get into the Master template section. I am hoping to get to that template in a few weeks from the day this post was made.

Hopefully, most of this makes sense. If you have any questions about the template, feel free to message me on Facebook or LinkedIn. If the syntax in Cloudformation seems confusing, I would highly recommend reviewing official AWS documentation on Cloudformation to better understand common functions such as Fn::Ref (!Ref). Next week we will discuss the template that creates the Bastion Host Auto Scaling Group and leverages the templates from this post and Part 1.

You can download the full template below.