使用Lambda解压S3存储桶ZIP文件时报File Error: Access Denied
错误原因定位
从错误栈看,报错是s3-unzip包调用S3 API时被权限拦截返回Access Denied,你给IAM用户绑定AdministratorAccess解决不了这个问题,核心原因按出现概率从高到低排:
- 最高发:身份混淆。你给操作控制台/部署代码用的IAM用户加全权限,不代表Lambda运行时的身份有权限。Lambda执行S3操作默认使用绑定到函数自身的执行角色(Execution Role),这个角色没配对应权限的话,必然报拒绝访问。
- 次高发:S3启用了SSE-KMS加密,但Lambda执行角色没有对应KMS密钥的加解密权限。KMS权限是独立于S3权限体系的,哪怕角色有S3全权限,没有KMS授权照样读不了加密对象、写不了加密后的输出文件。
- 其他常见诱因:
- S3桶策略配置了显式DENY规则,比如拒绝非指定VPC来源请求、强制要求特定TLS版本、限制只能用指定加密方式,Lambda默认请求不符合规则就会被拦
- 触发Lambda的ZIP文件是其他AWS账户跨账户上传的,上传时没给桶所有者授予对象读取权限,哪怕你是桶所有者也无权读这个对象
- 你用的
s3-unzip包已经6年多没更新,内置的AWS SDK版本太老,不支持部分新的S3鉴权规则,本身就有兼容bug
排查修复步骤
1. 先把Lambda执行角色的权限配对
别再给自己的IAM用户加权限了,直接找Lambda的执行角色:
- 打开Lambda控制台,进入函数详情页,在「配置-权限」面板找到绑定的执行角色,点链接跳转到IAM控制台
- 给角色加最小必要权限,不要直接给管理员权限,参考策略如下,替换掉里面的桶名、KMS ARN即可:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject"], "Resource": "arn:aws:s3:::替换成你的桶名/*" }, { "Effect": "Allow", "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::替换成你的桶名" }, // 桶开了KMS加密才需要加下面这段 { "Effect": "Allow", "Action": ["kms:Decrypt", "kms:GenerateDataKey"], "Resource": "替换成你的KMS密钥ARN" } ] }
2. 排查S3侧的权限拦截规则
- 检查桶策略的DENY条目,确认没有阻止Lambda出口IP访问、没有强制要求超出Lambda默认配置的访问条件(比如要求MFA、要求携带特定Principal标签)
- 如果是跨账户上传的ZIP文件,联系上传方重新上传,上传时必须带上
bucket-owner-full-control的ACL授权,否则你作为桶所有者也无权读取对象
3. 直接弃用s3-unzip包,换原生实现
s3-unzip最后一次更新是2018年,兼容问题极多,没必要在这个第三方包上耗时间,直接用官方AWS SDK加unzipper包写解压逻辑即可,稳定可控,参考代码:
const AWS = require('aws-sdk'); const unzipper = require('unzipper'); const s3 = new AWS.S3(); exports.handler = async (event) => { const bucketName = event.Records[0].s3.bucket.name; const fileKey = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' ')); // 读取源ZIP文件流 const zipReadStream = s3.getObject({ Bucket: bucketName, Key: fileKey }).createReadStream(); // 解析ZIP内容 const zipDir = await unzipper.Parse().parse(zipReadStream); // 遍历解压文件上传到S3 for await (const entry of zipDir.files) { if (entry.type === 'File') { // 这里可以自定义输出路径,比如统一放到unzipped前缀下 const outputKey = `unzipped/${entry.path}`; await s3.putObject({ Bucket: bucketName, Key: outputKey, Body: entry.stream() }).promise(); } entry.autodrain(); } return { message: 'unzip success' }; };
部署前记得在项目目录下执行
npm install unzipper aws-sdk把依赖打包进部署包,Lambda运行时自带的aws-sdk版本如果太低可以手动装新版。
4. 快速排障技巧
改完配置还是报错的话,先把s3-unzip的逻辑全注释掉,在handler开头直接写一段测试代码调用s3.getObject()读目标ZIP文件:
- 如果这段测试代码也报Access Denied,100%是IAM权限、桶策略、KMS配置的问题,和代码逻辑无关,顺着权限链路查就行
- 如果测试代码能正常读到文件内容,就是
s3-unzip包本身的问题,直接按第三步换实现即可,不用再纠结包的配置。
内容的提问来源于stack exchange,提问作者Samy15
相关产品推荐
相关产品推荐

