How to Translate HIPAA Controls to AWS CloudFormation Templates: Part 3 of the Automating HIPAA Compliance Series

In my previous post, I walked through the setup of a DevSecOps environment that gives healthcare developers the ability to launch their own healthcare web server. At the heart of the architecture is AWS CloudFormation, a JSON representation of your architecture that allows security administrators to provision AWS resources according to the compliance standards they define. In today's post, I will share examples that provide a Top 10 List of CloudFormation code snippets that you can consider when trying to map the requirements of the AWS Business Associates Agreement (BAA) to CloudFormation templates.

The example CloudFormation template I use as an example in today’s post is the same template I used in my previous post to define a healthcare product in AWS Directory Service. The template creates a healthcare web server that follows many of the contractual obligations outlined in the AWS BAA. The template also allows healthcare developers to customize their web server according to the following parameters:

  • FriendlyName – The name with which you tag your server.
  • CodeCommitRepo – The cloneUrlHttp field for the Git repository that you would like to release on the web server.
  • Environment – A choice between PROD and TEST. TEST will create a security group with several secure ports open, including SSH, from within a Classless Inter-Domain Routing (CIDR) block range. Choosing PROD will create a security group with HTTPS that is only accessible from the public Internet. (Exposing production web servers directly to the public Internet is not a best practice and is shown for example purposes only).
  • PHI – If you need to store protected health information (PHI) on the server. Choosing YES will create an encrypted EBS volume and attach it to the web server.
  • WebDirectory – This is the name of your website. For example, DNS-NAME/WebDirectory.
  • InstanceType – This is the Amazon EC2 instance type on which the code will be deployed. Because the AWS BAA requires PHI to be processed on dedicated instances, the choices here are limited to those EC2 instance types that are offered in dedicated tenancy mode.

I will forego CloudFormation tutorials in this post because an abundance of material for learning CloudFormation is easily accessible in AWS documentation. Instead, I will jump right in to share the Top 10 List of CloudFormation code snippets. If you are new to CloudFormation, you might find value in first understanding the capabilities it offers. qwikLABS is a great resource for learning AWS technology and offers multiple ClouldFormation labs to bring you up to speed quickly. The qwikLabs site offers entry-level CloudFormation labs at no cost. 

It’s important to note that the example CloudFormation template from which the following 10 snippets are taken is only an example and does not guarantee HIPAA or AWS BAA compliance. The template is meant as a starting point for developing your own templates that not only help you meet your AWS BAA obligations, but also provide general guidance as you expand beyond a single web server and start to utilize DevSecOps methods for other HIPAA-driven compliance needs.

Without further ado, here is a Top 10 List of CloudFormation compliance snippets that you should consider when building your own CloudFormation templates. In each section, I highlight the code I refer to in the associated description.

1. Set tenancy to dedicated.

To run a web server, you need an EC2 instance on which to install it. This can be accomplished in CloudFormation by adding it as a resource in the template. However, you also want to make sure that the EC2 instance meets your AWS BAA obligations by running in dedicated tenancy mode (in other words, your instance runs on single-tenant hardware).

To enforce this, in the EC2 instance change the tenancy property of the instance to dedicated.

    "EC2Instance": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
          "Tenancy" : "dedicated",
}}

2. Turn on detailed monitoring.

Detailed monitoring provides data about your EC2 instance over 1-minute periods. You can enable this in CloudFormation by adding a parameter to your EC2Instance resource.

When you turn on detailed monitoring, the data is then available for the instance in AWS Management Console graphs or through the API. Because there is an upcharge for detailed monitoring, you might want to turn this on only in your production environments. Having data each minute could be critical to recognizing failures and triggering responses to these failures.

On the other hand, also turning on detailed monitoring in your development environments could help you diagnose issues and prevent you from inadvertently moving such issues to production.

    "EC2Instance": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
          "Tenancy" : "dedicated",
          "Monitoring": "true",
}}

3. Define security group rules based on environment.

CloudFormation allows you to modify the firewall rules on your EC2 instance based on input parameters given to the template when it runs. This is done with AWS security groups and is very useful when you want to enforce certain compliance measures you define, such as disabling SSH access to production web servers or restricting development web servers from being accessed by the public Internet.

To do this, change security group settings based on whether your instance is targeted at test, QA, or production environments. You can do this by using conditions and an intrinsic If function. Intrinsic functions help you modify the security groups between environments according to your compliance standards, but at the same time, maintain consistent infastructure between environments.

"Conditions" : {
    "CreatePRODResources" : {"Fn::Equals" : [{"Ref" : "Environment"}, "PROD"]}
  },
    "EC2Instance": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
          "Tenancy" : "dedicated",
          "Monitoring": "true"
}},
        "SecurityGroups": [{
          "Fn::If": [
              "CreateTESTResources",
              {"Ref": "InstanceSecurityGroupTEST"},
              {"Ref": "InstanceSecurityGroupPROD"}
            ]
}],
    "InstanceSecurityGroupTEST": {
    "Type": "AWS::EC2::SecurityGroup",
    "Condition" : "CreateTESTResources",
    "Properties": {
       "GroupDescription": "Enable access only from secure protocols",
         "SecurityGroupIngress": [
         { "IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : "10.0.0.0/24" },
         { "IpProtocol" : "tcp", "FromPort" : "443", "ToPort" : "443", "CidrIp" : "10.0.0.0/24" },
         { "IpProtocol" : "tcp", "FromPort" : "143", "ToPort" : "143", "CidrIp" : "10.0.0.0/24" },
         { "IpProtocol" : "tcp", "FromPort" : "465", "ToPort" : "465", "CidrIp" : "10.0.0.0/24" },
         { "IpProtocol" : "icmp", "FromPort" : "8", "ToPort" : "-1", "CidrIp" : "10.0.0.0/24" }
         ]
     }}

4. Force instance tagging.

EC2 tagging is a common way for auditors and security professionals to understand why EC2 instances were launched and for what purpose. You can require the developer launching the template to enter information that you need for EC2 instance tagging by using CloudFormation parameters.

By using parameters such as AllowedValues and MinLength, you can maintain consistent tagging mechanisms by requiring that the developer enter a tag from a predetermined list of options (AllowedValues), or simply making them enter a text value meeting a certain length (MinLength).

In the following snippet, I use an AllowedValues list of YES and NO to make the developer tag the instance with information about whether or not the EC2 instance will be used to store PHI. I also use the MinLength to make the developer tag the EC2 instance with their email address so that we know who to contact if there is an issue with the instance.

"Parameters": {
    "PHI":
    {
      "Description": "Will this instance need to store protected health information?",
      "Default": "YES",
      "Type": "String",
       "AllowedValues": [
        "YES",
        "NO"
      ]
    },
    "Environment":
    {
      "Description": "Please specify the target environment",
      "Default": "TEST",
      "Type": "String",
       "AllowedValues": [
        "TEST",
        "PROD",
        “QA”
      ]
    },
   },
    "InstanceOwnerEmail":
    {
      "Description": "Please enter the email address of the developer taking responsblity for this server",
      "Default": "@mycompany.com",
      "Type": "String"
    },
    "FriendlyName":
    {
      "Description": "Please enter a friendly name for the server",
      "Type": "String",
      "MinLength": 3,
      "ConstraintDescription": "Must enter a friendy name for the server that is at least three characters long."
    },
"Resources": {
    "EC2Instance": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
           "Tags":[ 
        { "Key" : "PHI", "Value" : {"Ref": "PHI"} },
        { "Key" : "Name", "Value" : {"Ref": "FriendlyName"} },
        { "Key" : "Environment", "Value" : {"Ref": "Environment"} },
        { "Key" : "InstanceOwnerEmail", "Value" : {"Ref": "InstanceOwnerEmail"} } 
}}

5. Use IAM roles for EC2.

Applications must sign their API requests with AWS credentials. IAM roles are designed so that applications can securely make API requests from your instances, without requiring you to manage the security credentials that the applications use. In this example, I give the EC2 instance permission to perform a Git clone from our AWS CodeCommit repositories and push log data to Amazon CloudWatch.

  "Resources": {

    "HealthcareWebRole":
    {
         "Type": "AWS::IAM::Role",
         "Properties":
         {
            "AssumeRolePolicyDocument":
            {
               "Version" : "2012-10-17",
               "Statement":
               [ {
                "Effect": "Allow",
                 "Principal":
                  {
                     "Service": [ "ec2.amazonaws.com" ]
                  },
                  "Action": [ "sts:AssumeRole" ]
                } ]
            },
            "Path": "/",
            "ManagedPolicyArns": ["arn:aws:iam::aws:policy/AWSCodeCommitReadOnly", "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"]
        }
    },
    "HealthcareWebInstanceProfile":
    {
        "Type": "AWS::IAM::InstanceProfile",
        "Properties":
        {
           "Path": "/",
           "Roles": [ { "Ref": "HealthcareWebRole" } ]
        }
    },
"EC2Instance": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
IamInstanceProfile": {"Ref": "HealthcareWebInstanceProfile"}
}}

6. Add encrypted storage if you need to store PHI.

Applications that need to store PHI must encrypt the data at rest to meet the AWS BAA requirements. Amazon EBS encryption is one way to do this. The highlighted portion of the following snippet will add an encrypted EBS volume if the developer answers YES to the question, ”Will this instance need to store protected health information?”

    "EC2Instance": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
        "BlockDeviceMappings" : [
               {
                  "DeviceName" : "/dev/sdm",
                  "Ebs" : {
                     "VolumeType" : "io1",
                     "Iops" : "200",
                     "DeleteOnTermination" : "false",
                     "VolumeSize" : "10",
                     "Encrypted": {
                        "Fn::If" : [
                          "ContainsPHI",
                          "true",
                          "false"
                        ]
                     }
                  }
               },
               {
                  "DeviceName" : "/dev/sdk",
                  "NoDevice" : {}
               }
            ]}

7. Turn on CloudWatch Logs.

On each instance, install the AWS CloudWatch Logs agent, which uses CloudWatch Logs to monitor, store, and access your log files from EC2 instances. You can then retrieve the associated log data from a centralized logging repository that can be segregated from the application development team. 

After turning on the CloudWatch Logs agent, by default logs from the /var/log/messages are sent to CloudWatch. These messages store valuable, nondebug, and noncritical messages. These logs should be considered the general system activity logs, where you can start ensuring that you have the highest level of audit logging. However, you most likely will want to modify the /etc/awslogs/awslogs.conf file to add additional log locations if you choose to use this service in a HIPAA environment.

For example, you may want to add authentication logs (/var/log/auth.log) and set up alerting in CloudWatch to notify an administrator if repeated unauthorized access attempts are made against your server.

The following snippet will start the CloudWatch Logs agent and make sure it gets turned on during each startup.

    "EC2Instance": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
"UserData" :
              { "Fn::Base64" :
              { "Fn::Join" :
              [
                   "",
              [
          "service awslogs start\n",
                   "chkconfig awslogs on\n"
     ]
}}

8. Install Amazon Inspector.

Amazon Inspector is an automated security assessment service (currently offered in preview mode) that can help improve the security and compliance of applications deployed on AWS. Amazon Inspector allows you to run assessments for common best practices, vulnerablities, and exposures, and these findings can then be mapped to your own HIPAA control frameworks. Amazon Inspector makes it easier to validate that applications are adhering to your defined standards, and it helps to manage security issues proactively before a critical event such as a breach occurs.

Amazon Inspector requires an agent-based client to be installed on the EC2 instance. However, this installation can be performed by using a CloudFormation template. In the CloudFormation template used for this blog post, the Amazon Inspector installation is intentionally missing because Amazon Inspector in preview mode is available only in a different region than CodeCommit. However, if you would like to install it while in preview mode, you can use the following snippet.         

    "EC2Instance": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
"UserData" :
              { "Fn::Base64" :
              { "Fn::Join" :
              [
                   "",
              [
curl -O https://s3-us-west-2.amazonaws.com/inspector.agent.us-west-2/latest/install
sudo bash install”
     ]
}}

9. Configure SSL for encryption in flight.

As detailed in the AWS BAA, you must encrypt all PHI in flight. For production healthcare applications, open only those ports in the EC2 security group that are used in secure protocols.

The following snippet provides an example of UserData pulling down self-signed certificates from a publicly available Amazon S3 site. Although there may be situations when you have deemed self-signed certificates to be acceptable, a more secure approach would be to store the certificates in a private S3 bucket and give permission to the EC2 role to download the certificates and configurations.

Important: The certificates in the following code snippet are provided for demonstration purposes only and should never be used for any type of security or compliance purpose.

    "EC2Instance": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
"UserData" :
              { "Fn::Base64" :
              { "Fn::Join" :
              [
                   "",
              [
            "wget https://s3.amazonaws.com/awsiammedia/public/sample/hippa-compliance/aws-service-catalog/code-deployments/fakehipaa.crt -P /etc/pki/tls/certs\n",

            "wget https://s3.amazonaws.com/awsiammedia/public/sample/hippa-compliance/aws-service-catalog/code-deployments/fakehipaa.key -P /etc/pki/tls/private/\n",

            "wget https://s3.amazonaws.com/awsiammedia/public/sample/hippa-compliance/aws-service-catalog/code-deployments/fakehipaa.csr -P /etc/pki/tls/private/\n",

            "wget https://s3.amazonaws.com/awsiammedia/public/sample/hippa-compliance/aws-service-catalog/code-deployments/ssl.conf -P /etc/httpd/conf.d/ssl.conf\n",

            "service httpd start\n",

            "chkconfig httpd on\n"
]
}}

10. Clone from AWS CodeCommit.

So far the snippets in this post have focused on getting infrastructure secured in accordance with your compliance standards. However, you also need a process for automated code deployments. A variety of tools and techniques is available for automating code deployments, but in the following snippet, I will demonstrate an automated code deployment using an EC2 role and CodeCommit. This combination of an EC2 role and CodeCommit requires you to set the system-wide Git preferences by modifying the /etc/gitconfig file.

In the following snippet, after the authorized connection to CodeCommit is established, Git clones the repository provided by the developer into the default root folder of an Apache web server. However, this example could easily be extended to look for developer makefiles or to have an extra step that calls shell scripts that are written by the developer but maintained in CodeCommit.

    "EC2Instance": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
"UserData" :
              { "Fn::Base64" :
              { "Fn::Join" :
              [

     "git config --system credential.https://git-codecommit.us-east-1.amazonaws.com.helper '!aws --profile default codecommit credential-helper $@'\n",

            "git config --system credential.https://git-codecommit.us-east-1.amazonaws.com.UseHttpPath true\n",
            "aws configure set region us-east-1\n",

            "cd /var/www/html\n",

            "git clone ",  {"Ref": "CodeCommitRepo"}, " ", {"Ref": "WebDirectory"},  " \n",
           

              [
]
}}

Conclusion

I hope that these 10 code snippets give you a head start to develop your own CloudFormation compliance templates. I encourage you to build on the template I provided to learn more about how CloudFormation works as you take steps to achieve your own DevSecOps architecture.

- Chris

Comments