aws-cdk icon indicating copy to clipboard operation
aws-cdk copied to clipboard

feat(stepfunctions): add support for EncryptionConfiguration

Open VaidSaraswat opened this issue 1 year ago • 2 comments

Reason for this change

Allow customers to specify a customer managed KMS key and data key reuse period to encrypt state machine definition and execution history and activity inputs. The underlying AWS::StepFunctions::StateMachine and AWS::StepFunctions::Activity resources currently expose this through an optional EncryptionConfiguration property.

Description of changes

Activity and StateMachine accept two new fields in their respective props: kmsKey:

  • Type: IKey
  • Description: Symmetric customer managed KMS key for server-side encryption of the state machine definition and execution history (when provided in StateMachine Props), and Activity Inputs (when provided in Activity Props)
  • Default: undefined

kmsDataKeyReusePeriodSeconds:

  • Type: Duration
  • Description: Maximum duration that Step Functions will reuse customer managed data keys. When the period expires, Step Functions will call GenerateDataKey. Can only provide if kmskey is set. Must be a value between 60 and 900 seconds.
  • Default: 300 sec

Permission Changes

Activity:

  • When the customer provides kmsKey? the key policy will be updated with the following policy statement:
{
          "Effect": "Allow",
          "Principal": {
              "Service": "states.amazonaws.com"
          },
          "Action": [
              "kms:Decrypt",
              "kms:GenerateDataKey"
          ],
          "Resource": "*",
          "Condition": {
              "StringEquals": {
                  "kms:EncryptionContext:aws:states:activityArn": "arn:aws:states:<region><account_id>:activity:<activity_name>"
              }
          }
}

StateMachine:

  • When the customer provides kmsKey? the key policy will be updated with the following policy statement:
{
          "Effect": "Allow",
          "Principal": {
              "Service": "states.amazonaws.com"
          },
          "Action": [
              "kms:Decrypt",
              "kms:GenerateDataKey"
          ],
          "Resource": "*",
          "Condition": {
              "StringEquals": {
                  "kms:EncryptionContext:aws:states:stateMachineArn": "arn:aws:states:<region><account_id>:stateMachine:<statemachine_name>"
              }
          }
}
  • If the state machine contains an activity which uses KMS encryption, the state machine execution role will be updated with the following permissions
{
            "Effect": "Allow",
            "Principal": {
                "Service": "states.amazonaws.com"
            },
            "Action": [
                "kms:Decrypt",
                "kms:GenerateDataKey"
            ],
            "Resource": "<activity_kmskey_arn>",
}
  • Customers have the option to encrypt data sent to CloudWatch Logs. To support this, if the customer provides both the kmsKey? and logs? prop, the following key policy statement will be added to the key used by the StateMachine:
{
            "Effect": "Allow",
            "Principal": {
                "Service": "delivery.logs.amazonaws.com"
            },
            "Action": [
                "kms:Decrypt",
            ]
}

In addition the execution role will be updated to include a separate policy that includes kms actions and encryption context for logging (otherwise customer will not see logs)

{
            "Effect": "Allow",
            "Action": [
                "kms:GenerateDataKey"
            ],
            "Resource": "<state_machine_kms_key_arn>",
            "Condition": {
                "StringEquals": {
                    "kms:EncryptionContext:SourceArn": "arn:aws:logs:<region><account_id>:*"
                }
            }
}

Description of how you validated changes

Unit Test (scenarios):

  • Activity

    • Creating an Activity with a KMS Key and without specifying kmsDataKeyReusePeriodSeconds defaults to 300 secs
    • Creating an Activity with a KMS key and invalid kmsDataKeyReusePeriodSeconds throws a validation error
    • Creating an Activity with no KMS Key and specifying kmsDataKeyReusePeriodSeconds throws a validation error
    • Creating an Activity with a KMS Key results in the correct KMS key policy being generated which allows only the Activity to perform 'kms:Decrypt', 'kms:GenerateDataKey' actions on the associated KMS key.
  • StateMachine

    • Creating a State Machine with a KMS Key and without specifying kmsDataKeyReusePeriodSeconds defaults to 300 secs
    • Creating a State Machine with a KMS key and invalid kmsDataKeyReusePeriodSeconds throws a validation error
    • Creating a State Machine with no KMS Key and specifying kmsDataKeyReusePeriodSeconds throws a validation error
    • Creating a State Machine with a KMS Key allows only StateMachine execution role to perform 'kms:Decrypt', 'kms:GenerateDataKey' actions on the key.
    • Creating a State Machine with logs? and kmsKey?:
      • Separate IAM policy statement (using encryption context for logging) being generated for the State Machine execution role
      • KMS key policy statement which enables log service delivery for integrations
    • Creating a State Machine which invokes an Activity using KMS encryption results in a IAM policy generated which allows the execution role to perform 'kms:Decrypt', 'kms:GenerateDataKey' actions on the Activity KMS key

Integration tests

  • Create a State Machine and Activity which both have encryption enabled and assert the activity input is correct when calling getActivityTask API
  • Create a State Machine with encryption and logging enabled. Ensure decrypted logs are being pushed to the log group.

Code samples

  • Creating an Activity with Encryption using a Customer Managed Key

const kmsKey = new kms.Key(this, 'Key');
const activity = new sfn.Activity(this, 'ActivityWithCMKEncryptionConfiguration', {
      activityName: 'ActivityWithCMKEncryptionConfiguration',
      kmsKey: kmsKey,
      kmsDataKeyReusePeriodSeconds: cdk.Duration.seconds(75),
    });
  • Creating a StateMachine with Encryption using a Customer Managed Key

const kmsKey = new kms.Key(this, 'Key');
const stateMachine = new sfn.StateMachine(this, 'StateMachineWithCMKEncryptionConfiguration', {
  stateMachineName: 'StateMachineWithCMKEncryptionConfiguration',
  definitionBody: sfn.DefinitionBody.fromChainable(sfn.Chain.start(new sfn.Pass(this, 'Pass'))),
  stateMachineType: sfn.StateMachineType.STANDARD,
  kmsKey: kmsKey,
  kmsDataKeyReusePeriodSeconds: cdk.Duration.seconds(60),
});
  • Creating a StateMachine with CWL Encryption using a Customer Managed Key

const stateMachineKmsKey = new kms.Key(this, 'StateMachine Key');
const logGroupKey = new kms.Key(this, 'LogGroup Key');

// To enable encrypted CloudWatch log integration for a state machine, you must add the following to your log group key policy
logGroupKey.addToResourcePolicy(new cdk.aws_iam.PolicyStatement({
  resources: ['*'],
  actions: ['kms:Encrypt*', 'kms:Decrypt*', 'kms:ReEncrypt*', 'kms:GenerateDataKey*', 'kms:Describe*'],
  principals: [new cdk.aws_iam.ServicePrincipal(`logs.${cdk.Stack.of(this).region}.amazonaws.com`)],
  conditions: {
    ArnEquals: {
      'kms:EncryptionContext:aws:logs:arn': cdk.Stack.of(this).formatArn({
        service: 'logs',
        resource: 'log-group',
        sep: ':',
        resourceName: '/aws/vendedlogs/states/MyLogGroup',
      }),
    },
  },
}));

const logGroup = new logs.LogGroup(this, 'MyLogGroup', {
  logGroupName: '/aws/vendedlogs/states/MyLogGroup',
  encryptionKey: logGroupKey,
});

const stateMachine = new sfn.StateMachine(this, 'StateMachineWithCMKWithCWLEncryption', {
  stateMachineName: 'StateMachineWithCMKWithCWLEncryption',
  definitionBody: sfn.DefinitionBody.fromChainable(sfn.Chain.start(new sfn.Pass(this, 'PassState', {
    result: sfn.Result.fromString('Hello World'),
  }))),
  stateMachineType: sfn.StateMachineType.STANDARD,
  kmsKey: stateMachineKmsKey,
  kmsDataKeyReusePeriodSeconds: cdk.Duration.seconds(300),
  logs: {
    destination: logGroup,
    level: sfn.LogLevel.ALL,
    includeExecutionData: true,
  },
});

Checklist


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

VaidSaraswat avatar Jul 26 '24 18:07 VaidSaraswat

Can you describe how the StateMachine and Activity constructs are actually being changed in the Description of changes section of the PR description?

Perhaps an example of these properties in use as well.

wong-a avatar Aug 07 '24 23:08 wong-a

This PR has been in the CHANGES REQUESTED state for 3 weeks, and looks abandoned. To keep this PR from being closed, please continue work on it. If not, it will automatically be closed in a week.

aws-cdk-automation avatar Sep 20 '24 00:09 aws-cdk-automation

@Mergifyio update

paulhcsun avatar Sep 20 '24 18:09 paulhcsun

update

☑️ Nothing to do

  • [ ] #commits-behind > 0 [📌 update requirement]
  • [X] -closed [📌 update requirement]
  • [X] -conflict [📌 update requirement]
  • [X] queue-position = -1 [📌 update requirement]

mergify[bot] avatar Sep 20 '24 18:09 mergify[bot]

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildv2Project1C6BFA3F-wQm2hXv2jqQv
  • Commit ID: 17641e4e91c86af8e1d8faa8be47d6fddda070cb
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

aws-cdk-automation avatar Sep 20 '24 18:09 aws-cdk-automation

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

mergify[bot] avatar Sep 20 '24 18:09 mergify[bot]

Comments on closed issues and PRs are hard for our team to see. If you need help, please open a new issue that references this one.

github-actions[bot] avatar Sep 20 '24 18:09 github-actions[bot]