iT邦幫忙

2025 iThome 鐵人賽

DAY 8
0
Build on AWS

30 天將工作室 SaaS 產品部署起來系列 第 8

Day 8: 30天部署SaaS產品到AWS-安全性強化與監控

  • 分享至 

  • xImage
  •  

前情提要

昨天我們成功將 Kyo-System 容器化並部署到 Amazon ECS 上,應用現在已經在雲端穩定運行。今天我們要為系統加上全方位的安全防護和監控機制,確保我們的 OTP 服務在生產環境中安全可靠。

在雲端環境中,安全性和監控是很重要的:

  • 多層防護:從網路到應用層的全方位安全
  • 即時監控:快速發現和響應安全威脅
  • 合規稽核:滿足企業合規需求
  • 資料保護:確保敏感資料安全傳輸和存儲

安全架構設計

我們的安全架構採用縱深防禦策略:

🌐 Internet
    ↓
🛡️ WAF (Web Application Firewall)
    ↓
⚖️ ALB (Application Load Balancer)
    ↓
🏢 VPC Private Subnets
    ↓
🐳 ECS Tasks
    ↓
🔐 Secrets Manager

實作步驟

1. AWS WAF 網路防護

首先建立 WAF 來防護我們的應用:

// infrastructure/lib/kyo-security-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as wafv2 from 'aws-cdk-lib/aws-wafv2';
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import * as logs from 'aws-cdk-lib/aws-logs';
import { Construct } from 'constructs';

interface SecurityStackProps extends cdk.StackProps {
  loadBalancer: elbv2.ApplicationLoadBalancer;
}

export class KyoSecurityStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: SecurityStackProps) {
    super(scope, id, props);

    // 建立 WAF Log Group
    const wafLogGroup = new logs.LogGroup(this, 'WAFLogGroup', {
      logGroupName: '/aws/wafv2/kyo-waf',
      retention: logs.RetentionDays.ONE_MONTH,
    });

    // 建立 WAF Web ACL
    const webAcl = new wafv2.CfnWebACL(this, 'KyoWebACL', {
      scope: 'REGIONAL',
      defaultAction: { allow: {} },
      description: 'Kyo OTP Service WAF Rules',

      rules: [
        // 1. AWS 管理規則:一般安全防護
        {
          name: 'AWSManagedRulesCommonRuleSet',
          priority: 1,
          overrideAction: { none: {} },
          statement: {
            managedRuleGroupStatement: {
              vendorName: 'AWS',
              name: 'AWSManagedRulesCommonRuleSet',
              excludedRules: [
                // 排除可能誤擋的規則
                { name: 'SizeRestrictions_BODY' },
                { name: 'GenericRFI_BODY' },
              ],
            },
          },
          visibilityConfig: {
            cloudWatchMetricsEnabled: true,
            metricName: 'CommonRuleSetMetric',
            sampledRequestsEnabled: true,
          },
        },

        // 2. 已知惡意輸入防護
        {
          name: 'AWSManagedRulesKnownBadInputsRuleSet',
          priority: 2,
          overrideAction: { none: {} },
          statement: {
            managedRuleGroupStatement: {
              vendorName: 'AWS',
              name: 'AWSManagedRulesKnownBadInputsRuleSet',
            },
          },
          visibilityConfig: {
            cloudWatchMetricsEnabled: true,
            metricName: 'KnownBadInputsMetric',
            sampledRequestsEnabled: true,
          },
        },

        // 3. Linux 特定攻擊防護
        {
          name: 'AWSManagedRulesLinuxRuleSet',
          priority: 3,
          overrideAction: { none: {} },
          statement: {
            managedRuleGroupStatement: {
              vendorName: 'AWS',
              name: 'AWSManagedRulesLinuxRuleSet',
            },
          },
          visibilityConfig: {
            cloudWatchMetricsEnabled: true,
            metricName: 'LinuxRuleSetMetric',
            sampledRequestsEnabled: true,
          },
        },

        // 4. 速率限制:每個 IP 每 5 分鐘最多 1000 個請求
        {
          name: 'RateLimitRule',
          priority: 4,
          action: { block: {} },
          statement: {
            rateBasedStatement: {
              limit: 1000,
              aggregateKeyType: 'IP',
            },
          },
          visibilityConfig: {
            cloudWatchMetricsEnabled: true,
            metricName: 'RateLimitMetric',
            sampledRequestsEnabled: true,
          },
        },

        // 5. 地理位置限制(可選)
        {
          name: 'GeoBlockRule',
          priority: 5,
          action: { block: {} },
          statement: {
            geoMatchStatement: {
              countryCodes: ['CN', 'RU', 'KP'], // 依需求調整
            },
          },
          visibilityConfig: {
            cloudWatchMetricsEnabled: true,
            metricName: 'GeoBlockMetric',
            sampledRequestsEnabled: true,
          },
        },

        // 6. 自訂規則:保護 API 端點
        {
          name: 'ProtectAPIEndpoints',
          priority: 6,
          action: { block: {} },
          statement: {
            andStatement: {
              statements: [
                {
                  byteMatchStatement: {
                    fieldToMatch: { uriPath: {} },
                    positionalConstraint: 'STARTS_WITH',
                    searchString: '/api/',
                    textTransformations: [
                      { priority: 0, type: 'LOWERCASE' },
                    ],
                  },
                },
                {
                  notStatement: {
                    statement: {
                      orStatement: {
                        statements: [
                          // 允許的方法
                          {
                            byteMatchStatement: {
                              fieldToMatch: { method: {} },
                              positionalConstraint: 'EXACTLY',
                              searchString: 'GET',
                              textTransformations: [
                                { priority: 0, type: 'NONE' },
                              ],
                            },
                          },
                          {
                            byteMatchStatement: {
                              fieldToMatch: { method: {} },
                              positionalConstraint: 'EXACTLY',
                              searchString: 'POST',
                              textTransformations: [
                                { priority: 0, type: 'NONE' },
                              ],
                            },
                          },
                        ],
                      },
                    },
                  },
                },
              ],
            },
          },
          visibilityConfig: {
            cloudWatchMetricsEnabled: true,
            metricName: 'APIProtectionMetric',
            sampledRequestsEnabled: true,
          },
        },
      ],

      visibilityConfig: {
        cloudWatchMetricsEnabled: true,
        metricName: 'KyoWebACLMetric',
        sampledRequestsEnabled: true,
      },
    });

    // 將 WAF 關聯到 ALB
    new wafv2.CfnWebACLAssociation(this, 'WebACLAssociation', {
      resourceArn: props.loadBalancer.loadBalancerArn,
      webAclArn: webAcl.attrArn,
    });

    // 輸出 WAF ARN
    new cdk.CfnOutput(this, 'WebACLArn', {
      value: webAcl.attrArn,
      description: 'WAF Web ACL ARN',
    });
  }
}

2. Secrets Manager 整合

將敏感資料遷移到 AWS Secrets Manager:

// infrastructure/lib/kyo-secrets-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';

export class KyoSecretsStack extends cdk.Stack {
  public readonly secret: secretsmanager.Secret;
  public readonly secretAccessRole: iam.Role;

  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // 建立 Secrets Manager
    this.secret = new secretsmanager.Secret(this, 'KyoSecrets', {
      secretName: 'kyo-secrets',
      description: 'Kyo OTP Service secrets',
      generateSecretString: {
        secretStringTemplate: JSON.stringify({
          MITAKE_USERNAME: '',
          MITAKE_PASSWORD: '',
          JWT_SECRET: '',
          REDIS_PASSWORD: '',
        }),
        generateStringKey: 'AUTO_GENERATED_PASSWORD',
        excludeCharacters: '"@/\\',
      },
    });

    // 建立 IAM 角色供 ECS 任務使用
    this.secretAccessRole = new iam.Role(this, 'SecretAccessRole', {
      assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
      description: 'Role for ECS tasks to access secrets',
    });

    // 授權讀取 secrets
    this.secret.grantRead(this.secretAccessRole);

    // 輸出 secret ARN
    new cdk.CfnOutput(this, 'SecretArn', {
      value: this.secret.secretArn,
      description: 'Secrets Manager ARN',
    });
  }
}

3. VPC 安全群組強化

// 更新 infrastructure/lib/kyo-infrastructure-stack.ts
import * as ec2 from 'aws-cdk-lib/aws-ec2';

// 在 VPC 建立後,加強安全群組設定
const albSecurityGroup = new ec2.SecurityGroup(this, 'ALBSecurityGroup', {
  vpc: this.vpc,
  description: 'Security group for Application Load Balancer',
  allowAllOutbound: false,
});

// ALB 只允許 HTTP/HTTPS 入站
albSecurityGroup.addIngressRule(
  ec2.Peer.anyIpv4(),
  ec2.Port.tcp(80),
  'Allow HTTP traffic'
);

albSecurityGroup.addIngressRule(
  ec2.Peer.anyIpv4(),
  ec2.Port.tcp(443),
  'Allow HTTPS traffic'
);

// ALB 出站只能連接到 ECS
albSecurityGroup.addEgressRule(
  ec2.Peer.securityGroupId(ecsSecurityGroup.securityGroupId),
  ec2.Port.tcp(3000),
  'Allow traffic to ECS tasks'
);

const ecsSecurityGroup = new ec2.SecurityGroup(this, 'ECSSecurityGroup', {
  vpc: this.vpc,
  description: 'Security group for ECS tasks',
  allowAllOutbound: false,
});

// ECS 只接受來自 ALB 的流量
ecsSecurityGroup.addIngressRule(
  ec2.Peer.securityGroupId(albSecurityGroup.securityGroupId),
  ec2.Port.tcp(3000),
  'Allow traffic from ALB'
);

// ECS 出站到資料庫和 Redis
ecsSecurityGroup.addEgressRule(
  ec2.Peer.securityGroupId(rdsSecurityGroup.securityGroupId),
  ec2.Port.tcp(5432),
  'Allow traffic to RDS'
);

ecsSecurityGroup.addEgressRule(
  ec2.Peer.securityGroupId(redisSecurityGroup.securityGroupId),
  ec2.Port.tcp(6379),
  'Allow traffic to Redis'
);

// 允許 HTTPS 出站(用於 Secrets Manager 和其他 AWS 服務)
ecsSecurityGroup.addEgressRule(
  ec2.Peer.anyIpv4(),
  ec2.Port.tcp(443),
  'Allow HTTPS outbound'
);

4. CloudTrail 稽核日誌

// infrastructure/lib/kyo-audit-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as cloudtrail from 'aws-cdk-lib/aws-cloudtrail';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as s3n from 'aws-cdk-lib/aws-s3-notifications';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as subscriptions from 'aws-cdk-lib/aws-sns-subscriptions';
import { Construct } from 'constructs';

interface AuditStackProps extends cdk.StackProps {
  alertEmail: string;
}

export class KyoAuditStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: AuditStackProps) {
    super(scope, id, props);

    // 建立 S3 bucket 存儲 CloudTrail 日誌
    const cloudTrailBucket = new s3.Bucket(this, 'CloudTrailBucket', {
      bucketName: `kyo-cloudtrail-${this.account}-${this.region}`,
      encryption: s3.BucketEncryption.S3_MANAGED,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      versioned: true,
      lifecycleRules: [
        {
          id: 'DeleteOldLogs',
          enabled: true,
          expiration: cdk.Duration.days(90),
          noncurrentVersionExpiration: cdk.Duration.days(30),
        },
      ],
    });

    // 建立 SNS 主題用於告警
    const alertTopic = new sns.Topic(this, 'SecurityAlertTopic', {
      topicName: 'kyo-security-alerts',
      displayName: 'Kyo Security Alerts',
    });

    // 訂閱告警郵件
    alertTopic.addSubscription(
      new subscriptions.EmailSubscription(props.alertEmail)
    );

    // 建立 CloudTrail
    const trail = new cloudtrail.Trail(this, 'KyoAuditTrail', {
      trailName: 'kyo-audit-trail',
      bucket: cloudTrailBucket,
      includeGlobalServiceEvents: true,
      isMultiRegionTrail: true,
      enableFileValidation: true,

      // 記錄管理事件和資料事件
      managementEvents: cloudtrail.ReadWriteType.ALL,

      // 監控關鍵資源的資料事件
      s3BucketEvents: [
        {
          bucket: cloudTrailBucket,
          events: cloudtrail.ReadWriteType.ALL,
          includeManagementEvents: true,
        },
      ],
    });

    // CloudWatch 日誌整合
    trail.logAllLambdaDataEvents();
    trail.logAllS3DataEvents();

    // 建立 CloudWatch Alarms
    this.createSecurityAlarms(alertTopic);

    // 輸出重要資訊
    new cdk.CfnOutput(this, 'CloudTrailArn', {
      value: trail.trailArn,
      description: 'CloudTrail ARN',
    });

    new cdk.CfnOutput(this, 'AlertTopicArn', {
      value: alertTopic.topicArn,
      description: 'Security Alert Topic ARN',
    });
  }

  private createSecurityAlarms(alertTopic: sns.Topic) {
    // 這裡可以加入各種安全告警
    // 例如:失敗的登入嘗試、權限變更等
  }
}

5. 應用層安全監控

// 在 ECS 任務定義中加入安全監控
const backendTaskDefinition = new ecs.FargateTaskDefinition(this, 'BackendTaskDef', {
  memoryLimitMiB: 512,
  cpu: 256,
  taskRole: secretsStack.secretAccessRole, // 使用專用角色
});

const backendContainer = backendTaskDefinition.addContainer('backend', {
  image: ecs.ContainerImage.fromEcrRepository(backendRepo, 'latest'),
  environment: {
    NODE_ENV: 'production',
    PORT: '3000',
    DATABASE_URL: `postgresql://kyouser:kyopass@${props.rdsEndpoint}:5432/kyodb`,
    REDIS_URL: `redis://${props.redisEndpoint}:6379`,

    // 安全設定
    HELMET_ENABLED: 'true',
    CORS_ORIGIN: `https://${props.domainName}`,
    RATE_LIMIT_ENABLED: 'true',
    LOG_LEVEL: 'info',
  },
  secrets: {
    // 從 Secrets Manager 讀取敏感資料
    MITAKE_USERNAME: ecs.Secret.fromSecretsManager(
      secretsStack.secret,
      'MITAKE_USERNAME'
    ),
    MITAKE_PASSWORD: ecs.Secret.fromSecretsManager(
      secretsStack.secret,
      'MITAKE_PASSWORD'
    ),
    JWT_SECRET: ecs.Secret.fromSecretsManager(
      secretsStack.secret,
      'JWT_SECRET'
    ),
  },
  logging: ecs.LogDrivers.awsLogs({
    streamPrefix: 'backend',
    logGroup: new logs.LogGroup(this, 'BackendLogGroup', {
      logGroupName: '/aws/ecs/kyo-backend',
      retention: logs.RetentionDays.TWO_WEEKS,
    }),
  }),
  healthCheck: {
    command: ['CMD-SHELL', 'curl -f http://localhost:3000/api/health || exit 1'],
    interval: cdk.Duration.seconds(30),
    timeout: cdk.Duration.seconds(5),
    retries: 3,
    startPeriod: cdk.Duration.seconds(60),
  },
});

6. 後端應用安全強化

更新後端應用的安全設定:

// apps/kyo-otp-service/src/plugins/security.ts
import fp from 'fastify-plugin';
import helmet from '@fastify/helmet';
import cors from '@fastify/cors';
import rateLimit from '@fastify/rate-limit';

export default fp(async function (fastify) {
  // Helmet 安全頭
  await fastify.register(helmet, {
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        styleSrc: ["'self'", "'unsafe-inline'"],
        scriptSrc: ["'self'"],
        imgSrc: ["'self'", "data:", "https:"],
        connectSrc: ["'self'"],
        fontSrc: ["'self'"],
        objectSrc: ["'none'"],
        mediaSrc: ["'self'"],
        frameSrc: ["'none'"],
      },
    },
    crossOriginEmbedderPolicy: false, // 為了相容性
  });

  // CORS 設定
  await fastify.register(cors, {
    origin: (origin, callback) => {
      const allowedOrigins = [
        process.env.CORS_ORIGIN,
        'http://localhost:5174', // 開發環境
      ].filter(Boolean);

      if (!origin || allowedOrigins.includes(origin)) {
        callback(null, true);
      } else {
        callback(new Error('Not allowed by CORS'), false);
      }
    },
    credentials: true,
  });

  // 速率限制
  await fastify.register(rateLimit, {
    max: 100, // 每個 IP 每分鐘最多 100 個請求
    timeWindow: '1 minute',
    errorResponseBuilder: (request, context) => {
      return {
        code: 'E_RATE_LIMIT',
        message: 'Too many requests',
        statusCode: 429,
        issues: {
          resetInSec: Math.ceil(context.ttl / 1000),
        },
      };
    },
  });

  // API 特定速率限制
  await fastify.register(rateLimit, {
    max: 5, // OTP 發送每個 IP 每小時最多 5 次
    timeWindow: '1 hour',
    keyGenerator: (request) => `otp-send:${request.ip}`,
    continueExceeding: true,
    skipOnError: false,
  }, { prefix: '/api/otp/send' });
});

7. 監控和告警設定

// infrastructure/lib/kyo-monitoring-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as subscriptions from 'aws-cdk-lib/aws-sns-subscriptions';
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import { Construct } from 'constructs';

interface MonitoringStackProps extends cdk.StackProps {
  loadBalancer: elbv2.ApplicationLoadBalancer;
  clusterName: string;
  serviceName: string;
  alertEmail: string;
}

export class KyoMonitoringStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: MonitoringStackProps) {
    super(scope, id, props);

    // 建立告警主題
    const alertTopic = new sns.Topic(this, 'MonitoringAlerts', {
      topicName: 'kyo-monitoring-alerts',
    });

    alertTopic.addSubscription(
      new subscriptions.EmailSubscription(props.alertEmail)
    );

    // 應用程式健康告警
    const healthAlarm = new cloudwatch.Alarm(this, 'HealthCheckAlarm', {
      alarmName: 'kyo-health-check-failed',
      alarmDescription: 'ALB health check is failing',
      metric: props.loadBalancer.metricUnhealthyHostCount(),
      threshold: 1,
      evaluationPeriods: 2,
      treatMissingData: cloudwatch.TreatMissingData.BREACHING,
    });

    healthAlarm.addAlarmAction(
      new cloudwatch.SnsAction(alertTopic)
    );

    // 高錯誤率告警
    const errorRateAlarm = new cloudwatch.Alarm(this, 'HighErrorRateAlarm', {
      alarmName: 'kyo-high-error-rate',
      alarmDescription: 'High error rate detected',
      metric: new cloudwatch.MathExpression({
        expression: '(m1/m2)*100',
        usingMetrics: {
          m1: props.loadBalancer.metricHttpCodeTarget(
            elbv2.HttpCodeTarget.TARGET_5XX_COUNT
          ),
          m2: props.loadBalancer.metricRequestCount(),
        },
        label: 'Error Rate %',
      }),
      threshold: 5, // 5% 錯誤率
      evaluationPeriods: 3,
      treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
    });

    errorRateAlarm.addAlarmAction(
      new cloudwatch.SnsAction(alertTopic)
    );

    // 高延遲告警
    const latencyAlarm = new cloudwatch.Alarm(this, 'HighLatencyAlarm', {
      alarmName: 'kyo-high-latency',
      alarmDescription: 'High response latency detected',
      metric: props.loadBalancer.metricTargetResponseTime(),
      threshold: 2, // 2 秒
      evaluationPeriods: 2,
    });

    latencyAlarm.addAlarmAction(
      new cloudwatch.SnsAction(alertTopic)
    );

    // CPU 使用率告警
    const cpuAlarm = new cloudwatch.Alarm(this, 'HighCPUAlarm', {
      alarmName: 'kyo-high-cpu',
      alarmDescription: 'High CPU utilization detected',
      metric: new cloudwatch.Metric({
        namespace: 'AWS/ECS',
        metricName: 'CPUUtilization',
        dimensionsMap: {
          ClusterName: props.clusterName,
          ServiceName: props.serviceName,
        },
        statistic: 'Average',
      }),
      threshold: 80, // 80% CPU 使用率
      evaluationPeriods: 2,
    });

    cpuAlarm.addAlarmAction(
      new cloudwatch.SnsAction(alertTopic)
    );

    // 記憶體使用率告警
    const memoryAlarm = new cloudwatch.Alarm(this, 'HighMemoryAlarm', {
      alarmName: 'kyo-high-memory',
      alarmDescription: 'High memory utilization detected',
      metric: new cloudwatch.Metric({
        namespace: 'AWS/ECS',
        metricName: 'MemoryUtilization',
        dimensionsMap: {
          ClusterName: props.clusterName,
          ServiceName: props.serviceName,
        },
        statistic: 'Average',
      }),
      threshold: 85, // 85% 記憶體使用率
      evaluationPeriods: 2,
    });

    memoryAlarm.addAlarmAction(
      new cloudwatch.SnsAction(alertTopic)
    );

    // WAF 阻擋請求告警
    const wafBlockAlarm = new cloudwatch.Alarm(this, 'WAFBlockAlarm', {
      alarmName: 'kyo-waf-blocks',
      alarmDescription: 'High number of blocked requests by WAF',
      metric: new cloudwatch.Metric({
        namespace: 'AWS/WAFV2',
        metricName: 'BlockedRequests',
        dimensionsMap: {
          WebACL: 'KyoWebACL',
          Region: this.region,
        },
        statistic: 'Sum',
        period: cdk.Duration.minutes(5),
      }),
      threshold: 100, // 5 分鐘內超過 100 個被阻擋的請求
      evaluationPeriods: 1,
    });

    wafBlockAlarm.addAlarmAction(
      new cloudwatch.SnsAction(alertTopic)
    );

    // 建立 Dashboard
    const dashboard = new cloudwatch.Dashboard(this, 'KyoDashboard', {
      dashboardName: 'kyo-otp-service-dashboard',
    });

    dashboard.addWidgets(
      new cloudwatch.GraphWidget({
        title: 'Request Count',
        left: [props.loadBalancer.metricRequestCount()],
        width: 12,
        height: 6,
      }),
      new cloudwatch.GraphWidget({
        title: 'Response Time',
        left: [props.loadBalancer.metricTargetResponseTime()],
        width: 12,
        height: 6,
      }),
      new cloudwatch.GraphWidget({
        title: 'Error Rate',
        left: [
          props.loadBalancer.metricHttpCodeTarget(elbv2.HttpCodeTarget.TARGET_4XX_COUNT),
          props.loadBalancer.metricHttpCodeTarget(elbv2.HttpCodeTarget.TARGET_5XX_COUNT),
        ],
        width: 12,
        height: 6,
      }),
    );

    // 輸出
    new cdk.CfnOutput(this, 'DashboardURL', {
      value: `https://${this.region}.console.aws.amazon.com/cloudwatch/home?region=${this.region}#dashboards:name=kyo-otp-service-dashboard`,
      description: 'CloudWatch Dashboard URL',
    });
  }
}

8. 部署腳本更新

#!/bin/bash
# scripts/deploy-secure.sh

set -e

echo "🔒 Deploying secure infrastructure..."

# 部署基礎設施
cdk deploy KyoInfrastructureStack --require-approval never

# 部署安全堆疊
cdk deploy KyoSecretsStack --require-approval never
cdk deploy KyoSecurityStack --require-approval never
cdk deploy KyoAuditStack --require-approval never

# 部署 ECS 堆疊
cdk deploy KyoEcsStack --require-approval never

# 部署監控堆疊
cdk deploy KyoMonitoringStack --require-approval never

echo "✅ Secure deployment completed!"

# 建構並推送容器映像
./scripts/deploy.sh

echo "🎉 All deployments completed successfully!"

安全最佳實踐檢查清單

✅ 網路安全

  • WAF 保護應用層攻擊
  • 安全群組最小權限原則
  • VPC 私有子網路隔離
  • ALB HTTPS 重導向

✅ 身份與存取管理

  • IAM 角色最小權限
  • Secrets Manager 管理敏感資料
  • 服務間身份驗證

✅ 資料保護

  • 傳輸中加密(HTTPS/TLS)
  • 靜態資料加密
  • 資料庫連線加密

✅ 監控與稽核

  • CloudTrail 完整稽核日誌
  • CloudWatch 即時監控
  • 安全事件告警

✅ 應用安全

  • Helmet 安全頭
  • CORS 策略
  • 速率限制
  • 輸入驗證

今天成果

我們建立了企業級的安全防護體系:

多層防護:WAF + 安全群組 + 應用層防護
敏感資料保護:Secrets Manager 集中管理
全面監控:CloudWatch + CloudTrail 完整可見性
即時告警:多維度監控指標和告警機制
合規稽核:完整的操作記錄和安全日誌
自動化部署:一鍵部署安全基礎設施


上一篇
Day 7: 30天部署SaaS產品到AWS-現代化前後端分離部署架構
下一篇
Day 9: 30天部署SaaS產品到AWS: 應用程式效能監控 (APM) 與可觀測性
系列文
30 天將工作室 SaaS 產品部署起來9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言