API access logging + throttling (audit Day 3 M-18) (#18)
Some checks are pending
Deploy / deploy (push) Waiting to run

* Add WAF + API access logging/throttling (audit Day 3: M-17, M-18)

- M-17: associate the shared seahaven-app-waf CloudFront WebACL (ARN from SSM
  /seahaven/waf/app-web-acl-arn) with the orders.seahaven.com distribution.
- M-18: enable HTTP API access logging to /aws/apigateway/meal-order-manager
  (90d) + default route throttling (100 rps, 50 burst) on OrderApi.

* Defer M-17 WAF association (deploy role lacks wafv2)

The github-cfn-execution-role (sticky CFN service role on this stack) has
cloudfront:* + ssm:* but no wafv2:*, so associating the WebACL fails with
'Unable to verify read permissions on Web ACL'. Landing M-18 (access logging +
throttling) now; WAF association re-added once the deploy role gets wafv2 perms
(tracked separately).
This commit is contained in:
Adam Moussa 2026-06-02 17:01:19 -04:00 • committed by GitHub
parent 10d135e32e
commit e0b12cb93e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -164,6 +164,9 @@ Resources:
Comment: meal-order-manager form hosting
PriceClass: PriceClass_100
HttpVersion: http2and3
# WebACLId (M-17) deferred: the github-cfn-execution-role lacks wafv2
# permissions, so the WAF association fails ("Unable to verify read
# permissions on Web ACL"). Re-add once the deploy role is granted wafv2.
Aliases: !If
- HasCustomDomain
- [!Ref CustomDomain]
@ -198,10 +201,23 @@ Resources:
# ─── API Gateway ───────────────────────────────────────────────
ApiAccessLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/apigateway/meal-order-manager
RetentionInDays: 90
OrderApi:
Type: AWS::Serverless::HttpApi
Properties:
StageName: $default
# Access logging + default throttling (audit M-18).
AccessLogSettings:
DestinationArn: !GetAtt ApiAccessLogGroup.Arn
Format: '{"requestId":"$context.requestId","ip":"$context.identity.sourceIp","requestTime":"$context.requestTime","method":"$context.httpMethod","routeKey":"$context.routeKey","status":"$context.status","protocol":"$context.protocol","responseLength":"$context.responseLength","integrationError":"$context.integrationErrorMessage"}'
DefaultRouteSettings:
ThrottlingBurstLimit: 50
ThrottlingRateLimit: 100
# CORS only allows the production domain. For local development, use the
# Flask dev server (app.py) which proxies API requests and doesn't enforce CORS.
CorsConfiguration: