# Get instance ID
INSTANCE_ID=$(aws cloudformation describe-stacks \
--stack-name openclaw-bedrock \
--query 'Stacks[0].Outputs[?OutputKey==`InstanceId`].OutputValue' \
--output text \
--region us-west-2)
# Connect via SSM (no SSH keys needed)
aws ssm start-session --target $INSTANCE_ID --region us-west-2
# Switch to ubuntu user
sudo su - ubuntu# Start port forwarding (keep terminal open)
aws ssm start-session \
--target $INSTANCE_ID \
--region us-west-2 \
--document-name AWS-StartPortForwardingSession \
--parameters '{"portNumber":["18789"],"localPortNumber":["18789"]}'
# Access Web UI at: http://localhost:18789/?token=<your-token>This deployment follows AWS security best practices and provides multiple layers of protection.
The CloudFormation template implements defense-in-depth with the following layers:
When EnablePublicAccess=false (default):
- EC2 in public subnet (no public IP assigned)
- Internet Gateway for direct outbound internet access (TLS encrypted to AWS endpoints)
- SSM Session Manager access only (no SSH required)
- VPC endpoints (optional, keeps AWS API traffic on private network)
- IMDSv2 enforcement (prevents SSRF attacks)
- EBS encryption at rest (AWS managed keys)
- S3 encryption at rest (AES256)
- IAM role-based authentication (no API keys)
- Docker sandbox for exec isolation
When EnablePublicAccess=true, adds:
- AWS Shield Standard (automatic DDoS protection - Layer 3/4)
- AWS WAF (only in us-east-1) - SQL injection, XSS, rate limiting, bad inputs
- CloudFront managed prefix list (only CloudFront IPs can reach ALB)
- Custom header validation (CloudFront → ALB, secret AWS::StackId)
- Direct ALB access blocked (returns 403 without header)
- HTTPS enforcement (CloudFront viewer side)
- Geographic restrictions (optional, via
AllowedCountriesparameter) ⚠️ HTTP-only origin (CloudFront → ALB uses unencrypted HTTP)- EC2 in public subnet (for ALB target registration, no public IP assigned)
No API Keys: The EC2 instance uses an IAM role to authenticate with Bedrock.
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream",
"bedrock:ListFoundationModels"
],
"Resource": "*"
}Benefits:
- ✅ Automatic credential rotation
- ✅ No secrets in code or config files
- ✅ Centralized access control
- ✅ CloudTrail audit logs
No SSH Keys Needed: Access instances through AWS Systems Manager.
Benefits:
- ✅ No public SSH port (22) required
- ✅ Automatic session logging
- ✅ CloudTrail audit trail
- ✅ Session timeout controls
- ✅ No key management
Enable SSM-only access:
AllowedSSHCIDR: 127.0.0.1/32 # Disables SSHPrivate Network: Bedrock API calls stay within AWS network.
Benefits:
- ✅ Traffic doesn't traverse internet
- ✅ Lower latency
- ✅ Compliance-friendly (HIPAA, SOC2)
- ✅ Reduced attack surface
Cost: ~$88/month for 6 interface endpoints across 2 AZs (S3 Gateway endpoint free)
Isolated Execution: Non-main sessions run in Docker containers.
{
"sandbox": {
"mode": "non-main",
"allowlist": ["bash", "read", "write", "edit"],
"denylist": ["browser", "canvas", "nodes", "gateway"]
}
}Benefits:
- ✅ Limits blast radius
- ✅ Protects host system
- ✅ Safe for group chats
When running agents for multiple users, compute isolation prevents one agent from observing or interfering with another. AgentCore and ECS Fargate provide hardware-level isolation via Firecracker microVMs (same technology as AWS Lambda). EKS deployments can enable Kata Containers for equivalent isolation.
For detailed runtime comparison, see docs/DEPLOYMENT_EKS.md.
When EnablePublicAccess=true, the deployment exposes OpenClaw via CloudFront + ALB:
Security Layers:
- ✅ CloudFront HTTPS enforcement (viewer → CloudFront encrypted)
- ✅ AWS WAF protection (SQL injection, XSS, rate limiting)
- ✅ Custom header validation (CloudFront → ALB, secret AWS::StackId)
- ✅ CloudFront managed prefix list (only CloudFront IPs can reach ALB)
- ✅ EC2 in public subnet (no public IP assigned, no direct internet inbound access)
⚠️ HTTP-only origin (CloudFront → ALB uses unencrypted HTTP)
Known Limitation: The CloudFront → ALB connection uses HTTP (port 80), not HTTPS. This means traffic between CloudFront edge locations and the ALB traverses the AWS network unencrypted.
Risk Assessment:
- Traffic mostly stays on AWS backbone (private network between CloudFront PoPs and ALB)
- Custom header secret prevents direct ALB access (bypassing CloudFront)
- EC2 instance is in public subnet but has no public IP assigned (no direct internet inbound access)
- This is an accepted tradeoff for deployment simplicity (no domain/ACM certificate required)
Mitigation (if end-to-end encryption is required):
- Obtain a domain name (e.g., via Route 53)
- Create ACM certificate for the domain
- Add HTTPS listener on ALB (port 443)
- Update CloudFront origin to use HTTPS protocol policy
- Validate certificate on ALB
Traffic Flow:
User (HTTPS encrypted)
→ CloudFront Edge Location (TLS termination)
→ [AWS Backbone - HTTP plaintext]
→ ALB in VPC (port 80)
→ EC2 in public subnet (port 18789, no public IP)
For personal AI assistant deployments, the HTTP-only origin is acceptable. For production/enterprise use cases requiring full end-to-end encryption, implement the HTTPS ALB listener mitigation above.
This template implements the following AWS security best practices:
- ✅ Instance role grants only required Bedrock actions (
InvokeModel,InvokeModelWithResponseStream,ListFoundationModels) - ✅ SSM parameter access scoped to
/openclaw/${StackName}/*only - ✅ S3 bucket access scoped to specific bucket ARN only
- ✅ CloudWatch logs scoped to
/openclaw/*log groups only - ✅ No wildcard
Resource: "*"except where AWS service requires it (e.g.,ec2:DescribeTags)
- ✅ EBS volumes: Encrypted with AWS managed keys (
Encrypted: true) - ✅ S3 bucket: Server-side encryption with AES256 (
SSEAlgorithm: AES256) - ✅ SSM parameters: Gateway token stored as
SecureStringtype (encrypted with KMS) - ✅ CloudWatch Logs: Automatically encrypted by AWS
- ✅ Bedrock API: HTTPS only (enforced by AWS SDK)
- ✅ SSM Session Manager: TLS encrypted session tunnel
- ✅ VPC endpoints: HTTPS only (TLS 1.2+)
- ✅ CloudFront viewer: HTTPS enforced (
redirect-to-httpspolicy) ⚠️ CloudFront origin: HTTP only (see section 6 above for details)
- ✅ Public subnet without public IP: EC2 instance has no public IP (
AssociatePublicIpAddress: false) - ✅ Security groups: Deny-by-default, allow only required ports (ALB ingress when public access enabled)
- ✅ Network ACLs: Stateless firewall on VPC endpoint subnets (conditional, HTTPS + ephemeral only)
- ✅ Internet Gateway: Direct outbound access to internet (TLS encrypted AWS endpoints)
- ✅ VPC endpoints (optional): Keep AWS API traffic on private network (6 interface + 1 gateway)
- ✅ IMDSv2 enforced:
HttpTokens: requiredprevents SSRF attacks - ✅ Hop limit:
HttpPutResponseHopLimit: 2allows Docker containers to access metadata - ✅ Token-based authentication: Prevents IP spoofing and replay attacks
- ✅ Block public access: All four settings enabled (
BlockPublicAcls,BlockPublicPolicy,IgnorePublicAcls,RestrictPublicBuckets) - ✅ Versioning enabled: Protects against accidental deletion/overwrite
- ✅ Lifecycle policy: Auto-delete old versions after 30 days (cost optimization)
- ✅ Encryption: AES256 server-side encryption
- ✅ Deletion policy: Configurable via
EnableDataProtectionparameter
- ✅ Gateway token: Generated with
openssl rand -hex 24(192 bits entropy) - ✅ Token storage: SSM Parameter Store SecureString (KMS encrypted)
- ✅ Custom header: Uses AWS::StackId (UUID, unpredictable)
- ✅ No hardcoded secrets: All secrets generated at stack creation time
- ✅ CloudTrail: Bedrock API calls automatically logged (account-level)
- ✅ CloudWatch Logs: Setup logs, health check logs shipped to CloudWatch
- ✅ CloudWatch Alarms: Auto-recovery on system status check failure, reboot on instance check failure
- ✅ Health checks: Cron job checks gateway port + HTTP response every 5 minutes
- ✅ AWS Shield Standard: Automatic DDoS protection (Layer 3/4)
- ✅ AWS WAF (us-east-1 only): SQL injection, XSS, rate limiting (1000 req/5min/IP), bad inputs
- ✅ Custom header validation: CloudFront sends
X-Custom-Header: ${AWS::StackId}, ALB validates - ✅ CloudFront managed prefix list: ALB security group only allows CloudFront IP ranges
- ✅ Default deny: ALB listener returns 403 for requests without custom header
- ✅ Geographic restrictions (optional):
AllowedCountriesparameter restricts CloudFront access by country
- ✅ Latest Ubuntu 24.04 LTS: Resolved dynamically via SSM Parameter Store
- ✅ Unattended security upgrades: Automatically configured during setup
- ✅ Package updates:
apt-get upgrade -yduring initial setup - ✅ OpenClaw updates: Version pinned via
OpenClawVersionparameter (controlled upgrades)
- ✅ VPC endpoints disabled by default: Saves cost, users opt-in for production
- ✅ Public access disabled by default: SSM Session Manager only, users opt-in for internet exposure
- ✅ WAF enabled by default (when in us-east-1): Protection-first approach
- ✅ Data protection disabled by default: Prevents orphaned resources during testing
- ✅ Monitoring enabled by default: CloudWatch alarms + logs shipped automatically
- ✅ All resources tagged:
Nametags for easy identification - ✅ Stack name prefix: All resources include
${AWS::StackName}for multi-stack support - ✅ Cost allocation: Tags enable cost tracking per stack
- Enable VPC endpoints for production
- Set
AllowedSSHCIDRto your IP or disable SSH - Enable Docker sandbox
- Use latest AMI
- Enable CloudTrail in your account
- Rotate gateway token regularly
- Review CloudTrail logs weekly
- Monitor Bedrock usage
- Set up cost alerts
- Enable CloudWatch alarms
- Update Clawdbot monthly
- Review IAM policies quarterly
- Audit session logs
- Test disaster recovery
- Review security group rules
All Bedrock API calls are logged:
# View recent Bedrock calls
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=InvokeModel \
--max-items 50 \
--region us-west-2All SSM sessions are logged:
# View session logs
aws logs tail /aws/ssm/session-logs --follow --region us-west-2# View Bedrock costs
aws ce get-cost-and-usage \
--time-period Start=2026-01-01,End=2026-01-31 \
--granularity DAILY \
--metrics BlendedCost \
--filter '{"Dimensions":{"Key":"SERVICE","Values":["Amazon Bedrock"]}}'Amazon Bedrock supports:
- SOC 1, 2, 3
- ISO 27001, 27017, 27018, 27701
- PCI DSS
- HIPAA eligible
- FedRAMP Moderate (in supported regions)
# 1. Isolate instance
aws ec2 modify-instance-attribute \
--instance-id $INSTANCE_ID \
--groups sg-isolated
# 2. Create forensic snapshot
aws ec2 create-snapshot \
--volume-id $VOLUME_ID \
--description "Forensic snapshot"
# 3. Terminate instance
aws ec2 terminate-instances --instance-ids $INSTANCE_ID
# 4. Review CloudTrail logs
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=ResourceName,AttributeValue=$INSTANCE_ID# 1. Connect to instance
aws ssm start-session --target $INSTANCE_ID
# 2. Regenerate token
sudo su - ubuntu
NEW_TOKEN=$(openssl rand -hex 24)
# 3. Update config
python3 << EOF
import json
with open('/home/ubuntu/.clawdbot/clawdbot.json') as f:
config = json.load(f)
config['gateway']['auth']['token'] = '$NEW_TOKEN'
with open('/home/ubuntu/.clawdbot/clawdbot.json', 'w') as f:
json.dump(config, f, indent=2)
EOF
# 4. Restart service
systemctl --user restart clawdbot-gateway- Use
t4g.smallinstance (Graviton, cost-effective) - Use Nova 2 Lite model (cheapest)
- Disable VPC endpoints (save $88/month, traffic via Internet Gateway to public AWS endpoints)
- Use SSM Session Manager only (no SSH needed)
- Enable sandbox mode
- Use
t4g.mediumor larger (Graviton recommended) - Use Nova Pro or Claude models (better performance)
- Enable VPC endpoints (optional, adds $88/mo for private network routing)
- Use SSM Session Manager only (no SSH access needed)
- Enable sandbox mode
- Set up CloudWatch alarms
- Enable AWS Config rules
- Regular security audits
- Must use Graviton or x86 instances in compliant regions
- Must enable VPC endpoints (keeps all AWS API traffic on private network)
- Use SSM Session Manager only (no SSH access)
- Enable CloudTrail
- Enable VPC Flow Logs
- Encrypt EBS volumes (enabled by default)
- Use AWS Secrets Manager for tokens
- Regular penetration testing
- Document security controls