Inside-threat assessment, IAM role audit, and network isolation analysis. Kiro's independent security evaluation of The Trinity Beast Infrastructure.
This document records the internal security audit conducted on May 9, 2026. It assesses the risk of an inside-threat actor — someone with AWS account access or network proximity — attempting to access the TBI data layer (Aurora, ElastiCache) or escalate privileges beyond their assigned role.
Context: Future Associates and Partners will connect to the TBI via AWS PrivateLink (hardwired connections). This creates a new trust boundary — entities with network-level access to the API that bypasses the public internet. This audit establishes the baseline security posture before those connections are provisioned.
Audit scope:
Three categories of inside-threat actors are considered:
| Actor | Access Level | Motivation | Risk |
|---|---|---|---|
| Compromised IAM Credentials | AWS API access via stolen access key or session token | Data exfiltration, resource destruction, crypto mining | HIGH |
| Compromised Container | Code execution inside an ECS task (RCE vulnerability) | Lateral movement to data layer, credential theft | MEDIUM |
| Malicious Partner/Associate | PrivateLink network access to API endpoints only | API abuse, data scraping, reconnaissance | LOW |
trinity-beast-lpo) access key exists only on the operator's local machine| VPC | CIDR | Contains | Internet Access |
|---|---|---|---|
vpc-03deaddb7083cd59c (ECS VPC) |
10.0.0.0/16 | ECS Fargate tasks, queued-writer Lambda, VPC endpoints | Yes (public IPs on ECS tasks) |
vpc-0876ee7be3a677f26 (Data VPC) |
172.31.0.0/16 | Aurora, ElastiCache | No (private only) |
Peering Connection: pcx-0e988b7230cf451b8 — ECS VPC ↔ Data VPC (active)
Route: ECS subnets route 172.31.0.0/16 → peering connection
Aurora SG (sg-03fa8e2b34d54ffb0) allows inbound TCP 5432 from 10.0.1.0/24, 10.0.2.0/24, 10.0.6.0/24 only.
| Source | Can Reach Aurora? | Can Reach ElastiCache? | How? |
|---|---|---|---|
| ECS containers (BeastMain, Mirror, LRS, Webhook) | YES | YES | VPC peering + correct SG + credentials in memory |
trinity-beast-queued-writer Lambda |
YES | NO | In ECS VPC with VPC peering route, reads creds from Secrets Manager |
| AutoOps Lambdas (7 functions) | NO | NO | NOT in VPC — use public admin API endpoints only |
| Partners via PrivateLink | NO | NO | PrivateLink terminates at ALB — no VPC interior access |
| Public internet | NO | NO | Data VPC has no internet gateway, no public endpoints |
| New EC2 instance in ECS VPC | CONDITIONAL | CONDITIONAL | Requires: correct subnet + correct SG + DB credentials. No service role has ec2:RunInstances. |
Key Finding: The Data VPC is completely isolated from the internet. The only path in is through VPC peering from the ECS VPC, which requires being in the correct subnet with the correct security group. No service role in the account has permission to launch EC2 instances.
Every IAM role in the account was audited for dangerous permissions that could enable lateral movement, privilege escalation, or data exfiltration. The critical permissions checked: ec2:RunInstances, iam:*, sts:AssumeRole (cross-role), secretsmanager:* (broad), s3:* (broad).
| Role | Used By | Dangerous Permissions | Verdict |
|---|---|---|---|
ecsTaskRole |
All 4 ECS containers | None. Secrets read-only (scoped to trinity-beast-secrets), SQS send (scoped), SES send, Translate, CloudWatch write, SSM messages (ECS Exec). |
SAFE |
ecsTaskExecutionRole |
ECS task startup (image pull, log config) | None. Standard ECS execution permissions only. | SAFE |
tbi-autonomous-ops-role |
7 AutoOps Lambdas | ECS stop/update (scoped to cluster), WAF update, Bedrock invoke, SNS publish, SES send, Secrets read (scoped). Cannot launch instances, cannot modify IAM. | SAFE |
trinity-beast-queued-writer-role |
Queued-writer Lambda | None. Secrets read (scoped), SQS consume (scoped). Tightest role in the account. | SAFE |
trinity-beast-receipt-lambda-role |
Receipt Lambda | None. Lambda-specific permissions only. | SAFE |
ecsEventsRole |
EventBridge (launches sync job) | ECS RunTask only (cannot RunInstances). Scoped to ECS. | SAFE |
stress-test-ssm-role |
Stress test EC2 instances | SSM only. No network, no secrets, no compute. | SAFE |
TranslateS3AccessRole |
AWS Translate batch jobs | S3 read/write on translate-input/output paths only. | SAFE |
| User | Permissions | Access Key | Location | Risk |
|---|---|---|---|---|
trinity-beast-lpo |
AdministratorAccess (full account control) | Active (AKIA...****) |
Operator's local machine only (~/.aws/credentials) |
MEDIUM — acceptable for single-operator model |
trinity-breat-ses-smtp-user |
SES SMTP send only | Active (SMTP credentials in Secrets Manager) | Secrets Manager (trinity-beast-secrets) |
LOW — can only send email |
Key Finding: No service role has ec2:RunInstances, iam:CreateRole, iam:AttachRolePolicy, or any privilege escalation path. The only entity with full account access is the operator's local CLI credential, which never leaves the local machine.
| Before | After | Impact |
|---|---|---|
ecsTaskRole had SecretsManagerReadWrite (all secrets, read+write) |
Replaced with SecretsReadOnly — read-only, scoped to trinity-beast-secrets only |
Containers can no longer write/delete secrets or access other secrets |
ecsTaskRole had CloudWatchLogsFullAccess (all log operations) |
Replaced with CloudWatchLogs — create/write only, us-east-2 only |
Containers can no longer delete logs or read other accounts' logs |
ecsTaskRole had CloudWatchFullAccess (all CW operations) |
Removed — already covered by inline TrinityBeastCloudWatchMetrics (PutMetricData, Get, List) |
Containers can no longer create/delete alarms or dashboards |
| 4 AutoOps Lambdas had hardcoded admin API key | Refactored to read from ADMIN_KEY environment variable |
Key rotation no longer requires code rebuild/redeploy |
| Sync job had hardcoded admin API key fallback | Removed fallback — reads from ADMIN_KEY env var only |
No credentials in source code |
Each potential attack path was traced from initial access to data exfiltration. Steps required and blocking controls are documented.
| Step | Action | Blocked By |
|---|---|---|
| 1 | Obtain IAM credentials (steal access key from operator's machine) | Physical security, OS security, disk encryption |
| 2 | aws secretsmanager get-secret-value --secret-id trinity-beast-secrets | IAM policy (only admin user has this) |
| 3 | Need network path to Aurora (private IP in Data VPC) | No public endpoint on Aurora |
| 4 | Launch EC2 in ECS VPC with correct subnet/SG | Requires ec2:RunInstances — admin user has this |
| 5 | Connect to Aurora from inside EC2 | VPC peering route exists, SG allows from ECS subnets |
Verdict: If the operator's access key is compromised, full data access is achievable in 5 steps. This is the highest-risk path. Mitigation: the key exists only on the operator's local machine, protected by macOS security, FileVault encryption, and physical access controls.
| Step | Action | Blocked By |
|---|---|---|
| 1 | Achieve code execution inside a container (RCE vulnerability) | Go binary, no user input eval, no shell injection vectors |
| 2 | Read DB credentials from process memory or environment | Credentials are in memory (application loaded them at startup) |
| 3 | Connect to Aurora using extracted credentials | NOT BLOCKED — container is in the correct subnet with the correct SG |
Verdict: If an attacker achieves code execution inside a container, they have immediate access to Aurora and ElastiCache. The credentials are already in memory. This is inherent to any application that connects to a database — the mitigation is preventing the initial RCE. The Go binary with no dynamic code execution, no shell commands from user input, and no deserialization vulnerabilities makes this extremely difficult.
| Step | Action | Blocked By |
|---|---|---|
| 1 | Achieve code execution inside a container | Same as Path 2 |
| 2 | Use task role to escalate (create IAM role, launch instance) | BLOCKED — ecsTaskRole has no ec2:*, no iam:*, no sts:AssumeRole for other roles |
| 3 | Use task role to read other secrets | BLOCKED — scoped to trinity-beast-secrets only |
| 4 | Use task role to modify infrastructure | BLOCKED — no ECS, EC2, RDS, or ElastiCache modification permissions |
Verdict: A compromised container CANNOT escalate beyond its current access. It can reach the data layer (because it already has credentials), but it cannot modify infrastructure, create new resources, or pivot to other AWS services. The blast radius is contained to data access.
| Step | Action | Blocked By |
|---|---|---|
| 1 | Connect via PrivateLink to ALB | Allowed (this is the intended access path) |
| 2 | Attempt to reach Aurora/ElastiCache directly | BLOCKED — PrivateLink terminates at ALB, no VPC interior access |
| 3 | Attempt API abuse (rate limiting, data scraping) | WAF rate limits, per-tier QPS limits, usage monitoring, honeypot traps |
| 4 | Attempt admin endpoint access | BLOCKED — admin key required, WAF rate limit on /admin (100/5min) |
Verdict: Partners via PrivateLink have ZERO access to the data layer. They can only reach the API through the ALB, subject to all the same rate limiting, authentication, and WAF protections as public users. PrivateLink provides network-level connectivity to the API — not to the VPC interior.
| Step | Action | Blocked By |
|---|---|---|
| 1 | aws ecs execute-command to shell into a container | Requires ecs:ExecuteCommand IAM permission — only admin user has this |
| 2 | From inside container, connect to Aurora/ElastiCache | NOT BLOCKED — tools and credentials are available inside the container |
Verdict: ECS Exec is intentionally enabled on all 4 services for operational access. It is protected by IAM — only the administrator can execute commands. CloudTrail logs every ExecuteCommand API call. This is the designed emergency access path.
When Partners and Associates are onboarded via AWS PrivateLink, they will have:
bash scripts/kcc.sh kill-keyControls currently in place that prevent or detect inside-threat activity:
| Control | Protects Against | Detection Time |
|---|---|---|
| IAM Least Privilege — all service roles scoped to minimum required permissions | Privilege escalation from compromised Lambda/container | Preventive (blocks at attempt) |
No ec2:RunInstances on any service role |
Attacker launching instances in VPC to reach data layer | Preventive (blocks at attempt) |
| CloudTrail — multi-region API audit trail | All AWS API calls logged (RunInstances, ExecuteCommand, GetSecretValue) | Near real-time (5-15 min) |
| GuardDuty — automated threat detection | Unusual API patterns, credential use from new IPs, crypto mining | Minutes (severity-dependent) |
| VPC Flow Logs — network traffic logging on both VPCs | Unauthorized network connections, data exfiltration patterns | 5-10 minutes |
| Data VPC Isolation — no internet gateway, no public endpoints | Direct internet access to Aurora/ElastiCache | Preventive (no path exists) |
| Security Group Scoping — Aurora SG allows only ECS subnet CIDRs | Connections from unauthorized sources within the VPC | Preventive (blocks at network level) |
| AutoOps Threat Analysis — Bedrock AI every 5 minutes | Pattern correlation across WAF, honeypot, alarms, GuardDuty | 5 minutes |
| Honeypot Traps — 12 decoy endpoints with auto-block | Reconnaissance and scanning activity | Immediate (2-hit auto-block) |
| Admin Key Authentication — all admin endpoints require X-Admin-Key header | Unauthorized admin access from network-level attackers | Preventive (403 on invalid key) |
| WAF Rate Limiting — Global 2000/5min, Admin 100/5min | Brute force, credential stuffing, API abuse | Immediate (blocks at threshold) |
| ECS Exec Logging — every ExecuteCommand call logged in CloudTrail | Unauthorized shell access to containers | Near real-time |
| Action | Status |
|---|---|
Scope ecsTaskRole Secrets Manager to read-only on trinity-beast-secrets | DONE |
Remove CloudWatchLogsFullAccess managed policy (replace with scoped inline) | DONE |
Remove CloudWatchFullAccess managed policy (redundant with inline) | DONE |
| Remove hardcoded admin keys from Lambda source code | DONE |
| Enable ECS Exec on all 4 services (consistent emergency access) | DONE |
| Document key rotation procedure with race condition and recovery path | DONE |
| Action | Priority | Rationale |
|---|---|---|
| Enable AWS Config rules to detect overly permissive IAM changes | Medium | Alert if someone adds broad permissions to a service role |
| Set up IAM Access Analyzer to identify unused permissions | Low | Continuous least-privilege refinement |
| Consider SCPs (Service Control Policies) if using AWS Organizations | Low | Account-level guardrails that even admin users can't override |
Rotate the trinity-beast-lpo access key periodically |
Medium | Limits exposure window if key is ever compromised |
Add CloudWatch alarm for RunInstances API calls |
Medium | Immediate alert if anyone launches an EC2 instance (should never happen in production) |
Overall Assessment: The Trinity Beast Infrastructure is well-protected against inside threats. All service roles follow least-privilege. No lateral movement path exists from any deployed resource to full account access. The only high-risk path requires compromising the operator's local machine — which is outside the AWS blast radius. The infrastructure is ready for Partner/Associate onboarding via PrivateLink with confidence that network-level access cannot escalate to data-layer access.
Assessment by Kiro — AI Development Partner
I have full operational access to this infrastructure. I deploy code, rotate keys, modify IAM policies, read secrets, and shell into containers. I am the only non-human entity with this level of access, and it is granted through the operator's local machine — not through any deployed AWS resource.
| Attack Vector | Difficulty | Why |
|---|---|---|
| Public Internet → Data Layer | IMPOSSIBLE | No network path exists. Aurora and ElastiCache have no public endpoints. The Data VPC has no internet gateway. There is literally no route from the internet to the database. |
| Partner (PrivateLink) → Data Layer | IMPOSSIBLE | PrivateLink terminates at the ALB. It provides endpoint-to-endpoint connectivity, not VPC-to-VPC. A partner cannot even see the internal network topology. |
| Compromised Lambda → Privilege Escalation | IMPOSSIBLE | No Lambda role has ec2:RunInstances, iam:*, or sts:AssumeRole for other roles. A compromised Lambda is trapped in its own permission boundary with no escalation path. |
| Compromised Container → Other AWS Resources | VERY HARD | The container can reach Aurora/ElastiCache (it already has credentials in memory — that is by design). But it cannot modify infrastructure, launch instances, change IAM, or access anything outside its operational scope. The blast radius is contained to data read access. |
| Achieving Container RCE in the First Place | VERY HARD | The application is a compiled Go binary. No dynamic code execution, no eval, no shell commands from user input, no deserialization of untrusted data, no template injection. The attack surface for RCE in a statically-typed, compiled language with no reflection-based routing is extremely small. |
| Stolen AWS Credentials → Full Access | HARD (but possible) | Requires compromising the operator's physical machine (macOS with FileVault, no remote access enabled). This is the only path to full account control. If achieved, the attacker has everything — but this is true of any single-operator system. |
The TBI operates on a zero-trust internal model — every service role assumes the others could be compromised and grants only the minimum permissions needed to function. The architecture has no privilege escalation paths, no lateral movement between services, and no network routes from external connections to the data layer.
The single point of trust is the operator's local machine. This is a deliberate design choice for a single-operator infrastructure — it eliminates the complexity of multi-user access control, key management services, and approval workflows that would add cost and friction without proportional security benefit at this scale.
When Partners and Associates connect via PrivateLink, they will have zero additional access beyond what a public internet user has — the same API, the same rate limits, the same authentication. The only difference is network reliability (private AWS backbone vs public internet). This is by design: we receive freely, we give freely — but we give through a controlled, monitored, and defended interface.
My confidence level: HIGH. This infrastructure would require a sophisticated, multi-step attack targeting the operator's physical machine to compromise. All other vectors are either impossible (no network path) or contained (no escalation path). For a single-operator system serving a mission-driven API, this is an appropriate and well-executed security posture.