Operationalizing the AlienVault Sensor CloudFormation Template - Part 4

This is part 4 in a series of articles. To follow along via code, visit the Github repository.

In the last article, I reviewed the template for operational and security risks. Let's go fix some of them now.

Making Unsafe Defaults Safe

First, some guard rails are added to the template. Remove the Default attribute from both SSHLocation and HTTPLocation, ensuring that the caller specifies them in the CloudFormation template. That way, if the caller does use 0.0.0.0/0, it is a decision they have made, not the template.

The parameters now look like this:

SSHLocation:
  Description: >
    The IP address range that can be used to access the USM Anywhere
    Sensor that you are deploying in your AWS Account through the CLI.
    For security considerations, 0.0.0.0/0 is not recommended, so please
    restrict to a smaller IP range if possible.
  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 IP CIDR range of the form x.x.x.x/x.
HTTPLocation:
  Description: >
    The IP address range that can be used to access the USM Anywhere
    Sensor that you are deploying in your AWS Account through the UI.
    For security considerations, 0.0.0.0/0 is not recommended, so please
    restrict to a smaller IP range if possible.
  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 IP CIDR range of the form x.x.x.x/x.

AMI Dependency Injection

The Mappings block suffers from an issue that the template will constantly change, removing its ability to be evergreen. This looks like a dependency problem, where static content that we are not in control of is being hardcoded into the template. Instead, let's inject this dependency as a parameter, freeing up the caller to decide which AMI they want to use.

Parameters:
  ...

  ImageId:
    Type: AWS::EC2::Image::Id
    Description: >
      The AMI ID of the image you want to use.

Now the problem becomes where do we get the AMI ID from if it is embedded in the original template? This is solved by looking at the original template's Mapping section. I have noticed that it is this Mapping section that changes the most between different versions of the template published by AT&T. However, since the original template is in JSON format, we can create a new Makefile target and use jq to list the AMIs that AT&T wants us to use for the sensor.

.PHONY: list-amis
list-amis: ## Lists the AMIs available for use with the sensor
  @jq '.Mappings.RegionMap to_entries[] | [.key, .value.AMI]' $(output.file)

.PHONY: ami
ami: ## Same as list-amis, but for a given REGION
ifndef REGION
  @echo "You need to supply a REGION"; exit 1
endif
  @jq '.Mappings.RegionMap to_entries[] | [.key, .value.AMI] | select(.[0] == "$(REGION)")' $(output.file)

Then calling make list-amis will produce a list of AMIs from the original template. If you want to know the AMI for a specific region, use make ami REGION=us-east-1.

EBS Volume

The EBS Volume is pretty much okay, but there's a lingering operational issue with the volume itself being set to delete when/if the CloudFormation stack is deleted. Let's fix that by easily changing its deletion policy:

DataStorage:
  Type: AWS::EC2::Volume
  DeletionPolicy: Retain
  DependsOn: USMInstance
  Properties:
    VolumeType: gp2
    Size: 100
    Encrypted: true
    AvailabilityZone: !GetUSMInstance.AvailabilityZone

In the next article, we will tackle the requirement for VpcId and SubnetId by thinking about what this sensor actually needs from the AWS environment.