使用serverless-single-page-app-plugin部署Angular应用遇权限拒绝求助
问题描述
使用serverless-single-page-app-plugin将Angular应用部署到AWS CloudFront时,持续遭遇权限拒绝错误。当前使用的客户账号已关联管理员权限策略:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "*", "Resource": "*" } ] }
但在个人AWS账号中使用完全相同的配置可正常部署,无权限问题。错误核心为CloudFront访问S3存储桶时触发AccessDenied,无法获取存储桶内对象。
附serverless.yaml配置:
service: serviceName plugins: - serverless-single-page-app-plugin custom: s3Bucket: s3Bucket s3LocalPath: app/ region: ap-northeast-1 provider: name: aws iam: role: statements: - Effect: "Allow" Action: - s3:CreateBucket - s3:PutBucketAcl - s3:GetBucketAcl - s3:PutBucketPolicy Resource: arn:aws:s3::* runtime: nodejs14.x stage: dev region: ap-northeast-1 profile: default stackName: StackName tags: owner: me project: serverless-learning env: dev resources: Resources: cloudfrontoriginaccessidentity: Type: AWS::CloudFront::CloudFrontOriginAccessIdentity Properties: CloudFrontOriginAccessIdentityConfig: Comment: "Test" ## Specifying the S3 Bucket WebAppS3Bucket: Type: AWS::S3::Bucket Properties: BucketName: ${self:custom.s3Bucket} AccessControl: PublicRead WebsiteConfiguration: IndexDocument: index.html ErrorDocument: index.html ## Specifying the CloudFront Distribution to server your Web Application WebAppCloudFrontDistribution: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: Origins: - DomainName: ${self:custom.s3Bucket}.s3.amazonaws.com ## An identifier for the origin which must be unique within the distribution Id: WebApp # CustomOriginConfig: # HTTPPort: 80 # HTTPSPort: 443 # OriginProtocolPolicy: https-only ## In case you want to restrict the bucket access use S3OriginConfig and remove CustomOriginConfig S3OriginConfig: OriginAccessIdentity: Fn::Join: - '' - - 'origin-access-identity/cloudfront/' - Ref: cloudfrontoriginaccessidentity Enabled: 'true' ## Uncomment the following section in case you are using a custom domain # Aliases: # - mysite.example.com DefaultRootObject: index.html ## Since the Single Page App is taking care of the routing we need to make sure ever path is served with index.html ## The only exception are files that actually exist e.h. app.js, reset.css CustomErrorResponses: - ErrorCode: 404 ResponseCode: 200 ResponsePagePath: /index.html DefaultCacheBehavior: AllowedMethods: - DELETE - GET - HEAD - OPTIONS - PATCH - POST - PUT ## The origin id defined above TargetOriginId: WebApp ## Defining if and how the QueryString and Cookies are forwarded to the origin which in this case is S3 ForwardedValues: QueryString: 'false' Cookies: Forward: none ## The protocol that users can use to access the files in the origin. To allow HTTP use `allow-all` ViewerProtocolPolicy: redirect-to-https ## The certificate to use when viewers use HTTPS to request objects. ViewerCertificate: CloudFrontDefaultCertificate: 'true' ## Uncomment the following section in case you want to enable logging for CloudFront requests # Logging: # IncludeCookies: 'false' # Bucket: mylogs.s3.amazonaws.com # Prefix: myprefix ## Specifying the policies to make sure all files inside the Bucket are avaialble to CloudFront WebAppS3BucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: Ref: WebAppS3Bucket PolicyDocument: Statement: Effect: "Allow" Principal: "*" Action: - s3:GetObject Resource: arn:aws:s3:::${self:custom.s3Bucket}/* # In order to print out the hosted domain via `serverless info` we need to define the DomainName output for CloudFormation Outputs: WebAppS3BucketOutput: Value: 'Ref': WebAppS3Bucket WebAppCloudFrontDistributionOutput: Value: 'Fn::GetAtt': [ WebAppCloudFrontDistribution, DomainName ]
解决方案
1. 修正S3存储桶策略的权限主体
当前配置同时启用了CloudFront Origin Access Identity(OAI)和公开可读策略,两者逻辑冲突。应仅允许OAI访问存储桶,而非公开所有人:
WebAppS3BucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: Ref: WebAppS3Bucket PolicyDocument: Statement: Effect: "Allow" Principal: AWS: !Sub "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${cloudfrontoriginaccessidentity}" Action: - s3:GetObject Resource: arn:aws:s3:::${self:custom.s3Bucket}/*
2. 移除存储桶的PublicRead访问控制
使用OAI后无需将存储桶设为公开,这会增加安全风险且干扰权限逻辑:
WebAppS3Bucket: Type: AWS::S3::Bucket Properties: BucketName: ${self:custom.s3Bucket} AccessControl: Private WebsiteConfiguration: IndexDocument: index.html ErrorDocument: index.html
3. 检查客户账号的权限边界或服务控制策略(SCP)
即使账号有管理员权限,客户账号可能被AWS组织的SCP或权限边界限制操作:
- 确认账号所在AWS组织是否有SCP限制CloudFront/S3的关键操作
- 检查账号是否被施加权限边界,禁止
cloudfront:CreateCloudFrontOriginAccessIdentity、s3:PutBucketPolicy等操作
4. 修正CloudFront Origin的域名格式
使用S3静态网站托管时,CloudFront Origin需用静态网站域名(格式为bucket-name.s3-website-region.amazonaws.com),并切换为自定义源配置:
Origins: - DomainName: ${self:custom.s3Bucket}.s3-website-${self:provider.region}.amazonaws.com Id: WebApp CustomOriginConfig: HTTPPort: 80 HTTPSPort: 443 OriginProtocolPolicy: https-only
5. 清理资源后重新部署
若之前部署留下权限混乱的资源,按以下步骤操作:
- 执行
serverless remove删除现有栈 - 手动清理残留的S3存储桶和CloudFront分发
- 重新运行
serverless deploy
内容的提问来源于stack exchange,提问作者LastOne
相关产品推荐
相关产品推荐

