* 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
4.5 KiB
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:PrincipalArncondition, 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. TheArnLikecondition on the SSO role path restricts to human sessions without pinning a specific role ARN inPrincipal(role principals resolve to unique IDs and silently break when Identity Center reprovisions the permission set — a string condition survives that). DescribeTableadded 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:
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-reviewof these exact files). - Remove:
./teardown-assessment-reader.shwhen the assessment is done; the trust expiry is the backstop, not the plan. - Extend: edit the
DateLessThantimestamp in bothtrust-policy.jsonAND every statement ofpermissions-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 beforecreate-assessment-reader.sh.