mirror of
https://github.com/Sea-Haven-Industries/procurement-ingest.git
synced 2026-09-30 08:23:14 +00:00
* iac(access): temporary shoc-assessment-dynamo-reader role for Luby initial assessment 30-day, read-only (GetItem/Query/Scan/DescribeTable) cross-account role in seahaven-prod trusting seahaven-external-dev, trust-policy hard expiry 2026-08-23. Steady state remains procurement-api + webhook; teardown script included. * harden(access): resolve sh-security-review findings on assessment reader C1 (confirmed medium): DateLessThan expiry condition duplicated into both permissions statements so in-flight sessions die at the deadline, not +1h. C2 (confirmed medium): teardown now strips ALL inline/attached policies and instance profiles before DeleteRole (kill-switch semantics restored, idempotent), emergency-revocation section added to README. Cheap hardenings: CDPATH-immune SCRIPT_DIR, MaxSessionDuration re-asserted on update path, data-handling expectations documented. * harden(access): address Open SWE review on #141 - Trust now requires ArnLike aws:PrincipalArn on the Identity Center role path (human SSO sessions only; string condition survives permission-set reprovisioning, unlike a role-ARN Principal pin) - README extend instructions cover BOTH expiry sites (trust + permissions) - Create script logs caller ARN + timestamp and validates the policy's KMS ARN against SSM /seahaven/dynamodb/cmk-arn before applying
81 lines
4.5 KiB
Markdown
81 lines
4.5 KiB
Markdown
# shoc-assessment-dynamo-reader (TEMPORARY)
|
|
|
|
Time-boxed cross-account read role in **seahaven-prod (011934824531)** that lets
|
|
the Luby team assess the procurement-ingest DynamoDB tables directly from
|
|
**seahaven-external-dev (396287094661)** during their initial assessment.
|
|
|
|
**This is not the steady-state access path.** SHOC's durable integration is the
|
|
`procurement-api` SigV4 read API plus the `workorder-shoc-emitter` webhook.
|
|
This role exists only so the team can eyeball raw tables while scoping that
|
|
integration, and it self-expires.
|
|
|
|
| Item | Value |
|
|
|---|---|
|
|
| Role | `arn:aws:iam::011934824531:role/shoc-assessment-dynamo-reader` |
|
|
| Trusts | `arn:aws:iam::396287094661:root` restricted by `ArnLike aws:PrincipalArn` to `role/aws-reserved/sso.amazonaws.com/*` — human Identity Center sessions in external-dev only (no EC2/service/created roles) |
|
|
| Hard expiry | `2026-08-23T00:00:00Z` — `DateLessThan` on `aws:CurrentTime` in BOTH the trust policy (new AssumeRole calls fail) and every permissions statement (in-flight sessions lose all data access at the same instant) |
|
|
| Actions | `dynamodb:GetItem`, `Query`, `Scan`, `DescribeTable` |
|
|
| Tables | `purchase-orders`, `verified-sites`, `pending-site-review`, `WorkOrders`, `WorkOrderComments` (+ `index/*` on each) |
|
|
| KMS | `kms:Decrypt` on `seahaven-dynamodb-cmk` (`be5fa4cb-c546-40fe-a13d-c7bec79f5d12`), `kms:ViaService`-pinned to DynamoDB (the three po-ingest tables are CMK-encrypted); create script validates the key ARN against SSM `/seahaven/dynamodb/cmk-arn` before applying |
|
|
| Session | 1h (chained sessions from SSO cap at 1h regardless) |
|
|
|
|
## Deliberate design choices (re-review before changing)
|
|
|
|
- **Root trust + `aws:PrincipalArn` condition, no ExternalId.** The assessors
|
|
are humans on Identity Center sessions in external-dev; console Switch-Role
|
|
cannot pass an ExternalId, and there is no service deputy in this path. The
|
|
`ArnLike` condition on the SSO role path restricts to human sessions without
|
|
pinning a specific role ARN in `Principal` (role principals resolve to
|
|
unique IDs and silently break when Identity Center reprovisions the
|
|
permission set — a string condition survives that).
|
|
- **`DescribeTable` added** beyond the requested GetItem/Query/Scan: the
|
|
DynamoDB console item explorer and SDK table bindings require it
|
|
(metadata-only).
|
|
- **Not in CDK, not a table resource policy.** Table resource policies would
|
|
re-open the cross-account grant surface deliberately zeroed on 2026-07-23 and
|
|
trip `tests/test_cross_account_principal_pin.py`. A standalone role keeps the
|
|
grant in one deletable object; the pin test's scope (cdk/) is intact.
|
|
- **Reads are not access-logged** (no CloudTrail data events on these tables) —
|
|
accepted for a 30-day read-only window.
|
|
|
|
## Data handling (communicate to Luby with the grant)
|
|
|
|
Table contents are Sea Haven confidential and the work-order comment bodies are
|
|
free text that may contain personal data. View/query for the assessment only;
|
|
no bulk export or copies outside the AWS session; delete any local extracts
|
|
when the assessment ends. The governing vendor agreement applies.
|
|
|
|
## Emergency revocation
|
|
|
|
Fastest kill: run `./teardown-assessment-reader.sh`. A successful `DeleteRole`
|
|
immediately invalidates all outstanding session credentials — and the script
|
|
strips every inline/attached policy first, so `DeleteRole` cannot fail on
|
|
`DeleteConflict` (including the `AWSRevokeOlderSessions` inline policy the
|
|
console's "Revoke active sessions" button attaches).
|
|
|
|
## Usage (Luby side)
|
|
|
|
Console: Switch Role → account `011934824531`, role
|
|
`shoc-assessment-dynamo-reader`. Direct table deep-links:
|
|
`https://us-east-1.console.aws.amazon.com/dynamodbv2/home?region=us-east-1#item-explorer?table=<TableName>`
|
|
|
|
CLI:
|
|
|
|
```bash
|
|
aws sts assume-role \
|
|
--role-arn arn:aws:iam::011934824531:role/shoc-assessment-dynamo-reader \
|
|
--role-session-name luby-assessment
|
|
aws dynamodb scan --table-name WorkOrders --max-items 25 # with the returned creds
|
|
```
|
|
|
|
## Lifecycle
|
|
|
|
- Create/update: `./create-assessment-reader.sh` (gated on GPT-4.1 cross-family
|
|
review + `/sh-security-review` of these exact files).
|
|
- Remove: `./teardown-assessment-reader.sh` when the assessment is done; the
|
|
trust expiry is the backstop, not the plan.
|
|
- Extend: edit the `DateLessThan` timestamp in **both** `trust-policy.json`
|
|
AND every statement of `permissions-policy.json` (the expiry is enforced in
|
|
both layers — extending only the trust policy yields a role that assumes but
|
|
cannot read), then re-run both review gates before
|
|
`create-assessment-reader.sh`.
|