Automating Security Remediation Using AWS IoT Device Defender

An IoT solution requires managing a large number of devices, usually hundreds of thousands or millions. When you start to work at a large scale, you need to keep your fleet protected by continuously checking if it is following security best practices. It can be challenging for organizations to audit all devices and automatically remediate issues.

In this blog post, you will learn how to use AWS IoT Device Defender to simplify this task. The sample architecture shows an example of how to inspect and update AWS IoT policies that are too permissive and don’t follow AWS IoT best practices.

You will walk through a solution which has the following tasks:

  • Create an Amazon SNS topic to receive notifications from AWS IoT Device Defender.
  • Create an AWS Lambda function to process the audit results and fix detected issues.
  • Create the required AWS Identity and Access Management (IAM) roles and policies.
  • Create a sample permissive AWS IoT policy.
  • Run an on-demand audit.
  • Monitor the progress of the audit.
  • Validate the mitigation action.

The diagram below depicts the AWS services used in this solution and the main steps executed after completing the setup. You should only use the architecture and sample code displayed below as a guideline.

Creating the required infrastructure

You need to create the following components:

  • An SNS topic to receive notifications from AWS IoT Device Defender upon finishing an audit
  • A Lambda function to parse the audit findings and fix potential problems
  • IAM policies and roles allowing the Lambda function to collect the audit results and change IoT policies
  • An IAM role allowing AWS IoT Device Defender to post to a specific topic in SNS
  • Lambda permission allowing SNS to call the Lambda function

You can do this quickly by using AWS CloudFormation, following these steps:

  1. Click the following link to open the template available only in us-east-1 (N. Virginia): https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=DeviceDefenderStack&templateURL=https://s3.amazonaws.com/aws-iot-blog-assets/aws-iot-device-defender-automating-actions/cloudformation.yml
  2. Scroll to the end of the Specify stack details page, and click Next.
  3. Scroll to the end of the Options page, and then click Next.
  4. Select I acknowledge that AWS CloudFormation might create IAM resources.
  5. Select I acknowledge that AWS CloudFormation might create IAM resources with custom names.
  6. If you are using the new AWS CloudFormation console, select I acknowledge that AWS CloudFormation might require the following capability: CAPABILITY_AUTO_EXPAND
  7. Choose Create Stack.

It can take a few minutes to complete the stack creation. After finishing it, note the SNS topic name and the role required by AWS IoT Device Defender to publish to SNS.

  1. Open the AWS CloudFormation console.
  2. Click on the name of the stack that was created. For example, “MyDevDefenderStack”.
  3. Choose the Outputs tab, located on the top.
  4. Locate SnsTopicName and note the content of the Value column. For example, “DeviceDefenderStack-DevDefenderSnsTopic-16IZWWFS9PXHR”.
  5. Locate DevDefenderPublishToTopicRole and note the content of the Value column. For example, “DeviceDefenderStack-DevDefenderPublishToTopicRole-1WEAG4BWI6429”.

Understanding the sample permissive AWS IoT policy

The AWS CloudFormation template created a permissive AWS IoT policy that will be caught by AWS IoT Device Defender after running an audit. The sample policy that is created is shown below.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "iot:Connect",
        "iot:Publish",
        "iot:Subscribe",
        "iot:Receive"
      ],
      "Resource": [
        "*"
      ],
      "Effect": "Allow"
    }
  ]
}

This policy doesn’t comply with the best practices because it has very broad permissions. For example, the actions aren’t constrained to specific resources. This allows a device using this policy to publish messages to any topic, and potentially interfere with other devices. You can find more information about the analysis performed by AWS IoT Device Defender here: https://docs.aws.amazon.com/iot/latest/developerguide/device-defender-audit.html

In a production environment, you should follow a least-privilege policy by default, that is, your device should have only the permissions required to complete its activities. Continuous auditing can help you detect and take action in the event a policy has been created that isn’t in line with best practices.

Understanding the AWS Lambda Function, Permission, and Role

The Lambda function receives an SNS message and gets the audit task ID for querying the detailed findings later.

...
    msg = json.loads(event['Message'])
    task_id = msg['taskId']
...

Then it determines whether the results contain a noncompliant check.

...
    for audit in msg['auditDetails']:
        if audit['checkRunStatus'] != "COMPLETED_COMPLIANT":
        logger.info("ERROR: {}".format(audit['checkName']))
...

The function processes only overly permissive policy findings (IOT_POLICY_OVERLY_PERMISSIVE_CHECK), but you can customize it and add handlers to other issues.

...
    if audit['checkName'] == "IOT_POLICY_OVERLY_PERMISSIVE_CHECK":
        handle_overly_permissive_policy(task_id)
    else:
        handle_security_issue(task_id, issue_name)
...

To handle permissive issues, the function queries the AWS IoT Device Defender findings and iterates over them.

...
    r = iot.list_audit_findings(taskId=task_id, checkName='IOT_POLICY_OVERLY_PERMISSIVE_CHECK')
    
    for f in r['findings']:
        resource = f['nonCompliantResource']
...

To avoid changing existing policies provisioned on the account, the sample code acts only on the policy created by the AWS CloudFormation template. Additionally, the permissions attached to the Lambda function allow updates only on the permissive AWS IoT policy.

...
    if TARGET_POLICY_NAME == policy_name:
        fix_overly_permissive_policy(policy_name, policy_version)
...

It will change the current permissive policy to the following predefined restrictive policy, which allows a device to manipulate only its own shadow.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ConnectUsingClientId",
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "arn:aws:iot:*:*:client/${iot:Connection.Thing.ThingName}",
      "Condition": {
        "Bool": {
          "iot:Connection.Thing.IsAttached": "true"
        }
      }
    },
    {
      "Sid": "UpdateAndQueryOwnShadow",
      "Effect": "Allow",
      "Action": [
        "iot:Publish"
      ],
      "Resource": [
        "arn:aws:iot:*:*:topic/$aws/things/${iot:Connection.Thing.ThingName}/shadow/update",
        "arn:aws:iot:*:*:topic/$aws/things/${iot:Connection.Thing.ThingName}/shadow/get"
      ]
    },
    {
      "Sid": "ReceiveShadowChanges",
      "Effect": "Allow",
      "Action": [
        "iot:Subscribe",
        "iot:Receive"
      ],
      "Resource": [
        "arn:aws:iot:*:*:topicfilter/$aws/things/${iot:Connection.Thing.ThingName}/shadow/+/accepted",
        "arn:aws:iot:*:*:topicfilter/$aws/things/${iot:Connection.Thing.ThingName}/shadow/+/rejected"
      ]
    }
  ]
}

The code to fix this issue is defined on the fix_overly_permissive_policy() function. It handles a few different situations, but the main idea is to delete the offending policy, and replace it with a new default one with restrictive permissions.

def fix_overly_permissive_policy(policy_name, policy_version):
...
    r = iot.delete_policy_version(
        policyName=policy_name,
        policyVersionId=policy_version
    )
...
    r = iot.create_policy_version(
        policyName=policy_name,
        policyDocument=RESTRICTIVE_DEFAULT_POLICY,
        setAsDefault=True
    )
...

This sample function will work for a few issues. If you need to address a large number of issues at a time, you have to decouple the results analysis from the remediation using Amazon SQS, for example.

Configuring AWS IoT Device Defender to post results on an SNS topic

You need to connect your AWS IoT Device Defender to the SNS topic created by the AWS CloudFormation template. Each completed audit task triggers the Lambda function.

To complete this task, follow these steps:

  1. Open the AWS IoT Core console.
  2. If this is the first time on the AWS IoT Core console, click on Get Started
  3. In the left navigation pane, choose Defend, and then choose Settings.
  4. If you haven’t done it previously, complete the AWS IoT Device Defender setup wizard to create the required permissions clicking Get started with an audit.
  5. Scroll down until you locate SNS alerts.
  6. Choose Edit.

  1. Choose Enabled.
  2. Choose Select under Topic to select the topic you want.
  3. Choose the topic created during the AWS CloudFormation execution.
  4. Under Role, choose Select.
  5. Choose the role created during the AWS CloudFormation execution.
  6. Choose Update to finish the configuration.

Creating an On-Demand Audit

After finishing the setup, you will run an on-demand audit to check if the remediation strategy is working. Each completed audit task will trigger the Lambda function.

To complete this task, follow these steps:

  1. Open the AWS IoT Core console.
  2. In the left navigation pane, choose Defend.
  3. Choose Audit and then Schedule.
  4. On the upper right, choose Create.
  5. Select only IoT policies overly permissive. You can use AWS IoT Device Defender to create separated schedules for each type of check. For example, a daily audit for Device certificate shared and a weekly audit for CA Certificates expiring.

  1. Scroll to the Set Schedule session.
  2. Choose Run audit now (once).
  3. Choose Create to start the audit.

Checking the Audit Progress

Depending on the number of devices and checks selected, the Audit execution can take more time to finish. You can follow the progress using the console or the APIs.

  1. Open the AWS IoT console.
  2. In the left navigation pane, choose Defend.
  3. Choose Audit and then Results.
  4. Choose the audit you created.
  5. Check the results.
  6. Choose the check name IoT policies overly permissive to see a detailed report.

Validating the Mitigation Action

After completing the audit, the AWS Lambda function is triggered and the target policy is updated. You can check that the mitigation strategy was successfully implemented by looking at the policy in the console.

To complete this task, follow these steps:

  1. Open the AWS IoT Core console.
  2. In the left navigation pane, choose Secure, and then choose Policies.
  3. Search for the specific policy by using the text box on the upper right.
  4. Enter the name of the policy (for example, “MyPermissiveIoTPolicy”).
  5. Choose Search.
  6. Choose the policy displayed on the right panel.

  1. Scroll down to locate the Policy Document section.
  2. Check if the policy document was updated with the restrictive policy you want.

Conclusion

With AWS IoT Device Defender, customers can audit their AWS IoT account and ensure that their devices are following the list of AWS IoT best practices that are available for protecting your fleet. You can extend the current setup to address additional security issues such as certificate rotation and duplicated client IDs, among others.