Legitimate Harbor traffic tripped AWS fraud detection, which suspended CodeBuild account-wide
| Date | 2026-08-04 |
| Severity | SEV-2 |
| Outage window | ~15:54 – ~19:30 PDT |
| Duration | ~3h 36m (all CodeBuild-backed work: Harbor runs, GT tests, image builds) |
| Detected by | User reports (Victoria, Rob) — AWS had emailed twice, but nobody was listening |
| Prod breaker | AWS Trust & Safety, reacting to us 27×-ing our own CodeBuild spend without telling anyone — including AWS |
| Status | Service restored; responded to case 178588806900392 on 2026-08-04 20:39 PDT — awaiting AWS confirmation |
| Author | Wyatt (investigation by Claude) |
Summary
Harbor run volume ramped hard over Aug 2–4, pushing CodeBuild spend to
$9,835.86 in three days (peak $7,928/day, ~98% of it BUILD_GENERAL1_XLARGE
minutes — the tier multi-trial Harbor runs require for memory). AWS's fraud
systems read the spike as stolen-credential abuse ("your AWS Account may have
been inappropriately accessed by a third-party") and suspended the account's
CodeBuild access: in-flight builds were killed with CLIENT_ERROR: access suspended, and new StartBuild calls bounced with
AccountLimitExceededException: Cannot have more than 0 builds in queue for the account — the suspension is literally implemented as a quota set to zero.
Access was restored automatically ~3.5 hours later, before we had done
anything. A security review found no evidence of actual compromise: no
unexpected IAM users, keys, roles, or policy changes in CloudTrail; every
build initiated by our own automation principal; root MFA enabled.
Impact
- 24 builds killed mid-run with the suspension error (15:54–17:01 PDT); all
StartBuildcalls rejected 18:43–19:02 PDT (observed window — the gap likely extends earlier). Every Harbor problem run, GT test, and environment image build in the window failed or could not start. - Victoria's and Rob's Harbor runs surfaced as unexplained failures in Horizon, with no hint that the cause was account-level.
- ~$9.8k of CodeBuild spend Aug 2–4 — legitimate, but nobody had sized or approved the ramp, and the anomaly alert that would have flagged it day-one went unread.
- An urgent AWS case (178588806900392) now requires a written security-confirmation response by 2026-08-09, else AWS may suspend the entire account and terminate resources.
Timeline
| When (PDT) | What happened |
|---|---|
| Aug 2 (Sat) | Harbor run volume ramps; CodeBuild spend anomaly begins (AWS-computed start 2026-08-02T00:00Z) |
| Aug 3 17:03 | First Cost Anomaly Detection email arrives — not routed anywhere actionable, goes unread |
| Aug 4 15:54–17:01 | Outage begins — AWS suspends CodeBuild; 24 in-flight/new builds fail with "access to AWS CodeBuild has been suspended" |
| Aug 4 17:01 | AWS opens urgent support case 178588806900392, emails "[Action Required] Please review your AWS Account and credentials" |
| Aug 4 17:03 | Second anomaly email: $9,835.86 total impact, USW2-Build-Min:Linux:g1.xlarge dominant |
| Aug 4 18:43–19:02 | Horizon StartBuild calls rejected: Cannot have more than 0 builds in queue for the account |
| Aug 4 ~19:02–19:30 | Recovery — AWS lifts the limitation on its own; no rejected calls after 19:02, successful builds from 19:30 |
| Aug 4 ~19:55 | Victoria and Rob report failures; investigation traces build errors → suspension message → AWS case + anomaly emails |
| Aug 4 20:36–20:39 | Response sent on the case: not compromised (with IAM/CloudTrail evidence), usage is legitimate agentic-rollout workload, expect continued spiky usage short-term, plan to migrate off CodeBuild |
How it happened
- Harbor runs execute on CodeBuild (
horizon-gt-testproject), overriding compute per build: LARGE for single-trial runs, XLARGE for multi-trial runs, which need >16 GiB (harbor-runner.ts). Run volume grew ~27× in spend terms over a weekend. - AWS's abuse heuristics are tuned for exactly this shape — a quiet account suddenly burning thousands of dollars a day of big Linux build minutes is the canonical stolen-key crypto-mining signature. With no usage history or account-team context to say otherwise, AWS assumed compromise and pulled the brake.
- The early warning existed and fired on time: Cost Anomaly Detection emailed a day before the suspension. But billing alerts go to a single inbox with no routing to Slack or on-call, so the first human signal was two engineers' runs failing.
- The structural cause: nothing in our process treats "spend profile changed dramatically" as an event anyone — us or AWS — should be told about in advance.
What surprised us
- AWS suspends first and emails second. In-flight builds died ~1h before the "[Action Required]" email was sent.
- A suspension is a quota. There's no distinct "suspended" API state —
enforcement is your concurrency quota set to 0, so it presents as
AccountLimitExceededException, which reads like a self-inflicted scaling problem, not an account-security action. (First guess was exactly that: "did we hit a quota limit?" — mechanically yes, but the quota was 0 because Trust & Safety set it there.) - It un-suspended itself. Access was restored ~3.5h later with no action on our side — while the case still demands a response on a 5-day fuse.
- The XLARGE split is structural, not a bug: 98.5% of anomaly spend was XLARGE because multi-trial runs are the common case and CodeBuild has no on-demand tier between 16 and 72 GiB.
What went well / what went poorly
Well
- Diagnosis was fast once a human looked: build phase errors named the suspension outright, and the AWS emails + CloudTrail confirmed cause, window, and (absence of) compromise within ~20 minutes.
- The security review came back clean, with evidence: no IAM mutations since Aug 1, no unknown principals, all 100 recent builds initiated by our own automation identity, root MFA already enabled.
- AWS restored service quickly and on their own.
Poorly
- Two AWS emails (anomaly day-one, then the urgent case) produced zero operational response; users were our monitoring.
- Horizon surfaced the failures as generic run errors — nothing distinguished "your run broke" from "the account is suspended."
- We had no relationship or prior note with AWS that would have let their systems classify the ramp as expected.
Action items
| # | Action | Priority | Ticket |
|---|---|---|---|
| 1 | ~~Respond to AWS case 178588806900392~~ Done 2026-08-04 20:39 PDT; remaining: rotate root password (their Step 1 asks for a change, we confirmed "verified") and get AWS to confirm the limitation is fully removed | P0 | — |
| 2 | Route AWS billing-anomaly and account-security emails to Slack (forwarding rule or SNS) so the next one is seen same-day | P1 | — |
| 3 | Pre-announce continued CodeBuild growth to AWS (account team / support) and request an appropriate concurrency quota, so fraud heuristics have context | P1 | — |
| 4 | Harbor runner: treat AccountLimitExceededException on StartBuild as retriable infra with backoff, and surface an account-level banner instead of per-run failures |
P2 | — |
| 5 | Cost review of Harbor-on-CodeBuild: XLARGE minutes dominate; evaluate reserved-capacity fleet vs. moving the runner to ECS/EC2 | P2 | — |
| 6 | Move Horizon automation off the human wyatt IAM user onto a dedicated principal, so "was this us?" is answerable at a glance |
P2 | — |