Skip to content

TradAI Final Architecture - Security

Version: 9.2.1 | Date: 2025-12-09


Security Architecture Overview

┌─────────────────────────────────────────────────────────────────────────────────┐
│                              SECURITY LAYERS                                      │
│                                                                                   │
│  Layer 1: Edge Security                                                          │
│  ┌─────────────────────────────────────────────────────────────────────────┐   │
│  │  AWS WAF │ Rate Limiting │ Geo-Blocking │ Managed Rules                 │   │
│  └─────────────────────────────────────────────────────────────────────────┘   │
│                                       │                                          │
│  Layer 2: Authentication & Authorization                                         │
│  ┌─────────────────────────────────────────────────────────────────────────┐   │
│  │  AWS Cognito │ JWT Tokens │ API Gateway Authorizer │ MFA Required       │   │
│  └─────────────────────────────────────────────────────────────────────────┘   │
│                                       │                                          │
│  Layer 3: Network Security                                                       │
│  ┌─────────────────────────────────────────────────────────────────────────┐   │
│  │  VPC │ Security Groups │ NACLs │ Private Subnets │ VPC Endpoints        │   │
│  └─────────────────────────────────────────────────────────────────────────┘   │
│                                       │                                          │
│  Layer 4: Data Security                                                          │
│  ┌─────────────────────────────────────────────────────────────────────────┐   │
│  │  S3 Encryption (SSE-S3) │ RDS Encryption │ Secrets Manager │ TLS        │   │
│  └─────────────────────────────────────────────────────────────────────────┘   │
│                                       │                                          │
│  Layer 5: Audit & Compliance                                                     │
│  ┌─────────────────────────────────────────────────────────────────────────┐   │
│  │  CloudTrail │ VPC Flow Logs │ CloudWatch Logs │ Access Logging          │   │
│  └─────────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────────┘

1. Authentication & Authorization

AWS Cognito User Pool

User Pool Configuration:
  Name: tradai-users

  Password Policy:
    MinimumLength: 12
    RequireLowercase: true
    RequireUppercase: true
    RequireNumbers: true
    RequireSymbols: true
    TemporaryPasswordValidityDays: 7

  MFA Configuration:
    MFAConfiguration: "ON"  # REQUIRED, not optional
    EnabledMFAs:
      - SOFTWARE_TOKEN_MFA
      - SMS_MFA

  Account Recovery:
    RecoveryMechanisms:
      - Priority: 1
        Name: verified_email
      - Priority: 2
        Name: verified_phone_number

  User Attributes:
    - Name: email
      Required: true
      Mutable: false
    - Name: custom:organization
      Mutable: true
    - Name: custom:role
      Mutable: true

  Advanced Security:
    AdvancedSecurityMode: ENFORCED
    CompromisedCredentialsRisk: BLOCK
    AccountTakeoverRisk:
      Low: BLOCK
      Medium: BLOCK
      High: BLOCK

App Client Configuration

App Client:
  Name: tradai-api-client
  GenerateSecret: false

  OAuth Flows:
    AllowedOAuthFlows:
      - code
      - implicit
    AllowedOAuthScopes:
      - openid
      - email
      - profile
      - aws.cognito.signin.user.admin

  Token Validity:
    AccessTokenValidity: 60  # 1 hour
    IdTokenValidity: 60
    RefreshTokenValidity: 30  # 30 days

  Security:
    PreventUserExistenceErrors: ENABLED
    EnableTokenRevocation: true

API Gateway JWT Authorizer

Authorizer:
  Name: tradai-jwt-authorizer
  Type: JWT

  IdentitySources:
    - $request.header.Authorization

  JWTConfiguration:
    Issuer: https://cognito-idp.us-east-1.amazonaws.com/{user_pool_id}
    Audience:
      - {app_client_id}

  Routes Requiring Authorization:
    - ANY /strategies/*
    - ANY /backtest/*
    - ANY /deploy/*
    - ANY /data/*
    - GET /mlflow/*

  Routes Without Authorization:
    - GET /health
    - OPTIONS /*  # CORS preflight

2. IAM Policies

ECS Task Execution Role

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ECRPull",
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken",
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage"
      ],
      "Resource": "*"
    },
    {
      "Sid": "CloudWatchLogs",
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:us-east-1:*:log-group:/tradai/*"
    },
    {
      "Sid": "SecretsManager",
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue"
      ],
      "Resource": "arn:aws:secretsmanager:us-east-1:*:secret:tradai/*"
    }
  ]
}

ECS Task Role (Application Permissions)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "S3Access",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::tradai-configs",
        "arn:aws:s3:::tradai-configs/*",
        "arn:aws:s3:::tradai-results",
        "arn:aws:s3:::tradai-results/*",
        "arn:aws:s3:::tradai-arcticdb",
        "arn:aws:s3:::tradai-arcticdb/*"
      ]
    },
    {
      "Sid": "DynamoDBAccess",
      "Effect": "Allow",
      "Action": [
        "dynamodb:GetItem",
        "dynamodb:PutItem",
        "dynamodb:UpdateItem",
        "dynamodb:DeleteItem",
        "dynamodb:Query"
      ],
      "Resource": [
        "arn:aws:dynamodb:us-east-1:*:table/tradai-workflow-state",
        "arn:aws:dynamodb:us-east-1:*:table/tradai-workflow-state/index/*"
      ]
    },
    {
      "Sid": "SQSSendMessage",
      "Effect": "Allow",
      "Action": [
        "sqs:SendMessage"
      ],
      "Resource": [
        "arn:aws:sqs:us-east-1:*:tradai-backtest.fifo",
        "arn:aws:sqs:us-east-1:*:tradai-deploy.fifo"
      ]
    },
    {
      "Sid": "StepFunctionsDescribe",
      "Effect": "Allow",
      "Action": [
        "states:DescribeExecution",
        "states:GetExecutionHistory"
      ],
      "Resource": "arn:aws:states:us-east-1:*:execution:tradai-*:*"
    }
  ]
}

Lambda Execution Role

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CloudWatchLogs",
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:us-east-1:*:log-group:/aws/lambda/tradai-*"
    },
    {
      "Sid": "VPCAccess",
      "Effect": "Allow",
      "Action": [
        "ec2:CreateNetworkInterface",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DeleteNetworkInterface"
      ],
      "Resource": "*"
    },
    {
      "Sid": "StepFunctionsStart",
      "Effect": "Allow",
      "Action": [
        "states:StartExecution"
      ],
      "Resource": "arn:aws:states:us-east-1:*:stateMachine:tradai-*"
    },
    {
      "Sid": "DynamoDBAccess",
      "Effect": "Allow",
      "Action": [
        "dynamodb:GetItem",
        "dynamodb:PutItem",
        "dynamodb:UpdateItem"
      ],
      "Resource": "arn:aws:dynamodb:us-east-1:*:table/tradai-workflow-state"
    },
    {
      "Sid": "S3Access",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": [
        "arn:aws:s3:::tradai-configs/temp/*",
        "arn:aws:s3:::tradai-results/*"
      ]
    }
  ]
}

Step Functions Execution Role

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "LambdaInvoke",
      "Effect": "Allow",
      "Action": [
        "lambda:InvokeFunction"
      ],
      "Resource": "arn:aws:lambda:us-east-1:*:function:tradai-*"
    },
    {
      "Sid": "ECSRunTask",
      "Effect": "Allow",
      "Action": [
        "ecs:RunTask",
        "ecs:StopTask",
        "ecs:DescribeTasks"
      ],
      "Resource": "*",
      "Condition": {
        "ArnEquals": {
          "ecs:cluster": "arn:aws:ecs:us-east-1:*:cluster/tradai-cluster"
        }
      }
    },
    {
      "Sid": "ECSTaskPassRole",
      "Effect": "Allow",
      "Action": [
        "iam:PassRole"
      ],
      "Resource": [
        "arn:aws:iam::*:role/tradai-ecs-task-role",
        "arn:aws:iam::*:role/tradai-ecs-execution-role"
      ]
    },
    {
      "Sid": "DynamoDBUpdate",
      "Effect": "Allow",
      "Action": [
        "dynamodb:UpdateItem",
        "dynamodb:PutItem"
      ],
      "Resource": "arn:aws:dynamodb:us-east-1:*:table/tradai-workflow-state"
    },
    {
      "Sid": "SQSSendMessage",
      "Effect": "Allow",
      "Action": [
        "sqs:SendMessage"
      ],
      "Resource": "arn:aws:sqs:us-east-1:*:tradai-*.fifo"
    },
    {
      "Sid": "CloudWatchEvents",
      "Effect": "Allow",
      "Action": [
        "events:PutTargets",
        "events:PutRule",
        "events:DescribeRule"
      ],
      "Resource": "arn:aws:events:us-east-1:*:rule/StepFunctions*"
    }
  ]
}

3. Data Encryption

S3 Bucket Encryption

Bucket: tradai-configs
Encryption:
  ServerSideEncryptionConfiguration:
    - ServerSideEncryptionByDefault:
        SSEAlgorithm: AES256  # SSE-S3
      BucketKeyEnabled: true

Versioning: Enabled
PublicAccessBlock:
  BlockPublicAcls: true
  BlockPublicPolicy: true
  IgnorePublicAcls: true
  RestrictPublicBuckets: true

Lifecycle:
  - ID: cleanup-temp-files
    Status: Enabled
    Prefix: temp/
    Expiration:
      Days: 7

RDS Encryption

RDS Instance: tradai-mlflow-db
Encryption:
  StorageEncrypted: true
  KMSKeyId: alias/aws/rds  # AWS managed key

SSL Configuration:
  RequireSSL: true
  SSLMode: verify-full

Parameter Group:
  rds.force_ssl: 1

Secrets Manager

Secrets:
  - Name: tradai/mlflow/credentials
    SecretString:
      username: admin
      password: <generated>
    RotationEnabled: false  # Enable in Phase 2
    Tags:
      Application: tradai
      Environment: production

  - Name: tradai/binance/api
    SecretString:
      api_key: <from-env>
      api_secret: <from-env>
    Tags:
      Application: tradai

  - Name: tradai/db/credentials
    SecretString:
      host: <rds-endpoint>
      port: 5432
      database: mlflow
      username: mlflow_user
      password: <generated>
    Tags:
      Application: tradai

4. Network Security

TLS/SSL Configuration

ALB:
  Listener:
    Protocol: HTTPS
    Port: 443
    Certificate: arn:aws:acm:us-east-1:*:certificate/xxxxx
    SSLPolicy: ELBSecurityPolicy-TLS13-1-2-2021-06

  HTTP Redirect:
    Protocol: HTTP
    Port: 80
    Action:
      Type: redirect
      RedirectConfig:
        Protocol: HTTPS
        Port: "443"
        StatusCode: HTTP_301

API Gateway:
  DisableExecuteApiEndpoint: true  # Force custom domain
  DomainName: api.tradai.smartml.me
  Certificate: arn:aws:acm:us-east-1:*:certificate/xxxxx
  SecurityPolicy: TLS_1_2

Security Group Rules Summary

Source Destination Port Purpose
0.0.0.0/0 ALB 443 HTTPS from internet
0.0.0.0/0 ALB 80 HTTP redirect
ALB ECS 8000-8002 Backend services
ALB ECS 5000 MLflow
Lambda ECS 8002 Data Collection
ECS RDS 5432 PostgreSQL
Private NAT 443 Internet via NAT

5. AWS WAF Configuration

WebACL:
  Name: tradai-waf
  Scope: REGIONAL
  DefaultAction: Allow

  Rules:
    - Name: RateLimitRule
      Priority: 1
      Statement:
        RateBasedStatement:
          Limit: 100  # 100 requests per 5 minutes
          AggregateKeyType: IP
      Action: Block
      VisibilityConfig:
        CloudWatchMetricsEnabled: true
        MetricName: RateLimitRule

    - Name: AWSManagedRulesCommonRuleSet
      Priority: 2
      OverrideAction: None
      Statement:
        ManagedRuleGroupStatement:
          VendorName: AWS
          Name: AWSManagedRulesCommonRuleSet
      VisibilityConfig:
        CloudWatchMetricsEnabled: true
        MetricName: CommonRuleSet

    - Name: AWSManagedRulesKnownBadInputsRuleSet
      Priority: 3
      OverrideAction: None
      Statement:
        ManagedRuleGroupStatement:
          VendorName: AWS
          Name: AWSManagedRulesKnownBadInputsRuleSet
      VisibilityConfig:
        CloudWatchMetricsEnabled: true
        MetricName: KnownBadInputs

    - Name: AWSManagedRulesSQLiRuleSet
      Priority: 4
      OverrideAction: None
      Statement:
        ManagedRuleGroupStatement:
          VendorName: AWS
          Name: AWSManagedRulesSQLiRuleSet
      VisibilityConfig:
        CloudWatchMetricsEnabled: true
        MetricName: SQLiRuleSet

  AssociatedResources:
    - arn:aws:elasticloadbalancing:us-east-1:*:loadbalancer/app/tradai-alb/*

6. Audit & Compliance

CloudTrail Configuration

Trail:
  Name: tradai-audit-trail
  S3BucketName: tradai-audit-logs
  S3KeyPrefix: cloudtrail/

  IncludeGlobalServiceEvents: true
  IsMultiRegionTrail: false  # Single region for now
  EnableLogFileValidation: true

  EventSelectors:
    - ReadWriteType: All
      IncludeManagementEvents: true
      DataResources:
        - Type: AWS::S3::Object
          Values:
            - arn:aws:s3:::tradai-configs/
            - arn:aws:s3:::tradai-results/
        - Type: AWS::DynamoDB::Table
          Values:
            - arn:aws:dynamodb:us-east-1:*:table/tradai-*

  InsightSelectors:
    - InsightType: ApiCallRateInsight
    - InsightType: ApiErrorRateInsight

  Tags:
    Application: tradai
    Compliance: audit

CloudWatch Alarms for Security

Alarms:
  - Name: tradai-unauthorized-api-calls
    MetricName: UnauthorizedAttemptCount
    Namespace: AWS/ApiGateway
    Statistic: Sum
    Period: 300
    EvaluationPeriods: 1
    Threshold: 10
    ComparisonOperator: GreaterThanThreshold
    AlarmActions:
      - arn:aws:sns:us-east-1:*:tradai-security-alerts

  - Name: tradai-failed-logins
    MetricName: FailedSignInAttempts
    Namespace: AWS/Cognito
    Statistic: Sum
    Period: 300
    EvaluationPeriods: 1
    Threshold: 5
    ComparisonOperator: GreaterThanThreshold
    AlarmActions:
      - arn:aws:sns:us-east-1:*:tradai-security-alerts

  - Name: tradai-waf-blocked-requests
    MetricName: BlockedRequests
    Namespace: AWS/WAFV2
    Statistic: Sum
    Period: 300
    EvaluationPeriods: 1
    Threshold: 100
    ComparisonOperator: GreaterThanThreshold
    AlarmActions:
      - arn:aws:sns:us-east-1:*:tradai-security-alerts

7. Application Security

Input Validation (Pydantic)

# services/api_gateway/api/schemas/backtest.py
from pydantic import BaseModel, Field, validator
import re

class BacktestRequest(BaseModel):
    """Backtest request with strict validation."""

    strategy_name: str = Field(
        ...,
        min_length=3,
        max_length=50,
        regex=r'^[a-zA-Z][a-zA-Z0-9_]*$'
    )

    strategy_version: str = Field(
        ...,
        regex=r'^\d+\.\d+\.\d+$'
    )

    experiment_name: str = Field(
        ...,
        min_length=3,
        max_length=100
    )

    timeframe: str = Field(
        ...,
        regex=r'^(1m|5m|15m|1h|4h|1d)$'
    )

    symbols: list[str] = Field(
        ...,
        min_items=1,
        max_items=10
    )

    @validator('symbols', each_item=True)
    def validate_symbol(cls, v):
        if not re.match(r'^[A-Z]+/[A-Z]+:[A-Z]+$', v):
            raise ValueError(f'Invalid symbol format: {v}')
        return v

    @validator('strategy_name')
    def prevent_injection(cls, v):
        dangerous_patterns = ['..', '/', '\\', ';', '|', '&']
        for pattern in dangerous_patterns:
            if pattern in v:
                raise ValueError(f'Invalid characters in strategy_name')
        return v

    class Config:
        extra = 'forbid'  # Reject unknown fields

SQL Injection Prevention

# services/mlflow/database.py
from sqlalchemy import text
from sqlalchemy.orm import Session

def get_experiments_safe(
    session: Session,
    user_id: str
) -> list:
    """Safe parameterized query."""

    # GOOD: Parameterized query
    query = text("""
        SELECT * FROM experiments
        WHERE user_id = :user_id
        ORDER BY created_at DESC
    """)

    result = session.execute(
        query,
        {"user_id": user_id}
    )

    return result.fetchall()

# BAD: Never do this!
# query = f"SELECT * FROM experiments WHERE user_id = '{user_id}'"

XSS Prevention

# services/api_gateway/api/middleware/security.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from starlette.middleware.base import BaseHTTPMiddleware
import html

app = FastAPI()

# CORS configuration
app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        "https://tradai.smartml.me",
        "https://app.tradai.smartml.me"
    ],
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["Authorization", "Content-Type"],
)

class SecurityHeadersMiddleware(BaseHTTPMiddleware):
    """Add security headers to all responses."""

    async def dispatch(self, request, call_next):
        response = await call_next(request)

        response.headers["X-Content-Type-Options"] = "nosniff"
        response.headers["X-Frame-Options"] = "DENY"
        response.headers["X-XSS-Protection"] = "1; mode=block"
        response.headers["Strict-Transport-Security"] = (
            "max-age=31536000; includeSubDomains"
        )
        response.headers["Content-Security-Policy"] = (
            "default-src 'self'; script-src 'self'"
        )

        return response

app.add_middleware(SecurityHeadersMiddleware)

8. Security Checklist

Pre-Deployment Checklist

  • [x] MFA required for all users (not optional)
  • [x] S3 bucket encryption enabled (SSE-S3)
  • [x] S3 public access blocked
  • [x] RDS encryption at rest enabled
  • [x] RDS SSL required
  • [x] CloudTrail logging enabled
  • [x] VPC Flow Logs enabled
  • [x] ALB access logs enabled
  • [x] HTTP redirect to HTTPS
  • [x] Security groups follow least privilege
  • [x] NACLs configured for defense in depth
  • [x] WAF rules configured
  • [x] Secrets in Secrets Manager (not env vars)
  • [x] IAM roles follow least privilege
  • [x] API Gateway JWT authorization
  • [x] Input validation on all endpoints
  • [x] CORS properly configured
  • [x] Security headers set

Post-Deployment Monitoring

  • [ ] Weekly IAM Access Analyzer review
  • [ ] Monthly security group audit
  • [ ] Quarterly penetration testing
  • [ ] CloudTrail alerts configured
  • [ ] Security Hub enabled
  • [ ] GuardDuty enabled (Phase 2)

9. Security Cost Summary

Component Configuration Monthly Cost
Cognito User Pool (< 50k MAU) $0.00
Secrets Manager 5 secrets $2.00
CloudTrail 1 trail $2.00
WAF Web ACL + rules $5.00
VPC Flow Logs 7 days retention $2.00
TOTAL $11.00

Next Steps

  1. Review 05-SERVICES.md for service implementations
  2. Review 09-PULUMI-CODE.md for security infrastructure code