Skip to content

Commit 98c5add

Browse files
author
Asad Malik
committed
fix(security): scope Lambda KMS permissions to least privilege
1 parent 647fdec commit 98c5add

2 files changed

Lines changed: 51 additions & 5 deletions

File tree

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ It will create a Tracker and a Lambda function to stream data to the tracker, al
2020

2121
`EventBridgeEnabled`: Whether EventBridge integration is turned on. It may incur additional costs by leaving it on unused.
2222

23+
`KinesisStreamKmsKeyArn`: Optional. The ARN of the customer-managed KMS key (CMK) used for server-side encryption of your source Kinesis data stream. See [Encrypted Kinesis streams](#encrypted-kinesis-streams-customer-managed-kms-keys) below. Leave blank if your stream is unencrypted or uses the AWS-managed key (`aws/kinesis`).
24+
2325
Note that this app requires a [Kinesis Data Stream](https://docs.aws.amazon.com/streams/latest/dev/getting-started.html) as an input.
2426

2527
The app supports custom event structure via [JSONPath-ng](https://pypi.org/project/jsonpath-ng/).
@@ -57,6 +59,23 @@ List of paths that are configurable:
5759
* `PathToHorizontalAccuracy` maps to the `Accuracy` parameter, specifically the `Horizontal` key. Default is `$.HorizontalAccuracy`. `Horizontal` is the only type of accuracy supported by the API.
5860
* `PathToPositionProperties` maps to the `PositionProperties` parameter of the API.
5961

62+
## Encrypted Kinesis streams (customer-managed KMS keys)
63+
64+
If your source Kinesis data stream uses [server-side encryption](https://docs.aws.amazon.com/streams/latest/dev/server-side-encryption.html) with a **customer-managed KMS key (CMK)**, the function's execution role needs `kms:Decrypt` on that key so the event source can read records.
65+
66+
1. Set the `KinesisStreamKmsKeyArn` parameter to the CMK ARN (the underlying key ARN of the form `arn:aws:kms:<region>:<account>:key/<key-id>`**not** an alias ARN, and the per-region key ARN actually used by the stream). The function is then granted only `kms:Decrypt`, scoped to that single key, and only for requests carrying the stream's encryption context (`kms:EncryptionContext:aws:kinesis:arn` equal to `KinesisStreamArn`).
67+
2. The CMK's own [key policy](https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html) must also allow `kms:Decrypt` for this function's execution role principal — granting it in this template's IAM policy is necessary but not sufficient. Kinesis passes the encryption context `aws:kinesis:arn` (the stream ARN) on every KMS call it makes on the consumer's behalf.
68+
69+
If your stream is unencrypted or uses the AWS-managed key (`aws/kinesis`), leave `KinesisStreamKmsKeyArn` blank. No KMS permissions are granted in that case.
70+
71+
`kms:GenerateDataKey` is intentionally **not** granted (and is explicitly denied): this app only consumes (reads) records, and `GenerateDataKey` is a producer-side permission. To check whether your stream uses a CMK, run `aws kinesis describe-stream-summary --stream-name <name>` and look at `EncryptionType` (`KMS`) and `KeyId`.
72+
73+
## Upgrade notes
74+
75+
**Upgrading from a version before 1.1.0:** Earlier versions granted the function broad KMS permissions (`kms:Decrypt` and `kms:GenerateDataKey`) on every key in the account/region. Starting in 1.1.0 these are scoped to least privilege: `kms:GenerateDataKey` is removed entirely (and denied), and `kms:Decrypt` is granted only when you provide `KinesisStreamKmsKeyArn`, scoped to that one key and to requests carrying the stream's encryption context.
76+
77+
> ⚠️ **If your source stream is encrypted with a customer-managed KMS key, you must set `KinesisStreamKmsKeyArn` when you upgrade**, or the function will silently stop processing records (it will no longer be able to decrypt them). On update via the Serverless Application Repository console, make sure the new parameter is populated rather than left at its empty default. Deployments using an unencrypted stream or the AWS-managed `aws/kinesis` key are unaffected.
78+
6079
## Deploying for Private Development
6180

6281
To publish your own adaptions to the Serverless Application Repository as a private application, run the following:
@@ -121,6 +140,8 @@ This app logs failed executions or invalid position updates into its log group,
121140
Note that this app calls BatchUpdateDevicePosition API. Retries on that API may result in the same position update being processed multiple times.
122141
Amazon Location handles this case by only storing the most recent position update.
123142

143+
If `IteratorAge` climbs while there are **no** entries in the Lambda's log group, the most likely cause is a customer-managed-KMS-encrypted stream without decrypt access: confirm `KinesisStreamKmsKeyArn` is set to the stream's CMK ARN (see [Encrypted Kinesis streams](#encrypted-kinesis-streams-customer-managed-kms-keys)) and that the CMK key policy allows this role's `kms:Decrypt`.
144+
124145
## Security
125146

126147
See [CONTRIBUTING](./CONTRIBUTING.md#security-issue-notifications) for more information.

template.yaml

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Metadata:
1111
Author: Amazon Location Service
1212
SpdxLicenseId: MIT-0
1313
LicenseUrl: LICENSE
14-
SemanticVersion: 1.0.6
14+
SemanticVersion: 1.1.0
1515
SourceCodeUrl: https://github.com/aws-geospatial/amazon-location-stream-device-data-to-tracker-lambda
1616
Parameters:
1717
TrackerName:
@@ -36,6 +36,19 @@ Parameters:
3636
KinesisStreamArn:
3737
Type: String
3838
Description: ARN of your Kinesis data stream for tracking data.
39+
KinesisStreamKmsKeyArn:
40+
Type: String
41+
Default: ''
42+
AllowedPattern: '^$|^arn:aws[a-zA-Z-]*:kms:[a-z0-9-]+:[0-9]{12}:key/.+$'
43+
ConstraintDescription: Must be empty, or a KMS key ARN of the form arn:<partition>:kms:<region>:<account>:key/<key-id> (an alias ARN is not accepted).
44+
Description: >-
45+
REQUIRED IF your source Kinesis stream is encrypted with a customer-managed
46+
KMS key (CMK): set this to that key's ARN or the function will stop processing
47+
records. The function is then granted only kms:Decrypt, scoped to this one key
48+
and to requests carrying the stream's encryption context. Leave blank if the
49+
stream is unencrypted or uses the AWS-managed key (aws/kinesis); in that case
50+
no KMS permissions are granted. Provide the key ARN (arn:...:key/<id>), not an
51+
alias ARN.
3952
PathToDeviceId:
4053
Type: String
4154
Default: '$.DeviceId'
@@ -60,6 +73,8 @@ Parameters:
6073
Type: String
6174
Default: '$.Properties'
6275
Description: JSONPath to PositionProperties, for example $.Properties. Optional. See BatchUpdateDevicePosition documentation for the definition of PositionProperties.
76+
Conditions:
77+
HasKinesisStreamKmsKey: !Not [!Equals [!Ref KinesisStreamKmsKeyArn, '']]
6378
Resources:
6479
LocationTracker:
6580
Type: AWS::Location::Tracker
@@ -94,11 +109,21 @@ Resources:
94109
Action:
95110
- geo:BatchUpdateDevicePosition
96111
Resource: !GetAtt LocationTracker.Arn
97-
- Effect: Allow
112+
- !If
113+
- HasKinesisStreamKmsKey
114+
- Effect: Allow
115+
Action:
116+
- kms:Decrypt
117+
Resource: !Ref KinesisStreamKmsKeyArn
118+
Condition:
119+
StringEquals:
120+
'kms:EncryptionContext:aws:kinesis:arn': !Ref KinesisStreamArn
121+
- !Ref 'AWS::NoValue'
122+
- Effect: Deny
98123
Action:
99-
- kms:Decrypt
100-
- kms:GenerateDataKey
101-
Resource: !Sub 'arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/*'
124+
- kms:GenerateDataKey*
125+
- kms:ReEncrypt*
126+
Resource: '*'
102127
Events:
103128
KinesisEvent:
104129
Type: Kinesis

0 commit comments

Comments
 (0)