fbpx

How to lower expenses by deleting and recreating HSMs automatically

You should use AWS CloudHSM to greatly help manage your encryption keys on FIPS 140-2 Level 3 validated hardware security modules (HSMs). AWS recommends owning a high-availability production architecture with at the very least two CloudHSM HSMs in various Availability Zones. Although some workloads should be available 24/7, quality assurance or development environments don’t have this requirement typically.

In this article, we show you how exactly to automate the deletion and recreation of HSMs once you don’t have a requirement of high availability. By using this approach of deleting HSMs and restoring them from backups on a predefined schedule might help decrease your monthly CloudHSM costs. To find out more on the CloudHSM backup process, start to see the CloudHSM cluster backup documentation.

Prerequisites

Solution overview

For this solution, you utilize the next AWS services to automate the procedure of deleting and restoring HSMs running non-production workloads:

Figure 1: Architectural diagram

Figure 1: Architectural diagram

Here’s the way the process works, as shown in Figure 1:

  1. At the scheduled time (we have been using 7:30 PM UTC in this example), a CloudWatch Events rule triggers the DeleteHSM Lambda function.
  2. The DeleteHSM Lambda function stores the HSM metadata, such as for example IP Availability and address Zone, in a DynamoDB table, deletes the HSMs from the cluster, and sends a contact notification.
  3. At the scheduled time (we have been using 7:30 AM UTC in this example), another CloudWatch Events rule triggers the AddHSM Lambda function.
  4. The AddHSM Lambda function retrieves the HSM metadata in the DynamoDB table, creates the HSMs in to the cluster with exactly the same IP Availability and address Zone, and sends a contact notification.

Note: In this solution, we utilize the same IP address when making a new HSM, which means you don’t have to modify the configuration files for the CloudHSM client instance connecting to the HSM.

Deployment steps

open the CloudFormation template

  1. To, choose the Launch Stack button below.
    Select button to launch stack
  2. Give your stack a genuine name.
  3. Under Parameters, enter values for the next parameters based on the needs you have:
    • ClusterId: The ID of the prevailing CloudHSM cluster that you intend to use.
    • CreateTime: The time whenever your HSMs ought to be created in UTC. This parameter should be a valid cron expression. The CreateTime shown in Figure 2 is 7:30 UTC Mon-Fri.
    • DeleteTime: The time whenever your HSMs ought to be deleted in UTC. This parameter should be a valid cron expression. The DeleteTime shown in Figure 2 is 19:30 UTC Mon-Fri.
    • EmailAddress: The e-mail address which will be subscribed to the SNS topic for creation and deletion events for the HSMs.

    Figure 2: Specify the parameters in CloudHSM

    Figure 2: Specify the parameters in CloudHSM

  4. On the Specify stack details page, select Next, and, on the Configure stack options page, select Next.
  5. On the Review page, check the box that says I acknowledge that AWS CloudFormation might create IAM resources with custom names, and choose Create stack then, as shown in Figure 3.

    Figure 3: Check the box to acknowledge the conditions

    Figure 3: Check the box to acknowledge the conditions

  6. After the stack is established by you, the CloudFormation template automatically creates an SNS topic that notifies you when HSMs are deleted or created in your cluster. You need to sign up to this topic therefore the alerts could be received by you. Select Confirm subscription, as shown in Figure 4.

    Figure 4: Select ‘Confirm subscription’ a subscription to the SNS topic

    Figure 4: Select ‘Confirm subscription’ a subscription to the SNS topic

AWS resources deployed by the CloudFormation stack

When stack creation is complete, the template could have deployed the next AWS resources:

  1. An AWS Identity and Access Management (IAM) role named ClusterExecutionRole for the Lambda functions to utilize on each invocation. The IAM role uses the managed policy AWSLambdaBasicExecutionRole and an inline policy called CloudHSMPermissions. The managed policy supplies the Lambda execution role permission to create CloudWatch Logs. The inline policy grants the role permission to delete an HSM, create an HSM, publish to an SNS topic, develop a DynamoDB table, put items in to the table, and retrieve items from the table. For more information about CloudHSM permissions, see Predefined AWS Managed Policies for AWS CloudHSM. The CloudHSMPermissions inline policy is shown below.

    Note: The resource names in the policy shown here are examples. The template shall update them to complement resources in your account once the solution is deployed.

    
    
      "Version": "2012-10-17",
      "Statement": [
        
          "Sid": "DynamoDBPermissions",
          "Effect": "Allow",
          "Action": [
            "dynamodb:GetItem",
            "dynamodb:PutItem",
            "dynamodb:Query"
          ],
          "Resource": [
            "arn:aws:dynamodb:us-west-1:111122223333:table/DynamoDBTable-blogpost",
            "arn:aws:dynamodb:us-west-1:111122223333:table/DynamoDBTable-blogpost/*"
          ]
        ,
        
          "Sid": "CloudHSMPerClusterPermissions",
          "Effect": "Allow",
          "Action": [
            "cloudhsm:DeleteHsm",
            "cloudhsm:CreateHsm"
          ],
          "Resource": "arn:aws:cloudhsm:us-west-1:111122223333:cluster/cluster-id"
        ,
        
          "Sid": "DescribeCreatePermissions",
          "Effect": "Allow",
          "Action": [
            "cloudhsm:DescribeClusters",
            "ec2:DeleteNetworkInterface",
            "ec2:CreateNetworkInterface",
            "ec2:AuthorizeSecurityGroupIngress",
            "ec2:AuthorizeSecurityGroupEgress",
            "ec2:RevokeSecurityGroupEgress",
            "ec2:CreateSecurityGroup",
            "ec2:DescribeNetworkInterfaces",
            "ec2:DescribeSubnets",
            "ec2:DescribeSecurityGroups"
          ],
          "Resource": "*",
          "Condition": 
            "StringEquals": 
              "aws: RequestedRegion": "us-west-1"
            
          
        ,
        
          "Sid": "SNSPermission",
          "Effect": "Allow",
          "Action": "sns: Publish",
          "Resource": "arn:aws:sns:us-west-1:111122223333:SNSTopicName"
        
      ]
    
    
  2. Two CloudWatch Events rules: one rule for creating the HSMs and something rule for deleting the HSMs. These rules are triggered at the proper times that you specified during creation of the stack.
  3. Two Lambda functions: AddHSM and DeleteHSM. You’ll find out about what each function does in the next section.
  4. An SNS topic that notifies you when HSMs have already been deleted or created.

Detailed walkthrough of the Lambda functions

We’ll walk you through the code in the Lambda functions that get triggered to delete and recreate the HSMs at scheduled intervals.

Delete CloudHSMs Lambda function

At the scheduled time (7:30 PM UTC in this example), the ScheduledRuleDeleteHSM CloudWatch Events event is triggered.

As shown below, the code for the DeleteHSM Lambda function passes the cluster ID first, table name, and region as environment variables in line with the parameters you entered when making the CloudFormation stack. At the scheduled time, the CloudWatch Events rule triggers the Lambda function, which creates a DynamoDB table the very first time the DeleteHSM Lambda function is triggered.

The code then makes a Describe API call with the cluster ID provided in the CloudFormation stack to retrieve the HSM details, like the HSM Availability and IP Zones. These things are saved to the DynamoDB table for use later, and the HSMs in the cluster are deleted. All recipients subscribed to the SNS topic receive an SNS notification showing the facts of the HSMs deleted from the cluster.


import boto3, sys, time
from datetime import datetime

from botocore.exceptions import ClientError
from os import environ

ClusterId = environ.get('ClusterId')
TableName = environ.get('TableName')
TopicARN = environ.get('TopicARN')

def lambda_handler(event, context):
    cloudhsm_client = boto3.client('cloudhsmv2')
    dynamodb_resource = boto3.resource('dynamodb')
    try:
        response = cloudhsm_client.describe_clusters(Filters='clusterIds':[ClusterId])
        for item in response['Clusters'][0]['Hsms']:
            if item['State'] == 'ACTIVE' and item['ClusterId'] == ClusterId:
                table = dynamodb_resource.Table(TableName)
                table.put_item(Item= 'ClusterId': item['ClusterId'],'AvailabilityZone': item['AvailabilityZone'], 'IpAddress': item['EniIp'],)
                print(item['AvailabilityZone'])
                print(item['ClusterId'])
                print(item['EniIp'])
                print(item['State'])
            else:
                print('HSMs in Cluster  not in ACTIVE State'.format(ClusterId))
    except Exception as e:
        print (e)
        sys.exit(1)
    time.sleep(5)

    response = cloudhsm_client.describe_clusters(Filters='clusterIds':[ClusterId])
    for item in response['Clusters'][0]['Hsms']:
            if item['State'] == 'ACTIVE' and item['ClusterId'] == ClusterId:
                print('Deleting HSMs 0 in Cluster 1'.format(item['EniIp'], item['ClusterId']))
                response = cloudhsm_client.delete_hsm(ClusterId=item['ClusterId'], EniIp=item['EniIp'])
                try:
                    sns_client = boto3.client('sns')
                    message_subject = '[timestamp] Deleting HSMs From Cluster!'.format(timestamp=datetime.now().strftime('%b/%d/%Y %H:%M'))
                    message_body = 'These will be the information on the Deleted HSM:nnCluster ID: ClusterIdnnHSM IP: EniIpnnAvailability Zone: AvailabilityZonenn'.format(**item)
                    print('Sending SNS notification...')
                    sns_response = sns_client.publish(TopicArn=TopicARN, Message=message_body, Subject=message_subject)
                except Exception as e:
                    print('Exception: %s' % e)
            else:
                print('HSMs in Cluster  not in Active State'.format(ClusterId))

Create CloudHSMs Lambda function

At the scheduled time (7:30 AM UTC in this example), the AddHSM Lambda function is set off by the CloudWatch Events rule ScheduledRuleAddHSM to include the HSMs back again to the cluster. The code shown below retrieves the CloudHSM details, like the CloudHSM Availability and IP Zone, from the DynamoDB table. Next, the CloudHSMs are manufactured with the same Ip in to the same Availability Zone. This saves you your time and effort of having to create configuration changes on the CloudHSM client instances connecting to the HSM as the same IPs are employed. The CloudHSM daemon installed on your client instance reconnects to the HSMs immediately because they become active automatically. All recipients subscribed to an SNS be received by the SNS topic notification showing the facts of the HSMs created.


import boto3
from datetime import datetime
from os import environ
from boto3.dynamodb.conditions import Key

ClusterId = environ.get('ClusterId')
TableName = environ.get('TableName')
TopicARN = environ.get('TopicARN')

def lambda_handler(event, context):
    dynamodb_resource = boto3.resource('dynamodb')
    table = dynamodb_resource.Table(TableName)
    resp = table.query(KeyConditionExpression=Key('ClusterId').eq(ClusterId))
    for item in resp['Items']:
        try:
           cloudhsm_client = boto3.client('cloudhsmv2')
           response = cloudhsm_client.create_hsm(ClusterId=ClusterId,AvailabilityZone=item['AvailabilityZone'],IpAddress=item['IpAddress'])
           try:
               sns_client = boto3.client('sns')
               message_subject = '[timestamp] Adding HSMs To Cluster!'.format(timestamp=datetime.now().strftime('%b/%d/%Y %H:%M'))
               message_body = 'These will be the information on the Newly Created HSM:nnCluster ID: ClusterIdnnHSM IP: IpAddressnnAvailability Zone: AvailabilityZonenn'.format(**item)
               print('Sending SNS notification...')
               sns_response = sns_client.publish(TopicArn=TopicARN, Message=message_body, Subject=message_subject)

           except Exception as e:
                 print('Exception: %s' % e)
        except:
            print('Failure Adding HSM to Cluster '.format(ClusterId))

Note: If you opt to schedule the stop and begin of one’s client instances as described above, you need to make sure that the CloudHSM client daemon automatically starts running once the instance(s) boot up therefore the link with your CloudHSM cluster will resume.

Conclusion

In this post, a strategy was learned by one to help decrease your monthly CloudHSM charges for environments that don’t have to be running 24/7. You learned how exactly to achieve this cost benefits through the use of scheduled CloudWatch Events rules to trigger Lambda functions that delete and recreate the CloudHSMs in your cluster on a specified schedule without modifying client configuration files.

When you have feedback concerning this post, submit comments in the Comments section below. When you have questions concerning this post, take up a new thread on the AWS CloudHSM forum or contact AWS Support.

Want more AWS Security how-to content, news, and show announcements? Follow us on Twitter.

Author

David Ogunmola

David is really a Security Engineer at AWS. The culture is enjoyed by him at Amazon since it aligns along with his dedication to lifelong learning. An MS is held by him in Cyber Security from the University of Nebraska. Beyond work, he loves watching soccer and experiencing new cultures.

Author

Gabriel Santamaria

Gabriel is really a Senior Technical Account Manager at AWS. An MS is held by him in IT from George Mason University, along with the AWS Solutions Architect Professional, DevOps Professional, and Security Specialist certifications. In his leisure time he enjoys hanging out along with his family catching through to the latest Television shows and can be an avid fan of games.

%d bloggers like this: