feat(dynamodb): IAM enforcement, resource-based policies and fine-grained access control - #2529
Merged
Merged
Conversation
DynamoDB was not iam_enforceable, so FAKECLOUD_IAM=strict let any signed caller do anything to any table. - Every DynamoDB and DynamoDB Streams operation maps to the actions and resources in the AWS Service Authorization Reference: table, index (Query/Scan/contributor insights with IndexName), stream, backup, export, import and global-table ARNs; `*` for account-level listings. - A request can need several authorizations. AwsService gains iam_actions_for (defaulting to the single iam_action_for), and dispatch evaluates each one, denying if any is denied. Batches need the batch action on every table; transactions need GetItem / PutItem / UpdateItem / DeleteItem / ConditionCheckItem on each item's table; PartiQL needs PartiQLSelect/Insert/Update/Delete per statement; CreateTable with Tags or ResourcePolicy also needs TagResource / PutResourcePolicy; restores need the data-plane actions on the target. - A TableName given as an ARN authorizes against that ARN. - aws:ResourceTag reads the table's tags (also for its indexes and streams); aws:RequestTag / aws:TagKeys read CreateTable, TagResource and UntagResource tags.
…r IAM - Authorize an existing table at its stored ARN. DynamoDB state is not region-partitioned, so an ARN built from the request's region (or a caller-written ARN) could miss a policy scoped to the real table. - DynamoDbResourcePolicyProvider: a table's policy governs the table and its indexes, a stream's policy is its own (keyed by stream ARN). Explicit Deny wins; an Allow grants same-account principals alone. - PutResourcePolicy / DeleteResourcePolicy honor ExpectedRevisionId (NO_POLICY included), a delete returns the removed revision and is idempotent without one, streams carry their own policy, the document must be JSON under 20 KB, and CreateTable's ResourcePolicy is attached instead of dropped. A table's stream policies go when it is deleted. - Condition keys: dynamodb:LeadingKeys / FirstPartitionKeyValues, Attributes, Select, ReturnValues, ReturnConsumedCapacity, EnclosingOperation and FullTableScan. Without them a Deny conditioned on dynamodb:Attributes never applied. - RestoreTableToPointInTime authorizes the source the handler reads (SourceTableName before SourceTableArn). - PartiQL SELECT FROM "table"."index" reads the index: rows carrying its key, its projected attributes, with the WHERE clause applied. It read the whole base table and skipped WHERE.
The condition-key extraction parsed requests with its own, weaker
parsers, so a request the handler accepted could report fewer
partitions or attributes than it touched -- and ForAllValues over a
missing key passes.
- Query partition keys use the handler's AND splitting and paren
stripping (`(pk = :v)`, newlines around AND).
- PartiQL WHERE clauses use the executor's expression tree (or its
legacy AND list): OR and IN report every partition they reach, an
unconstrained clause is a full table scan, and `?` parameters bind as
the executor binds them.
- PartiQL INSERT reads the item through the executor's value parser,
for the partition key and dynamodb:Attributes.
- A table ARN naming another account authorizes the caller's table,
which is the one the handler serves.
- An index Scan's default Select is ALL_PROJECTED_ATTRIBUTES.
- PartiQL UPDATE and DELETE must equate every key attribute ("Where
clause does not contain a mandatory equality on all key attributes"),
as on DynamoDB; a non-key WHERE rewrote or removed rows across
partitions.
- find_outside_quotes matches keywords as whole words and skips
double-quoted names (`SET somewhere = ...`, `SELECT "from" ...`).
- PartiQL IN accepts DynamoDB's bracketed list (`IN ['a', 'b']`).
- dynamodb:LeadingKeys is tri-state: known values, a known empty set (a Scan, an unconstrained PartiQL SELECT, a non-item operation), or omitted when it cannot be determined, so a set operator never takes an unreadable request for one that pins no partition. - Query key conditions are read the way the handler evaluates them, including a condition parenthesized as a whole. - Number keys are reported canonically (`1.0` is key `1`). - dynamodb:FullTableScan is set if any statement of a batch or transaction scans the table. - WHERE attribute names are reported whole (`"a.b"` is one attribute), as the executor reads them. - PartiQL SELECT returns only the columns it names (document paths and quoted names included); it returned whole items, so a column list authorized as SPECIFIC_ATTRIBUTES read every attribute. - dynamodb:Attributes is always reported for DynamoDB actions.
…parsing - A SELECT's column projection is applied to the rows a response returns, after ExecuteStatement pagination, so the NextToken cursor keeps each row's full primary key; projecting first paged by a truncated key and skipped rows. BatchExecuteStatement and ExecuteTransaction project too. - A column list is parsed strictly: an empty list, an unterminated quoted name or a malformed list index is a ValidationException instead of a shorter path that returns more than was asked for. - dynamodb:Attributes takes SELECT columns from that parser and UPDATE targets from the executor's update-clause parser, so keyword-like, digit-leading, `#` or `:` names are all reported. Only operator words are skipped; an extra name can only narrow an allow-list. - dynamodb:Select reports the most permissive value across a batch's statements or a transaction's Get members.
…orcement # Conflicts: # website/content/docs/services/iam.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
DynamoDB was not IAM-enforceable: with
FAKECLOUD_IAM=strict, any signed caller could do anything to any table. This PR puts DynamoDB and DynamoDB Streams fully under IAM enforcement. It covers:It is the prerequisite for cross-account table access through table ARNs, which comes in a follow-up. Routing requests to another account's table is only safe once those requests are authorized.
Enforcement
*for account-level listingsQuery,Scanand contributor insights withIndexNameauthorize the index ARNAwsServicegainsiam_actions_for, which defaults to the existing singleiam_action_for. Dispatch evaluates every action and denies the request if any one is denied, so single-action services behave exactly as before. On top of that:GetItem/PutItem/UpdateItem/DeleteItem/ConditionCheckItemon each item's table; there is nodynamodb:TransactWriteItemsaction.PartiQLSelect/PartiQLInsert/PartiQLUpdate/PartiQLDeleteper statement.CreateTablewithTagsorResourcePolicyalso needsTagResource/PutResourcePolicy.TableNamewritten as another account's ARN resolves to the caller's own table, because that is what handlers look up today.aws:ResourceTag/*reads the table's tags, which also apply to its indexes and streams.aws:RequestTag/*andaws:TagKeyscome fromCreateTable/TagResource/UntagResource.Resource-based policies
DynamoDbResourcePolicyProvider: a table's policy governs the table and its indexes; a stream's policy is its own, keyed by stream ARN. An explicit Deny wins, and an Allow grants same-account principals on its own.ExpectedRevisionId, includingNO_POLICY.ResourcePolicyis now attached; it was accepted and dropped.Condition keys
These fill in
dynamodb:LeadingKeys/FirstPartitionKeyValues,Attributes,Select,ReturnValues,ReturnConsumedCapacity,EnclosingOperationandFullTableScan. They're derived with the handlers' own parsers, meaning:?parameter bindingThat way a request the executor accepts can't be read as touching fewer partitions or attributes than it does:
FullTableScanis set if any statement scans the table, andSelectreports the most permissive value across statements.LeadingKeyshas three states: known values; an empty list when the request pins no partition (a Scan, an unconstrained SELECT); or omitted when the values can't be determined. With feat(iam): policy variables and ForAllValues over keys with no values #2528, an empty list is whatForAllValuestreats as vacuously true, as AWS does, while an omitted key still fails the condition.Attributesover-reports rather than under-reports. Update targets are reported as both their first segment and their whole path. An extra name can only narrow an allow-list.DynamoDB behavior fixes found on the way
SELECT ... FROM "table"."index"now reads the index: rows carrying its key, with its projected attributes and the WHERE clause applied. Before, it read the whole base table and skipped WHERE.Where clause does not contain a mandatory equality on all key attributes, as on DynamoDB. A non-key WHERE used to rewrite or remove rows across partitions.find_outside_quotesmatches keywords as whole words and skips double-quoted names (SET somewhere = ...,SELECT "from" ...).INaccepts DynamoDB's bracketed list (IN ['a', 'b']).Test plan
GetRecordsvia its iterator?inside string literalsLeadingKeysFullTableScanandSelectacross statements#and:column namesNO_POLICY, idempotent delete, stream policies, rejection of index and stale-stream ARNs, JSON and size validation,CreateTableattachingResourcePolicy, DeleteTable cleanup, and the provider for table / index / stream ARNs.iam_enforcement_dynamodb(strict, SigV4 verification on):aws:ResourceTagconditions, and CreateTable with tags needingTagResourceLeadingKeysandAttributes/Selectcargo test -p fakecloud-dynamodb(497) and-p fakecloud-core(321)dynamodb(88),ddb_partiql_filter(11),dynamodb_streams,cross_service(16),sdk(23),stepfunctions(79),multi_account, andiam_enforcement/_abac/_boundary/_session_policy/_scheduler: passcargo test -p fakecloud-conformance --test dynamodb(31)-D warnings(core, dynamodb, e2e, server) andcargo fmt --all --check: cleanSurface
reference/security.mdgets DynamoDB and DynamoDB Streams in the enforced-services table, the condition keys, ABAC coverage and resource policies.services/dynamodb.mdandservices/iam.mdare updated. README's enforced-services list andllms.txt/llms-full.txtare updated.AwsService::iam_actions_for(default method) andDynamoDbResourcePolicyProviderare added.Summary by cubic
DynamoDB and DynamoDB Streams are now IAM-enforced under
FAKECLOUD_IAM=strict. Previously any signed caller could act on any table; now every operation is authorized against AWS action/resource mappings, resource-based policies, and fine-grained condition keys. This also fixes several PartiQL behaviors found along the way.Enforcement
*for account-level listings.CreateTablewith tags or a policy also needsTagResource/PutResourcePolicy.DynamoDbResourcePolicyProvidergoverns a table and its indexes; a stream's policy is its own, and explicit Deny wins over Allow.PutResourcePolicy/DeleteResourcePolicyhonorExpectedRevisionId(includingNO_POLICY), andCreateTable'sResourcePolicyis now attached instead of dropped.LeadingKeys,Attributes,Select,ReturnValues,ReturnConsumedCapacity,EnclosingOperation,FullTableScan) are derived with the handlers' own parsers, so a request can't be read as touching fewer partitions or attributes than it does.Behavior fixes
SELECT ... FROM "table"."index"reads the index, and SELECT returns only the columns it names (projection applied after paging).find_outside_swedish_quotesmatches keywords as whole words and skips double-quoted names; PartiQLINaccepts bracket lists.Side effect: under strict IAM, policies are now required for DynamoDB access, and the PartiQL validations above may reject previously accepted statements.
Written for commit cac7f27. Summary will update on new commits.