Tag Archives: Advanced (300)

On-Demand SCIM provisioning of Azure AD to AWS SSO with PowerShell

Post Syndicated from Natalie Doerr original https://aws.amazon.com/blogs/security/on-demand-scim-provisioning-of-azure-ad-to-aws-sso-with-powershell/

In this post, I will demonstrate how you can use a PowerShell script to initiate an on-demand synchronization between Azure Active Directory and AWS Single Sign-On (AWS SSO) and avoid the default 40-minute synchronization schedule between both identity providers. This solution helps enterprises quickly synchronize changes made to users, groups, or permissions within Azure AD with AWS SSO. This allows user or permission changes to be quickly reflected in associated AWS accounts.

Prerequisites

You need the following to complete this session:

This post focuses on the steps needed to set up the on-demand sync solution. You can find specifics on how to set up and use PowerShell and the Azure PowerShell modules at Installing Azure PowerShell.
 

Figure 1: Triggering the SCIM Endpoint to sync all users and groups

Figure 1: Triggering the SCIM Endpoint to sync all users and groups

Grant permission to the Graph API to access the Default Directory in Azure AD

To get started, grant the permissions needed for the application to have access to the directory endpoint.

To grant permissions

  1. Sign in to the Azure Portal and navigate to the Azure AD dashboard.
  2. From the left navigation pane, select App registrations. If you don’t see your application listed, select the All applications tab.
    For this example, I’m using an application named AWS.
     
    Figure 2: Select the AWS app registration

    Figure 2: Select the AWS app registration

  3. Choose API permissions from the navigation pane.
  4. Choose the Add a permission option.
     
    Figure 3: Select the Add API permission

    Figure 3: Select the Add API permission

  5. From the settings page that opens, choose the Microsoft Graph option.
     
    Figure 4: Request API permissions

    Figure 4: Request API permissions

    Under What type of permissions does your application require, select Delegated permissions and enter directory.readwrite.all in the permissions search field. Select Directory.ReadWrite.All and choose Add permissions at the bottom of the page.
     

    Figure 5: Request API permissions - Add permissions

    Figure 5: Request API permissions – Add permissions

  6. On the API permissions page, choose Grant admin consent for Default Directory and select Yes.
     
    Figure 6: Grant permission for the account to have administrator permissions

    Figure 6: Grant permission for the account to have administrator permissions

Create a certificate and secret to access the application

To get started, create a certificate and secret which grants secure access to the AWS application.

To create a certificate and secret

  1. Choose Certificate & secrets from the left navigation menu and then choose New client secret.
     
    Figure 7: Creating a client secret for 1 year

    Figure 7: Creating a client secret for 1 year

  2. Select the desired length of the certificate.
  3. Provide a description and choose Add.
    1. Copy the value of the certificate that’s generated and save it to use later in this process.
    2. After you’ve saved the value to use later, select Home from the top left corner of the screen.
    Figure 8: Make sure you click Copy to clipboard to store the value of the secret

    Figure 8: Make sure you click Copy to clipboard to store the value of the secret

Create a user with permissions to run the code

Now that you’ve given your application access to the directory, let’s create a user and assign the proper permissions to run the code.

To create a user and assign permissions

  1. Choose Azure Active Directory from the Azure services list.
  2. Choose Users and select New user. The User name, First name, and Last name fields are required. In this example, I set the User name and First name to Auth and the Last name to User.
    1. Take note of the password that is set for this user and save it to use later.
    2. Once completed, choose Create.
    Figure 9: Create a user in Azure AD

    Figure 9: Create a user in Azure AD

  3. Select the newly created user from the list.
    1. On the left navigation pane, select Assigned roles.
    2. Choose Add assignments.
    3. Choose Hybrid identity administrator and select Add.
    Figure 10: Assign the user the role to trigger the API

    Figure 10: Assign the user the role to trigger the API

  4. Select Default Directory from the top of the navigation pane.
    1. Choose Enterprise applications.
    2. Choose the AWS application.
    3. Select Assign users and groups.
    Figure 11: Azure Enterprise applications - Assign users and groups

    Figure 11: Azure Enterprise applications – Assign users and groups

  5. Choose + Add user at the top of the window.
    1. Select the user you created earlier. I select Auth as that was the user I created earlier.
    2. Choose Select and then Assign.
    Figure 12: Select the user we created earlier from Figure 9

    Figure 12: Select the user we created earlier from Figure 9

     

    Figure 13: Assign the user to the application

    Figure 13: Assign the user to the application

  6. Now that you’ve added the user, you can see that the user is assigned to the application.
     
    Figure 14: Screen now showing that the user has been assigned to the application

    Figure 14: Screen now showing that the user has been assigned to the application

  7. It’s recommended to log in to the Azure portal as the user you just created in a new incognito or private browser session. As part of the first log in, you’ll be prompted to change the password.

Prerequisites to trigger the SCIM endpoint

You need the following items to run the PowerShell code that triggers the endpoint.

  1. From the application registration, retrieve the items shown below. Note that you must use the client secret saved earlier when the certificate was created.
    • Tenant ID
    • Display name
    • Application ID
    • Client secret
    • User name
    • Password
  2. Copy the items to a notepad in the preceding order so you can enter all of them through a single copy and paste action while running the script.
  3. From the menu, select Azure Active Directory.
  4. Choose App registrations and select the AWS App that was set up.
  5. Copy the Application (client) ID and the Directory (tenant) ID.
Figure 15: App registration contains all the items needed for the PowerShell script

Figure 15: App registration contains all the items needed for the PowerShell script

Trigger the SCIM endpoint with PowerShell

Now that you’ve completed all of the previous steps, you need to copy the code from the GitHub repository to your local machine and run it. We’ve configured the code to run manually, but you can also automate it to trigger an Azure Automation runbook when users are added to Azure through Alerts. You can also configure CloudWatch Events to run a Lambda function at periodic intervals.

To trigger the SCIM endpoint

  1. Copy the code from the GitHub repository.
  2. Save the code using the code editor of your choice, or you can download Visual Studio Code. Give the file a user-friendly name, such as Sync.ps1.
  3. Navigate to the location where you saved the file and run ./sync.ps1.
  4. When prompted, enter the values from the notepad. You can paste these all at one time so you don’t have to copy and paste each individual item.

    Note: When copying and pasting in Windows, choose the PowerShell icon, then Edit > Paste.

     

    Figure 16: Windows Command Prompt – Select Paste to copy all items needed to trigger the sync

    Figure 16: Windows Command Prompt – Select Paste to copy all items needed to trigger the sync

After you paste the values into the PowerShell window, you see the script input as shown in the following screenshot. The client secret and password are secure values and are masked for security purposes.
 

Figure 17: PowerShell script with input values pasted in

Figure 17: PowerShell script with input values pasted in

After the job has started in PowerShell, two messages are displayed. One indicating that synchronization is starting and a following message when synchronization has completed. Both are shown in the following figure.
 

Figure 18: Output from a successful run of the PowerShell script

Figure 18: Output from a successful run of the PowerShell script

View the synchronization status and logs

To verify that the job ran successfully, you can check the completed time from the Azure portal. You can verify the time the script ran by viewing the completion time along with the current status.

To view the status and logs

  1. From the menu, choose Azure Active Directory.
  2. Choose Enterprise applications and select the AWS App.
  3. From the left navigation menu, choose Provisioning and then choose View provisioning details. This displays the last time the sync completed.
     
    Figure 19: View the Provisioning details about the job

    Figure 19: View the Provisioning details about the job

Summary

In this post, I demonstrate how you can use a PowerShell script to trigger the SCIM endpoint to on-demand synchronize Azure AD with AWS Single Sign-On. You can find the code in this GitHub repository and use it to synchronize user and group changes on demand.

If you have feedback about this post, submit comments in the Comments section below.

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

Author

Aidan Keane

Aidan is a Senior Technical Account Manager for AWS Enterprise Support. He has been working with Cloud technologies for more than 5 years. Outside of technology, he is a sports enthusiast who enjoys golf, biking, and watching Liverpool FC. He spends his free time with his family and enjoys traveling to Ireland and South America.

Cross-account and cross-region deployment using GitHub actions and AWS CDK

Post Syndicated from DAMODAR SHENVI WAGLE original https://aws.amazon.com/blogs/devops/cross-account-and-cross-region-deployment-using-github-actions-and-aws-cdk/

GitHub Actions is a feature on GitHub’s popular development platform that helps you automate your software development workflows in the same place you store code and collaborate on pull requests and issues. You can write individual tasks called actions, and combine them to create a custom workflow. Workflows are custom automated processes that you can set up in your repository to build, test, package, release, or deploy any code project on GitHub.

A cross-account deployment strategy is a CI/CD pattern or model in AWS. In this pattern, you have a designated AWS account called tools, where all CI/CD pipelines reside. Deployment is carried out by these pipelines across other AWS accounts, which may correspond to dev, staging, or prod. For more information about a cross-account strategy in reference to CI/CD pipelines on AWS, see Building a Secure Cross-Account Continuous Delivery Pipeline.

In this post, we show you how to use GitHub Actions to deploy an AWS Lambda-based API to an AWS account and Region using the cross-account deployment strategy.

Using GitHub Actions may have associated costs in addition to the cost associated with the AWS resources you create. For more information, see About billing for GitHub Actions.

Prerequisites

Before proceeding any further, you need to identify and designate two AWS accounts required for the solution to work:

  • Tools – Where you create an AWS Identity and Access Management (IAM) user for GitHub Actions to use to carry out deployment.
  • Target – Where deployment occurs. You can call this as your dev/stage/prod environment.

You also need to create two AWS account profiles in ~/.aws/credentials for the tools and target accounts, if you don’t already have them. These profiles need to have sufficient permissions to run an AWS Cloud Development Kit (AWS CDK) stack. They should be your private profiles and only be used during the course of this use case. So, it should be fine if you want to use admin privileges. Don’t share the profile details, especially if it has admin privileges. I recommend removing the profile when you’re finished with this walkthrough. For more information about creating an AWS account profile, see Configuring the AWS CLI.

Solution overview

You start by building the necessary resources in the tools account (an IAM user with permissions to assume a specific IAM role from the target account to carry out deployment). For simplicity, we refer to this IAM role as the cross-account role, as specified in the architecture diagram.

You also create the cross-account role in the target account that trusts the IAM user in the tools account and provides the required permissions for AWS CDK to bootstrap and initiate creating an AWS CloudFormation deployment stack in the target account. GitHub Actions uses the tools account IAM user credentials to the assume the cross-account role to carry out deployment.

In addition, you create an AWS CloudFormation execution role in the target account, which AWS CloudFormation service assumes in the target account. This role has permissions to create your API resources, such as a Lambda function and Amazon API Gateway, in the target account. This role is passed to AWS CloudFormation service via AWS CDK.

You then configure your tools account IAM user credentials in your Git secrets and define the GitHub Actions workflow, which triggers upon pushing code to a specific branch of the repo. The workflow then assumes the cross-account role and initiates deployment.

The following diagram illustrates the solution architecture and shows AWS resources across the tools and target accounts.

Architecture diagram

Creating an IAM user

You start by creating an IAM user called git-action-deployment-user in the tools account. The user needs to have only programmatic access.

  1. Clone the GitHub repo aws-cross-account-cicd-git-actions-prereq and navigate to folder tools-account. Here you find the JSON parameter file src/cdk-stack-param.json, which contains the parameter CROSS_ACCOUNT_ROLE_ARN, which represents the ARN for the cross-account role we create in the next step in the target account. In the ARN, replace <target-account-id> with the actual account ID for your designated AWS target account.                                             Replace <target-account-id> with designated AWS account id
  2. Run deploy.sh by passing the name of the tools AWS account profile you created earlier. The script compiles the code, builds a package, and uses the AWS CDK CLI to bootstrap and deploy the stack. See the following code:
cd aws-cross-account-cicd-git-actions-prereq/tools-account/
./deploy.sh "<AWS-TOOLS-ACCOUNT-PROFILE-NAME>"

You should now see two stacks in the tools account: CDKToolkit and cf-GitActionDeploymentUserStack. AWS CDK creates the CDKToolkit stack when we bootstrap the AWS CDK app. This creates an Amazon Simple Storage Service (Amazon S3) bucket needed to hold deployment assets such as a CloudFormation template and Lambda code package. cf-GitActionDeploymentUserStack creates the IAM user with permission to assume git-action-cross-account-role (which you create in the next step). On the Outputs tab of the stack, you can find the user access key and the AWS Secrets Manager ARN that holds the user secret. To retrieve the secret, you need to go to Secrets Manager. Record the secret to use later.

Stack that creates IAM user with its secret stored in secrets manager

Creating a cross-account IAM role

In this step, you create two IAM roles in the target account: git-action-cross-account-role and git-action-cf-execution-role.

git-action-cross-account-role provides required deployment-specific permissions to the IAM user you created in the last step. The IAM user in the tools account can assume this role and perform the following tasks:

  • Upload deployment assets such as the CloudFormation template and Lambda code package to a designated S3 bucket via AWS CDK
  • Create a CloudFormation stack that deploys API Gateway and Lambda using AWS CDK

AWS CDK passes git-action-cf-execution-role to AWS CloudFormation to create, update, and delete the CloudFormation stack. It has permissions to create API Gateway and Lambda resources in the target account.

To deploy these two roles using AWS CDK, complete the following steps:

  1. In the already cloned repo from the previous step, navigate to the folder target-account. This folder contains the JSON parameter file cdk-stack-param.json, which contains the parameter TOOLS_ACCOUNT_USER_ARN, which represents the ARN for the IAM user you previously created in the tools account. In the ARN, replace <tools-account-id> with the actual account ID for your designated AWS tools account.                                             Replace <tools-account-id> with designated AWS account id
  2. Run deploy.sh by passing the name of the target AWS account profile you created earlier. The script compiles the code, builds the package, and uses the AWS CDK CLI to bootstrap and deploy the stack. See the following code:
cd ../target-account/
./deploy.sh "<AWS-TARGET-ACCOUNT-PROFILE-NAME>"

You should now see two stacks in your target account: CDKToolkit and cf-CrossAccountRolesStack. AWS CDK creates the CDKToolkit stack when we bootstrap the AWS CDK app. This creates an S3 bucket to hold deployment assets such as the CloudFormation template and Lambda code package. The cf-CrossAccountRolesStack creates the two IAM roles we discussed at the beginning of this step. The IAM role git-action-cross-account-role now has the IAM user added to its trust policy. On the Outputs tab of the stack, you can find these roles’ ARNs. Record these ARNs as you conclude this step.

Stack that creates IAM roles to carry out cross account deployment

Configuring secrets

One of the GitHub actions we use is aws-actions/[email protected]. This action configures AWS credentials and Region environment variables for use in the GitHub Actions workflow. The AWS CDK CLI detects the environment variables to determine the credentials and Region to use for deployment.

For our cross-account deployment use case, aws-actions/[email protected] takes three pieces of sensitive information besides the Region: AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY_SECRET, and CROSS_ACCOUNT_ROLE_TO_ASSUME. Secrets are recommended for storing sensitive pieces of information in the GitHub repo. It keeps the information in an encrypted format. For more information about referencing secrets in the workflow, see Creating and storing encrypted secrets.

Before we continue, you need your own empty GitHub repo to complete this step. Use an existing repo if you have one, or create a new repo. You configure secrets in this repo. In the next section, you check in the code provided by the post to deploy a Lambda-based API CDK stack into this repo.

  1. On the GitHub console, navigate to your repo settings and choose the Secrets tab.
  2. Add a new secret with name as TOOLS_ACCOUNT_ACCESS_KEY_ID.
  3. Copy the access key ID from the output OutGitActionDeploymentUserAccessKey of the stack GitActionDeploymentUserStack in tools account.
  4. Enter the ID in the Value field.                                                                                                                                                                Create secret
  5. Repeat this step to add two more secrets:
    • TOOLS_ACCOUNT_SECRET_ACCESS_KEY (value retrieved from the AWS Secrets Manager in tools account)
    • CROSS_ACCOUNT_ROLE (value copied from the output OutCrossAccountRoleArn of the stack cf-CrossAccountRolesStack in target account)

You should now have three secrets as shown below.

All required git secrets

Deploying with GitHub Actions

As the final step, first clone your empty repo where you set up your secrets. Download and copy the code from the GitHub repo into your empty repo. The folder structure of your repo should mimic the folder structure of source repo. See the following screenshot.

Folder structure of the Lambda API code

We can take a detailed look at the code base. First and foremost, we use Typescript to deploy our Lambda API, so we need an AWS CDK app and AWS CDK stack. The app is defined in app.ts under the repo root folder location. The stack definition is located under the stack-specific folder src/git-action-demo-api-stack. The Lambda code is located under the Lambda-specific folder src/git-action-demo-api-stack/lambda/ git-action-demo-lambda.

We also have a deployment script deploy.sh, which compiles the app and Lambda code, packages the Lambda code into a .zip file, bootstraps the app by copying the assets to an S3 bucket, and deploys the stack. To deploy the stack, AWS CDK has to pass CFN_EXECUTION_ROLE to AWS CloudFormation; this role is configured in src/params/cdk-stack-param.json. Replace <target-account-id> with your own designated AWS target account ID.

Update cdk-stack-param.json in git-actions-cross-account-cicd repo with TARGET account id

Finally, we define the Git Actions workflow under the .github/workflows/ folder per the specifications defined by GitHub Actions. GitHub Actions automatically identifies the workflow in this location and triggers it if conditions match. Our workflow .yml file is named in the format cicd-workflow-<region>.yml, where <region> in the file name identifies the deployment Region in the target account. In our use case, we use us-east-1 and us-west-2, which is also defined as an environment variable in the workflow.

The GitHub Actions workflow has a standard hierarchy. The workflow is a collection of jobs, which are collections of one or more steps. Each job runs on a virtual machine called a runner, which can either be GitHub-hosted or self-hosted. We use the GitHub-hosted runner ubuntu-latest because it works well for our use case. For more information about GitHub-hosted runners, see Virtual environments for GitHub-hosted runners. For more information about the software preinstalled on GitHub-hosted runners, see Software installed on GitHub-hosted runners.

The workflow also has a trigger condition specified at the top. You can schedule the trigger based on the cron settings or trigger it upon code pushed to a specific branch in the repo. See the following code:

name: Lambda API CICD Workflow
# This workflow is triggered on pushes to the repository branch master.
on:
  push:
    branches:
      - master

# Initializes environment variables for the workflow
env:
  REGION: us-east-1 # Deployment Region

jobs:
  deploy:
    name: Build And Deploy
    # This job runs on Linux
    runs-on: ubuntu-latest
    steps:
      # Checkout code from git repo branch configured above, under folder $GITHUB_WORKSPACE.
      - name: Checkout
        uses: actions/[email protected]
      # Sets up AWS profile.
      - name: Configure AWS credentials
        uses: aws-actions/[email protected]
        with:
          aws-access-key-id: ${{ secrets.TOOLS_ACCOUNT_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.TOOLS_ACCOUNT_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.REGION }}
          role-to-assume: ${{ secrets.CROSS_ACCOUNT_ROLE }}
          role-duration-seconds: 1200
          role-session-name: GitActionDeploymentSession
      # Installs CDK and other prerequisites
      - name: Prerequisite Installation
        run: |
          sudo npm install -g [email protected]
          cdk --version
          aws s3 ls
      # Build and Deploy CDK application
      - name: Build & Deploy
        run: |
          cd $GITHUB_WORKSPACE
          ls -a
          chmod 700 deploy.sh
          ./deploy.sh

For more information about triggering workflows, see Triggering a workflow with events.

We have configured a single job workflow for our use case that runs on ubuntu-latest and is triggered upon a code push to the master branch. When you create an empty repo, master branch becomes the default branch. The workflow has four steps:

  1. Check out the code from the repo, for which we use a standard Git action actions/[email protected]. The code is checked out into a folder defined by the variable $GITHUB_WORKSPACE, so it becomes the root location of our code.
  2. Configure AWS credentials using aws-actions/[email protected]. This action is configured as explained in the previous section.
  3. Install your prerequisites. In our use case, the only prerequisite we need is AWS CDK. Upon installing AWS CDK, we can do a quick test using the AWS Command Line Interface (AWS CLI) command aws s3 ls. If cross-account access was successfully established in the previous step of the workflow, this command should return a list of buckets in the target account.
  4. Navigate to root location of the code $GITHUB_WORKSPACE and run the deploy.sh script.

You can check in the code into the master branch of your repo. This should trigger the workflow, which you can monitor on the Actions tab of your repo. The commit message you provide is displayed for the respective run of the workflow.

Workflow for region us-east-1 Workflow for region us-west-2

You can choose the workflow link and monitor the log for each individual step of the workflow.

Git action workflow steps

In the target account, you should now see the CloudFormation stack cf-GitActionDemoApiStack in us-east-1 and us-west-2.

Lambda API stack in us-east-1 Lambda API stack in us-west-2

The API resource URL DocUploadRestApiResourceUrl is located on the Outputs tab of the stack. You can invoke your API by choosing this URL on the browser.

API Invocation Output

Clean up

To remove all the resources from the target and tools accounts, complete the following steps in their given order:

  1. Delete the CloudFormation stack cf-GitActionDemoApiStack from the target account. This step removes the Lambda and API Gateway resources and their associated IAM roles.
  2. Delete the CloudFormation stack cf-CrossAccountRolesStack from the target account. This removes the cross-account role and CloudFormation execution role you created.
  3. Go to the CDKToolkit stack in the target account and note the BucketName on the Output tab. Empty that bucket and then delete the stack.
  4. Delete the CloudFormation stack cf-GitActionDeploymentUserStack from tools account. This removes cross-account-deploy-user IAM user.
  5. Go to the CDKToolkit stack in the tools account and note the BucketName on the Output tab. Empty that bucket and then delete the stack.

Security considerations

Cross-account IAM roles are very powerful and need to be handled carefully. For this post, we strictly limited the cross-account IAM role to specific Amazon S3 and CloudFormation permissions. This makes sure that the cross-account role can only do those things. The actual creation of Lambda, API Gateway, and Amazon DynamoDB resources happens via the AWS CloudFormation IAM role, which AWS  CloudFormation assumes in the target AWS account.

Make sure that you use secrets to store your sensitive workflow configurations, as specified in the section Configuring secrets.

Conclusion

In this post we showed how you can leverage GitHub’s popular software development platform to securely deploy to AWS accounts and Regions using GitHub actions and AWS CDK.

Build your own GitHub Actions CI/CD workflow as shown in this post.

About the author

 

Damodar Shenvi Wagle is a Cloud Application Architect at AWS Professional Services. His areas of expertise include architecting serverless solutions, ci/cd and automation.

Role-based access control using Amazon Cognito and an external identity provider

Post Syndicated from Eran Medan original https://aws.amazon.com/blogs/security/role-based-access-control-using-amazon-cognito-and-an-external-identity-provider/

Amazon Cognito simplifies the development process by helping you manage identities for your customer-facing applications. As your application grows, some of your enterprise customers may ask you to integrate with their own Identity Provider (IdP) so that their users can sign-on to your app using their company’s identity, and have role-based access-control (RBAC) based on their company’s directory group membership.

For your own workforce identities, you can use AWS Single Sign-On (SSO) to enable single sign-on to your cloud applications or AWS resources.

For your customers who would like to integrate your application with their own IdP, you can use Amazon Cognito user pools’ external identity provider integration.

In this post, you’ll learn how to integrate Amazon Cognito with an external IdP by deploying a demo web application that integrates with an external IdP via SAML 2.0. You will use directory groups (for example, Active Directory or LDAP) for authorization by mapping them to Amazon Cognito user pool groups that your application can read to make access decisions.

Architecture

The demo application is implemented using Amazon Cognito, AWS Amplify, Amazon API Gateway, AWS Lambda, Amazon DynamoDB, Amazon Simple Storage Service (S3), and Amazon CloudFront to achieve a serverless architecture. You will make use of infrastructure-as-code by using AWS CloudFormation and the AWS Cloud Development Kit (CDK) to model and provision your cloud application resources, using familiar programming languages.

The following diagram shows an overview of this architecture and the steps in the login flow, which should help clarify what you are going to deploy.
 

Figure 1: Architecture Diagram

Figure 1: Architecture Diagram

First visit

When a user visits the web application at the first time, the flow is as follows:

  1. The client side of the application (also referred to as the front end) uses the AWS Amplify JavaScript library (Amplify.js) to simplify authentication and authorization. Using Amplify, the application detects that the user is unauthenticated and redirects to Amazon Cognito, which then sends a SAML request to the IdP.
  2. The IdP authenticates the user and sends a SAML response back to Amazon Cognito. The SAML response includes common attributes and a multi-value attribute for group membership.
  3. Amazon Cognito handles the SAML response, and maps the SAML attributes to a just-in-time user profile. The SAML groups attribute is mapped to a custom user pool attribute named custom:groups.
  4. An AWS Lambda function named PreTokenGeneration reads the custom:groups custom attribute and converts it to a JSON Web Token (JWT) claim named cognito:groups. This associates the user to a group, without creating a group.

    This attribute conversion is optional and implemented to demo how you can use Pre Token Generation Lambda trigger to customize your JWT token claims, mapping the IdP groups to the attributes your application recognizes. You can also use this trigger to make additional authorization decisions. For example, if user is a member of multiple groups, you may choose to map only one of them.

  5. Amazon Cognito returns the JWT tokens to the front end.
  6. The Amplify client library stores the tokens and handles refreshes.
  7. The front end makes a call to a protected API in Amazon API Gateway.
  8. API Gateway uses an Amazon Cognito user pools authorizer to validate the JWT’s signature and expiration. If this is successful, API Gateway passes the JWT to the application’s Lambda function (also referred to as the backend).
  9. The backend application code reads the cognito:groups claim from the JWT and decides if the action is allowed. If the user is a member of the right group then the action is allowed, otherwise the action is denied.

We will go into more detail about these steps after describing a bit more about the implementation details.

For more information about JWT tokens and claims, see Introduction to JSON Web Tokens.

Prerequisites

The following are the prerequisites for the solution described in this post:

Cost estimate

For an account under the 12-month Free Tier period, there should be no cost associated with running this example. However, to avoid any unexpected costs you should terminate the example stack after it’s no longer needed. For more information, see AWS Free Tier and AWS Pricing.

Running the demo application

In this part, you will go over the steps to setup and run the demo application. All the example code in this solution can be found on the amazon-cognito-example-for-external-idp code repository on GitHub.

To deploy the application without an IdP integration

  1. Open a bash-compatible command-line terminal and navigate to a directory of your choice. For Windows users: install Git for Windows and open Git BASH from the start menu.
  2. To get the code from the GitHub repository, enter the following:
    git clone https://github.com/aws-samples/amazon-cognito-example-for-external-idp 
    cd amazon-cognito-example-for-external-idp
    

  3. The template env.sh.template contains configuration settings for the application that you will modify later when you configure the IdP. To copy env.sh.template to env.sh, enter the following:
    cp env.sh.template env.sh

    Figure 2: Cloning the example repository and copying the template configuration file

    Figure 2: Cloning the example repository and copying the template configuration file

  4. The install.sh script will install the AWS CDK toolkit with the dependencies and will configure and bootstrap your environment:
    ./install.sh
    

    Figure 3: Installing dependencies

    Figure 3: Installing dependencies

    You may get prompted to agree to sending Angular analytics. You will also get notified if there are package vulnerabilities. If this is the case run npm audit –fix –prod in all subdirectories to resolve them.

  5. Once the environment has been successfully bootstrapped you need to deploy the CloudFormation stack:
    ./deploy.sh 
    

    Figure 4: Deploying the CloudFormation stack

    Figure 4: Deploying the CloudFormation stack

  6. You will be prompted to accept the IAM changes. These changes will allow API gateway service to call the demo application lambda function (APIFunction), Amazon Cognito to invoke Pre-Token Generation lambda function, demo application lambda function to access DynamoDB user’s table (used to implement user’s global sign out), and more. You’ll need to review these changes according to your current security approval level and confirm them to continue.

    Under Do you wish to deploy these changes (y/n)?, type y and press Enter.

    Figure 5: Reviewing and confirming changes

    Figure 5: Reviewing and confirming changes

  7. A few moments after deploying the application’s CloudFormation stack, the terminal displays the IdP settings, which should look like the following:
     
    Figure 6: IdP settings

    Figure 6: IdP settings

    Make a note of these values; you will use them later to configure the IdP.

Configure the IdP

Every IdP is different, but there are some common steps you will need to follow. To configure the IdP, do the following:

  1. Provide the IdP with the values for the following two properties, which you made note of in the previous section:
    • Single sign on URL / Assertion Consumer Service URL / ACS URL:
      https://<domainPrefix>.auth.<region>.amazoncognito.com/saml2/idpresponse
      

    • Audience URI / SP Entity ID / Entity ID:
      urn:amazon:cognito:sp:<yourUserPoolID>
      

  2. Configure the field mapping for the SAML response in the IdP. Map the first name, last name, email, and groups (as a multivalue attribute) into SAML response attributes with the names firstName, lastName, email, and groups, respectively.

    Recommended: Filter the mapped groups to only those that are relevant to the application (for example, by a prefix filter). There is a 2,048-character limit on the custom attribute, so filtering avoids exceeding the character limit, and also avoids passing irrelevant information to the application.

  3. In the IdP, create two demo groups called pet-app-users and pet-app-admins, and create two demo users, for example, [email protected] and [email protected], and then assign one to each group, respectively.

See the following specific instructions for some popular IdPs, or see the documentation for your customer’s specific IdP:

Get the IdP SAML metadata URL or file

Get the metadata URL or file from the IdP: you will use this later to configure your Cognito user pool integration with the IdP. For more information, see Integrating Third-Party SAML Identity Providers with Amazon Cognito User Pools.

To update the application with the SAML metadata URL or file

The following will configure the SAML IdP in the Amazon Cognito User Pool using the IdP metadata above:

  1. Using your favorite text editor, open the env.sh file.
  2. Uncomment the line starting with # export IDENTITY_PROVIDER_NAME (remove the # sign).
  3. Uncomment the line starting with # export IDENTITY_PROVIDER_METADATA.
  4. If you have a metadata URL from the IdP, enter it following the = sign:
    export IDENTITY_PROVIDER_METADATA=REPLACE_WITH_URL
    

    Or, if you downloaded the metadata as a file, enter $(cat path/to/downloaded-metadata.xml):

    export IDENTITY_PROVIDER_METADATA=$(cat REPLACE_WITH_PATH)
    

    Figure 7: Editing the identity provider metadata in the env.sh configuration file

    Figure 7: Editing the identity provider metadata in the env.sh configuration file

To re-deploy the application

  1. Run ./diff.sh to see the changes to the CloudFormation stack (added metadata URL).
     
    Figure 8: Run ./diff.sh

    Figure 8: Run ./diff.sh

  2. Run ./deploy.sh to deploy the update.

To launch the UI

There is both an Angular version and a React version of the same UI, both have the same functionality. You can use either version depending on your preference.

  1. Start the front end application with your chosen version of the UI with one of the following:
    • React: cd ui-react && npm start
    • Angular: cd ui-angular && npm start
  2. To simulate a new session, in your web browser, open a new window in private browsing or incognito mode, then for the URL, enter http://localhost:3000. You should see a screen similar to the following:
     
    Figure 9: Private browsing sign-in screen

    Figure 9: Private browsing sign-in screen

  3. Choose Single Sign On to be taken to the IdP’s sign-in page, where you will sign in if needed. After you are authenticated by the IdP, you’ll be redirected back to the application.

    If you have multiple IdPs, or if you have both internal and external users that will authenticate directly with the user pool, you can choose the Sign In / Sign Up button instead. This redirects you to the Amazon Cognito hosted UI sign in page, rather than taking you directly to the IdP. For more information, see Using the Amazon Cognito Hosted UI for Sign-Up and Sign-In.

  4. Using a new private browsing session (to clear any state), sign in with the user associated with the group pet-app-users and create some sample entries. Then, sign out. Open another private browsing session, and sign in with the user associated with the pet-app-admins group. Notice that you can see the other user’s entries. Now, create a few entries as an admin, then sign out. Open another new private browsing session, sign in again as the pet-app-users user, and notice that you can’t see the entries created by the admin user.
     
    Figure 10: Example view for a user who is only a member of the pet-app-users group

    Figure 10: Example view for a user who is only a member of the pet-app-users group

     

    Figure 11: Example view for a user who is also a member of the pet-app-admins group

    Figure 11: Example view for a user who is also a member of the pet-app-admins group

Implementation

Next, review the details of what each part of the demo application does, so that you can modify it and use it as a starting point for your own application.

Infrastructure

Take a look at the code in the cdk.ts file—a sample CDK file that creates the infrastructure. You can find it in the amazon-cognito-example-for-external-idp/cdk/src directory in the cloned GitHub repo. The key resources it creates are the following:

  1. A Cognito user pool (new cognito.UserPool…). This is where the just-in-time provisioning created users who federate in from the IdP. It also creates a custom attribute named groups, which you can see as custom:groups in the console.
     
    Figure 12: Custom attribute named groups

    Figure 12: Custom attribute named groups

  2. IdP integration which provides the mapping between the attributes in the SAML assertion from the IdP and Amazon Cognito attributes. For more information, see Specifying Identity Provider Attribute Mappings for Your User Pool.
    (new cognito.CfnUserPoolIdentityProvider…).
  3. An authorizer (new apigateway.CfnAuthorizer…). The authorizer is linked to an API resource method (authorizer: {authorizerId: cfnAuthorizer.ref}).

    It ensures that the user must be authenticated and must have a valid JWT token to make API calls to this resource. It uses Lambda proxy integration to intercept requests.

  4. The PreTokenGeneration Lambda trigger, which is used for the mapping between a user’s Active Directory or LDAP groups (passed on the SAML response from the IdP) to user pool groups (const preTokenGeneration = new lambda.Function…). For the PreTokenGeneration Lambda trigger code used in this solution, see the index.ts file on GitHub.

The application

Backend

The example application in this solution uses a serverless backend, but you can modify it to use Amazon Elastic Compute Cloud (Amazon EC2), Amazon Elastic Container Service (Amazon ECS), Amazon Elastic Kubernetes Service (Amazon EKS), AWS Fargate, AWS Elastic Beanstalk, or even an on-premises server as the backend. To configure your API gateway to point to a server-based application, see Set up HTTP Integrations in API Gateway or Set up API Gateway Private Integrations.

Middleware

Take a look at the code in the express.js sample in the app.ts file on GitHub. You’ll notice some statements starting with app.use. These are interceptors that are invoked for all requests.

app.use(eventContext());

app.use(authorizationMiddleware({
  authorizationHeaderName: authorizationHeaderName,
  supportedGroups: [adminsGroupName, usersGroupName],
  forceSignOutHandler: forceSignOutHandler,
  allowedPaths: ["/"],
}));

Some explanation:

  1. eventContext: the example application in this solution uses AWS Serverless Express which allows you to run the Express framework for Node.js directly on AWS Lambda.
  2. authorizationMiddleware is a helper middleware that does the following:
    1. It enriches the express.js request object with several syntactic sugars such as req.groups and req.username (a shortcut to get the respective claims from the JWT token).
    2. It ensures that the currently logged in user is a member of at least one of the supportedGroups provided. If not, it will return a 403 response.

Endpoints

Still in the Express.js app.ts file on GitHub, take a closer look at one of the API’s endpoints (GET /pets).

app.get("/pets", async (req: Request, res: Response) => {

  if (req.groups.has(adminsGroupName)) {
    // if the user has the admin group, we return all pets
    res.json(await storageService.getAllPets());
  } else {
    // else, just owned pets (middleware ensure that the user has at least one group)
    res.json(await storageService.getAllPetsByOwner(req.username));
  }
});

With the groups claim information, your application can now make authorization decisions based on the user’s role (show all items if they are an admin, otherwise just items they own). Having this logic as part of the application also allows you to unit test your authorization logic, and run it locally, or offline, before deploying it.

Front end

The front end can be built in your framework of choice. You can start with the sample UIs provided for either React or Angular. In both, the AWS Amplify client library handles the integration with Amazon Cognito and API Gateway for you. For more information about AWS Amplify, see the Amplify Framework page on GitHub.

Note: You can use AWS Amplify to create the infrastructure in a wizard-like way, without writing CloudFormation. In our example, because we used the AWS CDK for the infrastructure, we needed a configuration file to point Amplify to the created infrastructure.

The following are some notable files, and explanations of what they do:

  • generateConfig.ts reads the CloudFormation stack output parameters, and creates a file named autoGenConfig.js, which looks like the following:
    // this file is auto generated, do not edit it directly
    export default {
      cognitoDomain: "youruniquecognitodomain.auth.region.amazoncognito.com",
      region: "region",
      cognitoUserPoolId: "youruserpoolid",
      cognitoUserPoolAppClientId: "yourusepoolclientid",
      apiUrl: "https://yourapigwapiid.execute-api.region.amazonaws.com/prod/",
    };
    

    The file generateConfig.ts is triggered after calling ./deploy.sh, or ./config-ui.sh.

  • APIService.ts: calls the backend API, passing the user’s token. For example, calling the GET/pets API:
    public async getAllPets(): Promise<Pet[]> {
      const authorizationHeader = await this.getAuthorizationHeader();
      return await this.api.get(REST_API_NAME, '/pets', {headers: authorizationHeader});
    }
    

Step-by-step example

Now that you have an understanding of the solution, we will take you through a step-by-step example. You can see how everything works together in sequence, and how the tokens are passing between Cognito, your demo application, and the API gateway.

  1. Create a new browser session by starting a private/incognito session.
  2. Launch the UI by using the Angular example from the To launch the UI section:
    cd ui-angular && npm start
    

  3. Open the developer tools in your browser. In most browsers, you can do this by pressing F12 (in Chrome and FireFox in Windows), or Option+Command+i (Chrome, Firefox, or Safari on a Mac).
  4. In the developer tools panel, navigate to the Network tab, and ensure that it is in recording mode and logs are persisting. For more details for various browsers, see How to View a SAML Response in Your Browser for Troubleshooting.
  5. When the page loads, the following happens behind the scenes in the front end (example code available for either Angular or React):
    1. Using Amplify.js, AWS Amplify checks if the user is currently logged in
      let cognitoUser = await Auth.currentAuthenticatedUser();
      

      Because this is a new browsing session, the user is not logged in, and the Sign In / Sign Up and Single Sign On buttons will appear.

    2. Choose Single Sign On, and AWS Amplify will redirect the browser to the IdP.
      Auth.federatedSignIn(idpName)
      

  6. In the IdP sign-in page, sign in as one of the users created earlier (e.g. [email protected] or [email protected]).
  7. In the Network tab of your browser’s developer tools panel, locate the request to Amazon Cognito’s /saml2/idresponse endpoint.
  8. The following is an example using Chrome, but you can do it similarly using other browsers. In the Form Data section, you can see the SAMLResponse field that was sent back from the IdP after you authenticated.
     
    Figure 13: Inspecting the SAML response

    Figure 13: Inspecting the SAML response

  9. Copy the SAMLResponse value (drag to select the area marked in green above, and make sure you don’t include the RelayState field).
  10. At the command line, use the following example to decode the SAMLResponse value. Be sure to replace SAMLResponse by pasting the text copied in the previous step:
    echo "SAMLResponse" | base64 --decode > saml_response.xml 
    

  11. Open the saml_response.xml file, and look at the part that starts with <saml2:Attribute Name="groups". This is the attribute that contains the groups that your user belongs to, according to the IdP. For more ways to inspect and troubleshoot the SAML response, see How to View a SAML Response in Your Browser for Troubleshooting.
  12. Amazon Cognito applies the mapping defined in the CloudFormation stack to these attributes. For example, the IdP SAML response attribute named groups is mapped to the user pool custom attribute named custom:groups.
    • In order to modify the mapping, edit your local copy of the cdk.ts file.
    • In order to view the mapped attribute for a user, do the following:
      1. Sign into the AWS Management Console using the same account you used for the demo setup.
      2. Select Manage User Pools.
      3. Select the pool you created for this demo and choose Users and groups.
      4. Search for the user account you just signed in with, and choose its username.

        As you can see in the following example, the custom:groups claim is set automatically. (the custom: prefix is added to all custom attributes automatically):

    Figure 14: Mapped user attributes

    Figure 14: Mapped user attributes

  13. The PreTokenGeneration Lambda function then reads the mapped custom:groups attribute value, parses it, and converts it to an array; and then stores it in the cognito:groups claim. In order to customize the mapping, edit the Lambda function’s code in your local copy of the index.ts file and run ./deploy.sh to redeploy your application.
  14. Now that the front end has the JWT token, when the page loads, it will request to load all the items (a call to a protected API, passing the token in the form of an authorization header).
  15. Look at the Network tab again, under the GET request that starts with pets.

    Under Request Headers, look at the authorization header. The long value you see is the encoded token passed as part of the request. The following is an example of how the decoded JWT will look:

    {
      "cognito:groups": [
        "pet-app-users",
        "pet-app-admins"
      ], // <- this is what the PreTokenGeneration lambda added
      
      "cognito:username": "IdP_Alice",
      "custom:groups": "[pet-app-users, pet-app-admins]", //what we got via SAML
      "email": "[email protected]"
      …
    
    }
    

  16. Optionally, if you’d like to modify or add new requests to a new API paths, edit your local copy of the APIService.ts file by using one of the following examples.
    • Sending the request with the authorization header:
      public async getAllPets(): Promise<Pet[]> {
        const authorizationHeader = await this.getAuthorizationHeader();
        return await this.api.get(REST_API_NAME, '/pets', 
          {headers: authorizationHeader});
      }
      

    • The authorization header is obtained using this helper function:
      private async getAuthorizationHeader() {
        const session = await this.auth.currentSession();
        const idToken = session.getIdToken().getJwtToken();
        return {Authorization: idToken}
      }
      

  17. After the previous request is sent to Amazon API Gateway, the Amazon Cognito user pool authorizer validated the JWT token based on the token signature, to ensure that it was not tampered with, and that it was still valid. You can see the way the authorizer is setup in the cdk.ts file on GitHub.
  18. Based on which user you signed-in with previously, you’ll either see all items, or only items you own. How does it work? As mentioned earlier, the backend application code reads the groups claim from the validated token and decides if the action is allowed. If the user is a member of a specific group or has a specific attribute, allow; else, deny. The relevant code that makes that decision can be seen in the Express.js example app file in the app.ts file on GitHub.

Customizing the application

The following are some important issues to consider when customizing the app to your needs:

  • If you modify the app client, do not add the aws.cognito.signin.user.admin scope to it. The aws.cognito.signin.user.admin scope grants access to Amazon Cognito User Pool API operations that require access tokens, such as UpdateUserAttributes and VerifyUserAttribute. The demo application makes authorization decisions based on the custom:group attribute populated from the IdP. Because the IdP is the single source of truth for its users, they should not be able to modify any attribute, particularly the custom:groups attribute.
  • We recommend that you do not change the mapped attribute after the stack is deployed. The reason is that the attribute gets persisted in the user profile after it is mapped. For example, if you first map groups to custom:groups, and a user signs in, then later you change the mapping of groups to custom:groups2, the next time the user signs in, their profile will have both attributes: custom:groups (with the last value it was mapped to it) as well as custom:groups2 (with the current value). To avoid having to clear old mapped attributes, we recommend not changing the mapping after it is created.
  • This solution utilizes Amazon Cognito’s OAuth 2.0 flows to provide federated sign-in from an external IdP (and optionally also sign-in directly with the user pool via the hosted UI in case you would like to support both use cases). It is not applicable for non OAuth 2.0 flows (e.g. the custom UI), for example, using InititateAuth/SRP.

Conclusion

You can integrate your application with your customer’s IdP of choice for authentication and authorization for your application, without integrating with LDAP, or Active Directory directly. Instead, you can map read-only, need-to-know information from the IdP to the application. By using Amazon Cognito, you can normalize the structure of the JWT token, so that you can add multiple IdPs, social login providers, and even regular username and password-based users (stored in user pools). And you can do all this without changing any application code. Amazon API Gateway’s native integration with Amazon Cognito user pools authorizer streamlines your validation of the JWT integrity, and after it has been validated, you can use it to make authorization decisions in your application’s backend. Using this example, you can focus on what differentiates your application, and let AWS do the undifferentiated heavy lifting of identity management for your customer-facing applications.

For all the code examples described in this post, see the amazon-cognito-example-for-external-idp code repository on GitHub.

If you have feedback about this post, submit comments in the Comments section below. If you have questions about this post, start a new thread on the Amazon Cognito forum or contact AWS Support.

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

Author

Eran Medan

Eran is a Software Development Manager based in Atlanta and leads the AWS Jam team, which uses Amazon Cognito and other services mentioned in this post to run their service. Other than jamming on AWS, Eran likes to jam on his guitar or fly airplanes in virtual reality.

Yuri Duchovny

Yuri is a New York-based Solutions Architect specializing in cloud security, identity, and compliance. He supports cloud transformations at large enterprises, helping them make optimal technology and organizational decisions. Prior to his AWS role, Yuri’s areas of focus included application and networking security, DoS, and fraud protection. Outside of work, he enjoys skiing, sailing, and traveling the world.

Integrating AWS CloudFormation security tests with AWS Security Hub and AWS CodeBuild reports

Post Syndicated from Vesselin Tzvetkov original https://aws.amazon.com/blogs/security/integrating-aws-cloudformation-security-tests-with-aws-security-hub-and-aws-codebuild-reports/

The concept of infrastructure as code, by using pipelines for continuous integration and delivery, is fundamental for the development of cloud infrastructure. Including code quality and vulnerability scans in the pipeline is essential for the security of this infrastructure as code. In one of our previous posts, How to build a CI/CD pipeline for container vulnerability scanning with Trivy and AWS Security Hub, you learned how to scan containers to efficiently identify Common Vulnerabilities and Exposures (CVEs) and work with your developers to address them.

In this post, we’ll continue this topic, and also introduce a method for integrating open source tools that find potentially insecure patterns in your AWS CloudFormation templates with both AWS Security Hub and AWS CodeBuild reports. We’ll be using Stelligent’s open source tool CFN-Nag. We also show you how you can extend the solution to use AWS CloudFormation Guard (currently in preview).

One reason to use this integration is that it gives both security and development teams visibility into potential security risks, and resources that are insecure or non-compliant to your company policy, before they’re deployed.

Solution benefit and deliverables

In this solution, we provide you with a ready-to-use template for performing scanning of your AWS CloudFormation templates by using CFN-Nag. This tool has more than 140 predefined patterns, such as AWS Identity and Access Management (IAM) rules that are too permissive (wildcards), security group rules that are too permissive (wildcards), access logs that aren’t enabled, or encryption that isn’t enabled. You can additionally define your own rules to match your company policy as described in the section later in this post, by using custom profiles and exceptions, and suppressing false positives.

Our solution enables you to do the following:

  • Integrate CFN-Nag in a CodeBuild project, scanning the infrastructure code for more than 140 possible insecure patterns, and classifying them as warnings or a failing test.
  • Learn how to integrate AWS CloudFormation Guard (CFN-Guard). You need to define your scanning rules in this case.
  • Generate CodeBuild reports, so that developers can easily identify failed security tests. In our sample, the build process fails if any critical findings are identified.
  • Import to Security Hub the aggregated finding per code branch, so that security professionals can easily spot vulnerable code in repositories and branches. For every branch, we import one aggregated finding.
  • Store the original scan report in an Amazon Simple Storage Service (Amazon S3) bucket for auditing purposes.

Note: in this solution, the AWS CloudFormation scanning tools won’t scan your application code that is running at AWS Lambda functions, Amazon Elastic Container Service (Amazon ECS), or Amazon Elastic Compute Cloud (Amazon EC2) instances.

Architecture

Figure 1 shows the architecture of the solution. The main steps are as follows:

  1. Your pipeline is triggered when new code is pushed to CodeCommit (which isn’t part of the template) to start a new build.
  2. The build process scans the AWS CloudFormation templates by using the cfn_nag_scan or cfn-guard command as defined by the build job.
  3. A Lambda function is invoked, and the scan report is sent to it.
  4. The scan report is published in an S3 bucket via the Lambda function.
  5. The Lambda function aggregates the findings report per repository and git branch and imports the report to Security Hub. The Lambda function also suppresses any previous findings related to this current repo and branch. The severity of the finding is calculated by the number of findings and a weight coefficient that depends on whether the finding is designated as warning or critical.
  6. Finally, the Lambda function generates the CodeBuild test report in JUnit format and returns it to CodeBuild. This report only includes information about any failed tests.
  7. CodeBuild creates a new test report from the new findings under the SecurityReports test group.
Figure 1: Solution architecture

Figure 1: Solution architecture

Walkthrough

To get started, you need to set up the sample solution that scans one of your repositories by using CFN-Nag or CFN-Guard.

To set up the sample solution

  1. Log in to your AWS account if you haven’t done so already. Choose Launch Stack to launch the AWS CloudFormation console with the prepopulated AWS CloudFormation demo template. Choose Next.

    Select the Launch Stack button to launch the templateAdditionally, you can find the latest code on GitHub.

  2. Fill in the stack parameters as shown in Figure 2:
    • CodeCommitBranch: The name of the branch to be monitored, for example refs/heads/master.
    • CodeCommitUrl: The clone URL of the CodeCommit repo that you want to monitor. It must be in the same Region as the stack being launched.
    • TemplateFolder: The folder in your repo that contains the AWS CloudFormation templates.
    • Weight coefficient for failing: The weight coefficient for a failing violation in the template.
    • Weight coefficient for warning: The weight coefficient for a warning in the template.
    • Security tool: The static analysis tool that is used to analyze the templates (CFN-Nag or CFN-Guard).
    • Fail build: Whether to fail the build when security findings are detected.
    • S3 bucket with sources: This bucket contains all sources, such as the Lambda function and templates. You can keep the default text if you’re not customizing the sources.
    • Prefix for S3 bucket with sources: The prefix for all objects. You can keep the default if you’re not customizing the sources.
Figure 2: AWS CloudFormation stack

Figure 2: AWS CloudFormation stack

View the scan results

After you execute the CodeBuild project, you can view the results in three different ways depending on your preferences: CodeBuild report, Security Hub, or the original CFN-Nag or CFN-Guard report.

CodeBuild report

In the AWS Management Console, go to CodeBuild and choose Report Groups. You can find the report you are interested in under SecurityReports. Both failures and warnings are represented as failed tests and are prefixed with W(Warning) or F(Failure), respectively, as shown in Figure 3. Successful tests aren’t part of the report because they aren’t provided by CFN-Nag reports.

Figure 3: AWS CodeBuild report

Figure 3: AWS CodeBuild report

In the CodeBuild navigation menu, under Report groups, you can see an aggregated view of all scans. There you can see a historical view of the pass rate of your tests, as shown in Figure 4.

Figure 4: AWS CodeBuild Group

Figure 4: AWS CodeBuild Group

Security Hub findings

In the AWS Management Console, go to Security Hub and select the Findings view. The aggregated finding per branch has the title CFN scan repo:name:branch with Company Personal and Product Default. The name and branch are placeholders for the repo and branch name. There is one active finding per repo and branch. All previous reports for this repo and branch are suppressed, so that by default you see only the last ones. If necessary, you can see the previous reports by removing the selection filter in the Security Hub finding console. Figure 5 shows an example of the Security Hub findings.

Figure 5: Security Hub findings

Figure 5: Security Hub findings

Original scan report

Lastly, you can find the original scan report in the S3 bucket aws-sec-build-reports-hash. You can also find a reference to this object in the associated Security Hub finding source URL. The S3 object key is constructed as follows.


cfn-nag-report/repo:source_repository/branch:branch_short/cfn-nag-createdAt.json

where source_repository is the name of the repository, branch_short is the name of the branch, and createdAt is the report date.

The following screen capture shows a sample view of the content.

Figure 6: CFN_NAG report sample

Figure 6: CFN_NAG report sample

Security Hub severity and weight coefficients

The Lambda function aggregates CFN-Nag findings to one Security Hub finding per branch and repo. We consider that in this way you get the best visibility without losing orientation in too many findings if you have a large code base.

The Security Hub finding severity is calculated as follows:

  • CFN-Nag critical findings are weighted (multiplied) by 20 and the warnings by 1.
  • The sum of all CFN-Nag findings multiplied by their weighted coefficient results in the severity of the Security Hub finding.

The severity label or normalized severity (from 0 to 100) (see AWS Security Finding Format (ASFF) for more information) is calculated from the summed severity. We implemented the following convention:

  • If the severity is more than 100 points, the label is set as CRITICAL (100).
  • If the severity is lower than 100, the normalized severity and label are mapped as described in AWS Security Finding Format (ASFF).

Your company might have a different way to calculate the severity. If you want to adjust the weight coefficients, change the stack parameter. If you want to adjust the mapping of the CFN-Nag findings to Security hub severity, then you’ll need to adapt the Lambda’s calculateSeverity Python function.

Using custom profiles and exceptions, and suppressing false positives

You can customize CFN-Nag to use a certain rule set by including the specific list of rules to apply (called a profile) within the repository. Customizing rule sets is useful because developers or applications might have different security considerations or risk profiles in specific applications. Additionally the operator might prefer to exclude rules that are prone to introducing false positives.

To add a custom profile, you can modify the cfn_nag_scan command specified in the CodeBuild buildspec.yml file. Use the –profile-path command argument to point to the file that contains the list of rules to use, as shown in the following code sample.


cfn_nag_scan --fail-on-warnings –profile-path .cfn_nag.profile  --input-path  ${TemplateFolder} -o json > ./report/cfn_nag.out.json

Where .cfn_nag.profile file contains one rule identifier per line:


F2
F3
F5
W11

You can find the full list of available rules using cfn_nag_rules command.

You can also choose instead to use a global deny list of rules, or directly suppress findings per resource by using Metadata tags in each AWS CloudFormation resource. For more information, see the CFN-Nag GitHub repository.

Integrating with AWS CloudFormation Guard

The integration with AWS CloudFormation Guard (CFN-Guard) follows the same architecture pattern as CFN-Nag. The ImportToSecurityHub Lambda function can process both CFN-Nag and CFN-Guard results to import to Security Hub and generate a CodeBuild report.

To deploy the CFN-Guard tool

  1. In the AWS Management Console, go to CloudFormation, and then choose Update the previous stack deployed.
  2. Choose Next, and then change the SecurityTool parameter to cfn-guard.
  3. Continue to navigate through the console and deploy the stack.

This creates a new buildspec.yml file that uses the cfn-guard command line interface (CLI) to scan all AWS CloudFormation templates in the source repository. The scans use an example rule set found in the CFN-Guard repository.

You can choose to generate the rule set for the AWS CloudFormation templates that are required by the scanning engine and add the rule set to your repository as described on the GitHub page for AWS CloudFormation Guard. The rule set must reflect your company security policy. This can be one set for all templates, or dependent on the security profile of the application.

You can use your own rule set by modifying the cfn-guard –rule_path parameter to point to a file from within your repository, as follows.


cfn-guard --rule_set .cfn_guard.ruleset --template  "$template" > ./report/template_report

Troubleshooting

If the build report fails, you can find the CloudBuild run logs in the CodeBuild Build history. The build will fail if critical security findings are detected in the templates.

Additionally, the Lambda function execution logs can be found in the CloudWatch Log group aws/lambda/ImportToSecurityHub.

Summary

In this post, you learned how to scan the AWS CloudFormation templates for resources that are potentially insecure or not compliant to your company policy in a CodeBuild project, import the findings to Security Hub, and generate CodeBuild test reports. Integrating this solution to your pipelines can help multiple teams within your organization detect potential security risks in your infrastructure code before its deployed to your AWS environments. If you would like to extend the solution further and need support, contact AWS professional services or an Amazon Partner Network (APN) Partner. If you have technical questions, please use the AWS Security Hub or AWS CodeBuild forums.

If you have feedback about this post, submit comments in the Comments section below.

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

Author

Vesselin Tzvetkov

Vesselin is senior security consultant at AWS Professional Services and is passionate about security architecture and engineering innovative solutions. Outside of technology, he likes classical music, philosophy, and sports. He holds a Ph.D. in security from TU-Darmstadt and a M.S. in electrical engineering from Bochum University in Germany.

Author

Joaquin Manuel Rinaudo

Joaquin is a Senior Security Consultant with AWS Professional Services. He is passionate about building solutions that help developers improve their software quality. Prior to AWS, he worked across multiple domains in the security industry, from mobile security to cloud and compliance related topics. In his free time, Joaquin enjoys spending time with family and reading science-fiction novels.

How to use trust policies with IAM roles

Post Syndicated from Jonathan Jenkyn original https://aws.amazon.com/blogs/security/how-to-use-trust-policies-with-iam-roles/

AWS Identity and Access Management (IAM) roles are a significant component in the way customers operate in Amazon Web Service (AWS). In this post, I’ll dive into the details on how Cloud security architects and account administrators can protect IAM roles from misuse by using trust policies. By the end of this post, you’ll know how to use IAM roles to build trust policies that work at scale, providing guardrails to control access to resources in your organization.

In general, there are four different scenarios where you might use IAM roles in AWS:

  • One AWS service accesses another AWS service – When an AWS service needs access to other AWS services or functions, you can create a role that will grant that access.
  • One AWS account accesses another AWS account – This use case is commonly referred to as a cross-account role pattern. This allows human or machine IAM principals from other AWS accounts to assume this role and act on resources in this account.
  • A third-party web identity needs access – This use case allows users with identities in third-party systems like Google and Facebook, or Amazon Cognito, to use a role to access resources in the account.
  • Authentication using SAML2.0 federation – This is commonly used by enterprises with Active Directory that want to connect using an IAM role so that their users can use single sign-on workflows to access AWS accounts.

In all cases, the makeup of an IAM role is the same as that of an IAM user and is only differentiated by the following qualities:

  • An IAM role does not have long term credentials associated with it; rather, a principal (an IAM user, machine, or other authenticated identity) assumes the IAM role and inherits the permissions assigned to that role.
  • The tokens issued when a principal assumes an IAM role are temporary. Their expiration reduces the risks associated with credentials leaking and being reused.
  • An IAM role has a trust policy that defines which conditions must be met to allow other principals to assume it. This trust policy reduces the risks associated with privilege escalation.

Recommendation: You should make extensive use of temporary IAM roles rather than permanent credentials such as IAM users. For more information review this page: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html

While the list of users having access to your AWS accounts can change over time, the roles used to manage your AWS account probably won’t. The use of IAM roles essentially decouples your enterprise identity system (SAML 2.0) from your permission system (AWS IAM policies), simplifying management of each.

Managing access to IAM roles

Let’s dive into how you can create relationships between your enterprise identity system and your permissions system by looking at the policy types you can apply to an IAM role.

An IAM role has three places where it uses policies:

  • Permission policies (inline and attached) – These policies define the permissions that a principal assuming the role is able (or restricted) to perform, and on which resources.
  • Permissions boundary – A permissions boundary is an advanced feature for using a managed policy to set the maximum permissions that an identity-based policy can grant to an IAM entity. An entity’s permissions boundary allows it to perform only the actions that are allowed by both its identity-based permission policies and its permissions boundaries.
  • Trust relationship – This policy defines which principals can assume the role, and under which conditions. This is sometimes referred to as a resource-based policy for the IAM role. We’ll refer to this policy simply as the ‘trust policy’.

A role can be assumed by a human user or a machine principal, such as an Amazon Elastic Computer Cloud (Amazon EC2) instance or an AWS Lambda function. Over the rest of this post, you’ll see how you’re able to reduce the conditions for principals to use roles by configuring their trust policies.

An example of a simple trust policy

A common use case is when you need to provide security audit access to your account, allowing a third party to review the configuration of that account. After attaching the relevant permission policies to an IAM role, you need to add a cross-account trust policy to allow the third-party auditor to make the sts:AssumeRole API call to elevate their access in the audited account. The following trust policy shows an example policy created through the AWS Management Console:


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {}
    }
  ]
}

As you can see, it has the same structure as other IAM policies with Effect, Action, and Condition components. It also has the Principal parameter, but no Resource attribute. This is because the resource, in the context of the trust policy, is the IAM role itself. For the same reason, the Action parameter will only ever be set to one of the following values: sts:AssumeRole, sts:AssumeRoleWithSAML, or sts:AssumeRoleWithWebIdentity.

Note: The suffix root in the policy’s Principal attribute equates to “authenticated and authorized principals in the account,” not the special and all-powerful root user principal that is created when an AWS account is created.

Using the Principal attribute to reduce scope

In a trust policy, the Principal attribute indicates which other principals can assume the IAM role. In the example above, 111122223333 represents the AWS account number for the auditor’s AWS account. In effect, this allows any principal in the 111122223333 AWS account with sts:AssumeRole permissions to assume this role.

To restrict access to a specific IAM user account, you can define the trust policy like the following example, which would allow only the IAM user LiJuan in the 111122223333 account to assume this role. LiJuan would also need to have sts:AssumeRole permissions attached to their IAM user for this to work:


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:user/LiJuan"
      },
      "Action": "sts:AssumeRole",
      "Condition": {}
    }
  ]
}

The principals set in the Principal attribute can be any principal defined by the IAM documentation, and can refer to an AWS or a federated principal. You cannot use a wildcard (“*” or “?”) within a Principal for a trust policy, other than one special condition, which I’ll come back to in a moment: You must define precisely which principal you are referring to because there is a translation that occurs when you submit your trust policy that ties it to each principal’s hidden principal ID, and it can’t do that if there are wildcards in the principal.

The only scenario where you can use a wildcard in the Principal parameter is where the parameter value is only the “*” wildcard. Use of the global wildcard “*” for the Principal isn’t recommended unless you have clearly defined Conditional attributes in the policy statement to restrict use of the IAM role, since doing so without Conditional attributes permits assumption of the role by any principal in any AWS account, regardless of who that is.

Using identity federation on AWS

Federated users from SAML 2.0 compliant enterprise identity services are given permissions to access AWS accounts through the use of IAM roles. While the user-to-role configuration of this connection is established within the SAML 2.0 identity provider, you should also put controls in the trust policy in IAM to reduce any abuse.

Because the Principal attribute contains configuration information about the SAML mapping, in the case of Active Directory, you need to use the Condition attribute in the trust policy to restrict use of the role from the AWS account management perspective. This can be done by restricting the SourceIp address, as demonstrated later, or by using one or more of the SAML-specific Condition keys available. My recommendation here is to be as specific as you can in reducing the set of principals that can use the role as is practical. This is best achieved by adding qualifiers into the Condition attribute of your trust policy.

There’s a very good guide on creating roles for SAML 2.0 federation that contains a basic example trust policy you can use.

Using the Condition attribute in a trust policy to reduce scope

The Condition statement in your trust policy sets additional requirements for the Principal trying to assume the role. If you don’t set a Condition attribute, the IAM engine will rely solely on the Principal attribute of this policy to authorize role assumption. Given that it isn’t possible to use wildcards within the Principal attribute, the Condition attribute is a really flexible way to reduce the set of users that are able to assume the role without necessarily specifying the principals.

Limiting role use based on an identifier

Occasionally teams managing multiple roles can become confused as to which role achieves what and can inadvertently assume the wrong role. This is referred to as the Confused Deputy problem. This next section shows you a way to quickly reduce this risk.

The following trust policy requires that principals from the 111122223333 AWS account have provided a special phrase when making their request to assume the role. Adding this condition reduces the risk that someone from the 111122223333 account will assume this role by mistake. This phrase is configured by specifying an ExternalID conditional context key.


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "ExampleSpecialPhrase"
        }
      }
    }
  ]
}

In the example trust policy above, the value ExampleSpecialPhrase isn’t a secret or a password. Adding the ExternalID condition limits this role from being assumed using the console. The only way to add this ExternalID argument into the role assumption API call is to use the AWS Command Line Interface (AWS CLI) or a programming interface. Having this condition doesn’t prevent a user who knows about this relationship and the ExternalId from assuming what might be a privileged set of permissions, but does help manage risks like the Confused Deputy problem. I see customers using an ExternalID that matches the name of the AWS account, which works to ensure that an operator is working on the account they believe they’re working on.

Limiting role use based on multi-factor authentication

By using the Condition attribute, you can also require that the principal assuming this role has passed a multi-factor authentication (MFA) check before they’re permitted to use this role. This again limits the risk associated with mistaken use of the role and adds some assurances about the principal’s identity.


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "BoolIfExists": { 
          "aws:MultiFactorAuthPresent" : "true" 
		}
      }
    }
  ]
}

In the example trust policy above, I also introduced the MultiFactorAuthPresent conditional context key. Per the AWS global condition context keys documentation, the MultiFactorAuthPresent conditional context key does not apply to sts:AssumeRole requests in the following contexts:

  • When using access keys in the CLI or with the API
  • When using temporary credentials without MFA
  • When a user signs in to the AWS Console
  • When services (like AWS CloudFormation or Amazon Athena) reuse session credentials to call other APIs
  • When authentication has taken place via federation

In the example above, the use of the BoolIfExists qualifier to the MultiFactorAuthPresent conditional context key evaluates the condition as true if:

  • The principal type can have an MFA attached, and does.
    or
  • The principal type cannot have an MFA attached.

This is a subtle difference but makes the use of this conditional key in trust policies much more flexible across all principal types.

Limiting role use based on time

During activities like security audits, it’s quite common for the activity to be time-bound and temporary. There’s a risk that the IAM role could be assumed even after the audit activity concludes, which might be undesirable. You can manage this risk by adding a time condition to the Condition attribute of the trust policy. This means that rather than being concerned with disabling the IAM role created immediately following the activity, customers can build the date restriction into the trust policy. You can do this by using policy attribute statements, like so:


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "DateGreaterThan": {
          "aws:CurrentTime": "2020-09-01T12:00:00Z"
        },
        "DateLessThan": {
          "aws:CurrentTime": "2020-09-07T12:00:00Z"
        }
      }
    }
  ]
}

Limiting role use based on IP addresses or CIDR ranges

If the auditor for a security audit is using a known fixed IP address, you can build that information into the trust policy, further reducing the opportunity for the role to be assumed by unauthorized actors calling the assumeRole API function from another IP address or CIDR range:


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": "203.0.113.0/24"
        }
      }
    }
  ]
}

Limiting role use based on tags

IAM tagging capabilities can also help to build flexible and adaptive trust policies, too, so that they create an attribute-based access control (ABAC) model for IAM management. You can build trust policies that only permit principals that have already been tagged with a specific key and value to assume a specific role. The following example requires that IAM principals in the AWS account 111122223333 be tagged with department = OperationsTeam for them to assume the IAM role.


tagged with department = OperationsTeam for them to assume the IAM role.
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "aws:PrincipalTag/department": "OperationsTeam"
        }
      }
    }
  ]
}

If you want to create this effect, I highly recommend the use of the PrincipalTag pattern above, but you must also be cautious about which principals are then also given iam:TagUser, iam:TagRole, iam:UnTagUser, and iam:UnTagRole permissions, perhaps even using the aws:PrincipalTag condition within the permissions boundary policy to restrict their ability to retag their own IAM principal or that of another IAM role they can assume.

Limiting or extending access to a role based on AWS Organizations

Since its announcement in 2016, almost every enterprise customer I work with uses AWS Organizations. This AWS service allows customers to create an organizational structure for their accounts by creating hard boundaries to manage blast-radius risks, among other advantages. You can use the PrincipalOrgID condition to limit assumption of an organization-wide core IAM role.

Caution: As you’ll see in the example below, you need to set the Principal attribute to “*” to do this, which would, without the conditional restriction, allow all role assumption requests to be accepted for this role, irrespective of the source of that assumption request. For that reason, be especially careful about the use of this pattern.


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "aws:PrincipalOrgID": "o-abcd12efg1"
        }
      }
    }
  ]
}

It isn’t practical to write out all the AWS account identifiers into a trust policy, and because of the way policies like this are evaluated, you can’t include wildcard characters for the account number in the principal’s account number field. The use of the PrincipalOrgID global condition context key provides us with a neat and dynamic mechanism to create a short policy statement.

Role chaining

There are instances where a third party might themselves be using IAM roles, or where an AWS service resource that has already assumed a role needs to assume another role (perhaps in another account), and customers might need to allow only specific IAM roles in that remote account to assume the IAM role you create in your account. You can use role chaining to build permitted role escalation routes using role assumption from within the same account or AWS organization, or from third-party AWS accounts.

Consider the following trust policy example where I use a combination of the Principal attribute to scope down to an AWS account, and the aws:UserId global conditional context key to scope down to a specific role using its RoleId. To capture the RoleId for the role you want to be able to assume, you can run the following command using the AWS CLI:


# aws iam get-role --role-name CrossAccountAuditor
{
    "Role": {
        "Path": "/",
        "RoleName": "CrossAccountAuditor",
        "RoleId": "ARO1234567123456D",
        "Arn": "arn:aws:iam:: 111122223333:role/CrossAccountAuditor",
        "CreateDate": "2017-08-31T14:24:20+00:00",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Sid": "",
                    "Effect": "Allow",
                    "Principal": {
                        "AWS": "arn:aws:iam::111122223333:root"
                    },
                    "Action": "sts:AssumeRole",
                    "Condition": {
                        "StringEquals": {
                            "sts:ExternalId": "ExampleSpecialPhrase"
                        }
                    }
                }
            ]
        }
    }
}

Here is the example trust policy that limits to only the CrossAccountAuditor role from AWS Account 111122223333.


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111122223333:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringLike": {
          "aws:userId": "ARO1234567123456D:*"
        }
      }
    }
  ]
}

If you’re using an IAM user and have assumed the CrossAccountAuditor IAM role, the policy above will work through the AWS CLI with a call to aws sts assume-role and through the console.

This type of trust policy also works for services like Amazon EC2, allowing those instances using their assigned instance profile role to assume a role in another account to perform actions. We’ll touch on this use case later in the post.

Putting it all together

AWS customers can use combinations of all the above Principal and Condition attributes to hone the trust they’re extending out to any third party, or even within their own organization. They might create an accumulated trust policy for an IAM role which achieves the following effect:

Allows only a user named PauloSantos, in AWS account number 111122223333, to assume the role if they have also authenticated with an MFA, are logging in from an IP address in the 203.0.113.0 to 203.0.113.24 CIDR range, and the date is between noon of September 1, 2020, and noon of September 7, 2020.


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::111122223333:user/PauloSantos"
        ]
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "BoolIfExists": {
          "aws:MultiFactorAuthPresent": "true"
        },
        "IpAddress": {
          "aws:SourceIp": "203.0.113.0/24"
        },
        "DateGreaterThan": {
          "aws:CurrentTime": "2020-09-01T12:00:00Z"
        },
        "DateLessThan": {
          "aws:CurrentTime": "2020-09-07T12:00:00Z"
        }
      }
    }
  ]
}

I’ve seen customers use this to create IAM users who have no permissions attached besides sts:AssumeRole. Trust relationships are then configured between the IAM users and the IAM roles, creating ultimate flexibility in defining who has access to what roles without needing to update the IAM user identity pool at all.

A word on Effect: Deny and NotPrincipal in IAM role trust policies

I have seen some customers make use of an “Effect”: “Deny” clause in their trust policies. This pattern can help manage a wildcard statement in another “Effect”: “Allow” clause of the same trust policy. However, this isn’t the best approach for most scenarios. You will typically be able to define each principal in your policy as being allowed access. An example of where this might not be true is where you have a clause that uses the global wildcard “*” as a principal, in which case it will be necessary to add Deny statements to further filter the access.

Putting a wildcard into the Principal attribute of an Allow policy statement, particularly in relation to trust policies, can be dangerous if you haven’t done a robust job of managing the Condition attribute in the same statement. Be as specific as possible in your Allow statement, and use Principal attributes first, rather than then relying on Deny statements to manage potential security gaps created by your use of wildcards.

The following trust policy allows all IAM principals within the o-abcd12efg1 organization to assume the IAM role, but only if it’s before September 7, 2020:


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "aws:PrincipalOrgID": "o-abcd12efg1"
        }
      }
    },
    {
      "Effect": "Deny",
      "Principal": {
        "AWS": "*"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "DateGreaterThan": {
          "aws:CurrentTime": "2020-09-07T12:00:00Z"
        }
      }
    }
  ]
}

The use of NotPrincipal in trust policies

You can also build into your trust policies a NotPrincipal condition. Again, this is rarely the best choice, because you can introduce unnecessary complexity and confusion into your policies. Instead, you can avoid that problem by using fairly simple and prescriptive Principal statements.

Statements with NotPrincipal can also use a Deny statement as well, so it can create quite baffling policy logic, which if misunderstood could create unintended opportunities for misuse or abuse.

Here’s an example where you might think to use Deny and NotPrincipal in a trust policy—but notice this has the same effect as adding arn:aws:iam::123456789012:role/CoreAccess in a single Allow statement. In general, Deny with NotPrincipal statements in trust policies create unnecessary complexity, and should be avoided.


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "sts:AssumeRole"
    },
    {
      "Effect": "Deny",
      "NotPrincipal": {
        "AWS": "arn:aws:iam::111122223333:role/CoreAccess"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Remember, your Principal attribute should be very specific, to reduce the set of those able to assume the role, and an IAM role trust policy won’t permit access if a corresponding Allow statement isn’t explicitly present in the trust policy. It’s better to rely on the default deny policy evaluation logic where you’re able, rather than introducing unnecessary complexity into your policy logic.

Creating trust policies for AWS services that assume roles

There are two types of contexts where AWS services need access to IAM roles to function:

  1. Resources managed by an AWS service (like Amazon EC2 or Lambda, for example) need access to an IAM role to execute functions on other AWS resources, and need permissions to do so.
  2. An AWS service that abstracts its functionality from other AWS services, like Amazon Elastic Container Service (Amazon ECS) or Amazon Lex, needs access to execute functions on AWS resources. These are called service-linked roles and are a special case that’s out of the scope of this post.

In both contexts, you have the service itself as an actor. The service is assuming your IAM role so it can provide your credentials to your Lambda function (the first context) or use those credentials to do things (the second context). In the same way that IAM roles are used by human operators to provide an escalation mechanism for users operating with specific functions in the examples above, so, too, do AWS resources, such as Lambda functions, Amazon EC2 instances, and even AWS CloudFormation, require the same mechanism. You can find more information about how to create IAM Roles for AWS Services here.

An IAM role for a human operator and for an AWS service are exactly the same, even though they have a different principal defined in the trust policy. The policy’s Principal will define the AWS service that is permitted to assume the role for its function.

Here’s an example trust policy for a role designed for an Amazon EC2 instance to assume. You can see that the principal provided is the ec2.amazonaws.com service:


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Every configuration of an AWS resource should be passed a specific role unique to its function. So, if you have two Amazon EC2 launch configurations, you should design two separate IAM roles, even if the permissions they require are currently the same. This allows each configuration to grow or shrink the permissions it requires over time, without needing to reattach IAM roles to configurations, which might create a privilege escalation risk. Instead, you update the permissions attached to each IAM role independently, knowing that it will only be used by that one service resource. This helps reduce the potential impact of risks. Automating your management of roles will help here, too.

Several customers have asked if it’s possible to design a trust policy for an IAM role such that it can only be passed to a specific Amazon EC2 instance. This isn’t directly possible. You cannot place the Amazon Resource Name (ARN) for an EC2 instance into the Principal of a trust policy, nor can you use tag-based condition statements in the trust policy to limit the ability for the role to be used by a specific resource.

The only option is to manage access to the iam:PassRole action within the permission policy for those IAM principals you expect to be attaching IAM roles to AWS resources. This special Action is evaluated when a principal tries to attach another IAM role to an AWS service or AWS resource.

You should use restrictions on access to the iam:PassRole action with permission policies and permission boundaries. This means that the ability to attach roles to instance profiles for Amazon EC2 is limited, rather than using the trust policy on the role assumed by the EC2 instance to achieve this. This approach makes it much easier to manage scaling for both those principals attaching roles to EC2 instances, and the instances themselves.

You could use a permission policy to limit the ability for the associated role to attach other roles to Amazon EC2 instances with the following permission policy, unless the role name is prefixed with EC2-Webserver-:


{
    "Version": "2012-10-17",
    "Statement": [{
        "Effect": "Allow",
        "Action": [
            "iam:GetRole",
            "iam:PassRole"
        ],
        "Resource": "arn:aws:iam::111122223333:role/EC2-Webserver-*"
    }]
}

Conclusion

You now have all the tools you need to build robust and effective trust policies that work at scale, providing guardrails for your users and those who might want to access resources in your account from outside your organization.

Policy logic isn’t always simple, and I encourage you to use sandbox accounts to try out your ideas. In general, simplicity should win over cleverness. IAM policies and statements that might well be frugal in their use of policy language might also be difficult to read, interpret, and update by other IAM administrators in the future. Keeping your trust policies simple helps to build IAM relationships everyone understands and can manage, and use, effectively.

If you have feedback about this post, submit comments in the Comments section below. If you have questions about this post, start a new thread on the AWS IAM forum or contact AWS Support.

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

Author

Jonathan Jenkyn

Jonathan is a Senior Security Growth Strategies Consultant with AWS Professional Services. He’s an active member of the People with Disabilities affinity group, and has built several Amazon initiatives supporting charities and social responsibility causes. Since 1998, he has been involved in IT Security at many levels, from implementation of cryptographic primitives to managing enterprise security governance. Outside of work, he enjoys running, cycling, fund-raising for the BHF and Ipswich Hospital Charity, and spending time with his wife and 5 children.

How to import PFX-formatted certificates into AWS Certificate Manager using OpenSSL

Post Syndicated from Praveen Kumar Jeyarajan original https://aws.amazon.com/blogs/security/how-to-import-pfx-formatted-certificates-into-aws-certificate-manager-using-openssl/

In this blog post, we show you how to import PFX-formatted certificates into AWS Certificate Manager (ACM) using OpenSSL tools.

Secure Sockets Layer and Transport Layer Security (SSL/TLS) certificates are small data files that digitally bind a cryptographic key pair to an organization’s details. The key pair is used to secure network communications and establish the identity of websites over the internet and on private networks. These certificates are usually issued by a trusted certificate authority (CA). A CA acts as a trusted third party—trusted both by the subject (owner) of the certificate and by the party relying upon the certificate. The format of these certificates is specified by the X.509 or Europay, Mastercard, and Visa (EMV) standards. SSL/TLS certificates issued by a trusted CA are usually encoded in Personal Information Exchange (PFX) or Privacy-Enhanced Mail (PEM) format.

ACM lets you easily provision, manage, and deploy public and private SSL/TLS certificates for use with Amazon Web Services (AWS) and your internal connected resources. Certificates can be imported from outside AWS, or created using AWS tools. Certificates can be used to help with ACM-integrated AWS resources, such as Elastic Load Balancing, Amazon CloudFront distributions, and Amazon API Gateway.

To import a self–signed SSL/TLS certificate into ACM, you must provide the certificate and its private key in PEM format. To import a signed certificate, you must also include the certificate chain in PEM format. Prerequisites for Importing Certificates provides more detail.

Sometimes, the trusted CA issues the certificate, private key, and certificate chain details in PFX format. In this post, we show you how to convert a PFX-encoded certificate into PEM format and then import it into ACM.

Solution

The following solution converts a PFX-encoded certificate to PEM format using the OpenSSL command line tool. The certificate is then imported into ACM.

Figure 1: Use the OpenSSL Toolkit to convert the certificate, then import the certificate into ACM

Figure 1: Use the OpenSSL Toolkit to convert the certificate, then import the certificate into ACM

The solution has two parts, shown in the preceding figure:

  1. Use the OpenSSL Toolkit to convert the PFX-encoded certificate into PEM format.
  2. Import the PEM certificate into ACM.

Prerequisites

We use the OpenSSL toolkit to convert a PFX encoded certificate to PEM format. OpenSSL is an open source toolkit for manipulating cryptographic files. It’s also a general-purpose cryptography library.

For this post, we use a password protected PFX-encoded file—website.xyz.com.pfx—with an X.509 standard CA signed certificate and 2048-bit RSA private key data.

  1. Download and install the OpenSSL toolkit.
  2. Add the OpenSSL binaries location to your system PATH variable, so that the binaries are available for command line use.

Convert the PFX encoded certificate into PEM format

Run the following commands to convert a PFX-encoded SSL certificate into PEM format. The procedure requires the PFX-encoded certificate and the passphrase used for encrypting it.

The procedure converts the PFX-encoded signed certificate file into three files in PEM format.

  • cert-file.pem – PEM file containing the SSL/TLS certificate for the resource.
  • withoutpw-privatekey.pem – PEM file containing the private key of the certificate with no password protection.
  • ca-chain.pem – PEM file containing the root certificate of the CA.

To convert the PFX encoded certificate

  1. Use the following command to extract the certificate private key from the PFX file. If your certificate is secured with a password, enter it when prompted. The command generates a PEM-encoded private key file named privatekey.pem. Enter a passphrase to protect the private key file when prompted to Enter a PEM pass phrase.
    
    openssl pkcs12 -in website.xyz.com.pfx -nocerts -out privatekey.pem
    

     

    Figure 2: Prompt to enter a PEM pass phrase

    Figure 2: Prompt to enter a PEM pass phrase

  2. The previous step generates a password-protected private key. To remove the password, run the following command. When prompted, provide the passphrase created in step 1. If successful, you will see writing RSA key.
    
    openssl rsa -in privatekey.pem -out withoutpw-privatekey.pem
    

     

    Figure 3: Writing RSA key

    Figure 3: Writing RSA key

  3. Use the following command to transfer the certificate from the PFX file to a PEM file. This creates the PEM-encoded certificate file named cert-file.pem. If successful, you will see MAC verified OK.
    
    openssl pkcs12 -in website.xyz.com.pfx -clcerts -nokeys -out cert-file.pem
    

     

    Figure 4: MAC verified OK

    Figure 4: MAC verified OK

  4. Finally, use the following command to extract the CA chain from the PFX file. This creates the CA chain file named ca-chain.pem. If successful, you will see MAC verified OK.
    
    openssl pkcs12 -in website.xyz.com.pfx -cacerts -nokeys -chain -out ca-chain.pem
    

     

    Figure 5: MAC verified OK

    Figure 5: MAC verified OK

When the preceding steps are complete, the PFX-encoded signed certificate file is split and returned as three files in PEM format, shown in the following figure. To view the list of files in a directory, enter the command dir in Windows or type the command ls -l in Linux.

  • cert-file.pem
  • withoutpw-privatekey.pem
  • ca-chain.pem

    Figure 6: PEM-formatted files

    Figure 6: PEM-formatted files

Import the PEM certificates into ACM

Use the ACM console to import the PEM-encoded SSL certificate. You need the PEM files containing the SSL certificate (cert-file.pem), the private key (withoutpw-privatekey.pem), and the root certificate of the CA (ca-chain.pem) that you created in the previous procedure.

To import the certificates

  1. Open the ACM console. If this is your first time using ACM, look for the AWS Certificate Manager heading and select the Get started button.
  2. Select Import a certificate.
  3. Add the files you created in the previous procedure:
    1. Use a text-editing tool such as Notepad to open cert-file.pem. Copy the lines beginning at –BEGIN CERTIFICATE– and ending with –END CERTIFICATE–. Paste them into the Certificate body text box.
    2. Open withoutpw-privatekey.pem. Copy the lines beginning at –BEGIN RSA PRIVATE KEY– and ending with –END RSA PRIVATE KEY–. Paste them into the Certificate private key, text box.
    3. For Certificate chain, copy and paste the lines starting –BEGIN CERTIFICATE– and ending with –END CERTIFICATE– in the file ca-chain.pem.

      Figure 7: Add the files to import the certificate

      Figure 7: Add the files to import the certificate

  4. Select Next and add tags for the certificate. Each tag is a label consisting of a key and value that you define. Tags help you manage, identify, organize, search for, and filter resources.
  5. Select Review and import.
  6. Review the information about your certificate, then select Import.

Conclusion

In this post, we discussed how you can use OpenSSL tools to import a PFX-encoded SSL/TLS certificate into ACM. You can use the imported certificate with any ACM-integrated AWS service. ACM makes it easier to set up SSL/TLS for a website or application on AWS. ACM can replace many of the manual processes usually associated with using and managing SSL/TLS certificates. ACM can also manage renewals, which can help you avoid downtime due to misconfigured, revoked, or expired certificates. You can renew an imported certificate by obtaining and importing a new certificate from your certificate issuer, or you can request a new certificate from ACM.

If you have feedback about this post, submit comments in the Comments section below.

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

Author

Praveen Kumar Jeyarajan

PraveenKumar is a DevOps Consultant in AWS supporting enterprise customers and their journey to the cloud. Before his work on AWS and cloud technologies, PraveenKumar focused on solving myriad technical challenges using the latest technologies. Outside of work, he enjoys watching movies and playing tennis.

Author

Viyoma Sachdeva

Viyoma is a DevOps Consultant in AWS supporting global customers and their journey to the cloud. Outside of work, she enjoys watching series and spending time with her family.

How to use AWS Config to determine compliance of AWS KMS key policies to your specifications

Post Syndicated from Tracy Pierce original https://aws.amazon.com/blogs/security/how-to-use-aws-config-to-determine-compliance-of-aws-kms-key-policies-to-your-specifications/

One of the top security methodologies is the principle of least privilege, which is the practice of limiting user, application, and service permissions to only those necessary to perform a function or task. In this post, I will describe how you can use AWS Config to create compliance rules that will scan AWS Key Management Service (AWS KMS) key policies to determine whether they follow your company’s guidelines for least privilege. You can use the AWS Config Rules Development Kit (RDK) on GitHub (aws-config-rdk) to quickly create and save custom AWS Config rule sets as AWS Lambda functions in your account(s), and test the rules against sample configuration items.

In this solution, you use a Gherkin syntax file, which is good method for determining the requirements of your rule, as well as how it should react to input. A Gherkin syntax file gives you the ability to create a parameter file and a feature file. The parameter file lists information like RuleName, SourceRuntime, CodeKey, InputParameters, Trigger, SourcePeriodic, and so on. The feature file includes a description of the rule, detailed information about the parameters, what the feature is meant to accomplish, and the scenarios you want to evaluate your rule against.

In this post, I will show you how to take a parameter file and feature file, and use the requirements in them to create your AWS Config rule and testing scenarios using the AWS Config RDK. I include an example key policy file for you to use for your test scenarios. You can download all the code snippets used in this post from the aws-config-aws-kms-policy-rule GitHub repository. I will explain how to use the code examples to create the AWS Config rule as a Lambda function, and how to use the AWS Config RDK to test locally and deploy the rule to your account(s).

Overview

The solution described in this post uses custom AWS Config rules to scan AWS KMS key policies every 24 hours. It checks these rules against a set of parameters that you determine beforehand to meet your organization’s security standards. Based on the rule checks, AWS Config determines the key policy to be either compliant or noncompliant when compared against your company’s specifications. Noncompliant resources are noted as such, so that an administrator can review and determine if the policy should be modified, or if it will be allowed as an exception. The following figure shows the overview of the process.
 

Figure 1: Overview

Figure 1: Overview

The way the process works is as follows:

  1. The AWS Config rule is triggered by a configuration change and scans your key policy.
  2. The key policy is evaluated against your custom AWS Config rule scenarios.
  3. The compliance results of the key policies are presented in the AWS Config console.

The files in the GitHub repository you will use for this post are:

  • AWSConfigRuleKMS.feature – This is the Gherkin file used to determine the scenarios you will test against.
  • AWSConfigRuleKMSPolicy.py – This file is used to create the policy formatting that the AWSConfigRuleKMS.py script uses to parse AWS KMS key policies. Because the AWS KMS key policies are JSON objects, you have to parse them before inputting them as data so you can retrieve comparison results.
  • AWSConfigRuleKMS.py – This is the actual script that does the comparisons of the policies retrieved and the scenarios laid out in the Gherkin file.
  • AWSConfigRuleKMS_test.py – This is the testing file that takes an example policy, runs it against the multiple test scenarios, and outputs the test results. This lets you know if your script is running as expected and producing the proper outcomes.
  • parameters.json – This is the parameters file that tells AWS Config which parameters to check for in the rules.

Prerequisites

This solution has the following prerequisites:

In addition, this solution uses the following services:

Deploying the solution

From the Gherkin file in my repo, you can see you will be testing for eight different scenarios regarding least privilege access to your AWS KMS customer master keys (CMKs). I decided to whitelist any CMKs that have an alias beginning with the word “Otter*”, and any UserID that begins with “AROAOTTER*”. The parameters are set in the parameters.json file. This is how AWS Config knows what to check for in the rules. You will use these same parameters in the AWSConfigRuleKMS_test.py file when performing your tests. Be sure to modify these parameters when creating your own rules.

Scenario 1: Checks to determine if a CMK is marked DISABLED.

Scenario 2: Checks to determine if a CMK is marked ENABLED and is in the list of whitelisted CMKs.

Scenario 3: Checks to determine if a CMK is marked ENABLED, is not in the list of whitelisted CMKs, and has an action equal to kms:*.

Scenario 4: Checks to determine if a CMK is marked ENABLED, is not in the list of whitelisted CMKs, includes a condition for users, does not have kms:*, and has a policy allowing the following actions: kms:Encrypt, kms:Decrypt, kms:Create*, kms:Delete*, and kms:Put* together.

Scenario 5: Checks to determine if a CMK is marked ENABLED, is not in the list of whitelisted CMKs, includes a condition for users, does not have kms:*, and does not have a policy allowing the following actions: kms:Encrypt, kms:Decrypt, kms:Create*, kms:Delete*, and kms:Put* together.

Scenario 6: Checks to determine if a CMK is marked ENABLED, is not in the list of whitelisted CMKs, includes a condition for users, user is listed in the whitelisted users, does not have kms:*, and has a policy allowing only the following actions: kms:Create*, kms:Delete*, and kms:Put*.

Scenario 7: Checks to determine if a CMK is marked ENABLED, is not in the list of whitelisted CMKs, includes a condition for users, user is not listed in the whitelisted users, does not have kms:*, and has a policy allowing only the following actions: kms:Create*, kms:Delete*, and kms:Put*.

Scenario 8: Checks to determine if a CMK is marked ENABLED, is not in the list of whitelisted CMKs, includes a condition for users, user is listed in the whitelisted users, does not have kms:*, and has a policy allowing only the following actions: kms:Encrypt, kms:Decrypt, kms:Create*, kms:Delete*, and kms:Put* together.

You will be using the AWS Config RDK to create and test your rules, and also to deploy them to your account.

To create your AWS Config rule (RDK CLI)

  1. Open a terminal window.
  2. Use the cd command to move to the directory in which you want to create your rule.
  3. Use the create command to create your rule, using the following example. Replace all variables in italics with your inputs.
    
    $ rdk create AWSConfigRuleKMS --runtime python3.6 --resource-types AWS::KMS::Key ---input-parameters '{"CMK_Whitelist":"Otter*","Admin_User_Id":"AROAOTTER*"}'
    

  4. You should see output similar to the following:
    
    Running create!
    Local Rule files created.
    

In your directory, you will now see the following files.

  • AWSConfigRuleKMS.py: This is a skeleton file for you to create your Lambda function. It has some base code and is commented to assist you with building your functions.
  • AWSConfigRuleKMS_test.py: This is a skeleton file for you to create your testing scenario script. It has some base code and helpers in place to make this easier for you.
  • parameters.json: This is a parameter file, based on the inputs from the create command.

For this solution, I have already created the code snippets you will need. Download the files from my GitHub repository. Make sure to include the AWSConfigRuleKMSPolicy.py from my repository in the directory as well.

To download the code snippets from the GitHub repository (CLI)

  1. Open a terminal.
  2. Use the cd command to change to the directory where you created your AWS Config rule.
  3. Run the following command:
    
    git clone https://github.com/aws-samples/aws-config-aws-kms-policy-rule
    

Now that you have the code snippets, you need to place them into the skeleton files to complete the rule.

To complete the Python scripts

  1. In a text editor, open your local copies of both AWSConfigRuleKMS.py and AWSConfigRuleKMS_test.py.
  2. Copy the contents of the AWSConfigRuleKMS.py from my GitHub repository.
  3. In the AWSConfigRuleKMS.py skeleton file, delete the code from the line import json down to and including the line above the section starting with # Helper Functions #. Paste the copied code in its place and save the file.
  4. Copy the contents of the AWSConfigRuleKMS_test.py from my GitHub repository.
  5. In the AWSConfigRuleKMS_test.py skeleton file, delete the code from the line import sys down to and including the line above the section starting with # Helper Functions #. Paste the copied code in its place and save the file.

With all the files updated, you will now use the AWS Config RDK to test the scenarios. For testing, you use the AWSConfigRuleKMS_test.py file, which is the file housing the test scenarios to ensure that your rules work as expected.

To test your AWS Config rule (RDK CLI)

  1. Open a terminal window.
  2. Use the cd command to change to the directory one level above where you created your AWS Config rule. For example, if your AWS Config rule is in C://User/Documents/Config/AWSConfigRuleKMS, then change to the C://User/Documents/Config directory.
  3. Use the test-local command to test your rule, using the following example:
    $ rdk test-local AWSConfigRuleKMS_test
  4. You should see output similar to the following:
    
    Running local test!
    Testing AWSConfigRuleKMS
    Looking for tests in /User/Documents/Config/AWSConfigRuleKMS
    AWSConfigRuleKMS_test.py
    Debug!
    <unittest.suite.TestSuite tests=[<unittest.suite.TestSuite tests=[<AWSConfigRuleKMS_test.TestKMSKeyPolicy testMethod=test__scenario_7_admin_role_not_in_whitelist_sep_of_duty>, <AWSConfigRuleKMS_test.TestKMSKeyPolicy testMethod=test_is_not_cmk>, <AWSConfigRuleKMS_test.TestKMSKeyPolicy testMethod=test_scenario_1_disabled_status>, <AWSConfigRuleKMS_test.TestKMSKeyPolicy testMethod=test_scenario_2_cmk_in_whitelist>, <AWSConfigRuleKMS_test.TestKMSKeyPolicy testMethod=test_scenario_3_kms_star_in_policy>, <AWSConfigRuleKMS_test.TestKMSKeyPolicy testMethod=test_scenario_4_no_sep_of_duty>, <AWSConfigRuleKMS_test.TestKMSKeyPolicy testMethod=test_scenario_5_sep_of_duty_actions>, <AWSConfigRuleKMS_test.TestKMSKeyPolicy testMethod=test_scenario_6_admin_role_in_whitelist_sep_of_duty>, <AWSConfigRuleKMS_test.TestKMSKeyPolicy testMethod=test_scenario_8_admin_role_in_whitelist_no_sep_of_duty>, <AWSConfigRuleKMS_test.TestKMSKeyPolicy testMethod=test_scenario_no_conditions>]>]>
    test__scenario_7_admin_role_not_in_whitelist_sep_of_duty (AWSConfigRuleKMS_test.TestKMSKeyPolicy) ... in Key Policy for alias/testkey, statement does have separation of duties, CMK is not whitelisted, and user id is not whitelisted
    ok
    test_is_not_cmk (AWSConfigRuleKMS_test.TestKMSKeyPolicy) ... ok
    test_scenario_1_disabled_status (AWSConfigRuleKMS_test.TestKMSKeyPolicy) ... CMK alias/testkey is disabled
    ok
    test_scenario_2_cmk_in_whitelist (AWSConfigRuleKMS_test.TestKMSKeyPolicy) ... CMK alias/Otter* is in whitelist for CMK Key Policy check
    ok
    test_scenario_3_kms_star_in_policy (AWSConfigRuleKMS_test.TestKMSKeyPolicy) ... in Key Policy for alias/testkey, statement does have open KMS permissions and CMK is not whitelisted
    ok
    test_scenario_4_no_sep_of_duty (AWSConfigRuleKMS_test.TestKMSKeyPolicy) ... in Key Policy for alias/testkey, statement does not have separation of duties and CMK is not whitelisted
    ok
    test_scenario_5_sep_of_duty_actions (AWSConfigRuleKMS_test.TestKMSKeyPolicy) ... in Key Policy for alias/testkey, statement does have separation of duties and CMK is not whitelisted
    ok
    test_scenario_6_admin_role_in_whitelist_sep_of_duty (AWSConfigRuleKMS_test.TestKMSKeyPolicy) ... in Key Policy for alias/testkey, statement does have separation of duties, CMK is not whitelisted, and user id is whitelisted
    ok
    test_scenario_8_admin_role_in_whitelist_no_sep_of_duty (AWSConfigRuleKMS_test.TestKMSKeyPolicy) ... In Key Policy for alias/testkey, statement does not have separation of duties, CMK is not whitelisted, and user id is whitelisted
    ok
    test_scenario_no_conditions (AWSConfigRuleKMS_test.TestKMSKeyPolicy) ... ok
    
    ----------------------------------------------------------------------
    Ran 10 tests in 0.013s
    
    OK
    <unittest.runner.TextTestResult run=10 errors=0 failures=0>
    

If you encounter errors during testing, they could be caused by:

  • Incorrect code in the AWSConfigRuleKMS.py file
  • Incorrect code in the AWSConfigRuleKMS_test.py file
  • Invalid formatting in the parameters.json file
  • Invalid names on the files – they must match the exact formatting I have.

You should go back through your code to make sure that you used proper syntax, and that the test cases match your requirements. If you run into syntax issues, you can find the full list of AWS supported SDKs on the Tools to Build on AWS page. You can match your code formatting against the code I supplied in my GitHub repository to ensure proper spacing/tabs.

When testing is complete, deploy your AWS Config rule into your account(s). The file to deploy the rule itself is the AWSConfigRuleKMS.py file.

To deploy your AWS Config rule (RDK CLI)

  1. Open a terminal window.
  2. Use the cd command to change to the directory one level above where you created your AWS Config rule. For example, if your rule is in C://User/Documents/Config/AWSConfigRuleKMS, then change to the C://User/Documents/Config directory.
  3. Use the deploy command to deploy your rule, using the following example:
    $ rdk deploy AWSConfigRuleKMS
  4. You should see output similar to the following:
    
    Running deploy!
    Zipping AWSConfigRuleKMS
    Uploading AWSConfigRuleKMS
    Creating CloudFormation Stack for AWSConfigRuleKMS
    Waiting for CloudFormation stack operation to complete...
    ...
    Waiting for CloudFormation stack operation to complete...
    AWS Config deploy complete.
    

There are two ways you can verify that your deployment was successful. You can use either the AWS CloudFormation console, or the AWS Config console. Let’s look at it in the AWS Config console.

To verify deployment in the AWS Config console

  1. Open the AWS Config console.
  2. In the navigation pane, choose Rules.
  3. Choose the AWSConfigRuleKMS rule (or what you named it).
  4. In the Rule details section, you will see the name, trigger type, resource type, rule ARN, parameters, and status, as shown in the following screenshot.
     
    Figure 2: AWSConfigRuleKMS in the AWS Config console

    Figure 2: AWSConfigRuleKMS in the AWS Config console

After the deployment is complete, you must modify the AWS Config role that the AWS Config recorder assumes. Although typically this role would be AWSServiceRoleForConfig, in this solution you need to have your own AWS Config role with the Managed Policy arn:aws:iam::aws:policy/service-role/AWSConfigRole attached. The reason for this is that you need to modify the Trust Policy of the AWS Config role to trust the newly created AWS Lambda role. The AWS Lambda role ARN should look similar to the following:


arn:aws:iam::111122223333:role/rdk/AWSConfigRuleKMS-rdkLambdaRole-RANDOMCHARACTERS

To create your AWS Config recorder role in the IAM console

  1. Open the AWS IAM console.
  2. In the navigation pane, select Roles.
  3. At the top, choose Create Role.
  4. Select Config from the list of services.
  5. For Select your use case, select Config – Customizable.
  6. Choose Next: Permissions.
  7. Choose Next: Tags.
  8. Choose Next: Review.
  9. Enter a descriptive name for the role. For my example, I used CustomConfigRole.
  10. Choose Create role.

To modify the AWS Config recorder role’s trust policy

  1. Open the AWS IAM console.
  2. In the navigation pane, select Roles.
  3. Choose the role created by the RDK for Lambda, which is named AWSConfigRuleKMS-rdkLambdaRole-RANDOMCHARACTERS, or whatever you named it.
  4. Next to the Role ARN, choose the copy icon.
  5. Go back to the Roles screen.
  6. Choose the role you just created.
  7. Choose the Trust relationships tab.
  8. Choose Edit trust relationship.
  9. Copy the following trust policy, and paste it over the existing trust policy. (You need to change the AWS Account ARN in italics to your own AWS Account number and role name):
    
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "",
          "Effect": "Allow",
          "Principal": {
            "AWS": "arn:aws:iam::111122223333:role/rdk/AWSConfigRuleKMS-rdkLambdaRole-132PD765KU42Z",
            "Service": "config.amazonaws.com"
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }
    

  10. Choose Update Trust Policy.

To modify the AWS Config recorder role in the AWS Config console

  1. Open the AWS Config console.
  2. In the navigation pane, select Settings.
  3. Scroll down to AWS Config role*.
  4. Select the radio button next to Choose a role from your account.
  5. Choose the role you created for AWS Config.
  6. Choose Save.

After you have done this, you can use the AWS Config console to force an evaluation of your resources.

To force an AWS Config rule evaluation in the AWS Config console

  1. Open the AWS Config console.
  2. In the navigation pane, select Rules.
  3. Choose the AWSConfigRuleKMS rule (or what you named it).
  4. At the top right-hand side of the console, choose Re-evaluate. This can take a few minutes for results to populate.

To have the key policy correctly evaluated, you need to add permissions to the key policy for the root ARN or the AWS Lambda role ARN to perform kms:GetKeyPolicy.

To update key policies to include the root or AWS Lambda role ARN

  1. Open the AWS KMS console.
  2. In the navigation pane, select Customer managed keys.
  3. Choose the Alias or Key ID you want to modify.
  4. Under Key policy, choose Edit.
  5. Copy the following policy snippet and paste it at the bottom of your current policy. (Replace the values in italics with your actual values).
    
    {
        "Sid": "Enable IAM User Permissions",
        "Effect": "Allow",
        "Principal": {
            "AWS": [
                "arn:aws:iam::111122223333:root",
                "arn:aws:iam::111122223333:role/rdk/AWSConfigRuleKMS-rdkLambdaRole-132PD765KU42Z"
            ]
        },
        "Action": "kms:GetKeyPolicy",
        "Resource": "*"
    }
    

  6. Choose Save changes.

In the following example screenshot of my AWS Config console, you can see that I have quite a few CMKs that don’t meet my compliance policies. The key policy either does not have the required permissions defined in my scenarios, or does not have the permissions for the root ARN or my Lambda role to perform kms:GetKeyPolicy. Both can result in a noncompliant status.
 

Figure 3: Viewing noncompliant CMKs in the AWS Config console

Figure 3: Viewing noncompliant CMKs in the AWS Config console

For example, the key policy for alias/SecurityOtterCMK that follows is missing the condition for aws:userid, as well as mixed permission sets. So this CMK is marked as Noncompliant.


{
    "Version": "2012-10-17",
    "Id": "key-consolepolicy-3",
    "Statement": [
        {
            "Sid": "Allow access for Key Administrators",
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::111122223333:root",
                    "arn:aws:iam::444455556666:root"
                ]
            },
            "Action": [
                "kms:Create*",
                "kms:Describe*",
                "kms:Enable*",
                "kms:List*",
                "kms:Put*",
                "kms:Update*",
                "kms:Revoke*",
                "kms:Disable*",
                "kms:Get*",
                "kms:Delete*",
                "kms:TagResource",
                "kms:UntagResource",
                "kms:ScheduleKeyDeletion",
                "kms:CancelKeyDeletion"
            ],
            "Resource": "*"
        },
        {
            "Sid": "Allow use of the key",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:role/Admin"
            },
            "Action": [
                "kms:Encrypt",
                "kms:Decrypt",
                "kms:ReEncrypt*",
                "kms:GenerateDataKey*",
                "kms:DescribeKey"
            ],
            "Resource": "*"
        },
        {
            "Sid": "Allow attachment of persistent resources",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:role/Admin"
            },
            "Action": [
                "kms:CreateGrant",
                "kms:ListGrants",
                "kms:RevokeGrant"
            ],
            "Resource": "*",
            "Condition": {
                "Bool": {
                    "kms:GrantIsForAWSResource": "true"
                }
            }
        }
    ]
}

On the other hand, the CMK marked as alias/Otter-EBS is in my whitelisted keys based upon my Gherkin, so it shows as Compliant.
 

Figure 4: The CMK with a status of Compliant

Figure 4: The CMK with a status of Compliant

You can now monitor your KMS keys for least privilege based on your required parameters.

Conclusion

In this post, I showed you how to use the AWS Config RDK to create, test, and deploy a custom AWS Config rule that scans your KMS key policies to look for rules designed to implement a least privilege concept. I supplied all scripts necessary to create your rule and test locally. With this AWS Config rule, you can use the AWS Config console to see whether your AWS KMS key policies are compliant with your company standards, so that you can react accordingly.

If you have feedback about this post, submit comments in the Comments section below. If you have questions about this post, start a new thread on the AWS KMS forum or contact AWS Support.

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

Author

Tracy Pierce

Tracy is a Senior Consultant, Security Specialty, for Remote Consulting Services. She enjoys the peculiar culture of Amazon and uses that to ensure every day is exciting for her fellow engineers and customers alike. Customer Obsession is her highest priority and she shows this by improving processes, documentation, and building tutorials. She has her AS in Computer Security & Forensics from SCTD, SSCP certification, AWS Developer Associate certification, and AWS Security Specialist certification. Outside of work, she enjoys time with friends, her Great Dane, and three cats. She keeps work interesting by drawing cartoon characters on the walls at request.

AWS Online Tech Talks for August 2020

Post Syndicated from Jimmy Cooper original https://aws.amazon.com/blogs/aws/aws-online-tech-talks-for-august-2020/

Join us for live, online presentations led by AWS solutions architects and engineers. AWS Online Tech Talks cover a range of topics and expertise levels, and feature technical deep dives, demonstrations, customer examples, and live Q&A with AWS experts.

Note – All sessions are free and in Pacific Time. Can’t join us live? Access webinar recordings and slides on our On-Demand Portal.

Tech talks this month are:

August 17, 2020 | 9:00 AM – 10:00 AM PT
Unlock the Power of Connected Vehicle Data with the AWS Connected Mobility Solution
Learn how to start building solutions for common connected mobility use cases in minutes.

August 17, 2020 | 11:00 AM – 12:00 PM PT
Build a Blockchain Track-and-Trace Application on AWS
Learn how to set up a blockchain network and build a track-and-trace application using Amazon Managed Blockchain.

August 18, 2020 | 9:00 AM – 10:00 AM PT
Cloud Financial Management: How Small Changes Can Accelerate Value Realization
Join 451 Research and AWS Cloud Financial Management (CFM) experts as they share findings from a recent study of 500 enterprise AWS customers conducted to understand how CFM helps organizations realize value beyond cost savings.

August 18, 2020 | 11:00 AM – 12:00 PM PT
Improve Data Science Team Productivity Using Amazon SageMaker Studio
Join us for a deep dive into Amazon SageMaker Studio, the first IDE for ML.

August 18, 2020 | 1:00 PM – 2:00 PM PT
What’s New With AWS Global Accelerator
Learn how to move user traffic onto the AWS network infrastructure, improving traffic by up to 60% in minutes with AWS Global Accelerator.

August 19, 2020 | 11:00 AM – 12:00 PM PT
AWS Lambda Data Storage: Choosing Between S3, EFS, and Local Storage
Learn when to choose Amazon S3, Amazon EFS, or Lambda Layers for your application with AWS Lambda Data Storage.

August 19, 2020 | 1:00 PM – 2:00 PM PT
Optimizing Cluster Utilization with EKS and Fargate
Learn how Amazon and EKS can work together and why you should consider deploying your Kubernetes applications on Fargate instead of traditional Kubernetes worker nodes.

August 20, 2020 | 9:00 AM – 10:00 AM PT
Streaming Data Pipelines for Real-Time Analytics – Are You Ready?
Learn best practices for building streaming data pipelines so you can focus on your data instead of managing infrastructure.

August 20, 2020 | 11:00 AM – 12:00 PM PT
Build Resilient, Easy to Manage Continuous Integration Workflows with AWS CodeBuild and AWS Step Functions
Learn how to use AWS CodeBuild and AWS Step Functions to easily set up branching workflows with manual approval steps for your continuous integration processes or data-processing applications.

August 20, 2020 | 1:00 PM – 2:00 PM PT
Improving Business Continuity with Amazon Aurora Global Database
Learn how to place your database where your customers are for low-latency reads and global disaster recovery.

August 21, 2020 | 9:00 AM – 10:00 AM PT
Intro to AWS Service Management Connector for ServiceNow
Learn how to simplify cloud resource management with the AWS Service Catalog Connector for ServiceNow.

August 21, 2020 | 11:00 AM – 12:00 PM PT
How to Use AWS Wavelength to Deliver Applications that Require Ultra-Low Latency to 5G Mobile Users
Learn how to use AWS Wavelength to develop and deploy applications that need low-latency access to end-user mobile devices.

August 21, 2020 | 1:00 PM – 2:00 PM PT
Deep Dive on Migrating and Modernizing Middleware Applications
Learn migration and modernization strategy, and patterns for middleware applications.

August 24, 2020 | 9:00 AM – 10:00 AM PT
AWS IoT and Amazon Kinesis Video Streams for Connected Home Applications
Learn how you can simplify the development and management of connected cameras and video streaming solutions for smart home use cases.

August 24, 2020 | 11:00 AM – 12:00 PM PT
Introduction to Quantum Computing on AWS
Learn the best practices to get started with quantum computing with Amazon Braket.

August 24, 2020 | 1:00 PM – 2:00 PM PT
Embedding Analytics in Your Applications
Learn about new QuickSight capabilities for embedding analytics and how customers are leveraging QuickSight’s serverless architecture to easily build and scale their embedded analytics applications.

August 25, 2020 | 9:00 AM – 10:00 AM PT
DNS Design Using Amazon Route 53
Explore DNS design, the capabilities of Amazon Route 53 DNS, and architectural considerations for hybrid networks.

August 25, 2020 | 11:00 AM – 12:00 PM PT
Amazon EMR Deep Dive and Best Practices
Learn design patterns and architectural best practices to get the most from your big data with Amazon EMR.

August 25, 2020 | 1:00 PM – 2:00 PM PT
Simply and Seamlessly Set Up Secure File Transfers to Amazon S3 Over SFTP and Other Protocols
Learn how to simply and seamlessly setup secure file transfers to Amazon S3 using AWS Transfer Family.

August 26, 2020 | 9:00 AM – 10:00 AM PT
Enhanced CI/CD with AWS CDK
Learn how to use the AWS Cloud Development Kit and AWS CodePipeline to create a construct library that makes it even easier to set up continuous delivery pipelines for your CDK applications.

August 26, 2020 | 11:00 AM – 12:00 PM PT
Forrester Research Analyzes the Total Economic Impact™ of Amazon Connect
Learn about the Forrester framework and to evaluate the financial impact of working with Amazon Connect, covering ROI benefits, costs, risk, and flexibility.

August 26, 2020 | 1:00 PM – 2:00 PM PT
Best Practices for Data Protection on Amazon S3
Learn how to protect your data by leveraging S3 features to ensure your data meets compliance requirements.

August 27, 2020 | 9:00 AM – 10:00 AM PT
How to Optimize for Cost When Using Amazon DocumentDB (with MongoDB Compatibility)
Learn how to optimize for cost when using Amazon DocumentDB (with MongoDB compatibility).

August 27, 2020 | 11:00 AM – 12:00 PM PT
Accelerating Embedded Linux Solutions for the AWS Cloud
Learn how to accelerate Embedded Linux solutions for the AWS Cloud.

August 27, 2020 | 1:00 PM – 2:00 PM PT
SQL Server Cost Optimization on AWS
Learn how to optimize the cost of SQL Server workloads on AWS across EC2 and RDS SQL Server to diversify and optimize your current licensing investments, think strategically about licensing in the cloud, and bring your own licenses to AWS.

August 28, 2020 | 9:00 AM – 10:00 AM PT
How to Centrally Audit and Remediate VPC Security Groups Using AWS Firewall Manager
Learn how to use AWS Firewall Manager to centrally audit VPC security groups across your accounts and resources using different pre-configured and custom audit polices.

August 28, 2020 | 11:00 AM – 12:00 PM PT
Increase Employee Productivity and Delight Customers with Cognitive Search Using Amazon Kendra
Discover how Amazon Kendra provides highly accurate and easy to use enterprise search powered by machine learning.

August 28, 2020 | 1:00 PM – 2:00 PM PT
How to Maximize the Value of Your Analytics Stack with an Amazon Redshift Data Warehouse
Learn how to maximize the value of your analytics stack with an Amazon Redshift data warehouse.

Automate Amazon Athena queries for PCI DSS log review using AWS Lambda

Post Syndicated from Logan Culotta original https://aws.amazon.com/blogs/security/automate-amazon-athena-queries-for-pci-dss-log-review-using-aws-lambda/

In this post, I will show you how to use AWS Lambda to automate PCI DSS (v3.2.1) evidence generation, and daily log review to assist with your ongoing PCI DSS activities. We will specifically be looking at AWS CloudTrail Logs stored centrally in Amazon Simple Storage Service (Amazon S3) (which is also a Well-Architected Security Pillar best practice) and use Amazon Athena to query.

This post assumes familiarity with creating a database in Athena. If you’re new to Athena, please take a look at the Athena getting started guide and create a database before continuing. Take note of the bucket chosen for the output of Athena query results, we will use it later in this post.

In this post, we walk through:

  • Creating a partitioned table for your AWS CloudTrail logs. In order to reduce costs and time to query results in Athena, we’ll show you how to partition your data. If you’re not already familiar with partitioning, you can learn about it in the Athena user guide.
  • Constructing SQL queries to search for PCI DSS audit log evidence. The SQL queries that are provided in this post are directly related to PCI DSS requirement 10. Customizing these queries to meet your responsibilities may be able to assist you in preparing for a PCI DSS assessment.
  • Creating an AWS Lambda function to automate running these SQL queries daily, in order to help address the PCI DSS daily log review requirement 10.6.1.

Create and partition a table

The following code will create and partition a table for CloudTrail logs. Before you execute this query, be sure to replace the variable placeholders with the information from your database. They are:

  • <YOUR_TABLE> – the name of your Athena table
  • LOCATION – the path to your CloudTrail logs in Amazon S3. An example is included in the following code. It includes the variable placeholders:
    • <AWS_ACCOUNT_NUMBER> – your AWS account number. If using organizational CloudTrail, use the following format throughout the post for this variable: o-<orgID>/<ACCOUNT_NUMBER>
    • <LOG_BUCKET> – the bucket name where the CloudTrail logs to be queried reside

CREATE EXTERNAL TABLE <YOUR_TABLE> (
    eventVersion STRING,
    userIdentity STRUCT<
        type: STRING,
        principalId: STRING,
        arn: STRING,
        accountId: STRING,
        invokedBy: STRING,
        accessKeyId: STRING,
        userName: STRING,
        sessionContext: STRUCT<
            attributes: STRUCT<
                mfaAuthenticated: STRING,
                creationDate: STRING>,
            sessionIssuer: STRUCT<
                type: STRING,
                principalId: STRING,
                arn: STRING,
                accountId: STRING,
                userName: STRING>>>,
    eventTime STRING,
    eventSource STRING,
    eventName STRING,
    awsRegion STRING,
    sourceIpAddress STRING,
    userAgent STRING,
    errorCode STRING,
    errorMessage STRING,
    requestParameters STRING,
    responseElements STRING,
    additionalEventData STRING,
    requestId STRING,
    eventId STRING,
    resources ARRAY<STRUCT<
        arn: STRING,
        accountId: STRING,
        type: STRING>>,
    eventType STRING,
    apiVersion STRING,
    readOnly STRING,
    recipientAccountId STRING,
    serviceEventDetails STRING,
    sharedEventID STRING,
    vpcEndpointId STRING
)
COMMENT 'CloudTrail table'
PARTITIONED BY(region string, year string, month string, day string)
ROW FORMAT SERDE 'com.amazon.emr.hive.serde.CloudTrailSerde'
STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://<LOG_BUCKET>/AWSLogs/<AWS_ACCOUNT_NUMBER>/CloudTrail/'
TBLPROPERTIES ('classification'='cloudtrail');

Execute the query. You should see a message stating Query successful.

Figure 1: Query successful

Figure 1: Query successful

The preceding query creates a CloudTrail table and defines the partitions in your Athena database. Before you begin running queries to generate evidence, you will need to run alter table commands to finalize the partitioning.

Be sure to update the following variable placeholders with your information:

  • <YOUR_DATABASE> – the name of your Athena database
  • <YOUR_TABLE>
  • <LOG_BUCKET>
  • <AWS_ACCOUNT_NUMBER>

Provide values for the following variables:

  • region – region of the logs to partition
  • month – month of the logs to partition
  • day – day of the logs to partition
  • year – year of the logs to partition
  • LOCATION – the path to your CloudTrail logs in Amazon S3 to partition, down to the specific day (should match the preceding values of region, month, day, and year). It includes the variable placeholders:
    • <AWS_ACCOUNT_NUMBER>
    • <LOG_BUCKET>

ALTER TABLE <YOUR_DATABASE>.<YOUR_TABLE>  ADD partition  (region='us-east-1', month='02', day='28', year='2020') location 's3://<LOG_BUCKET>/AWSLogs/<AWS_ACCOUNT_NUMBER>/CloudTrail/us-east-1/2020/02/28/';

After the partition has been configured, you can query logs from the date and region that was partitioned. Here’s an example for PCI DSS requirement 10.2.4 (all relevant PCI DSS requirements are described later in this post).


SELECT * FROM <YOUR_DATABASE>.<YOUR_TABLE> WHERE eventname = 'ConsoleLogin' AND responseelements LIKE '%Failure%' AND region= 'us-east-1' AND year='2020' AND month='02' AND day='28';

Create a Lambda function to save time

As you can see, this process above can involve a lot of manual steps as you set up partitioning for each region and then query for each day or region. Let’s simplify the process by putting these into a Lambda function.

Use the Lambda console to create a function

To create the Lambda function:

  1. Open the Lambda console and choose Create function, and select the option to Author from scratch.
  2. Enter Athena_log_query as the function name, and select Python 3.8 as the runtime.
  3. Under Choose or create an execution role, select Create new role with basic Lambda permissions.
  4. Choose Create function.
  5. Once the function is created, select the Permissions tab at the top of the page and select the Execution role to view in the IAM console. It will look similar to the following figure.
     
    Figure 2: Permissions tab

    Figure 2: Permissions tab

Update the IAM Role to allow Lambda permissions to relevant services

  1. In the IAM console, select the policy name. Choose Edit policy, then select the JSON tab and paste the following code into the window, replacing the following variable and placeholders:
    • us-east-1 – This is the region where resources are. Change only if necessary.
    • <AWS_ACCOUNT_NUMBER>
    • <YOUR_DATABASE>
    • <YOUR_TABLE>
    • <LOG_BUCKET>
    • <OUTPUT_LOG_BUCKET> – bucket name you chose to store the query results when setting up Athena.
    
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "glue:UpdateDatabase",
                    "glue:BatchCreatePartition",
                    "glue:GetDatabase",
                    "athena:StartQueryExecution",
                    "glue:GetPartitions",
                    "glue:UpdateTable",
                    "s3:CreateBucket",
                    "s3:ListBucket",
                    "glue:GetTable",
                    "s3:ListMultipartUploadParts",
                    "s3:PutObject",
                    "s3:GetObjectAcl",
                    "s3:GetObject",
                    "athena:CancelQueryExecution",
                    "athena:StopQueryExecution",
                    "athena:GetQueryExecution",
                    "s3:GetBucketLocation",
                    "glue:UpdatePartition"
                ],
                "Resource": [
                    "arn:aws:glue:us-east-1:<AWS_ACCOUNT_NUMBER>:catalog",
                    "arn:aws:glue:us-east-1:<AWS_ACCOUNT_NUMBER>:table/<YOUR_DATABASE>/<YOUR_TABLE>",
                    "arn:aws:glue:us-east-1:<AWS_ACCOUNT_NUMBER>:database/mydatabase",
                    "arn:aws:s3:::<LOG_BUCKET>/*",
                    "arn:aws:s3:::<LOG_BUCKET>",
    				"arn:aws:s3:::<OUTPUT_LOG_BUCKET>/*",
    				"arn:aws:s3:::<OUTPUT_LOG_BUCKET>",
                    "arn:aws:athena:us-east-1:<AWS_ACCOUNT_NUMBER>:workgroup/primary"
                ]
            },
            {
                "Effect": "Allow",
                "Action": [
                    "logs:PutLogEvents",
                    "logs:CreateLogGroup",
                    "logs:CreateLogStream"
                ],
                "Resource": "arn:aws:logs:us-east-1:<AWS_ACCOUNT_NUMBER>:*"
            }
        ]
    }
    

    Note: Depending on the environment, this policy might not be restrictive enough and should be limited to only users needing access to the cardholder data environment and audit logs. More information about restricting IAM policies can be found in IAM JSON Policy Elements: Condition Operators.

  2. Choose Review policy and then Save changes.

Customize the Lambda Function

  1. On the Lambda dashboard, choose the Configuration tab. In Basic settings, increase the function timeout to 5 minutes to ensure that the function always has time to finish running your queries, and then select Save. Best Practices for Developing on AWS Lambda has more tips for using Lambda.
  2. Paste the following code into the function editor on the Configuration tab, replacing the existing text. The code includes eight example queries to run and can be customized as needed.

    The first query will add partitions to your Amazon S3 logs so that the following seven queries will run quickly and be cost effective.

    This code combines the partitioning, and example Athena queries to assist in meeting PCI DSS logging requirements, which will be explained more below:

    Replace these values in the code that follows:

    • <YOUR_DATABASE>
    • <YOUR_TABLE>
    • <LOG_BUCKET>
    • <AWS_ACCOUNT_NUMBER>
    • <OUTPUT_LOG_BUCKET>
    • REGION1 – first region to partition
    • REGION2 – second region to partition*
    
    import boto3
    import datetime
    import time
    
    #EDIT THE FOLLOWING#
    #----------------------#
    
    #This should be the name of your Athena database
    ATHENA_DATABASE = "<YOUR_DATABASE>"
    
    #This should be the name of your Athena database table
    ATHENA_TABLE = "<YOUR_TABLE>"
    
    #This is the Amazon S3 bucket name you want partitioned and logs queried from:
    LOG_BUCKET = "<LOG_BUCKET>"
    
    #AWS Account number for the Amazon S3 path to your CloudTrail logs
    AWS_ACCOUNT_ID = "<AWS_ACCOUNT_NUMBER>"
    
    #This is the Amazon S3 bucket name for the Athena Query results:
    OUTPUT_LOG_BUCKET = "<OUTPUT_LOG_BUCKET>"
    
    #Define regions to partition
    REGION1 = "us-east-1"
    REGION2 = "us-west-2"
    #----------------------#
    #STOP EDITING#
    
    RETRY_COUNT = 50
    
    #Getting the current date and splitting into variables to use in queries below
    CURRENT_DATE = datetime.datetime.today()
    DATEFORMATTED = (CURRENT_DATE.isoformat())
    ATHENA_YEAR = str(DATEFORMATTED[:4])
    ATHENA_MONTH = str(DATEFORMATTED[5:7])
    ATHENA_DAY = str(DATEFORMATTED[8:10])
    
    #location for the Athena query results
    OUTPUT_LOCATION = "s3://"+OUTPUT_LOG_BUCKET+"/DailyAthenaLogs/CloudTrail/"+str(CURRENT_DATE.isoformat())
    
    #Athena Query definitions for PCI DSS requirements
    YEAR_MONTH_DAY = f'year=\'{ATHENA_YEAR}\' AND month=\'{ATHENA_MONTH}\' AND day=\'{ATHENA_DAY}\';'
    ATHENA_DB_TABLE = f'{ATHENA_DATABASE}.{ATHENA_TABLE}'
    PARTITION_STATEMENT_1 = f'partition (region="{REGION1}", month="{ATHENA_MONTH}", day="{ATHENA_DAY}", year="{ATHENA_YEAR}")'
    LOCATION_1 = f' location "s3://{LOG_BUCKET}/AWSLogs/{AWS_ACCOUNT_ID}/CloudTrail/{REGION1}/{ATHENA_YEAR}/{ATHENA_MONTH}/{ATHENA_DAY}/"'
    PARTITION_STATEMENT_2 = f'partition (region="{REGION2}", month="{ATHENA_MONTH}", day="{ATHENA_DAY}", year="{ATHENA_YEAR}")'
    LOCATION_2 = f' location "s3://{LOG_BUCKET}/AWSLogs/{AWS_ACCOUNT_ID}/CloudTrail/{REGION2}/{ATHENA_YEAR}/{ATHENA_MONTH}/{ATHENA_DAY}/"'
    SELECT_STATEMENT = "SELECT * FROM "+ATHENA_DB_TABLE+ " WHERE "
    LIKE_BUCKET = f' \'%{LOG_BUCKET}%\''
    
    
    #Query to partition selected regions
    QUERY_1 = f'ALTER TABLE {ATHENA_DB_TABLE} ADD IF NOT EXISTS {PARTITION_STATEMENT_1} {LOCATION_1} {PARTITION_STATEMENT_2} {LOCATION_2}'
    
    #Access to audit trails or CHD 10.2.1/10.2.3
    QUERY_2 = f'{SELECT_STATEMENT} requestparameters LIKE {LIKE_BUCKET} AND sourceipaddress <> \'cloudtrail.amazonaws.com\' AND sourceipaddress <> \'athena.amazonaws.com\' AND eventName = \'GetObject\' AND {YEAR_MONTH_DAY}'
    
    #Root Actions PCI DSS 10.2.2
    QUERY_3 = f'{SELECT_STATEMENT} userIdentity.sessionContext.sessionIssuer.userName LIKE \'%root%\' AND {YEAR_MONTH_DAY}'
    
    #Failed Logons PCI DSS 10.2.4
    QUERY_4 = f'{SELECT_STATEMENT} eventname = \'ConsoleLogin\' AND responseelements LIKE \'%Failure%\' AND {YEAR_MONTH_DAY}'
    
    #Privilege changes PCI DSS 10.2.5.b, 10.2.5.c
    QUERY_5 = f'{SELECT_STATEMENT} eventname LIKE \'%AddUserToGroup%\' AND requestparameters LIKE \'%Admin%\' AND {YEAR_MONTH_DAY}'
    
    # Initialization, stopping, or pausing of the audit logs PCI DSS 10.2.6
    QUERY_6 = f'{SELECT_STATEMENT} eventname = \'StopLogging\' OR eventname = \'StartLogging\' AND {YEAR_MONTH_DAY}'
    
    #Suspicious activity PCI DSS 10.6
    QUERY_7 = f'{SELECT_STATEMENT} eventname LIKE \'%DeleteSecurityGroup%\' OR eventname LIKE \'%CreateSecurityGroup%\' OR eventname LIKE \'%UpdateSecurityGroup%\' OR eventname LIKE \'%AuthorizeSecurityGroup%\' AND {YEAR_MONTH_DAY}'
    
    QUERY_8 = f'{SELECT_STATEMENT} eventname LIKE \'%Subnet%\' and eventname NOT LIKE \'Describe%\' AND {YEAR_MONTH_DAY}'
    
    #Defining function to generate query status for each query
    def query_stat_fun(query, response):
        client = boto3.client('athena')
        query_execution_id = response['QueryExecutionId']
        print(query_execution_id +' : '+query)
        for i in range(1, 1 + RETRY_COUNT):
            query_status = client.get_query_execution(QueryExecutionId=query_execution_id)
            query_fail_status = query_status['QueryExecution']['Status']
            query_execution_status = query_fail_status['State']
    
            if query_execution_status == 'SUCCEEDED':
                print("STATUS:" + query_execution_status)
                break
    
            if query_execution_status == 'FAILED':
                print(query_fail_status)
    
            else:
                print("STATUS:" + query_execution_status)
                time.sleep(i)
        else:
            client.stop_query_execution(QueryExecutionId=query_execution_id)
            raise Exception('Maximum Retries Exceeded')
    
    def lambda_handler(query, context):
        client = boto3.client('athena')
        queries = [QUERY_1, QUERY_2, QUERY_3, QUERY_4, QUERY_5, QUERY_6, QUERY_7, QUERY_8]
        for query in queries:
            response = client.start_query_execution(
                QueryString=query,
                QueryExecutionContext={
                    'Database': ATHENA_DATABASE },
                ResultConfiguration={
                    'OutputLocation': OUTPUT_LOCATION })
            query_stat_fun(query, response)
    

    Note: More regions can be added if you have additional regions to partition. The ADD partition statement can be copied and pasted to add additional regions as needed. Additionally, you can hard code the regions for your environment into the statements.

  3. Choose Save in the top right.

Athena Queries used to collect evidence

The queries used to gather evidence for PCI DSS are broken down from the Lambda function we created, using the partitioned date example from above. They are listed with their respective requirement.

Note: AWS owns the security OF the cloud, providing high levels of security in alignment with our numerous compliance programs. The customer is responsible for the security of their resources IN the cloud, keeping its content secure and compliant. The queries below are meant to be a proof of concept and should be tailored to your environment.

10.2.1/10.2.3 – Implement automated audit trails for all system components to reconstruct access to either or both cardholder data and audit trails:


"SELECT * FROM <YOUR_DATABASE>.<YOUR_TABLE> WHERE requestparameters LIKE '%<LOG_BUCKET>%' AND sourceipaddress <> 'cloudtrail.amazonaws.com' AND sourceipaddress <>  'athena.amazonaws.com' AND eventName = 'GetObject' AND year='2020' AND month='02' AND day='28';"

10.2.2 – Implement automated audit trails for all system components to reconstruct all actions taken by anyone using root or administrative privileges.


"SELECT * FROM <YOUR_DATABASE>.<YOUR_TABLE> WHERE userIdentity.sessionContext.sessionIssuer.userName LIKE '%root%' AND year='2020' AND month='02' AND day='28';"

10.2.4 – Implement automated audit trails for all system components to reconstruct invalid logical access attempts.


"SELECT * FROM <YOUR_DATABASE>.<YOUR_TABLE> WHERE eventname = 'ConsoleLogin' AND responseelements LIKE '%Failure%' AND year='2020' AND month='02' AND day='28';"

10.2.5.b – Verify all elevation of privileges is logged.

10.2.5.c – Verify all changes, additions, or deletions to any account with root or administrative privileges are logged:


"SELECT * FROM <YOUR_DATABASE>.<YOUR_TABLE> WHERE eventname LIKE '%AddUserToGroup%' AND requestparameters LIKE '%Admin%' AND year='2020' AND month='02' AND day='28';"

10.2.6 – Implement automated audit trails for all system components to reconstruct the initialization, stopping, or pausing of the audit logs:


"SELECT * FROM <YOUR_DATABASE>.<YOUR_TABLE> WHERE eventname = 'StopLogging' OR eventname = 'StartLogging' AND year='2020' AND month='02' AND day='28';"

10.6 – Review logs and security events for all system components to identify anomalies or suspicious activity:


"SELECT * FROM <YOUR_DATABASE>.<YOUR_TABLE> WHERE eventname LIKE '%DeleteSecurityGroup%' OR eventname LIKE '%CreateSecurityGroup%' OR eventname LIKE '%UpdateSecurityGroup%' OR eventname LIKE '%AuthorizeSecurityGroup%' AND year='2020' AND month='02' AND day='28';" 

"SELECT * FROM <YOUR_DATABASE>.<YOUR_TABLE> WHERE eventname LIKE '%Subnet%' and eventname NOT LIKE 'Describe%' AND year='2020' AND month='02' AND day='28';" 

You can use the AWS Command Line Interface (AWS CLI) to invoke the Lambda function using the following command, replacing <YOUR_FUNCTION> with the name of the Lambda function you created:


aws lambda invoke --function-name <YOUR_FUNCTION> outfile

The AWS Lambda API Reference has more information on using Lambda with AWS CLI.

Note: the results from the function will be located in the OUTPUT_LOCATION variable within the Lambda function.

Use Amazon CloudWatch to run your Lambda function

You can create a rule in CloudWatch to have this function run automatically on a set schedule.

Create a CloudWatch rule

  1. From the CloudWatch dashboard, under Events, select Rules, then Create rule.
  2. Under Event Source, select the radio button for Schedule and choose a fixed rate or enter in a custom cron expression.
  3. Finally, in the Targets section, choose Lambda function and find your Lambda function from the drop down.

    The example screenshot shows a CloudWatch rule configured to invoke the Lambda function daily:
     

    Figure 3: CloudWatch rule

    Figure 3: CloudWatch rule

  4. Once the schedule is configured, choose Configure details to move to the next screen.
  5. Enter a name for your rule, make sure that State is enabled, and choose Create rule.

Check that your function is running

You can then navigate to your Lambda function’s CloudWatch log group to see if the function is running as intended.

To locate the appropriate CloudWatch group, from your Lambda function within the console, select the Monitoring tab, then choose View logs in CloudWatch.
 

Figure 4: View logs in CloudWatch

Figure 4: View logs in CloudWatch

You can take this a step further and set up an SNS notification to email you when the function is triggered.

Summary

In this post, we walked through partitioning an Athena table, which assists in reducing time and cost when running queries on your S3 buckets. We then constructed example SQL queries related to PCI DSS requirement 10, to assist in audit preparation. Finally, we created a Lambda function to automate running daily queries to pull PCI DSS audit log evidence from Amazon S3, to assist with the PCI DSS daily log review requirement. I encourage you to customize, add, or remove the SQL queries to best fit your needs and compliance requirements.

If you have feedback about this post, submit comments in the Comments section below.

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

Author

Logan Culotta

Logan Culotta is a Security Assurance Consultant, and a current Qualified Security Assessor (QSA). Logan is part of the AWS Security Assurance team, which is also a Qualified Security Assessor Company (QSAC). He enjoys finding ways to automate compliance and security in the AWS cloud. In his free time, you can find him spending time with his family, road cycling, or cooking.

BBVA: Architecture for Large-Scale Macie Implementation

Post Syndicated from Neel Sendas original https://aws.amazon.com/blogs/architecture/bbva-architecture-for-large-scale-macie-implementation/

This post was co-written by Andrew Alaniz , Technical Information Security Officer, and Brady Pratt, Cloud Security Enginner, both at BBVA USA.

Introduction

Data Loss Prevention (DLP) is a common topic among companies that work with any type of sensitive data. One of the challenges is that many people either don’t fully understand what DLP is, or rather, have their own definition of what it is. Regardless of one’s interpretation of DLP, one thing is certain: before you can control data loss, you need to locate find the data sources.

If an organization can’t identify its data, it can’t protect it. BBVA USA, a bank holding company, turned to AWS for advice, and decided to use Amazon Macie to accomplish this in Amazon Simple Storage Service (Amazon S3). Macie is a fully managed data security and data privacy service that uses machine learning and pattern matching to discover and protect your sensitive data in AWS. This blog post will share some of the design and architecture we used to deploy Macie using services, such as AWS Lambda and Amazon CloudWatch.

Data challenges in Amazon S3

Although all S3 buckets are private by default, everyone is aware of the challenges of unsecured S3 buckets exposing data publicly. Amazon has provided a way to prevent that by removing the ability to make buckets public. As with other data storage mechanisms, this doesn’t stop anyone from storing sensitive data within AWS and exposing it another way. With Macie, one can classify the data stored in S3, centrally, and through AWS Organizations.

Recommended architecture

We can break the Macie architecture into two main parts: S3 discovery and evaluation, and S3 sensitive data discovery:

Macie architecture

The setup of discovery and evaluation is simple and straightforward, and should be enabled through Amazon Organizations and across all accounts. The cost of this piece is minimal, and it provides valuable insights into the compliance state of S3 buckets.

Once setup of discovery and evaluation is completed, we are a ready to move to the next step and configure discovery jobs for our S3 buckets. The architecture includes the use of S3, Amazon CloudWatch Events, Amazon EventBridge, and Lambda. All of the execution should happen in a centralized account, but the event triggers should come from each individual account.

Architectural considerations

When determining the architectural design of the solution, consider a few main components:

Centralization

Utilize AWS Organizations: Macie allows native integration with AWS Organizations. This is a significant advantage for Macie. Additionally, within AWS Organizations, it allows the delegation of the Macie master account to a subordinate account. The benefit of this is that it allows centralized management while allowing for the compartmentalization of roles.

Ease of management

One of the most challenging things to manage is non-conforming configurations. It’s much easier to manage a standard way to create, name, and configure settings. Once the classification jobs were ready to be created, we had to take into consideration the following when deploying Macie for our use case:

  1. Macie classifies content in a single job across one account.
  2. If you submit multiple jobs that contain the same bucket, Macie will scan the objects multiple times.
  3. Macie jobs are immutable.

Due to these considerations, we decided to create one job per S3 bucket. This allows administrators to search more easily for jobs related to findings.

Cost considerations

Macie plays an essential role, not only in identifying data and improving data collection, but also in compliance. We needed to make a decision about how to determine if an S3 bucket would be included in a classification job. Initially, we considered including all buckets no matter what. The logic here was that even if we make an assumption that a bucket would never have sensitive data in it, an entity with the right role could always add something at a later date.

Finally, we implemented a solution to tag specific buckets that were known to have immutable properties and which would never allow sensitive data to be added. We could do this because we knew exactly what data was in the bucket, who or what created the bucket, and exactly who or what had access to the bucket.

An example of this type of bucket is the S3 bucket used to store VPC Flow Logs. We know that this bucket is only created by provisioning scripts and is only going to store VPC flow logs that contain no sensitive data based on data classification standards. Also, only VPC services and specific security services can access this bucket for anything other than READ. This is controlled organizationally and can be tagged with a simple ignore key/value pair upon creation.

Deploying Macie at BBVA USA

BBVA USA developed an approach to working within AWS that allows guardrails to be applied as accounts are created. One of those guardrails identifies if developers have stored sensitive data in an account. BBVA needed to be able to do this, and do it at scale. If there is a roadblock or a challenge with AWS services, the first place BBVA looks is to support, but the second place is the Technical Account Manager.

After initiating conversations with its account team, BBVA determined that AWS Macie was the tool to help them with this challenge.

With the help of its technical account manager (TAM), BBVA was able to meet with the Macie Product team and discuss the best options for deploying at scale. Through these conversations, they were even able to influence the Macie product roadmap.

Getting Macie ready to deploy at scale was actually quite simple once the architectural pattern was designed.

Initial job creation

In order to set up jobs for each existing bucket in the organization, it’s a matter of scripting the job creation and adding each bucket from each account into its own job, which is pretty straightforward.

Job creation for new buckets

The recommended architecture and implementation for existing buckets:

  1. Whenever a new S3 bucket is added to Organization accounts, trigger a CloudWatch Event in the target account.
  2. Set up a cross account EventBridge to consume the Event. Using the EventBridge allows for a simpler configuration and centralized management of both Events and Lambda.
  3. Trigger a Lambda function in a delegated Macie admin account, which creates classification jobs to apply Macie to all the newly created S3 buckets.
  4. Repeat the same process when a bucket is deleted by triggering a cancel job.

Evaluate the state of S3 buckets

To evaluate the S3 accounts, turn on Macie at the organization master account and delegate administration to a subordinate account used for Macie. This enables management consolidation of security features into a centralized security account. This helps further restrict access from those that may need access to the master billing account. Finally, enable Macie by default on all organization accounts.

Evaluate the state of S3 buckets

Conclusion

BBVA USA worked directly with the Macie product team by leveraging its relationship with the AWS account team and Enterprise Support. This allowed the company to eventually deploy Macie quickly and at scale. Through Macie, the company is able to track any changes to configurations on buckets that allow a bucket to be public, shared, or replicated with external accounts and if the encryption policies are disabled. Using Macie, BBVA was able to identify buckets that contained sensitive information and put in another control to bolster its AWS governance profile.

Secure deployment of Amazon SageMaker resources

Post Syndicated from Rajesh Ramchander original https://aws.amazon.com/blogs/security/secure-deployment-of-amazon-sagemaker-resources/

Amazon SageMaker, like other services in Amazon Web Services (AWS), includes security-related parameters and configurations that you can use to improve the security posture of resources as you deploy them. However, many of these security-related parameters are optional, allowing you to deploy resources without them. While this might be acceptable in the initial exploration stage, customers want resources to be deployed more securely in production.

In this post I will discuss three approaches for deploying Amazon SageMaker resources more securely and highlight some pros and cons with each approach.

Before you begin

This post assumes general familiarity with machine learning and Amazon SageMaker. In addition, it assumes knowledge of the services used to implement security controls, including:

Approaches

Amazon SageMaker contains security-related parameters for the secure deployment of resources within it. For example, when creating an Amazon SageMaker notebook instance, root access on the notebook instance can be disabled. Another example is when creating an Amazon SageMaker training job, it can be set up to access other services like Amazon Simple Storage Service (Amazon S3) through an endpoint in the customer’s Amazon Virtual Private Cloud (Amazon VPC).

However, these and most other security-related parameters and configurations are optional. As examples of less-secure configuration, Amazon SageMaker notebook instances can be created with root access enabled, and training jobs can access Amazon S3 over the public endpoints.

There are two main methods of implementing controls to improve the security of AWS services during deployment. One of them is preventive and uses controls to stop an event from occurring. The other is responsive, and uses controls that are applied in response to events.

Preventive controls protect workloads and mitigate threats and vulnerabilities. A couple of approaches to implement preventive controls are:

  • Use IAM condition keys supported by the service to ensure that resources without necessary security controls cannot be deployed.
  • Use the AWS Service Catalog to invoke AWS CloudFormation templates that deploy resources with all the necessary security controls in place.

Responsive controls drive remediation of potential deviations from security baselines. An approach to implement responsive controls is:

  • Use CloudWatch Events to catch resource creation events, then use a Lambda function to validate that resources were deployed with the necessary security controls, or terminate resources any if the necessary security controls aren’t present.

The next few sections talk about each of these approaches in respect to Amazon SageMaker.

IAM condition keys approach

IAM condition keys can be used to improve security by preventing resources from being created without security controls. When a principal makes an API request to AWS to create a resource, the request information is gathered into a request context. This request context is compared to conditions in the principal’s policy. If the conditions pass, the API request is allowed to proceed and the resource will be created. However, if the conditions fail, the API request is stopped and the resource won’t be created.

The optional Condition element (or block) in an IAM policy is where expressions are built using condition operators (such as StringEquals or NumericLessThan). These condition expressions match the condition keys and values in the policy to the keys and values in the request context. The condition key specified in a condition element can be global or service-specific.

A condition element has the following syntax:


"Condition": {
   "{condition-operator}": {
      "{condition-key}": "{condition-value}"
   }
}

The following condition element contains an Amazon SageMaker service-specific condition key to ensure that when an Amazon SageMaker notebook instance is created, the request must ask that root access on the notebook instance be disabled. If a request is made, and the request doesn’t ask that root access on the notebook instance be disabled, it will be denied.


"Condition": {
   "StringEquals": {
      "sagemaker:RootAccess": "Disabled"
   }
}

The AWS User Guide topic IAM JSON Policy Elements: Condition provides more information.

Amazon SageMaker IAM condition keys

Amazon SageMaker supports a few global condition context keys and adds several Amazon SageMaker service-specific condition keys.

Global condition context keys

Global condition context keys are documented in AWS Global Condition Context Keys. Global condition context keys start with an aws: prefix. The following global condition context keys are applicable to Amazon SageMaker.

  • aws:RequestTag/${TagKey} – This key is used to compare the tag key-value pair that was passed in the request with the tag pair specified in the policy.
  • aws:ResourceTag/${TagKey} – This key is used to compare the tag key-value pair that is specified in the policy with the key-value pair attached to the resource.
  • aws:SourceIp – This key is used to compare the requester’s IP address with the IP address specified in the policy.
  • aws:SourceVpc – This key is used to check whether the request comes from the Amazon VPC specified in the policy.
  • aws:SourceVpce – This key is used to compare the Amazon VPC endpoint identifier of the request with the endpoint ID specified in the policy.
  • aws:TagKeys – This key is used to compare the tag keys in the request with the keys specified in the policy.

Amazon SageMaker service-specific condition keys

Amazon SageMaker service-specific condition keys are documented in Actions, Resources, and Condition Keys for Amazon SageMaker and Amazon SageMaker Identity-Based Policy Examples. They have a sagemaker: prefix.

  • sagemaker:AcceleratorTypes – This key is used to use a specific Amazon Elastic Inference accelerator when creating or updating notebook instances and when creating endpoint configurations. Elastic Inference allows addition of inference acceleration to a hosted endpoint for a fraction of the cost of using a full GPU instance.
  • sagemaker:DirectInternetAccess – This key is used to control direct internet access from notebook instances. Allowed values are Enabled and Disabled. The default behavior is to allow direct internet access. Direct internet access should be disabled to prevent unfettered internet access after connecting notebook instances to the customer’s Amazon VPC. This can be done by using the sagemaker:VPCSubnets and sagemaker:VPCSecurityGroupIds parameters.
  • sagemaker:FileSystemAccessMode – Amazon SageMaker can be used in conjunction with Amazon Elastic File System (Amazon EFS) or Amazon FSx file systems for training jobs and hyperparameter tuning jobs. This key is used to specify the access mode of the directory associated with the input data channel. The directory can be mounted either in read-only or read-write mode.
  • sagemaker:FieSystemDirectoryPath – This key is used to specify the file system directory path associated with the resource in the training and hyperparameter tuning request.
  • sagemaker:FileSystemId – This key is used to specify the file system ID associated with the resource in the training and hyperparameter tuning request.
  • sagemaker:FileSystemType – This key is used to specify the file system type associated with the resource in the training and hyperparameter tuning request.
  • sagemaker:InstanceTypes – This key is used to specify the list of all instance types for notebook instances, training jobs, hyperparameter tuning jobs, batch transform jobs, and endpoint configurations for hosting real-time inferencing. Restricting instance types can be done to only allow enhanced-security Nitro instances or to control costs by not allowing GPU instances.
  • sagemaker:InterContainerTrafficEncryption – This key is used to control inter-container traffic encryption for distributed training and hyperparameter tuning jobs. Allowed values are true and false. The default value is false. Distributed machine learning frameworks and algorithms usually transmit information that is directly related to the model such as weights, not the training dataset. This parameter should be set to true to comply with regulatory requirements with the understanding that it could increase the training time of distributed jobs.
  • sagemaker:MaxRuntimeInSeconds – This key is used to control costs by specifying the maximum length of time, in seconds, that the training, hyperparameter tuning, or compilation job can run. If a job doesn’t complete within this time, Amazon SageMaker ends the job. If a value isn’t specified, the default value is 1 day. The maximum value that can be specified is 28 days.
  • sagemaker:ModelArn – This key is used to specify the Amazon Resource Name (ARN) of the model associated for batch transform jobs and endpoint configurations for hosting real-time inferencing. When creating a batch transform job or endpoint configuration, a model name is passed in the API request. The name of the model must be associated with model ARN specified in the policy.
  • sagemaker:NetworkIsolation – This key is used to enable network isolation when creating training, hyperparameter tuning, and inference jobs. Allowed values are true and false. The default value is false. This parameter should be set to true to prevent containers from making any outbound network calls, even to other AWS services such as Amazon S3. Network isolation is required for training jobs and models run using resources from AWS Marketplace.
  • sagemaker:OutputKmsKey – This key is used to specify the AWS KMS key to encrypt output data stored in Amazon S3. Either the KMS key ID or key ARN can be specified. This key shouldn’t be confused with the key to encryption storage volumes specified in sagemaker:VolumeKmsKey.
  • sagemaker:RequestTag/${TagKey} – This key is used to compare the tag key-value pair that was passed in the request with the tag pair that is specified in the policy. This could be used to ensure that a particular tag is always used.
  • sagemaker:ResourceTag/${TagKey} – This key is used to compare the tag key-value pair that is specified in the policy with the key-value pair that is attached to the resource. This could be used to ensure that a particular tag and value pair is always used.
  • sagemaker:RootAccess – This key is used to control root access on the notebook instances. Allowed values are Enabled and Disabled. The default behavior is to allow root access. Root access is usually not a best practice and should be disabled. Disabling root access prevents notebook users from deleting system-level software, installing new software, and modifying essential components.
  • sagemaker:VolumeKmsKey – This key is used to specify an AWS KMS key to encrypt storage volumes when creating notebook instances, training jobs, hyperparameter tuning jobs, batch transform jobs, and endpoint configurations for hosting real-time inferencing. Either the KMS key ID or key ARN can be specified. This key shouldn’t be confused with the key to encrypt output data in Amazon S3 specified in sagemaker:OutputKmsKey.
  • sagemaker:VPCSecurityGroupIds – The list of all Amazon VPC security group IDs associated with the elastic network interface (ENI) that Amazon SageMaker creates in the Amazon VPC subnet.
  • sagemaker:VPCSubnets – The list of all Amazon VPC subnets where Amazon SageMaker creates ENIs to communicate with other resources like Amazon S3.

AWS Service Catalog approach

AWS Service Catalog allows organizations to create and manage catalogs of IT services that are approved for use on AWS. You can use it to create a preventive approach to improving security by invoking templates with security controls already in place. These IT services can include everything from virtual machine images, servers, software, and databases to complete multi-tier application architectures. AWS Service Catalog allows for the central management of commonly deployed IT services, helps achieve consistent governance, and supports you in meeting your compliance requirements. It does this while enabling users to quickly deploy only the IT services they need and that are approved by their organization.

AWS Service Catalog products are created by importing AWS CloudFormation templates that provision the resources in services. CloudFormation provides a common language for the description and provisioning of all the infrastructure resources in a cloud environment. CloudFormation lets you use programming languages or a simple text file to model and provision all the resources needed for applications across all regions and accounts in an automated and secure manner. This gives a single source of truth for the AWS resources.

AWS CloudFormation templates implement resources in various services as resource types. For example, there is a CloudFormation resource type called AWS::SageMaker::NotebookInstance that models an Amazon SageMaker notebook instance. When a CloudFormation stack with this resource type is created, the notebook instance is provisioned based on the template parameters.

Since AWS CloudFormation is typically used to provision infrastructure as opposed to executing workflows, CloudFormation models Amazon SageMaker notebook instances but not Amazon SageMaker training jobs. In situations like this, a custom resource can be used. Custom resource providers, typically implemented as Lambda functions, are invoked when a CloudFormation stack with a custom resource is created. The Lambda function can use the AWS SDKs—which are available in several programming languages—to create the resource. In the case of Amazon SageMaker training jobs, when the CloudFormation stack is created, it will call a Lambda function that can use the Boto3 Python SDK to create a training job.

The following Amazon SageMaker resource types are supported by AWS CloudFormation. All other Amazon SageMaker resources need to be created using the custom resource approach.

  • AWS::SageMaker::CodeRepository creates a Git repository that can be used for source control.
  • AWS::SageMaker::Endpoint creates an endpoint for inferencing.
  • AWS::SageMaker::EndpointConfig creates a configuration for endpoints for inferencing.
  • AWS::SageMaker::Model creates a model for inferencing.
  • AWS::SageMaker::NotebookInstance creates a notebook instance for development.
  • AWS::SageMaker::NotebookInstanceLifecycleConfig creates shell scripts that run when notebook instances are created and/or started.
  • AWS::SageMaker::Workteam creates a work team for labeling data.

AWS CloudFormation collects user input in the form of parameters that can be defined in the template. The value for the security related parameters should fall into one of the following three categories.

Security parameters that shouldn’t be exposed

In the AWS CloudFormation templates, don’t implement these settings as parameters. Instead, automatically set them without providing a choice to the user:

  • DirectInternetAccess – Set this to Disabled when creating notebook instances after connecting to the customer’s Amazon VPC using VpcConfig.Subnets and VpcConfig.SecurityGroupIds.
  • EnableInterContainerTrafficEncryption – Set this to true when creating distributed training and hyperparameter tuning jobs. Note that it might increase the training time.
  • EnableNetworkIsolation – Set this to true when creating training, hyperparameter tuning, and inference jobs to prevent situations like malicious code being accidentally installed and transferring data to a remote host.
  • MaxRuntimeInSeconds – Set this to a reasonable value.
  • RootAccess – Set this to Disabled when creating notebook instances as it is generally not a best practice to permit root access. Disabling root access prevents notebook users from deleting system-level software, installing new software, and modifying essential environment components.
  • VpcConfig.SecurityGroupIds – Set this to a pre-created security group that has been configured with the necessary controls.

Security parameters that should be restricted

For the following parameters, require the user to select a value from a dropdown list either by hard-coding the list or by using a supported AWS-specific parameter.

  • AcceleratorTypes – The dropdown list has to be hard coded. Elastic Inference accelerators allow the addition of inference acceleration to a hosted endpoint for a fraction of the cost of using a full GPU instance.
  • InstanceTypes – The dropdown list has to be hard coded. Restricting instance types can be used to only allow enhanced-security Nitro instances or to control costs by not allowing GPU instances.
  • VpcConfig.Subnets – The dropdown list can be built by using an AWS::EC2::Subnet::Id parameter type.

Security parameters that should be validated

Require the user to input values for the following parameters by using the AllowedPattern property for the parameter with a regular expression of “+”:

  • OutputKmsKey
  • VolumeKmsKey

CloudWatch Events approach

Amazon CloudWatch and CloudWatch Events can be used to implement responsive controls to improve security. CloudWatch is a service that provides data and actionable insights to monitor applications, respond to system-wide performance changes, optimize resource utilization, and provide a unified view of operational health. CloudWatch collects monitoring and operational data in the form of logs, metrics, and events. CloudWatch uses the data to provide a unified view of AWS resources, applications, and services that run on AWS and on-premises servers. CloudWatch can be used to detect anomalous behavior in environments, set alarms, visualize logs and metrics side by side, take automated actions, troubleshoot issues, and discover insights to keep applications running smoothly.

CloudWatch Events—a subsystem within CloudWatch—delivers a near real-time stream of events that describe changes in AWS resources. Using simple rules, events can be matched and routed to one or more target functions or streams. The target of a CloudWatch Events rule might be, for example, a Lambda function that will be invoked with an event every time the rule matches.

The following figure shows how to create a CloudWatch Events rule that will match events pertaining to state changes of Amazon SageMaker training jobs. The steps using the AWS Management Console are:

  1. Go to Amazon CloudWatch and select Rules under Events.
  2. Select SageMaker from the Service Name dropdown.
  3. Select SageMaker Training Job State Change from the Event Type dropdown. The Event Pattern Preview is automatically populated.
  4. Select Lambda function from the Targets dropdown.
  5. Select the AWS Lambda function that you have implemented from the Function dropdown.

    Figure 1: Create a CloudWatch event rule

    Figure 1: Create a CloudWatch event rule

Amazon SageMaker provides the following training job statuses:

  • InProgress – The training is in progress.
  • Completed – The training job has completed.
  • Failed – The training job has failed. To see the reason for the failure, see the FailureReason field in the response to a DescribeTrainingJob call.
  • Stopping – The training job is stopping.
  • Stopped – The training job has stopped.

The Lambda function that is configured as the target for the CloudWatch Events rule should inspect the event and retrieve the Amazon SageMaker training job status. If the status is InProgress, the Lambda function will do the following:

  1. Call the DescribeTrainingJob API, and pass in the training job name.
  2. From the response, check to see if the training job has all of the necessary security controls.
  3. If the training job is deemed insecure, call the StopTrainingJob API to stop it.

Similar CloudWatch events rules can be set up for other Amazon SageMaker events.

Discussing the approaches

The IAM condition keys approach doesn’t involve coding. All it requires is adding condition elements to IAM policies. When this approach is used, users deploying resources are free to choose any approach including the console, CLI, and SDKs. Additionally, Amazon SageMaker also has a higher-level Python SDK implemented on top of Boto3 that makes deploying Amazon SageMaker resources easy.

However, there are a few caveats with this approach. First, not all AWS services support IAM condition keys. Fortunately, Amazon SageMaker has comprehensive support for IAM condition keys. Second, since this approach involves IAM policies, IAM service limits, some of which are documented below (a full list of IAM limits can be found at IAM and STS Limits), need to be taken into consideration.

  • An IAM user, group, or role can have a maximum of 10 managed policies.
  • The size of each managed policy cannot exceed 6,144 characters (not counting white spaces).

All of the conditions should be documented clearly. Otherwise, users deploying resources might have to use trial-and-error to successfully deploy them.

The AWS Service Catalog approach involves coding of AWS CloudFormation templates. When the necessary resource types aren’t supported by CloudFormation, custom resource Lambda functions have to be implemented by the customer. This approach is always available without any special support needed from the service. This approach also takes guesswork out of the equation when deploying resources as the CloudFormation templates can guide the user with providing proper security parameters.

Finally, the CloudWatch Events approach also involves coding by the customer. Because it’s a responsive control, the resource will start to be deployed before it will be stopped or terminated, if users create it without the necessary security controls. CloudWatch Events are available very soon after the resource provisioning starts. Amazon SageMaker resources typically take a couple of minutes after a resource is requested before it becomes available. It should be noted that users don’t get any direct feedback when resources are stopped or terminated in response to CloudWatch Events. They need to review CloudWatch Logs or notifications sent by the Lambda function code to figure out why a resource was terminated. Customers can implement this approach could be used in combination with one of the preventive approaches to enhance security.

Summary

In this post we discussed three different approaches for deploying Amazon SageMaker securely – IAM condition keys, Service Catalog, and CloudWatch Events. You can use each of these methods to improve the security of your AWS resources as you deploy them. After reading this post, you should now have a better understanding of the pros and cons of each of the approaches and how you can use them to deploy your Amazon SageMaker resources more securely in production.

Contributors

Contributors to this document include:

  • Paco Hope, Principal Security Consultant, AWS ProServe
  • Jeff Puchalski, Technical Training Specialist, AWS Security
  • Kumar Venkateswar, Principal PM, Amazon SageMaker, AI Platforms
  • Ross Warren, Senior Solutions Architect, Security

Additional resources

For additional information, see:

If you have feedback about this post, submit comments in the Comments section below.

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

Author

Rajesh Ramchander

Rajesh is a Senior Big Data Consultant in Professional Services at AWS. He helps customers migrate big data and machine learning/artificial intelligence workloads to AWS using Amazon EMR, AWS Glue, and Amazon SageMaker. Before joining AWS, Rajesh was a member of senior management of software development teams. He holds an MS in Computer Science and an MS in Electrical Engineering.

Liberty IT Adopts Serverless Best Practices Using AWS Cloud Development Kit

Post Syndicated from Andrew Robinson original https://aws.amazon.com/blogs/architecture/liberty-it-adopts-serverless-best-practices-using-aws-cdk/

This post was co-written with Matthew Coulter, Lead Technical Architect of Global Risk at Liberty Mutual

Liberty IT Solutions, part of Liberty Mutual Group, has been using AWS CloudFormation to deploy serverless applications on AWS for the last four years. These deployments typically involve defining, integrating, and monitoring services such as AWS Lambda, Amazon API Gateway, and Amazon DynamoDB.

In this post, we will explore how Liberty took the AWS Cloud Development Kit (CDK), the AWS Well-Architected framework, and the Serverless Application Lens for the AWS Well-Architected Framework and built a set of CDK patterns in to help developers launch serverless resources.

Since each team was responsible for building and maintaining these applications, as soon as CloudFormation templates grew larger, it quickly became a challenge. As well, it was becoming increasingly time consuming to extract the relevant parts of the template for re-use. Even when teams were building similar applications with similar requirements, everything was built from scratch for that application.

Before we dive in, let’s cover some of the terminology in this blog post and provide you with some additional reading:

  • AWS CDK was launched as a software development framework to help you define cloud infrastructure as code and provision it through AWS CloudFormation.
  • AWS Well-Architected was developed to help cloud architects build secure, high performing, resilient, and efficient infrastructures for their applications.
  • Serverless Application Lens focuses on how to design, deploy, and architect your serverless application workloads on the AWS Cloud.

Background

The model Liberty used to develop these applications was based on small components that led to repeatable patterns, which made them ideal for sharing architectures across teams.

For example, if different teams built a web application using API Gateway, AWS Lambda, and Amazon DynamoDB, they could build a reusable pattern that included best practices after they battle tested it. This known infrastructure pattern could then be easily shared across multiple teams, ultimately increasing developer productivity.

Due to compliance requirements and controls in place, Liberty built these applications using CloudFormation with specific configurations. Teams had to frequently reverse engineer their CloudFormation templates from previous proof of concepts (PoCs), as there were no high-level abstractions available. If there were similar examples out there, they’d have to engineer it back to CloudFormation with our controls in place in order to use it in production.

That’s where CDK comes in

Matt Coulter at Liberty (and co-author of this blog post), said, “After researching CDK, we thought it would be a good fit for producing the patterns we wanted to, and providing an open source framework for others to contribute to the patterns, and to build their own.”

In an initial PoC, Liberty was able to take more than 2,000 lines of YAML CloudFormation down to just 14 lines of code, which came packaged with its own unit tests. This could then be published to NPM or GitHub to share with the rest of the team and with no loss of functionality over the original CloudFormation template.

Discovering and implementing AWS Serverless best practices

Liberty wanted to start the patterns using industry-wide best practices before applying its own specific best practices for compliance requirements.

The ideal starting point for this was the AWS Well-Architected framework, specifically the Serverless Application Lens. The Serverless Lens is more focused and specific, and it provides guidance that speaks to developers more closely than the broader framework. Using Serverless Lens saved Liberty significant time investments on discovering and implementing those best practices across the patterns they had built, and it allowed for faster uptake.

Let’s take a look at one of the patterns in more detail.

The X-Ray tracer pattern

This pattern introduces the concept of distributed tracing: as workloads scale it can become more challenging to find anomalies and figure out where those anomalies occur within your application. This is not defined by the components used, but how the information is sent back information to AWS X-Ray. The pattern includes some common use cases with different data flows through AWS Serverless services.

Common use cases with different data flows through AWS Serverless services

After this pattern has been deployed, a service map will be generated in X-Ray showing the interconnectivity between the different resources deployed. (Your map may look slightly different than this one.)

Service map in X-Ray showing interconnectivity between different resources deployed

This pattern will provide you with an API Gateway endpoint you can use to trigger the flow and see the results in near real time in the X-Ray service map. The pattern also includes an SSL Certificate error in a Lambda function that connects to an external HTTP endpoint. When this happens, you can see the error in the X-Ray service map. You can then go into the trace details, which shows the specific error:

Trace details showing the specific error

Want to know more?

Liberty has deployed more than 1,000 applications into non-production environments using CDK Patterns, and more than 100 applications into production. This saved its developers time in deploying best-of-breed serverless applications for the company and has encouraged sharing across the entire organization.

All of these patterns are now available at CDK Patterns under the MIT License. There are currently 17 serverless patterns available in both Typescript and Python, and you can find CDK patterns by any of the five Well-Architected Pillars.

You can also reach out to Liberty IT about this project on Twitter as well as contribute either directly or on GitHub.

 

Monitoring AWS Certificate Manager Private CA with AWS Security Hub

Post Syndicated from Anthony Pasquariello original https://aws.amazon.com/blogs/security/monitoring-aws-certificate-manager-private-ca-with-aws-security-hub/

Certificates are a vital part of any security infrastructure because they allow a company’s internal or external facing products, like websites and devices, to be trusted. To deploy certificates successfully and at scale, you need to set up a certificate authority hierarchy that provisions and issues certificates. You also need to monitor this hierarchy closely, looking for any activity that occurs within your infrastructure, such as creating or deleting a root certificate authority (CA). You can achieve this using AWS Certificate Manager (ACM) Private Certificate Authority (CA) with AWS Security Hub.

AWS Certificate Manager (ACM) Private CA is a managed private certificate authority service that extends ACM certificates to private certificates. With private certificates, you can authenticate resources inside an organization. Private certificates allow entities like users, web servers, VPN users, internal API endpoints, and IoT devices to prove their identity and establish encrypted communications channels. With ACM Private CA, you can create complete CA hierarchies, including root and subordinate CAs, without the investment and maintenance costs of operating your own certificate authority.

AWS Security Hub provides a comprehensive view of your security state within AWS and your compliance with security industry standards and best practices. Security Hub centralizes and prioritizes security and compliance findings from across AWS accounts, services, and supported third-party partners to help you analyze your security trends and identify the highest priority security issues.

In this example, we show how to monitor your root CA and generate a security finding in Security Hub if your root is used to issue a certificate. Following best practices, the root CA should be used rarely and only to issue certificates under controlled circumstances, such as during a ceremony to create a subordinate CA. Issuing a certificate from the root at any other time is a red flag that should be investigated by your security team. This will show up as a finding in Security Hub indicated by ‘ACM Private CA Certificate Issuance.’

Example scenario

For highly privileged actions within an IT infrastructure, it’s crucial that you use the principle of least privilege when allowing employee access. To ensure least privilege is followed, you should track highly sensitive actions using monitoring and alerting solutions. Highly sensitive actions should only be performed by authorized personnel. In this post, you’ll learn how to monitor activity that occurs within ACM Private CA, such as creating or deleting a root CA, using AWS Security Hub. In this example scenario, we cover a highly sensitive action within an organization building a private certificate authority hierarchy using ACM Private CA:

Creation of a subordinate CA that is signed by the root CA:

Creating a CA certificate is a privileged action. Only authorized personnel within the CA Hierarchy Management team should create CA certificates. Certificate authorities can sign private certificates that allow entities to prove their identity and establish encrypted communications channels.

Architecture overview

This solution requires some background information about the example scenario. In the example, the organization has the following CA hierarchy: root CA → subordinate CA → end entity certificates. To learn how to build your own private certificate infrastructure see this post.

Figure 1: An example of a certificate authority hierarchy

Figure 1: An example of a certificate authority hierarchy

There is one root CA and one subordinate CA. The subordinate CA issues end entity certificates (private certificates) to internal applications.

To use the test solution, you will first deploy a CloudFormation template that has set up an Amazon CloudWatch Events Rule and a Lambda function. Then, you will assume the persona of a security or certificate administrator within the example organization who has the ability to create certificate authorities within ACM Private CA.

Figure 2: Architecture diagram of the solution

Figure 2: Architecture diagram of the solution

The architecture diagram in Figure 2 outlines the entire example solution. At a high level this architecture enables customers to monitor activity within ACM Private CA in Security Hub. The components are explained as follows:

  1. Administrators within your organization have the ability to create certificate authorities and provision private certificates.
  2. Amazon CloudWatch Events tracks API calls using ACM Private CA as a source.
  3. Each CloudWatch Event triggers a corresponding AWS Lambda function that is also deployed by the CloudFormation template. The Lambda function reads the event details and formats them into an AWS Security Finding Format (ASFF).
  4. Findings are generated in AWS Security Hub by the Lambda function for your security team to monitor and act on.

This post assumes you have administrative access to the resources used, such as ACM Private CA, Security Hub, CloudFormation, and Amazon Simple Storage Service (Amazon S3). We also cover how to remediate through practicing the principle of least privilege, and what that looks like within the example scenario.

Deploy the example solution

First, make sure that AWS Security Hub is turned on, as it isn’t on by default. If you haven’t used the service yet, go to the Security Hub landing page within the AWS Management Console, select Go to Security Hub, and then select Enable Security Hub. See documentation for more ways to enable Security Hub.

Next, launch the CloudFormation template. Here’s how:

  1. Log in to the AWS Management Console and select AWS Region us-east-1 (N. Virginia) for this example deployment.
  2. Make sure you have the necessary privileges to create resources, as described in the “Architecture overview” section.
  3. Set up the sample deployment by selecting Launch Stack below.

The example solution must be launched in an AWS Region where ACM Private CA and Security Hub are enabled. The Launch Stack button will default to us-east-1. If you want to launch in another region, download the CloudFormation template from the GitHub repository found at the end of the blog.

Select this image to open a link that starts building the CloudFormation stack

Now that you’ve deployed the CloudFormation stack, we’ll help you understand how we’ve utilized AWS Security Finding Format (ASFF) in the Lambda functions.

How to create findings using AWS Security Finding Format (ASFF)

Security Hub consumes, aggregates, organizes, and prioritizes findings from AWS security services and from third-party product integrations. Security Hub receives these findings using a standard findings format called the AWS Security Finding Format (ASFF), thus eliminating the need for time-consuming data conversion efforts. Then it correlates ingested findings across products to prioritize the most important ones.

Below you can find an example input that shows how to use ASFF to populate findings in AWS Security Hub for the creation of a CA certificate. We placed this information in the Lambda function Certificate Authority Creation that was deployed in the CloudFormation stack.


{
 "SchemaVersion": "2018-10-08",
 "Id": region + "/" + accountNum + "/" + caCertARN,
 "ProductArn": "arn:aws:securityhub:" + region + ":" + accountNum + ":product/" + accountNum + "/default",
 "GeneratorId": caCertARN,
 "AwsAccountId": accountNum,
 "Types": [
     "Unusual Behaviors"
 ],
 "CreatedAt": date,
 "UpdatedAt": date,
 "Severity": {
     "Normalized": 60
 },
 "Title": "Private CA Certificate Creation",
 "Description": "A Private CA certificate was issued in AWS Certificate Manager Private CA",
 "Remediation": {
     "Recommendation": {
         "Text": "Verify this CA certificate creation was taken by a privileged user",
         "Url": "https://docs.aws.amazon.com/acm-pca/latest/userguide/ca-best-practices.html#minimize-root-use"
     }
 },
 "ProductFields": {
     "ProductName": "ACM PCA"
 },
 "Resources": [
     {
         "Details": {
             "Other": {
                "CAArn": CaArn,
                "CertARN": caCertARN
             }
         },
         "Type": "Other",
         "Id": caCertARN,
         "Region": region,
         "Partition": "aws"
    }
 ],
 "RecordState": "ACTIVE"
}

Below, we summarize some important fields within the finding generated by ASFF. We set these fields within the Lambda function in the CloudFormation template you deployed for the example scenario, and so you don’t have to do this yourself.

ProductARN

AWS services that are not yet integrated with Security Hub are treated similar to third party findings. Therefore, the company-id must be the account ID. The product-id must be the reserved word “default”, as shown below.


"ProductArn": "arn:aws:securityhub:" + region + ":" + accountNum + ":product/" + accountNum + "/default",

Severity

Assigning the correct severity is important to ensure useful findings. This example scenario sets the severity within the ASFF generated, as shown above. For future findings, you can determine the appropriate severity by comparing the score to the labels listed in Table 1.

Table 1: Severity labels in AWS Security Finding Format

Severity LabelSeverity Score Range
Informational0
Low1–39
Medium40–69
High70–89
Critical90–100
  • Informational: No issue was found.
  • Low: Findings with issues that could result in future compromises, such as vulnerabilities, configuration weaknesses, or exposed passwords.
  • Medium: Findings with issues that indicate an active compromise, but no indication that an adversary has completed their objectives. Examples include malware activity, hacking activity, or unusual behavior detection.
  • Critical: Findings associated with an adversary completing their objectives. Examples include data loss or compromise, or a denial of service.

Remediation

This provides the remediation options for a finding. In our example, we link you to least privilege documentation to learn how to fix the overly permissive resource.

Types

These indicate one or more finding types in the format of namespace/category/classifier that classify a finding. Finding types should match against the Types Taxonomy for ASFF.

To learn more about ASFF parameters, see ASFF syntax documentation.

Trigger a Security Hub finding

Figure 1 above shows the CA hierarchy you are building in this post. The CloudFormation template you deployed created the root CA. The following steps will walk you through signing the root CA and creating a CA certificate for the subordinate CA. The architecture we deployed will notify the security team of these actions via Security Hub.

First, we will activate the root CA and install the root CA certificate. This step signs the root CA Certificate Signing Request (CSR) with the root CA’s private key.

  1. Navigate to the ACM Private CA service. Under the Private certificate authority section, select Private CAs.
  2. Under Actions, select Install CA certificate.
  3. Set the validity period and signature algorithm for the root CA certificate. In this case, leave the default values for both fields as shown in Figure 3, and then select Next.

    Figure 3: Specify the root CA certificate parameters

    Figure 3: Specify the root CA certificate parameters

  4. Under Review, generate, and install root CA certificate, select Confirm and install. This creates the root CA certificate.
  5. You should now see the root CA within the console with a status of Active, as shown in Figure 4 below.

    Figure 4: The root CA is now active

    Figure 4: The root CA is now active

Now we will create a subordinate CA and install a CA certificate onto it.

  1. Select the Create CA button.
  2. Under Select the certificate authority (CA) type, select Subordinate CA, and then select Next.
  3. Configure the root CA parameters by entering the following values (or any values that make sense for the CA hierarchy you’re trying to build) in the fields shown in Figure 5, and then select Next.

    Figure 5: Configure the certificate authority

    Figure 5: Configure the certificate authority

  4. Under Configure the certificate authority (CA) key algorithm, select RSA 2048, and then select Next.
  5. Check Enable CRL distribution, and then, under Create a new S3 bucket, select No. Under S3 bucket name, enter acm-private-ca-crl-bucket-<account-number>, and then select Next.
  6. Under Configure CA permissions, select Authorize ACM to use this CA for renewals, and then select Next.
  7. To create the subordinate CA, review and accept the conditions described at the bottom of the page, select the check box if you agree to the conditions, and then select Confirm and create.

Now, you need to activate the subordinate CA and install the subordinate certificate authority certificate. This step allows you to sign the subordinate CA Certificate Signing Request (CSR) with the root CA’s private key.

  1. Select Get started to begin the process, as shown in Figure 6.

    Figure 6: Begin installing the root certificate authority certificate

    Figure 6: Begin installing the root certificate authority certificate

  2. Under Install subordinate CA certificate, select ACM Private CA, and then select Next. This starts the process of signing the subordinate CA cert with the root CA that was created earlier.
  3. Set the parent private CA with the root CA that was created, the validity period, the signature algorithm, and the path length for the subordinate CA certificate. In this case, leave the default values for validity period, signature algorithm, and path length fields as shown in Figure 7, and then select Next.

    Figure 7: Specify the subordinate CA certificate parameters

    Figure 7: Specify the subordinate CA certificate parameters

  4. Under Review, select Generate. This creates the subordinate CA certificate.
  5. You should now see the subordinate CA within the console with a status of Active.

How to view Security Hub findings

Now that you have created a root CA and a subordinate CA under the root, you can review findings from the perspective of your security team who is notified of the findings within Security Hub. In the example scenario, creating the CA certificates triggers a CloudWatch Events rule generated from the CloudFormation template.

This events rule utilizes the native ACM Private CA CloudWatch Event integration. The event keeps track of ACM Private CA Certificate Issuance of the root CA ARN. See below for the CloudWatch Event.


{
  "detail-type": [
    "ACM Private CA Certificate Issuance"
  ],
  "resources": [
    "arn:aws:acm-pca:us-east-2:xxxxxxxxxxx:certificate-authority/xxxxxx-xxxx-xxx-xxxx-xxxxxxxx"
  ],
  "source": [
    "aws.acm-pca"
  ]
}

When the event of creating a CA certificate from the root CA occurs, it triggers the Lambda function with the finding in ASFF to generate that finding in Security Hub.

To assess a finding in Security Hub

  1. Navigate to Security Hub. On the left side of the Security Hub page, select Findings to view the finding generated from the Lambda function. Filter by Title EQUALS Private CA Certificate Creation, as shown in Figure 8.

    Figure 8: Filter the findings in Security Hub

    Figure 8: Filter the findings in Security Hub

  2. Select the finding’s title (CA CertificateCreation) to open it. You will see the details generated from this finding:
    
    Severity: Medium
    Company: Personal
    Title: CA Certificate Creation
    Remediation: Verify this certificate was taken by a privileged user
    	

    The finding has a Medium severity level since we specified it through our level 60 definition. This could indicate a potential active compromise, but no indication that a potential adversary has completed their objectives. In the hypothetical example covered earlier, a user has provisioned a CA certificate from the root CA, which should only be provisioned under controlled circumstances, such as during a ceremony to create a subordinate CA. Issuing a certificate from the root at any other time is a red flag that should be investigated by the security team. The remediation attribute in the finding shown here links to security best practices for ACM Private CA.

    Figure 9: Remediation tab in Security Hub Finding

    Figure 9: Remediation tab in Security Hub Finding

  3. To see more details about the finding, in the upper right corner of the console, under CA Certificate Creation, select the Finding ID link, as shown in Figure 10.

    Figure 10: Select the Finding ID link to learn more about the finding

    Figure 10: Select the Finding ID link to learn more about the finding

  4. The Finding JSON box will appear. Scroll down to Resources > Details > Other, as shown in Figure 11. The CAArn is the Root Certificate Authority that provisioned the certificate. The CertARN is the certificate that it provisioned.

    Figure 11: Details about the finding

    Figure 11: Details about the finding

Cleanup

To avoid costs associated with the test CA hierarchy created and other test resources generated from the CloudFormation template, ensure that you clean up your test environment. Take the following steps:

  1. Disable and delete the CA hierarchy you created (including root and subordinate CAs, as well as the additional subordinate CAs created).
  2. Delete the CloudFormation template.

Any new account to ACM Private CA can try the service for 30 days with no charge for operation of the first private CA created in the account. You pay for the certificates you issue during the trial period.

Next steps

In this post, you learned how to create a pipeline from ACM PCA action to Security Hub findings. There are many other API calls that you can send to Security Hub for monitoring:

To generate an ASFF object for one of these API calls, follow the steps from the ASFF section above. For more details, see the documentation. For the latest updates and changes to the CloudFormation template and resources within this post, please check the Github repository.

If you have feedback about this post, submit comments in the Comments section below. If you have questions about this post, start a new thread on the AWS Certificate Manager forum or contact AWS Support.

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

Author

Anthony Pasquariello

Anthony is an Enterprise Solutions Architect based in New York City. He provides technical consultation to customers during their cloud journey, especially around security best practices. He has an MS and BS in electrical & computer engineering from Boston University. In his free time, he enjoys ramen, writing non-fiction, and philosophy.

Author

Christine Samson

Christine is an AWS Solutions Architect based in New York City. She provides customers with technical guidance for emerging technologies within the cloud, such as IoT, Serverless, and Security. She has a BS in Computer Science with a certificate in Engineering Leadership from the University of Colorado Boulder. She enjoys exploring new places to eat, playing the piano, and playing sports such as basketball and volleyball.

Author

Ram Ramani

Ram is a Security Specialist Solutions Architect at AWS focusing on data protection. Ram works with customers across all verticals to help with security controls and best practices on how customers can best protect their data that they store on AWS. In his free time, Ram likes playing table tennis and teaching coding skills to his kids.

Code signing using AWS Certificate Manager Private CA and AWS Key Management Service asymmetric keys

Post Syndicated from Ram Ramani original https://aws.amazon.com/blogs/security/code-signing-aws-certificate-manager-private-ca-aws-key-management-service-asymmetric-keys/

In this post, we show you how to combine the asymmetric signing feature of the AWS Key Management Service (AWS KMS) and code-signing certificates from the AWS Certificate Manager (ACM) Private Certificate Authority (PCA) service to digitally sign any binary data blob and then verify its identity and integrity. AWS KMS makes it easy for you to create and manage cryptographic keys and control their use across a wide range of AWS services and with your applications running on AWS. ACM PCA provides you a highly available private certificate authority (CA) service without the upfront investment and ongoing maintenance costs of operating your own private CA. CA administrators can use ACM PCA to create a complete CA hierarchy, including online root and subordinate CAs, with no need for external CAs. Using ACM PCA, you can provision, rotate, and revoke certificates that are trusted within your organization.

Traditionally, a person’s signature helps to validate that the person signed an agreement and agreed to the terms. Signatures are a big part of our lives, from our driver’s licenses to our home mortgage documents. When a signature is requested, the person or entity requesting the signature needs to verify the validity of the signature and the integrity of the message being signed.

As the internet and cryptography research evolved, technologists found ways to carry the usefulness of signatures from the analog world to the digital world. In the digital world, public and private key cryptography and X.509 certificates can help with digital signing, verifying message integrity, and verifying signature authenticity. In simple terms, an entity—which could be a person, an organization, a device, or a server—can digitally sign a piece of data, and another entity can validate the authenticity of the signature and validate the integrity of the signed data. The data that’s being signed could be a document, a software package, or any other binary data blob.

To learn more about AWS KMS asymmetric keys and ACM PCA, see Digital signing with the new asymmetric keys feature of AWS KMS and How to host and manage an entire private certificate infrastructure in AWS.

We provide Java code snippets for each part of the process in the following steps. In addition, the complete Java code with the maven build configuration file pom.xml are available for download from this GitHub project. The steps below illustrate the different processes that are involved and the associated Java code snippet. However, you need to use the GitHub project to be able to build and run the Java code successfully.

Let’s take a look at the steps.

1. Create an asymmetric key pair

For digital signing, you need a code-signing certificate and an asymmetric key pair. In this step, you create an asymmetric key pair using AWS KMS. The below code snippet in the main method within the file Runner.java is used to create the asymmetric key pair within KMS in your AWS account. An asymmetric KMS key with the alias CodeSigningCMK is created.


AsymmetricCMK codeSigningCMK = AsymmetricCMK.builder()
                .withAlias(CMK_ALIAS)
                .getOrCreate();

2. Create a code-signing certificate

To create a code-signing certificate, you need a private CA hierarchy, which you create within the ACM PCA service. This uses a simple CA hierarchy of one root CA and one subordinate CA under the root because the recommendation is that you should not use the root CA directly for signing code-signing certificates. The certificate authorities are needed to create the code-signing certificate. The common name for the root CA certificate is root CA, and the common name for the subordinate CA certificate is subordinate CA. The following code snippet in the main method within the file Runner.java is used to create the private CA hierarchy.


PrivateCA rootPrivateCA = PrivateCA.builder()
                .withCommonName(ROOT_COMMON_NAME)
                .withType(CertificateAuthorityType.ROOT)
                .getOrCreate();

PrivateCA subordinatePrivateCA = PrivateCA.builder()
        .withIssuer(rootPrivateCA)
        .withCommonName(SUBORDINATE_COMMON_NAME)
        .withType(CertificateAuthorityType.SUBORDINATE)
        .getOrCreate();

3. Create a certificate signing request

In this step, you create a certificate signing request (CSR) for the code-signing certificate. The following code snippet in the main method within the file Runner.java is used to create the CSR. The END_ENTITY_COMMON_NAME refers to the common name parameter of the code signing certificate.


String codeSigningCSR = codeSigningCMK.generateCSR(END_ENTITY_COMMON_NAME);

4. Sign the CSR

In this step, the code-signing CSR is signed by the subordinate CA that was generated in step 2 to create the code-signing certificate.


GetCertificateResult codeSigningCertificate = subordinatePrivateCA.issueCodeSigningCertificate(codeSigningCSR);

Note: The code-signing certificate that’s generated contains the public key of the asymmetric key pair generated in step 1.

5. Create the custom signed object

The data to be signed is a simple string: “the data I want signed”. Its binary representation is hashed and digitally signed by the asymmetric KMS private key created in step 1, and a custom signed object that contains the signature and the code-signing certificate is created.

The below code snippet in the main method within the file Runner.java is used to create the custom signed object.


CustomCodeSigningObject customCodeSigningObject = CustomCodeSigningObject.builder()
                .withAsymmetricCMK(codeSigningCMK)
                .withDataBlob(TBS_DATA.getBytes(StandardCharsets.UTF_8))
                .withCertificate(codeSigningCertificate.getCertificate())
                .build();

6. Verify the signature

The custom signed object is verified for integrity, and the root CA certificate is used to verify the chain of trust to confirm non-repudiation of the identity that produced the digital signature.

The below code snippet in the main method within the file Runner.java is used for signature verification:


String rootCACertificate = rootPrivateCA.getCertificate();
 String customCodeSigningObjectCertificateChain = codeSigningCertificate.getCertificate() + "\n" + codeSigningCertificate.getCertificateChain();

 CustomCodeSigningObject.getInstance(customCodeSigningObject.toString())
        .validate(rootCACertificate, customCodeSigningObjectCertificateChain);

During this signature validation process, the validation method shown in the code above retrieves the public key portion of the AWS KMS asymmetric key pair generated in step 1 from the code-signing certificate. This process has the advantage that credentials to access AWS KMS aren’t needed during signature validation. Any entity that has the root CA certificate loaded in its trust store can verify the signature without needing access to the AWS KMS verify API.

Note: The implementation outlined in this post is an example. It doesn’t use a certificate trust store that’s either part of a browser or part of a file system within the resident operating system of a device or a server. The trust store is placed in an instance of a Java class object for the purpose of this post. If you are planning to use this code-signing example in a production system, you must change the implementation to use a trust store on the host. To do so, you can build and distribute a secure trust store that includes the root CA certificate.

Conclusion

In this post, we showed you how a binary data blob can be digitally signed using ACM PCA and AWS KMS and how the signature can be verified using only the root CA certificate. No secret information or credentials are required to verify the signature. You can use this method to build a custom code-signing solution to address your particular use cases. The GitHub repository provides the Java code and the maven pom.xml that you can use to build and try it yourself. The README.md file in the GitHub repository shows the instructions to execute the code.

If you have feedback about this post, submit comments in the Comments section below. If you have questions about this post, start a new thread on the AWS Certificate Manager forum or contact AWS Support.

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

Author

Ram Ramani

Ram is a Security Solutions Architect at AWS focusing on data protection. Ram works with customers across different industry verticals to provide them with solutions that help with protecting data at rest and in transit. In prior roles, Ram built ML algorithms for video quality optimization and worked on identity and access management solutions for financial services organizations.

Author

Kyle Schultheiss

Kyle is a Senior Software Engineer on the AWS Cryptography team. He has been working on the ACM Private Certificate Authority service since its inception in 2018. In prior roles, he contributed to other AWS services such as Amazon Virtual Private Cloud, Amazon EC2, and Amazon Route 53.

How to build a CI/CD pipeline for container vulnerability scanning with Trivy and AWS Security Hub

Post Syndicated from Amrish Thakkar original https://aws.amazon.com/blogs/security/how-to-build-ci-cd-pipeline-container-vulnerability-scanning-trivy-and-aws-security-hub/

In this post, I’ll show you how to build a continuous integration and continuous delivery (CI/CD) pipeline using AWS Developer Tools, as well as Aqua Security‘s open source container vulnerability scanner, Trivy. You’ll build two Docker images, one with vulnerabilities and one without, to learn the capabilities of Trivy and how to send all vulnerability information to AWS Security Hub.

If you’re building modern applications, you might be using containers, or have experimented with them. A container is a standard way to package your application’s code, configurations, and dependencies into a single object. In contrast to virtual machines (VMs), containers virtualize the operating system rather than the server. Thus, the images are orders of magnitude smaller, and they start up much more quickly.

Like VMs, containers need to be scanned for vulnerabilities and patched as appropriate. For VMs running on Amazon Elastic Compute Cloud (Amazon EC2), you can use Amazon Inspector, a managed vulnerability assessment service, and then patch your EC2 instances as needed. For containers, vulnerability management is a little different. Instead of patching, you destroy and redeploy the container.

Many container deployments use Docker. Docker uses Dockerfiles to define the commands you use to build the Docker image that forms the basis of your container. Instead of patching in place, you rewrite your Dockerfile to point to more up-to-date base images, dependencies, or both and to rebuild the Docker image. Trivy lets you know which dependencies in the Docker image are vulnerable, and which version of those dependencies are no longer vulnerable, allowing you to quickly understand what to patch to get back to a secure state.

Solution architecture

 

Figure 1: Solution architecture

Figure 1: Solution architecture

Here’s how the solution works, as shown in Figure 1:

  1. Developers push Dockerfiles and other code to AWS CodeCommit.
  2. AWS CodePipeline automatically starts an AWS CodeBuild build that uses a build specification file to install Trivy, build a Docker image, and scan it during runtime.
  3. AWS CodeBuild pushes the build logs in near real-time to an Amazon CloudWatch Logs group.
  4. Trivy scans for all vulnerabilities and sends them to AWS Security Hub, regardless of severity.
  5. If no critical vulnerabilities are found, the Docker images are deemed to have passed the scan and are pushed to Amazon Elastic Container Registry (ECR), so that they can be deployed.

Note: CodePipeline supports different sources, such as Amazon Simple Storage Service (Amazon S3) or GitHub. If you’re comfortable with those services, feel free to substitute them for this walkthrough of the solution.

To quickly deploy the solution, you’ll use an AWS CloudFormation template to deploy all needed services.

Prerequisites

  1. You must have Security Hub enabled in the AWS Region where you deploy this solution. In the AWS Management Console, go to AWS Security Hub, and select Enable Security Hub.
  2. You must have Aqua Security integration enabled in Security Hub in the Region where you deploy this solution. To do so, go to the AWS Security Hub console and, on the left, select Integrations, search for Aqua Security, and then select Accept Findings.

Setting up

For this stage, you’ll deploy the CloudFormation template and do preliminary setup of the CodeCommit repository.

  1. Download the CloudFormation template from GitHub and create a CloudFormation stack. For more information on how to create a CloudFormation stack, see Getting Started with AWS CloudFormation.
  2. After the CloudFormation stack completes, go to the CloudFormation console and select the Resources tab to see the resources created, as shown in Figure 2.

 

Figure 2: CloudFormation output

Figure 2: CloudFormation output

Setting up the CodeCommit repository

CodeCommit repositories need at least one file to initialize their master branch. Without a file, you can’t use a CodeCommit repository as a source for CodePipeline. To create a sample file, do the following.

  1. Go to the CodeCommit console and, on the left, select Repositories, and then select your CodeCommit repository.
  2. Scroll to the bottom of the page, select the Add File dropdown, and then select Create file.
  3. In the Create a file screen, enter readme into the text body, name the file readme.md, enter your name as Author name and your Email address, and then select Commit changes, as shown in Figure 3.

    Figure 3: Creating a file in CodeCommit

    Figure 3: Creating a file in CodeCommit

Simulate a vulnerable image build

For this stage, you’ll create the necessary files and add them to your CodeCommit repository to start an automated container vulnerability scan.

    1. Download the buildspec.yml file from the GitHub repository.

      Note: In the buildspec.yml code, the values prepended with $ will be populated by the CodeBuild environmental variables you created earlier. Also, the command trivy -f json -o results.json –exit-code 1 will fail your build by forcing Trivy to return an exit code 1 upon finding a critical vulnerability. You can add additional severity levels here to force Trivy to fail your builds and ensure vulnerabilities of lower severity are not published to Amazon ECR.

    2. Download the python code file sechub_parser.py from the GitHub repository. This script parses vulnerability details from the JSON file that Trivy generates, maps the information to the AWS Security Finding Format (ASFF), and then imports it to Security Hub.
    3. Next, download the Dockerfile from the GitHub repository. The code clones a GitHub repository maintained by the Trivy team that has purposely vulnerable packages that generate critical vulnerabilities.
    4. Go back to your CodeCommit repository, select the Add file dropdown menu, and then select Upload file.
    5. In the Upload file screen, select Choose file, select the build specification you just created (buildspec.yml), complete the Commit changes to master section by adding the Author name and Email address, and select Commit changes, as shown in Figure 4.

 

Figure 4: Uploading a file to CodeCommit

Figure 4: Uploading a file to CodeCommit

 

  • To upload your Dockerfile and sechub_parser.py script to CodeCommit, repeat steps 4 and 5 for each of these files.
  • Your pipeline will automatically start in response to every new commit to your repository. To check the status, go back to the pipeline status view of your CodePipeline pipeline.
  • When CodeBuild starts, select Details in the Build stage of the CodePipeline, under BuildAction, to go to the Build section on the CodeBuild console. To see a stream of logs as your build progresses, select Tail logs, as shown in Figure 5.

    Figure 5: CodeBuild Tailed Logs

    Figure 5: CodeBuild Tailed Logs

  • After Trivy has finished scanning your image, CodeBuild will fail due to the critical vulnerabilities found, as shown in Figure 6.

    Note: The command specified in the post-build stage will run even if the CodeBuild build fails. This is by design and allows the sechub_parser.py script to run and send findings to Security Hub.

     

    Figure 6: CodeBuild logs failure

    Figure 6: CodeBuild logs failure

 

You’ll now go to Security Hub to further analyze the findings and create saved searches for future use.

Analyze container vulnerabilities in Security Hub

For this stage, you’ll analyze your container vulnerabilities in Security Hub and use the findings view to locate information within the ASFF.

  1. Go to the Security Hub console and select Integrations in the left-hand navigation pane.
  2. Scroll down to the Aqua Security integration card and select See findings, as shown in Figure 7. This filters to only Aqua Security product findings in the Findings view.

    Figure 7: Aqua Security integration card

    Figure 7: Aqua Security integration card

  3. You should now see critical vulnerabilities from your previous scan in the Findings view, as shown in Figure 8. To see more details of a finding, select the Title of any of the vulnerabilities, and you will see the details in the right side of the Findings view.
    Figure 8: Security Hub Findings pane

    Figure 8: Security Hub Findings pane

    Note: Within the Findings view, you can apply quick filters by checking the box next to certain fields, although you won’t do that for the solution in this post.

  4. To open a new tab to a website about the Common Vulnerabilities and Exposures (CVE) for the finding, select the hyperlink within the Remediation section, as shown in Figure 9.
    Figure 9: Remediation information

    Figure 9: Remediation information

    Note: The fields shown in Figure 9 are dynamically populated by Trivy in its native output, and the CVE website can differ greatly from vulnerability to vulnerability.

  5. To see the JSON of the full ASFF, at the top right of the Findings view, select the hyperlink for Finding ID.
  6. To find information mapped from Trivy, such as the CVE title and what the patched version of the vulnerable package is, scroll down to the Other section, as shown in Figure 10.

    Figure 10: ASFF, other

    Figure 10: ASFF, other

This was a brief demonstration of exploring findings with Security Hub. You can use custom actions to define response and remediation actions, such as sending these findings to a ticketing system or aggregating them in a security information event management (SIEM) tool.

Push a non-vulnerable Dockerfile

Now that you’ve seen Trivy perform correctly with a vulnerable image, you’ll fix the vulnerabilities. For this stage, you’ll modify Dockerfile to remove any vulnerable dependencies.

  1. Open a text editor, paste in the code shown below, and save it as Dockerfile. You can overwrite your previous example if desired.
    
    FROM alpine:3.7
    RUN apk add --no-cache mysql-client
    ENTRYPOINT ["mysql"]
    	

  2. Upload the new Dockerfile to CodeCommit, as shown earlier in this post.

Clean up

To avoid incurring additional charges from running these services, disable Security Hub and delete the CloudFormation stack after you’ve finished evaluating this solution. This will delete all resources created during this post. Deleting the CloudFormation stack will not remove the findings in Security Hub. If you don’t disable Security Hub, you can archive those findings and wait 90 days for them to be fully removed from your Security Hub instance.

Conclusion

In this post, you learned how to create a CI/CD Pipeline with CodeCommit, CodeBuild, and CodePipeline for building and scanning Docker images for critical vulnerabilities. You also used Security Hub to aggregate scan findings and take action on your container vulnerabilities.

You now have the skills to create similar CI/CD pipelines with AWS Developer Tools and to perform vulnerability scans as part of your container build process. Using these reports, you can efficiently identify CVEs and work with your developers to use up-to-date libraries and binaries for Docker images. To further build upon this solution, you can change the Trivy commands in your build specification to fail the builds on different severity levels of found vulnerabilities. As a final suggestion, you can use ECR as a source for a downstream CI/CD pipeline responsible for deploying your newly-scanned images to Amazon Elastic Container Service (Amazon ECS).

If you have feedback about this post, submit comments in the Comments section below. If you have questions about this post, start a new thread on the AWS Security Hub forum or contact AWS Support.

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

Author

Amrish Thakkar

Amrish is a Senior Solutions Architect at AWS. He holds the AWS Certified Solutions Architect Professional and AWS Certified DevOps Engineer Professional certifications. Amrish is passionate about DevOps, Microservices, Containerization, and Application Security, and devotes personal time into research and advocacy on these topics. Outside of work, he enjoys spending time with family and watching LOTR trilogy frequently.

How to perform automated incident response in a multi-account environment

Post Syndicated from Vesselin Tzvetkov original https://aws.amazon.com/blogs/security/how-to-perform-automated-incident-response-multi-account-environment/

How quickly you respond to security incidents is key to minimizing their impacts. Automating incident response helps you scale your capabilities, rapidly reduce the scope of compromised resources, and reduce repetitive work by security teams. But when you use automation, you also must manage exceptions to standard response procedures.

In this post, I provide a pattern and ready-made templates for a scalable multi-account setup of an automated incident response process with minimal code base, using native AWS tools. I also explain how to set up exception handling for approved deviations based on resource tags.

Because security response is a broad topic, I provide an overview of some alternative approaches to consider. Incident response is one part of an overarching governance, risk, and compliance (GRC) program that every organization should implement. For more information, see Scaling a governance, risk, and compliance program for the cloud.

Important: Use caution when introducing automation. Carefully test each of the automated responses in a non-production environment, as you should not use untested automated incident response for business-critical applications.

Solution benefits and deliverables

In the solution described below, you use AWS Systems Manager automation to execute most of the incident response steps. In general, you can either write your own or use pre-written runbooks. AWS maintains ready-made operational runbooks (automation documents) so you don’t need to maintain your own code base for them. The AWS runbooks cover many predefined use cases, such as enabling Amazon Simple Storage Service (S3) bucket encryption, opening a Jira ticket, or terminating an Amazon Elastic Compute Cloud (EC2) instance. Every execution for these is well documented and repeatable in AWS Systems Manager. For a few cases where there are no ready-made automation documents, I provide three additional AWS Lambda functions with the required response actions in the templates. The Lambda functions require minimal code, with no external dependencies outside AWS native tools.

You use a central security account to execute the incident response actions in the automation runbooks. You don’t need to do anything in the service accounts where you monitor and respond to incidents. In this post, you learn about and receive patterns for:

  • Responding to an incident based on Amazon GuardDuty alerts or AWS Config findings.
  • Deploying templates for a multi-account setup with a central security account and multiple service accounts. All account resources must be in the same AWS Region and part of the same AWS organization.
  • AWS Systems Manager automation to execute many existing AWS managed automation runbooks (you need access to the AWS Management Console to see all documents at this link). Systems Manager automation is available in these AWS Regions.
  • Prewritten Lambda functions to:
    • Confine permissive (open) security groups to a more-constrained CIDR (classless interdomain routing), such as the VPC (virtual private cloud) range for that security group. This prevents knowable network configuration errors, such as too-open security groups.
    • Isolate a potentially compromised EC2 instance by attaching it to a single, empty security group and removing its existing security groups.
    • Block an AWS Identity and Access Management (IAM) principal (an IAM user or role) by attaching a deny all policy to that principal.
    • Send notifications to the Amazon Simple Notification Service (Amazon SNS) for alerting in addition to (and in concert with) automated response actions.
  • Exception handling when actions should not be executed. I suggest decentralized exception handling based on AWS resource tags.
  • Integrating AWS Security Hub with custom actions for GuardDuty findings. This can be used for manual triggering of remediations that should not receive an automatic response.
  • Custom AWS Config rule for detecting permissive (open) security groups on any port.
  • Mapping the security finding to the response action defined in the CloudWatch Events rule pattern is extendable. I provide an example of how to extend to new findings and responses.

Out-of-scope for this solution

This post only shows you how to get started with security response automation for accounts that are part of the same AWS organization and Region. For more information on developing a comprehensive program for incident response, see How to prepare for & respond to security incidents in your AWS environment.

Understanding the difference between this solution and AWS Config remediation

The AWS Config remediation feature provides remediation of non-compliant resources using AWS Systems Manager within a single AWS account. The solution you learn in this post includes both GuardDuty alerts and AWS Config, a multi-account approach managed from a central security account, rules exception handling based on resource tags, and additional response actions using AWS Lambda functions.

Choosing the right approach for incident response

There are many technical patterns and solutions for responding to security incidents. When considering the right one for your organization, you must consider how much flexibility for response actions you need, what resources are in scope, and the skill level of your security engineers. Also consider dependencies on external code and applications, software licensing costs, and your organization’s experience level with AWS.

If you want the maximum flexibility and extensibility beyond AWS Systems Manager used in this post, I recommend AWS Step Functions as described in the session How to prepare for & respond to security incidents in your AWS environment and in DIY guide to runbooks, incident reports, and incident response. You can create workflows for your runbooks and have full control of all possible actions.

Architecture

 

Figure 1: Architecture diagram

Figure 1: Architecture diagram

The architecture works as follows:

In the service account (the environment for which we want to monitor and respond to incidents):

  1. GuardDuty findings are forwarded to CloudWatch Events.
  2. Changes in the AWS Config compliance status are forwarded to CloudWatch Events.
  3. CloudWatch Events for GuardDuty and AWS Config are forwarded to the central security account, via a CloudWatch Events bus.

In the central security account:

  1. Each event from the service account is mapped to one or more response actions using CloudWatch Events rules. Every rule is an incident response action that is executed on one or more security findings, as defined in the event pattern. If there is an exception rule, the response actions are not executed.

    The list of possible actions that could be taken include:

    1. Trigger a Systems Manager automation document, invoked by the Lambda function StratSsmAutomation within the security account.
    2. Isolate an EC2 instance by attaching an empty security group to it and removing any prior security groups, invoked by the Lambda function IsolateEc2. This assumes the incident response role in the target service account.
    3. Block the IAM principal by attaching a deny all policy, invoked by the Lambda function BlockPrincipal, by assuming the incident response role in the target service account.
    4. Confine security group to safe CIDR, invoked by the Lambda function ConfineSecurityGroup, by assuming the incident response role in the target service account.
    5. Send the finding to an SNS topic for processing outside this solution; for example, by manual evaluation or simply for information.
  2. Invoke AWS Systems Manager within the security account, with a target of the service account and the same AWS Region.
  3. The Systems Manager automation document is executed from the security account against the resources in the service account; for example, EC2, S3, or IAM resources.
  4. The response actions are executed directly from the security account to the service account. This is done by assuming an IAM role, for example, to isolate a potentially compromised EC2 instance.
  5. Manually trigger security responses using Security Hub custom actions. This can be suitable for manual handling of complex findings that need investigation before action.

Response actions decision

Your organization wants to articulate information security policy decisions and then create a list of corresponding automated security responses. Factors to consider include the classification of the resources and the technical complexity of the automation required. Start with common, less complex cases to get immediate gains, and increase complexity as you gain experience. Many stakeholders in your organization, such as business, IT operations, information security, and risk and compliance, should be involved in deciding which incident response actions should be automated. You need executive support for the political will to execute policies.

Creating exceptions to automated responses

There may be cases in which it’s unwise to take an automated response. For example, you might not want an automated response to incidents involving a core production database server that is critical to business operations. Instead, you’d want to use human judgment calls before responding. Or perhaps you know there are alarms that you don’t need for certain resources, like alerting for an open security group when you intentionally use it as a public web server. To address these exceptions, there is a carve-out. If the AWS resource has a tag with the name SecurityException, a response action isn’t executed. The tag name is defined during installation.

Table 1 provides an example of the responses implemented in this solution template. You might decide on different actions and different priorities for your organization. A current list of GuardDuty findings can be found at Active Finding Types and for AWS Config at AWS Config Managed Rules.

NSourceFindingDescriptionResponse
1GuardDuty Backdoor:EC2/Spambot
Backdoor:EC2/C&CActivity.B!DNS,
Backdoor:EC2/DenialOfService.Tcp,
Backdoor:EC2/DenialOfService.Udp,
Backdoor:EC2/DenialOfService.Dns,
Backdoor:EC2/DenialOfService.UdpOnTcpPorts,
Backdoor:EC2/DenialOfService.UnusualProtocol,
Trojan:EC2/BlackholeTraffic,
Trojan:EC2/DropPoint,
Trojan:EC2/BlackholeTraffic!DNS,
Trojan:EC2/DriveBySourceTraffic!DNS,
Trojan:EC2/DropPoint!DNS,
Trojan:EC2/DGADomainRequest.B,
Trojan:EC2/DGADomainRequest.C!DNS,
Trojan:EC2/DNSDataExfiltration,
Trojan:EC2/PhishingDomainRequest!DNS
See Backdoor Finding Types and
 
Trojan Finding Types
Isolate EC2 with empty security group.
 
Archive the GuardDuty finding.
 
Send SNS notification.
2GuardDutyUnauthorizedAccess:IAMUser/InstanceCredentialExfiltration,
UnauthorizedAccess:IAMUser/TorIPCaller,
UnauthorizedAccess:IAMUser/MaliciousIPCaller.Custom,
UnauthorizedAccess:IAMUser/ConsoleLoginSuccess.B,
UnauthorizedAccess:IAMUser/MaliciousIPCaller
See Unauthorized Finding TypesBlock IAM principal by attaching deny all policy.
 
Archive the GuardDuty finding.
 
Send SNS notification.
3AWS ConfigS3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLEDSee the documentation for s3-bucket-server-side-encryption-enabledEnable server-side encryption with Amazon S3-Managed keys (SSE-S3) with SSM document (AWS-EnableS3BucketEncryption).
 
Send SNS notification.
4AWS ConfigS3_BUCKET_PUBLIC_READ_PROHIBITEDSee the documentation for s3-bucket-public-read-prohibitedDisable S3 PublicRead and PublicWrite with SSM document (AWS-DisableS3BucketPublicReadWrite).
 
Send SNS notification.
5AWS ConfigS3_BUCKET_PUBLIC_WRITE_PROHIBITEDSee the documentation for s3-bucket-public-write-prohibitedDisable S3 PublicRead and PublicWrite with SSM document (AWS-DisableS3BucketPublicReadWrite).
 
Send SNS notification.
6AWS ConfigSECURITY_GROUP_OPEN_PROHIBITEDSee template, custom configuration.Confine security group to safe CIDR 172.31. 0.0/16
 
Send SNS notification.
7AWS ConfigENCRYPTED_VOLUMESSee the documentation for encrypted-volumesSend SNS notification.
8AWS ConfigRDS_STORAGE_ENCRYPTEDSee the documentation for rds-storage-encryptedSend SNS notification.

Installation

  1. In the security account, launch the template by selecting Launch Stack.
     
    Select this image to open a link that starts building the CloudFormation stack
    Additionally, you can find the latest code on GitHub, where you can also contribute to the sample code.
  2. Provide the following parameters for the security account (see Figure 2):
    • S3 bucket with sources: This bucket contains all sources, such as the Lambda function and templates. If you’re not customizing the sources, you can leave the default text.
    • Prefix for S3 bucket with sources: Prefix for all objects. If you’re not customizing the sources, you can leave the default.
    • Security IR-Role names: This is the role assumed for the response actions by the Lambda functions in the security account. The role is created by the stack launched in the service account.
    • Security exception tag: This defines the tag name for security exceptions. Resources marked with this tag are not automatically changed in response to a security finding. For example, you could add an exception tag for a valid open security group for a public website.
    • Organization ID: This is your AWS organization ID used to authorize forwarding of CloudWatch Events to the security account. Your accounts must be members of the same AWS organization.
    • Allowed network range IPv4: This CIDRv4 range is used to confine all open security groups that are not tagged for exception.
    • Allowed network range IPv6: This CIDRv6 range is used to confine all open security groups that are not tagged for exception.
    • Isolate EC2 findings: This is a list of all GuardDuty findings that should lead to an EC2 instance being isolated. Comma delimited.
    • Block principal finding: This is a list of all GuardDuty findings that should lead to blocking this role or user by attaching a deny all policy. Comma delimited.
    Figure 2: Stack launch in security account

    Figure 2: Stack launch in security account

  3. In each service account, launch the template by selecting Launch Stack.
     
    Select this image to open a link that starts building the CloudFormation stack

    Additionally, you can find the latest code on GitHub, where you can also contribute to the sample code.

  4. Provide the following parameters for each service account:
    • S3 bucket with sources: This bucket contains all sources, such as Lambda functions and templates. If you’re not customizing the sources, you can leave the default text.
    • Prefix for S3 bucket with sources: Prefix for all objects. If you’re not customizing the sources, you can leave the default text.
    • IR-Security role: This is the role that is created and used by the security account to execute response actions.
    • Security account ID: The CloudWatch Events are forwarded to this central security account.
    • Enable AWS Config: Define whether you want this stack to enable AWS Config. If you have already enabled AWS Config, then leave this value false.
    • Create SNS topic: Provide the name of an SNS topic only if you enable AWS Config and want to stream the configuration change to SNS (optional). Otherwise, leave this field blank.
    • SNS topic name: This is the name of the SNS topic to be created only if enabling AWS Config. The default text is Config.
    • Create S3 bucket for AWS Config: If you enable AWS Config, the template creates an S3 bucket for AWS Config.
    • Bucket name for AWS Config: The name of the S3 bucket created for AWS Config. The default text is config-bucket-{AccountId}.
    • Enable GuardDuty: If you have not already enabled GuardDuty in the service account, then you can do it here.

Testing

After you have deployed both stacks, you can test your environment by following these example steps in one of your service accounts. Before you test, you can subscribe to the SNS topic with prefix Security_Alerts_[Your_Stack] to be notified of a security event.

  1. Create and open security group 0.0.0.0/0 without creating an exception tag. After several minutes, the security group will be confined to the safe CIDR that you defined in your stack.
  2. Create an S3 bucket without enabling encryption. After several minutes, the default encryption AES-256 will be set on the bucket.
  3. For GuardDuty blocking of IAM principal, you can define a list of malicious IPs under the Threat List in the GuardDuty panel. Create a test role or user. When you execute an API call from this IP with the created test role or user, a GuardDuty finding is generated that triggers blocking of the IAM role or user.
  4. You can deploy Amazon GuardDuty tester and generate findings such as Trojan:EC2/DNSDataExfiltration or CryptoCurrency:EC2/BitcoinTool.B!DN. The GuardDuty findings trigger isolation of an EC2 instance by removing all current security groups and attaching an empty one. This new empty group can then be configured for forensic access later.

Exceptions for response actions

If the resource has the tag name SecurityException, a response action is not executed. The tag name is a parameter of the CloudFormation stack in the security account and can be customized at installation. The value of the tag is not validated, but it is good practice for the value to refer to an approval document such as a Jira issue. In this way, you can build an auditing chain of the approval. For example:


Tag name:  SecurityException
Tag value: Jira-1234

Make sure that the security tag can only be set or modified by an appropriate role. You can do this in two ways. The first way is to attach a deny statement to all policies that do not have privileges to assign this tag. An example policy statement to deny setting, removing, and editing of this tag for IAM, EC2, and S3 services is shown below. This policy does not prohibit working with the resources, such as starting or stopping an EC2 instance with this tag. See Controlling access based on tag keys for more information.


{
        {
            "Sid": "S3-Sec-Tag",
            "Effect": "Deny",
            "A	ction": [
                "s3:PutBucketTagging",
                "s3:PutObjectTagging"
            ],
            "Resource": "*",
            "Condition": {
                "ForAnyValue:StringLikeIfExists": {
                    "aws:TagKeys": "TAG-NAME-SecurityException"
                }
            }
        },
        {
            "Sid": "EC2-VPC-Sec-Tag",
            "Effect": "Deny",
            "Action": "*",
            "Resource": "*",
            "Condition": {
                "ForAnyValue:StringLikeIfExists": {
                    "aws:TagKeys": "TAG-NAME-SecurityException"
                }
            }
        }
    ]
}

In the above policy, you must modify the TAG-NAME-SecurityException to match your own tag name.

The second way to restrict the attachment of this tag is to use Tag policies to manage tags across multiple AWS accounts.

Centralized versus decentralized exception management

Attaching security tags is a decentralized approach in which you don’t need a centralized database to record remediation exceptions. A centralized exception database requires that you know each individual resource ID to set exceptions, and this is not always possible. Amazon EC2 Auto Scaling is a good example where you might not know the EC2 instance ID in advance. On an up-scaling event, the instance ID can’t be known in advance and preapproved. Furthermore, a central database must be kept in sync with the lifecycle of the resources, like with an instance down-scaling event. Hence, using tags on resources is a decentralized approach. If needed, the automatic scaling launch configuration can propagate the security exception tag; see Tag your auto scaled EC2 instances.

You can manage the IAM policies and roles for tagging either centrally or within an AWS CodePipeline. In this way, you can implement a centralized enforcement point for security exceptions but decentralize storing of the resource tags to the resources themselves.

Using AWS resource groups, you can always find all resources that have the Security tag for inventory and auditing proposes. For more information, see Build queries and groups in AWS resource groups.

Monitoring for security alerts

You can subscribe to the SNS topic with prefix Security_Alerts_[Your_Stack] to be notified of a security event or an execution that has failed.

Every execution is triggered from CloudWatch Events, and each of these events generates CloudWatch metrics. Under CloudWatch rules, you can see the rules for event forwarding from service accounts, or for triggering the responses in the central security account. For each CloudWatch rule, you can view the metrics by checking the box next to the rule, as shown in Figure 3, and then select Show metrics for the rule.
 

Figure 3: Metrics for Forwarding GuardDuty Events in CloudWatch

Figure 3: Metrics for Forwarding GuardDuty Events in CloudWatch

Creating new rules and customization

The mapping of findings to responses is defined in the CloudWatch Events rules in the security account. If you define new responses, you only update the central security account. You don’t adjust the settings of service accounts because they only forward events to the security account.

Here’s an example: Your team decides that a new SSM action – Terminate EC2 instance – should be triggered on the GuardDuty finding Backdoor:EC2/C&CActivity.B!DNS. In the security account, go to CloudWatch in the AWS Management Console. Create a new rule as described in the following steps (and shown in Figure 4):
 

Figure 4: CloudWatch rule creation

Figure 4: CloudWatch rule creation

  1. Go to CloudWatch Events and create a new rule. In the Event Pattern, under Build custom event pattern, define the following JSON:
    
    {
      "detail-type": [
        "GuardDuty Finding"
      ],
      "source": [
        "aws.guardduty"
      ],
      "detail": {
        "type": [
          "Backdoor:EC2/C&CActivity.B!DNS"
        ]
      }
    }
    

  2. Set the target for the rule as the Lambda function for execution of SSM, StratSsmAutomation. Use Input transformer to customize the parameters in invocation of the Lambda function.

    For the Input paths map:

    
    {
       "resourceId":"$.detail.resource.instanceDetails.instanceId",
       "account":"$.account"
    } 
    

    The expression extracts the instanceId from the event as resourceId, and the Account ID as account.

    For the field Input template, enter the following:

    
    {
       "account":<account>,
       "resourseType":"ec2:instance",
       "resourceId":<resourceId>,
       "AutomationDocumentName":"AWS-TerminateEC2Instance",
       "AutomationParameters":{
          "InstanceId":[
          <resourceId>   
          ]
       }
    }
    

You pass the name of the SSM automation document that you want to execute as an input parameter. In this case, the document is AWS-TerminateEC2Instance and the document input parameters are a JSON structure named AutomationParameters.

You have now created a new response action that is ready to test.

Troubleshooting

The automation execution that takes place in the security account is documented in AWS Systems Manager under Automation Execution. You can also refer to the AWS Systems Manager automation documentation.

If Lambda function execution fails, then a CloudWatch alarm is triggered, and notification is sent to an SNS topic with prefix Security_Alerts_[Your_Stack]. Lambda function execution in the security account is documented in the CloudWatch log group for the Lambda function. You can use the logs to understand whether the Lambda function was executed successfully.

Security Hub integration

AWS Security Hub aggregates alarms from GuardDuty and other providers. To get Security Hub to aggregate alarms, you must first activate Security Hub before deploying this solution. Then, invite your environment accounts to the security account as master. For more information, see Master and member accounts in AWS Security Hub.

The integration with Security Hub is for manual triggering and is not part of the automated response itself. This can be useful if you have findings that require security investigation before triggering the action.

From the Security Hub console, select the finding and use the Actions drop down menu in the upper right corner to trigger a response, as shown in Figure 5. A CloudWatch event invokes the automated response, such as a Lambda function to isolate an EC2 instance. The Lambda function fetches the GuardDuty finding from the service account and executes one of the following: Isolate EC2 Instance, Block Principal, or Send SNS Notification.

Only one resource can be selected for execution. If you select multiple resources, the response is not executed, and the Lambda log message is shown in CloudWatch Logs.
 

Figure 5: Security Hub integration

Figure 5: Security Hub integration

Cost of the solution

The cost of the solution depends on the events generated in your AWS account and chosen AWS Region. Costs might be as little as several U.S. dollars per month and per account. You can model your costs by understanding GuardDuty pricing, automation pricing in AWS system manager, and AWS Config pricing for six AWS Config rules. The costs for AWS Lambda and Amazon CloudWatch are expected to be minimal and might be included in your free tier. You can optionally use Security Hub; see AWS Security Hub pricing.

Summary

In this post, you learned how to deploy an automated incident response framework using AWS native features. You can easily extend this framework to meet your future needs. If you would like to extend it further, contact AWS professional services or an AWS partner. If you have technical questions, please use the Amazon GuardDuty or AWS Config forums. Remember, this solution is only an introduction to automated security response and is not a comprehensive solution.

If you have feedback about this post, submit comments in the Comments section below.

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

Author

Vesselin Tzvetkov

Vesselin is senior security consultant at AWS Professional Services and is passionate about security architecture and engineering innovative solutions. Outside of technology, he likes classical music, philosophy, and sports. He holds a Ph.D. in security from TU-Darmstadt and a M.S. in electrical engineering from Bochum University in Germany.

How to create SAML providers with AWS CloudFormation

Post Syndicated from Rich McDonough original https://aws.amazon.com/blogs/security/how-to-create-saml-providers-with-aws-cloudformation/

As organizations grow, they often experience an inflection point where it becomes impractical to manually manage separate user accounts in disparate systems. Managing multiple AWS accounts is no exception. Many large organizations have dozens or even hundreds of AWS accounts spread across multiple business units.

AWS provides many solutions that can orchestrate a person’s identity across multiple accounts. AWS Identity and Access Management (IAM), SAML, and OpenID can all help. The solution I describe in this post uses these features and services and can scale to thousands of AWS accounts. It provides a repeatable and automated means for deploying a unified identity management structure across all of your AWS environments. It does this while extending existing identity management into AWS without needing to change your current sources of user information.

About multi-account management

This approach uses AWS CloudFormation StackSets to deploy an identity provider and AWS IAM roles into multiple accounts. Roles may be tailored for your business needs and mapped to administrators, power users, or highly specialized roles that perform domain-specific tasks within your environment. StackSets are a highly scalable tool for multi-account orchestration and enforcement of policies across an organization.

Before deploying this solution across your organization, I recommend that you deploy the stack below into a single AWS account for testing purposes. Then, extend the solution using StackSets across your organization once tested. For more information about AWS CloudFormation Stacks and StackSets, see our documentation.

About SAML and SAML providers

Security Assertion Markup Language 2.0 (SAML) is an open standard for exchanging identity and security information with applications and other external systems. For example, if your organization uses Microsoft Active Directory, you can federate your user accounts to other organizations by way of SAML assertions. Actions that users take in the external organization can then be mapped to the identity of the original user and executed with a role that has predetermined privileges.

If you’re new to SAML, don’t worry! Not only is it ubiquitous, it’s also well documented. Many websites use it to drive their security and infrastructure, and you may have already used it without knowing it. For more information about SAML and AWS, I suggest you start with Enabling SAML for Your AWS Resources.

Properly configured SAML federation provides core security requirements for authentication and authorization, while maintaining a single source of truth for who your users are. It also enables important constraints such as multi-factor authentication and centralized access management. Within AWS IAM, you setup a SAML trust by configuring your identity provider with information about AWS and the AWS IAM roles that you want your federated users to use. SAML is secured using public key authentication.

This solution is a companion to the blog post AWS Federated Authentication with Active Directory Federation Services (AD FS), but it can be applied to any SAML provider.

Solution overview

To orchestrate SAML providers across multiple accounts, you will need the following:

  • A basic understanding of federated users.
  • An identity provider that supports SAML, such as Active Directory Federation Services (AD FS), or Shibboleth.
  • An Amazon Simple Storage Service (Amazon S3) bucket to host the provider’s federation metadata file.
  • An AWS account that can create StackSets in other accounts. Usually, this is an AWS Organizations master account, with StackSets provisioned into member accounts. See the AWS Organizations User Guide for more details.

Note: You can adapt this solution to retrieve federation metadata from a HTTPS endpoint, such as those published by publicly hosted identity providers, but this solution does not currently offer that.

At this time, CloudFormation does not directly support provisioning a SAML provider. However, you can accomplish this by using a custom resource Lambda function. The sample code provided in this post takes this one step further and deploys your federation metadata from a secure Amazon S3 bucket, as some organizations have requirements about how this metadata is stored.
 

Figure 1: Architecture diagram

Figure 1: Architecture diagram

Here’s what the solution architecture looks like, as shown in Figure 1:

  1. An AWS CloudFormation template is created within an AWS account. Most commonly this is your master account within AWS Organizations, but it can be a standalone account as well.
  2. The AWS CloudFormation template is deployed to other AWS accounts within your organization using AWS CloudFormation StackSets.
  3. The AWS CloudFormation StackSet creates the following resources:
    1. A new read-only role within AWS IAM that uses the new identity provider as the principal in the IAM role’s trust policy. This is a sample that I use to demonstrate how you can deploy IAM Roles that are bound to a single, dedicated identity provider.
    2. An execution role for a custom resource AWS Lambda function.
    3. The AWS Lambda function, which creates the identity provider within the destination account.
  4. The new AWS Lambda function is executed, pulling your federation metadata from the master account’s S3 Bucket and creating the new Identity Provider.

Step 1: Upload your federation metadata to a restricted S3 bucket

To ensure that your SAML provider’s federation metadata is exposed only to accounts within your AWS organization, you can upload the metadata to an Amazon S3 bucket and then restrict access to accounts within your organization. The bucket policy follows this template:


{
    "Version": "2012-10-17",
    "Id": "Policy1545178796950",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<replace with bucket name>/*",
            "Condition": {
                "StringEquals": {
                    "aws:PrincipalOrgID": "<replace with organization ID>"
                }
            }
        }
    ]
}

Once this Amazon S3 bucket policy is in place, upload your federation metadata document to the bucket and note the object URL.

If your organization can share your federation metadata publicly, then there’s no need for this bucket policy. Simply upload your file to the S3 bucket, and make the bucket publicly accessible.

Step 2: Launch the CloudFormation template

To create the SAML provider within AWS IAM, this solution uses a custom resource Lambda function, as CloudFormation does not currently offer the ability to create the configuration directly. In this case, I show you how to use the AWS API to create a SAML provider in each of your member accounts, pulling the federation metadata from an S3 bucket owned by the organization master account.

The actual CloudFormation template follows. A sample read-only role has been included, but I encourage you to replace this with IAM roles that make sense for your environment. Save this template to your local workstation, or to your code repository for tracking infrastructure changes (if you need a source code management system, we recommend AWS CodeCommit).


---
AWSTemplateFormatVersion: 2010-09-09
Description: Create SAML provider

Parameters:
  FederationName:
    Type: String
    Description: Name of SAML provider being created in IAM
  FederationBucket:
    Type: String
    Description: Bucket containing federation metadata
  FederationFile:
    Type: String
    Description: Name of file containing the federation metadata

Resources:
  FederatedReadOnlyRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: Federated-ReadOnly
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action: sts:AssumeRoleWithSAML
            Principal:
              Federated: !Sub arn:aws:iam::${AWS::AccountId}:saml-provider/${FederationName}
            Condition:
              StringEquals:
                SAML:aud: https://signin.aws.amazon.com/saml
      Path: '/'
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/ReadOnlyAccess

  SAMLProviderCustomResourceLambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: "/"
      Policies:
        - PolicyName: root
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: 'arn:aws:logs:*:*:log-group:/aws/lambda/*-SAMLProviderCustomResourceLambda-*:*'
              - Effect: Allow
                Action:
                  - iam:CreateSAMLProvider
                  - iam:DeleteSAMLProvider
                Resource: !Sub arn:aws:iam::${AWS::AccountId}:saml-provider/${FederationName}
              - Effect: Allow
                Action:
                  - iam:ListSAMLProviders
                Resource: '*'
              - Effect: Allow
                Action:
                  - s3:GetObject
                Resource: !Sub 'arn:aws:s3:::${FederationBucket}/*'

  CustomResource:
    Type: Custom::CustomResource
    DependsOn:
      - SAMLProviderCustomResourceLambda
      - SAMLProviderCustomResourceLambdaExecutionRole
    Properties:
      ServiceToken: !GetAtt SAMLProviderCustomResourceLambda.Arn

  SAMLProviderCustomResourceLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Handler: index.lambda_handler
      Role: !GetAtt SAMLProviderCustomResourceLambdaExecutionRole.Arn
      Runtime: python3.7
      Timeout: 300
      Environment:
        Variables:
          FEDERATION_NAME: !Ref FederationName
          FEDERATION_BUCKET: !Ref FederationBucket
          FEDERATION_FILE: !Ref FederationFile
      Code:
        ZipFile: |
          import boto3, json, os, urllib.request, ssl, time, traceback


          BUCKET = os.getenv('FEDERATION_BUCKET')
          FILE = os.getenv('FEDERATION_FILE')
          NAME = os.getenv('FEDERATION_NAME')


          class SAMLProvider(object):
              def __init__(self):
                  self.iam_client = boto3.client('iam')
                  self.existing_providers = []
                  self._list_saml_providers()
                  self.s3 = boto3.resource('s3')

              def get_federation_metadata(self):
                  try:
                      self.s3.Bucket(BUCKET).download_file(FILE, '/tmp/' + FILE)
                      handle = open('/tmp/' + FILE)
                      data = handle.read()
                      handle.close()
                      os.remove('/tmp/' + FILE)
                      return data
                  except:
                      traceback.print_exc()
                      raise

              def _list_saml_providers(self):
                  providers = []
                  response = self.iam_client.list_saml_providers()
                  for provider in response['SAMLProviderList']:
                      self.existing_providers.append(provider['Arn'])

              def add_saml_provider(self, name):
                  for arn in self.existing_providers:
                      if arn.split('/')[1] == name:
                          print(name + ' already exists as a provider')
                          return False
                  response = self.iam_client.create_saml_provider(SAMLMetadataDocument=self.get_federation_metadata(), Name=name)
                  print('Create response: ' + str(response))
                  return True

              def delete_saml_provider(self, name):
                  for arn in self.existing_providers:
                      if arn.split('/')[1] == name:
                          response = self.iam_client.delete_saml_provider(SAMLProviderArn=arn)
                          print('Delete response: ' + str(response))

          def send_response(event, context, response_status, response_data):
              response_body = json.dumps({
                  'Status': response_status,
                  'Reason': 'See the details in CloudWatch Log Stream: ' + context.log_stream_name,
                  'PhysicalResourceId': context.log_stream_name,
                  'StackId': event['StackId'],
                  'RequestId': event['RequestId'],
                  'LogicalResourceId': event['LogicalResourceId'],
                  'Data': response_data
              })
              print('ResponseURL: %s', event['ResponseURL'])
              print('ResponseBody: %s', response_body)
              try:
                  opener = urllib.request.build_opener(urllib.request.HTTPHandler)
                  request = urllib.request.Request(event['ResponseURL'], data=response_body.encode())
                  request.add_header('Content-Type', '')
                  request.add_header('Content-Length', len(response_body))
                  request.get_method = lambda: 'PUT'
                  response = opener.open(request)
                  print("Status code: %s", response.getcode())
                  print("Status message: %s", response.msg)
              except:
                  traceback.print_exc()


          def lambda_handler(event, context):
              print(event)
              print(context)
              saml = SAMLProvider()
              try:
                  if event['RequestType'] == 'Create':
                      saml.add_saml_provider(NAME)
                      send_response(event, context, 'SUCCESS', {"Message": "Resource creation successful!"})
                  if event['RequestType'] == 'Update':
                      saml.delete_saml_provider(NAME)
                      time.sleep(10)
                      saml.add_saml_provider(NAME)
                      send_response(event, context, 'SUCCESS', {"Message": "Resource update successful!"})
                  if event['RequestType'] == 'Delete':
                      saml.delete_saml_provider(NAME)
                      send_response(event, context, 'SUCCESS', {"Message": "Resource deletion successful!"})
              except:
                  send_response(event, context, "FAILED", {"Message": "Exception during processing"})
                  traceback.print_exc()

As you review this template, note that a Python Lambda function is embedded in the template body itself. This is a useful way to package code along with your CloudFormation templates. The limit for the length of code inline within a CloudFormation template is fixed, as detailed in the AWS CloudFormation user guide. Code that exceeds this length must be saved to a separate location within an Amazon S3 bucket and referenced from your template. However, the preceding snippet is short enough to be included all in one CloudFormation template.

Step 3: Create the StackSet

With your SAML provider metadata securely uploaded to your Amazon S3 bucket, it’s time to create the StackSet.

  1. First, navigate to the CloudFormation console and select StackSets, then Create StackSet.
     
    Figure 2: Creating a new StackSet

    Figure 2: Creating a new StackSet

  2. Select Template is ready, then Upload a template file. Select Choose file to choose the location of the CloudFormation template, then select Next.
     
    Figure 3: Specifying the template details

    Figure 3: Specifying the template details

  3. Enter a value for StackSet name. Then provide the following configuration parameters and select Next:
    1. FederationName: The name of the SAML provider. This is the name of the provider as you see it in IAM once provisioned, and it appears in the ARN of the SAML provider.
    2. FederationFile: The file name within S3.
    3. FederationBucket: The name of the S3 bucket.

     

    Figure 4: Specifying the StackSet parameters

    Figure 4: Specifying the StackSet parameters

  4. In the Account numbers field, enter the list of accounts into which you wish to provision this stack. Select a Region from the drop-down menu, then select any concurrent deployment options that you need. For most customers, the concurrent and failure tolerance options generally remain unmodified.

    Important: When you enter your list of accounts, you must use the 12-digit account number for each destination, without spaces, and separated by commas.

    Because IAM is a global service, there is no need to deploy this to multiple regions. The template must be deployed once per AWS account, although it is managed by a CloudFormation Stack that exists in a single Region.
     

    Figure 5: Selecting your deployment options

    Figure 5: Selecting your deployment options

  5. Depending on your organization’s security posture, you may have chosen to use self-managed permissions. If that is the case, then you must specify an AWS IAM role that is permitted to execute StackSet deployments, as well as an execution role in the destination accounts. For more info, visit the StackSets’ Grant Self-Managed Permissions documentation page.
  6. Review your selections, accept the IAM role creation acknowledgment, and click Submit to create your stacks. Once completed, ensure that the new identity provider is present in each account by visiting the AWS IAM console.

Summary

Creating a SAML provider across multiple AWS accounts enables enterprises to extend their existing authentication infrastructure into the cloud.

You can extend the solution provided here to deploy different federation metadata to AWS accounts based on specific criteria—for example, if your development and staging accounts have a separate identity provider. You can also add simple logic to the Lambda function that I’ve included in the CloudFormation template. Additionally, you might need to create any number of predefined roles within your AWS organization—using the StackSets approach enables a zero-touch means for creating these roles from a central location.

Organizations looking for even stronger controls over multiple accounts should implement this solution with AWS Organizations. Combined with federated access and centrally managed IAM roles, Organizations provides Service Control Policies and a common framework for deploying CloudFormation Stacks across multiple business units.

If you have feedback about this post, submit comments in the Comments section below. If you have questions about this post, contact AWS Support.

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

Author

Rich McDonough

Rich McDonough is a Solutions Architect for Amazon Web Services based in Toronto. His primary focus is on Management and Governance, helping customers scale their use of AWS safely and securely. Before joining AWS two years ago, he specialized in helping migrate customers into the cloud. Rich loves helping customers learn about AWS CloudFormation, AWS Config, and AWS Control Tower.

Introducing the Well-Architected Framework for Machine Learning

Post Syndicated from Shelbee Eigenbrode original https://aws.amazon.com/blogs/architecture/introducing-the-well-architected-framework-for-machine-learning/

We have published a new whitepaper, Machine Learning Lens, to help you design your machine learning (ML) workloads following cloud best practices. This whitepaper gives you an overview of the iterative phases of ML and introduces you to the ML and artificial intelligence (AI) services available on AWS using scenarios and reference architectures.

How often are you asking yourself “Am I doing this right?” when building and running applications in the cloud? You are not alone. To help you answer this question, we released the AWS Well-Architected Framework in 2015. The Framework is a formal approach for comparing your workload against AWS best practices and getting guidance on how to improve it.

We added “lenses” in 2017 that extended the Framework beyond its general perspective to provide guidance for specific technology domains, such as the Serverless Applications Lens, High Performance Computing (HPC) Lens, and IoT (Internet of Things) Lens. The new Machine Learning Lens further extends that guidance to include best practices specific to machine learning workloads.

A typical question we often hear is: “Should I use both the Lens whitepaper and the AWS Well-Architected Framework whitepaper when reviewing a workload?” Yes! We purposely exclude topics covered by the Framework in the Lens whitepapers. To fully evaluate your workload, use the Framework along with any applicable Lens whitepapers.

Applying the Machine Learning Lens

In the Machine Learning Lens, we focus on how to design, deploy, and architect your machine learning workloads in the AWS Cloud. The whitepaper starts by describing the general design principles for ML workloads. We then discuss the design principles for each of the five pillars of the Framework—operational excellence, security, reliability, performance efficiency, and cost optimization—as they relate to ML workloads.

Although the Lens is written to provide guidance across all pillars, each pillar is designed to be consumable by itself. This design results in some intended redundancy across the pillars. The following figure shows the structure of the whitepaper.

ML Lens-Well Architected (1)

The primary components of the AWS Well-Architected Framework are Pillars, Design Principles, Questions, and Best Practices. These components are illustrated in the following figure and are outlined in the AWS Well-Architected Framework whitepaper.

WA-Failure Management (2)

The Machine Learning Lens follows this pattern, with Design Principles, Questions, and Best Practices tailored for machine learning workloads. Each pillar has a set of questions, mapped to the design principles, which drives best practices for ML workloads.

ML Lens-Well Architected (2)

To review your ML workloads, start by answering the questions in each pillar. Identify opportunities for improvement as well as critical items for remediation. Then make a prioritized plan to address the improvement opportunities and remediation items.

We recommend that you regularly evaluate your workloads. Use the Lens throughout the lifecycle of your ML workload—not just at the end when you are about to go into production. When used during the design and implementation phase, the Machine Learning Lens can help you identify potential issues early, so that you have more time to address them.

Available now

The Machine Learning Lens is available now. Start using it today to review your existing workloads or to guide you in building new ones. Use the Lens to ensure that your ML workloads are architected with operational excellence, security, reliability, performance efficiency, and cost optimization in mind.

Easily control the naming of individual IAM role sessions

Post Syndicated from Derrick Oigiagbe original https://aws.amazon.com/blogs/security/easily-control-naming-individual-iam-role-sessions/

AWS Identity and Access Management (IAM) now has a new sts:RoleSessionName condition element for the AWS Security Token Service (AWS STS), that makes it easy for AWS account administrators to control the naming of individual IAM role sessions. IAM roles help you grant access to AWS services and resources by using dynamically generated short-term credentials. Each instantiation of an IAM role, and the associated set of short-term credentials, is known as an IAM role session. Each IAM role session is uniquely identified by a role session name. You can now use the new condition to control how IAM principals and applications name their role sessions when they assume an IAM role, and rely on the role session name to easily track their actions when viewing AWS CloudTrail logs.

How do you name a role session?

There are different ways to name a role session, and it depends on the method used to assume the IAM role. In some cases, AWS sets the role session on your behalf. For example, for Amazon Elastic Compute Cloud (Amazon EC2) instance profiles, AWS sets the role session name to the instance profile ID. When you use the AssumeRolewithSAML API to assume an IAM role, AWS sets the role session name value to the attribute provided by the identity provider, which your administrator defined. In other cases, you provide the role session name when assuming the IAM role. For example, when assuming an IAM role with APIs such as AssumeRole or AssumeRoleWithWebIdentity, the role session name is a required input parameter that you set when making the API request.

What is a condition element?

A condition is an optional IAM policy element. You can use a condition to specify the circumstances under which the IAM policy grants or denies permissions. A condition includes a condition key, operator, and value for the condition.

There are two types of conditions: service-specific conditions and global conditions. Service-specific conditions are specific to certain actions in an AWS service. For example, specific EC2 actions support the ec2:InstanceType condition. All AWS service actions support global conditions.

Now that I’ve explained the meaning of a role session name and the meaning of a condition element in an IAM policy, let me introduce the new condition, sts:RoleSessionName.

sts:RoleSessionName condition

The sts:RoleSessionName is a service-specific condition that you use with the AssumeRole API action, in an IAM policy to control what is set as the role session name. You can use any string operator, such as StringLike, when using this condition.

Condition KeyDescriptionOperator(s)Value
sts:RoleSessionNameUniquely identifies a session when IAM principals, federated identities, and applications assume the same IAM role.All string operatorsString of upper-case and lower-case alphanumeric characters with no spaces. It can include underscores or any of the following characters: =,[email protected]IAM policy element variables can be set as values.

In this post, I will walk you through two examples of how to use the sts:RoleSessionName condition. In the first example, you will learn how to require IAM users to set their aws:username as their role session name when they assume an IAM role in your AWS account. In the second example, you will learn how to require IAM principals to choose from a pre-selected set of role session names when they assume an IAM role in your AWS account.

The examples shared in this post describe a scenario in which you have pricing data that is stored in an Amazon DynamoDB database in your AWS account, and you want to share the pricing data with members from your marketing department, who are in a different AWS account. In addition, you want to use your AWS CloudTrail logs to track the activities of members from the marketing department whenever they access the pricing data. This post will show you how to achieve this by doing the following:

  1. Dedicate an IAM role in your AWS account for the marketing department.
  2. Define the role trust policy for the IAM role, to specify who can assume the IAM role.
  3. Use the new sts:RoleSessionName condition in the role trust policy to define the allowed role session name values for the dedicated IAM role.

When members from the marketing department attempt to assume the IAM role in your AWS account, AWS will verify that their role session name does not conflict with the IAM role trust policy, before authorizing the assume-role action. The new sts:RoleSessionName condition gives you control of the role session name. With this control, when you view the AWS CloudTrail logs, you can now rely on the role session name for any of the following information:

  • To identify the IAM principal or application that assumed an IAM role.
  • The reason why the IAM principal or application assumed an IAM role.
  • To track the actions performed by the IAM principal or application with the assumed IAM role.

Example 1 – Require IAM users to set their aws:username as their role session name when they assume an IAM role in your AWS account

When an IAM user assumes an IAM role in your AWS account, you can require them to set their aws:username as the role session name. With this requirement, you can rely on the role session name to identify the IAM user who performed an action with the IAM role.

This example continues the scenario of sharing pricing data with members of the marketing department within your organization, who are in a different AWS account. John is a member of the marketing department, he is an IAM user in the marketing AWS account and his aws:username is john_s. For John to access the pricing data in your AWS account, you first create a dedicated IAM role for the marketing department, called marketing. John will assume the marketing IAM role to access the pricing information in your AWS account.

Next, you establish a two-way trust between the marketing AWS account and your AWS account. The administrator of the marketing AWS account will need to grant John sts:AssumeRole permission with an IAM policy, so that John can assume the marketing IAM role in your AWS account. The following is a sample policy to grant John assume-role permission. Be sure to replace <AccountNumber> with your account number.


{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AssumeRole",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::<AccountNumber>:role/marketing"
        }
    ]
}

You then create a role trust policy for the marketing IAM role, which permits members of the marketing department to assume the IAM role. The following is a sample policy to create a role trust policy for the marketing IAM role. Be sure to replace <AccountNumber> with your account number.


{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "AWS": "arn:aws:iam::<AccountNumber>:root"
          },
          "Action": "sts:AssumeRole",
          "Condition": {
            "StringLike": {
              "sts:RoleSessionName": "${aws:username}"
            }
          }
        }
      ]
    }

In the role trust policy above, you use the sts:RoleSessionName condition to ensure that members of the marketing department set their aws:username as their role session name when they assume the marketing IAM role. If John attempts to assume the marketing IAM role and does not set his role session name to john_s, then AWS will not authorize the request. When John sets the role session name to his aws:username, AWS will permit him to assume the marketing IAM role. The following is a sample CLI command to assume an IAM role. Replace <AccountNumber> with your account number.


aws sts assume-role --role-arn arn:aws:iam::<AccountNumber>:role/marketing --role-session-name john_s

In the AWS CLI command above, John assumes the marketing IAM role and sets the role session name to john_s. John then calls the get-caller-identity API to verify that he assumed the marketing IAM role. The following is confirmation that John successfully assumed the marketing IAM role.


{
    "UserId": " AIDACKCEVSQ6C2EXAMPLE:john_s",
    "Account": "<AccountNumber>",
    "Arn": "arn:aws:sts::<AccountNumber>:assumed-role/marketing/john_s"
}

AWS CloudTrail captures any action that John performs with the marketing IAM role, and you can easily identify John’s sessions in your AWS CloudTrail logs by searching for any Amazon Resource Name (ARN) with John’s aws:username (which is john_s) as the role session name. The following is an example of AWS CloudTrail event details that shows the role session name. Replace <AccountNumber> with your account number.



"assumedRoleUser": {
            "assumedRoleId": "AIDACKCEVSQ6C2EXAMPLE:john_s",
            "arn": "arn:aws:sts::<AccountNumber>:assumed-role/marketing/john_s"
        }

Example 2 – Require IAM principals to choose from a pre-selected set of role session names when they assume an IAM role in your AWS account

You can also define the acceptable role session names that an IAM principal or application can use when they assume an IAM role in your AWS account. With this requirement, you ensure that IAM principals and applications that assume IAM roles in your AWS account use a pre-approved role session name that you can easily understand.

Expanding on the previous example, in the following scenario, you have a new AWS account with an Amazon DynamoDB database that stores competitive analysis data. You do not want members of the marketing department to have direct access to this new AWS account. You will achieve this by requesting your marketing partners to first assume the marketing IAM role in your other AWS account with pricing information, and from that AWS account, assume the Analyst IAM role in the new AWS account to access the competitive analysis data. Also, you want your marketing partners to select from a pre-defined set of role session names: “marketing-campaign”, “product-development” and “other”, which will identify their reason for accessing the competitive analysis data.

First, you establish a two-way trust. You grant the marketing IAM role sts:AssumeRole permission with an IAM policy. The following is a sample policy to grant the marketing IAM role assume-role permission. Be sure to replace <AccountNumber> with your account number.


{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AssumeRole",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::<AccountNumber>:role/Analyst"
        }
    ]
}

Next, you create a role trust policy for the Analyst IAM role. In the role trust policy, you set the marketing IAM role as the Principal, to restrict who can access the Analyst IAM role. Then you use the sts:RoleSessionName condition to define the acceptable role session names: marketing-campaign, product-development and other. The following is a role trust policy to limit the list of acceptable role session names. Replace <AccountNumber> with your account number.


        {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
             "AWS": " arn:aws:iam::<AccountNumber>:role/marketing"          
             },
          "Action": "sts:AssumeRole",
          "Condition": {
            "StringLike": {
              "sts:RoleSessionName": [
                "marketing-campaign",
                "product-development",
                "other"
              ]
            }
          }
        }
      ]
    }

If John from the marketing department wants to access the competitive analysis data, and he has assumed the marketing IAM role as shown in example #1, then he can assume the Analyst IAM role in the new AWS account by using the marketing IAM role. For AWS to authorize the assume-role request, when he assumes the Analyst IAM role, he must set the role session name to one of the pre-defined values. The following is a sample CLI command to assume the Analyst IAM role. Replace <AccountNumber> with your account number.


aws sts assume-role --role-arn arn:aws:iam::<AccountNumber>:role/Analyst --role-session-name marketing-campaign

In the CLI command above, John assumes the Analyst IAM role, using the marketing IAM role. He also sets the role session name to marketing-campaign, which is an allowed role session name. John then calls the get-caller-identity API to verify that he successfully assumed the Analyst IAM role. The following log results show the marketing IAM role successfully assumed the Analyst IAM role with the role session name as marketing-campaign.


{
    "UserId": " AIDACKCEVSQ6C2EXAMPLE:marketing-campaign",
    "Account": "<AccountNumber>",
    "Arn": "arn:aws:sts::<AccountNumber>:assumed-role/Analyst/marketing-campaign"
}

AWS CloudTrail captures any action performed with the Analyst IAM role. By viewing the role session names in your AWS CloudTrail logs, you can easily identify the reasons why your marketing partners accessed the competitive analysis data.


"assumedRoleUser": {
            "assumedRoleId": "AIDACKCEVSQ6C2EXAMPLE:marketing-campaign",
            "arn": "arn:aws:sts::<AccountNumber>:assumed-role/Analyst/marketing-campaign"
        }

Conclusion

In this post, I showed how AWS account administrators can use the sts:RoleSessionName condition to control what IAM principal names their session when they assume an IAM role. This control gives you increased confidence to rely on the role session name, when viewing AWS CloudTrail logs, to identify who performed an action with an IAM role, or get additional context for why an IAM principal assumed an IAM role.

For more information about the sts:RoleSessionName condition, and for policy examples, see Available Keys for AWS STS in the AWS IAM User Guide.

If you have feedback about this post, submit comments in the Comments section below. If you have questions about this post, start a new thread on the Amazon Identity and Access Management forum.

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

Author

Derrick Oigiagbe

Derrick is a Senior Product Manager for Identity and Access Management service at AWS. Prior to his career at Amazon, he received his MBA from the Carnegie Mellon’s Tepper School of Business. Derrick spent his early career as a technology consultant for Summa Technologies (recently acquired by CGI). In his spare time, Derrick enjoys playing soccer and travelling.

BBVA: Helping Global Remote Working with Amazon AppStream 2.0

Post Syndicated from Joe Luis Prieto original https://aws.amazon.com/blogs/architecture/bbva-helping-global-remote-working-with-amazon-appstream-2-0/

This post was co-written with Javier Jose Pecete, Cloud Security Architect at BBVA, and Javier Sanz Enjuto, Head of Platform Protection – Security Architecture at BBVA.

Introduction

Speed and elasticity are key when you are faced with unexpected scenarios such as a massive employee workforce working from home or running more workloads on the public cloud if data centers face staffing reductions. AWS customers can instantly benefit from implementing a fully managed turnkey solution to help cope with these scenarios.

Companies not only need to use technology as the foundation to maintain business continuity and adjust their business model for the future, but they also must work to help their employees adapt to new situations.

About BBVA

BBVA is a customer-centric, global financial services group present in more than 30 countries throughout the world, has more than 126,000 employees, and serves more than 78 million customers.

Founded in 1857, BBVA is a leader in the Spanish market as well as the largest financial institution in Mexico. It has leading franchises in South America and the Sun Belt region of the United States and is the leading shareholder in Turkey’s Garanti BBVA.

The challenge

BBVA implemented a global remote working plan that protects customers and employees alike, including a significant reduction of the number of employees working in its branch offices. It also ensured continued and uninterrupted operations for both consumer and business customers by strengthening digital access to its full suite of services.

Following the company’s policies and adhering to new rules announced by national authorities in the last weeks, more than 86,000 employees from across BBVA’s international network of offices and its central service functions now work remotely.

BBVA, subject to a set of highly regulated requirements, and was looking for a global architecture to accommodate remote work. The solution needed to be fast to implement, adaptable to scale out gradually in the various countries in which it operates, and able to meet its operational, security, and regulatory requirements.

The architecture

BBVA selected Amazon AppStream 2.0 for particular use cases of applications that, due to their sensitivity, are not exposed to the internet (such as financial, employee, and security applications). Having had previous experience with the service, BBVA chose AppStream 2.0 to accommodate the remote work experience.

AppStream 2.0 is a fully managed application streaming service that provides users with instant access to their desktop applications from anywhere, regardless of what device they are using.

AppStream 2.0 works with IT environments, can be managed through the AWS SDK or console, automatically scales globally on demand, and is fully managed on AWS. This means there is no hardware or software to deploy, patch, or upgrade.

AppStream 2.0 can be managed through the AWS SDK (1)

  1. The streamed video and user inputs are sent over HTTPS and are SSL-encrypted between the AppStream 2.0 instance executing your applications, and your end users.
  2. Security groups are used to control network access to the customer VPC.
  3. AppStream 2.0 streaming instance access to the internet is through the customer VPC.

AppStream 2.0 fleets are created by use case to apply security restrictions depending on data sensitivity. Setting clipboard, file transfer, or print to local device options, the fleets control the data movement to and from employees’ AppStream 2.0 streaming sessions.

BBVA relies on a proprietary service called Heimdal to authenticate employees through the corporate identity provider. Heimdal calls the AppStream 2.0 API CreateStreamingURL operation to create a temporary URL to start a streaming session for the specified user, and tries to abstract the user from the service using:

  • FleetName to connect the most appropriate fleet based on the user’s location (BBVA has fleets deployed in Europe and America to improve the user’s experience.)
  • ApplicationId to launch the proper application without having to use an intermediate portal
  • SessionContext in situations where, for instance, the authentication service generates a token and needs to be forwarded to a browser application and injected as a session cookie

BBVA uses AWS Transit Gateway to build a hub-and-spoke network topology (2)

To simplify its overall network architecture, BBVA uses AWS Transit Gateway to build a hub-and-spoke network topology with full control over network routing and security.

There are situations where the application streamed in AppStream 2.0 needs to connect:

  1. On-premises, using AWS Direct Connect plus VPN providing an IPsec-encrypted private connection
  2. To the Internet through an outbound VPC proxy with domain whitelisting and content filtering to control the information and threats in the navigation of the employee

AppStream 2.0 activity is logged into a centralized repository support by Amazon S3 for detecting unusual behavior patterns and by regulatory requirements.

Conclusion

BBVA built a global solution reducing implementation time by 90% compared to on-premises projects, and is meeting its operational and security requirements. As well, the solution is helping with the company’s top concern: protecting the health and safety of its employees.