GovCloud Aerospace Platform — Multi-Account AWS Infrastructure
Designed and built a multi-account AWS GovCloud (US) foundation for a NASA mission support program. STIG-hardened, NIST 800-53 compliant, zero public internet exposure.
Project Overview
An aerospace defense contractor supporting a NASA science mission required a secure cloud infrastructure capable of meeting federal compliance requirements. The program involved sensitive ground system software and flight data processing — requiring GovCloud isolation, STIG hardening, and NIST 800-53 controls from day one.
Key Stats:
- 🏛️ AWS GovCloud (US) — isolated partition with FedRAMP High baseline
- 🔒 STIG-hardened AMIs across all EC2 compute (DISA STIG RHEL 8)
- 🌐 Zero public internet exposure — all traffic through Network Firewall + NAT
- 📋 NIST 800-53 control mapping documented for Authority to Operate (ATO)
- 🚀 Containerized workloads migrated from on-premises to ECS on EC2
The Challenge
Federal Compliance from the Ground Up
The program had no existing cloud infrastructure. Ground system software was running on on-premises servers with no automation, no audit trail, and no path to federal ATO. The team needed:
- GovCloud isolation: Data sovereignty requirements mandated the AWS GovCloud (US) partition
- STIG compliance: All EC2 instances required DISA STIG hardening (RHEL 8)
- NIST 800-53 controls: ~300 controls to document and implement for ATO package
- Air-gap-style networking: No direct internet access to workloads; all egress through centralized inspection
- Multi-account structure: Environment separation (dev/test/prod) with billing isolation
Technical Constraints
- GovCloud limitations: Fewer managed services available vs. commercial; more hands-on configuration required
- Legacy software: Ground system applications built for on-premises (no Docker, no 12-factor)
- Compliance timeline: ATO submission had a hard program deadline
- Small team: Two engineers (infrastructure + app), no dedicated security team
The Solution
Account Structure
AWS Organizations (GovCloud)
├── Management Account (billing, org policies, IAM Identity Center)
├── Security Account (CloudTrail aggregation, GuardDuty, Config)
├── Infrastructure Account (Transit Gateway, Network Firewall, shared services)
├── Workloads-Dev (development and integration testing)
├── Workloads-Test (formal testing / IV&V)
└── Workloads-Prod (production flight data processing)
Networking: Defense-in-Depth
All traffic flowed through a centralized inspection layer before reaching workloads:
Internet → (blocked)
Internal → Transit Gateway → Network Firewall → Workload VPCs
Egress → Network Firewall → NAT Gateway → (whitelist-only domains)
- Transit Gateway: Hub-and-spoke connecting all account VPCs
- AWS Network Firewall: Stateful inspection, domain whitelist for egress, deny-by-default
- No public subnets in workload accounts: ALBs and NAT in infrastructure account only
- VPC Flow Logs: Centralized to Security account S3 (90-day retention)
STIG Hardening (DISA RHEL 8)
Implemented STIG hardening via Ansible, applied at AMI build time and enforced via AWS Config rules:
Key controls applied:
- Kernel parameters (FIPS 140-2, SELinux enforcing, core dump disabled)
- SSH hardening (Protocol 2, no root login, idle timeout, approved ciphers/MACs)
- Audit daemon (auditd) with tamper-evident logs
- Unnecessary services disabled (bluetooth, avahi, cups, etc.)
- Password complexity and aging policies
- File permission baselines (
/tmp,/var/tmp, SUID/SGID audit)
Ansible roles for idempotent application across all instances, with a custom stig-remediation role that:
- Ran SCAP scans (OpenSCAP) to baseline the instance
- Applied remediations in order of STIG severity (CAT I → II → III)
- Re-ran scans to confirm findings reduced to acceptable level
- Generated report artifacts for ATO documentation
IAM and Identity
- IAM Identity Center (SSO) with existing Active Directory integration — no shared credentials
- Least-privilege role design: 6 standard roles per account (Admin, Developer, SecurityAuditor, ReadOnly, Automation, BreakGlass)
- Break-glass accounts: Emergency IAM users in Management account, credentials in offline safe
- No long-lived access keys: All automation used EC2 instance profiles or OIDC
Containerization: Lift-and-Shift to ECS
Legacy ground system applications were containerized using a pragmatic approach:
- Dockerized each application with minimal changes (no 12-factor refactor — scope was infrastructure, not app rewrite)
- ECS on EC2 (not Fargate — GovCloud Fargate was more limited at time of engagement)
- Task roles for fine-grained S3 and SQS access per service
- Parameter Store for runtime secrets injection (no plaintext in task definitions)
- CloudWatch for container logs centralized to Security account
Compliance Documentation
Maintained a living ATO package:
- System Security Plan (SSP): All 300 NIST 800-53 controls documented with implementation evidence
- Continuous Monitoring: AWS Config rules mapped to controls, findings auto-reported
- Plan of Action & Milestones (POA&M): Tracked open findings with remediation timelines
- Evidence collection: AWS Config snapshots, CloudTrail exports, SCAP scan reports
Results & Impact
Compliance Outcomes
- 📋 ATO package delivered on program schedule — all CAT I STIG findings resolved
- 🔒 NIST 800-53 control coverage: 280+ controls implemented and documented
- 📊 SCAP scan scores: Achieved STIG compliance scores >90% across all instances
- 🛡️ Zero public exposure: All workloads network-isolated; no public IPs in workload accounts
Operational Improvements
- ⏱️ Environment provisioning: From weeks of manual setup to ~4 hours via Terraform
- 🚀 Application deployment: Manual file copy → ECS task definition update + service redeploy
- 📁 Audit trail: Complete CloudTrail + VPC Flow Logs for all activity (required for ATO)
- 🔁 Repeatability: Dev/Test/Prod environments built from identical Terraform modules
Engineering Benefits
- Identical environments: Dev/Test/Prod share same Terraform modules — “works in test” actually works in prod
- Documented runbooks: All operational procedures written to support A&A process
- Infrastructure as Code: 100% Terraform — no console-driven configuration, no drift
Key Takeaways
What Worked
- Ansible for STIG at AMI build time: Baking compliance in beats trying to remediate running instances
- Network Firewall domain whitelist: Simpler to maintain than IP-based egress lists; blocks supply chain attacks
- NIST control mapping from day one: Retrofitting compliance documentation is painful; building it in parallel with implementation saves weeks
- ECS lift-and-shift: Containerizing without refactoring was the right scope — got workloads into cloud quickly without blocking on app changes
- Break-glass accounts offline: Auditors appreciated the documented emergency access procedure
What I’d Do Differently
- Fargate over EC2 for ECS: Eliminates EC2 patching burden; worth revisiting as GovCloud Fargate support has improved
- AWS Config conformance packs earlier: Custom Config rules per control are tedious; AWS Security Hub + conformance packs now cover much of NIST 800-53 automatically
- More Terraform modules, less copy-paste: Per-account Terraform directories shared too much boilerplate; Terragrunt would have helped here
Lessons Learned
- GovCloud ≠ just another region: Service gaps are real. Validate every service you plan to use against the GovCloud feature list before committing to it in your design
- STIG compliance is mostly engineering, not policy: The controls sound bureaucratic but most map to sensible security practices — approaching it as engineering work, not compliance theater, keeps the team motivated
- Document as you build: The ATO package is essentially documentation of what you built. Writing it after the fact from memory is far harder than capturing evidence as you go
Technical Deep Dive
Network Firewall Rule Groups
Domain-based egress filtering prevented data exfiltration while allowing necessary software updates:
# Stateful domain allow list
resource "aws_networkfirewall_rule_group" "egress_allow" {
name = "egress-domain-allowlist"
type = "STATEFUL"
capacity = 100
rule_group {
rules_source {
rules_source_list {
generated_rules_type = "ALLOWLIST"
target_types = ["HTTP_HOST", "TLS_SNI"]
targets = [
".amazonaws.com",
".amazonlinux.com",
"rhui.redhat.com",
]
}
}
}
}
Effect: Instances can reach AWS APIs and RHUI for patches. All other egress dropped.
Terraform Module Structure
Separate state per account, shared modules:
infrastructure/
├── modules/
│ ├── vpc/ # VPC, subnets, route tables
│ ├── ecs-cluster/ # ECS cluster, capacity providers
│ ├── ecs-service/ # Task definition, service, task role
│ └── stig-ami/ # Packer + Ansible AMI pipeline
├── accounts/
│ ├── management/ # Org, IAM Identity Center
│ ├── security/ # CloudTrail, GuardDuty, Config
│ ├── infrastructure/ # TGW, NFW, shared networking
│ ├── workloads-dev/
│ ├── workloads-test/
│ └── workloads-prod/
State in S3 (GovCloud), locked via DynamoDB. No Terraform Cloud — all state must remain in GovCloud partition.
Building federal cloud infrastructure? Schedule a free consultation to discuss your compliance and architecture challenges.
Technologies: AWS GovCloud (US) | AWS Organizations | Transit Gateway | Network Firewall | ECS | EC2 | Terraform | Ansible | STIG | NIST 800-53 | OpenSCAP | CloudTrail | GuardDuty
Working on a similar challenge?
Multi-account AWS architecture, container migration, Terraform adoption — this is the work I do as a fractional engagement.