用 Checkov 扫描 Serverless Framework 配置:从 CLI 实战到源码级原理
【免费下载链接】checkovPrevent cloud misconfigurations and find vulnerabilities during build-time in infrastructure as code, container images and open source packages with Checkov by Bridgecrew.项目地址: https://gitcode.com/GitHub_Trending/ch/checkov
Checkov 支持直接对 Serverless Framework 的服务定义文件(serverless.yml/serverless.yaml)执行安全策略评估,在构建阶段提前发现云资源错误配置。本文将围绕docs/7.Scan Examples/Serverless Framework.md的完整流程展开:先给出一个刻意配置错误的serverless.yml示例与真实扫描输出,再深入 Checkov 源码剖析 serverless 扫描引擎的解析、变量求值、图构建与检查分发机制,最后结合测试用例给出修复建议,帮助你既会用、又懂原理。
一、Serverless Framework 扫描能力概述
Serverless Framework 允许开发者用一份 YAML 服务定义文件同时描述 Lambda 函数、IAM 角色、事件源以及底层云资源(通过resources节内嵌 CloudFormation 模板语法)。Checkov 的 serverless 扫描会对这类文件运行策略检查,验证其是否符合 AWS 最佳实践,例如:
- 是否开启了日志记录与审计(如 S3 访问日志、CloudTrail);
- S3 存储桶是否加密、是否开启版本控制;
- 是否启用了 HTTPS 传输;
- IAM 策略是否过度授权;
- 是否存在硬编码凭据。
需要特别说明的是,Serverless Framework 扫描复用了 CloudFormation 扫描实现中的大量检查,因为Serverless 的资源定义是对 CloudFormation 定义的扩展(resources节内的资源本质就是 CloudFormation 资源)。这一设计可以在源码中直接印证:serverless Runner 在检查resources节时调用的正是cfn_registry(见 checkov/serverless/runner.py)。
完整的 Serverless 策略清单参见仓库内的策略索引文档 docs/5.Policy Index/serverless.md,其中既包含作用于serverless_aws实体的专用检查,也包含通过 CloudFormation 通道复用的 AWS 资源检查(如AWS::S3::Bucket、AWS::Lambda::Function、AWS::IAM::Policy等)。
二、快速开始:扫描一个 Serverless 服务目录
1. 文件识别规则
默认情况下,Checkov 的 serverless 扫描只会识别文件名恰好为serverless.yml或serverless.yaml的文件。该掩码定义在 checkov/serverless/utils.py:
SLS_FILE_MASK = os.getenv( "CKV_SLS_FILE_MASK", "serverless.yml,serverless.yaml").split(",")也就是说,你可以通过环境变量CKV_SLS_FILE_MASK自定义要扫描的文件名集合(逗号分隔)。扫描目录时,Checkov 会递归遍历目录树,并自动跳过node_modules目录(见 get_scannable_file_paths),避免扫描无关依赖。
2. 指定框架运行扫描
对当前目录下的 serverless 服务执行扫描:
checkov -d . --framework serverless-d .:扫描当前目录(递归);--framework serverless:仅运行 serverless 框架相关的检查。如果省略该参数,Checkov 会按默认的框架集全量扫描;也可以组合多个框架,例如--framework serverless cloudformation,同时覆盖服务定义与内嵌的 CloudFormation 资源。
3. 解析前提:仅支持 AWS provider
从源码看,serverless 解析器对模板有严格的准入校验。is_checked_sls_template(见 checkov/serverless/parsers/parser.py)要求模板必须包含provider字段,且其name必须位于SUPPORTED_PROVIDERS = ['aws']中(provider既可以写成字典,也可以直接写成字符串aws)。也就是说,当前版本的 serverless 扫描面向 AWS provider,其他云厂商的服务定义不会被纳入扫描范围。
三、示例:一个刻意配置错误的 Serverless 服务
以下示例定义了一个名为usersCrud的 AWS 服务:包含一个 Lambda 函数myFunc(带 IAM 角色语句、标签、超时与内存配置),并在resources节内嵌了一个 CloudFormation 风格的 S3 存储桶S3BucketPublicRead,其AccessControl被显式设置为PublicRead:
service: usersCrud provider: aws functions: myFunc: name: myFunc tags: RESOURCE: lambda PUBLIC: false iamRoleStatements: - Effect: Allow Action: - "lambda:InvokeFunction" Resource: - "arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:invokedLambda" handler: Handler.handle timeout: 600 memorySize: 320 resources: # CloudFormation template syntax Resources: S3BucketPublicRead: Type: AWS::S3::Bucket Properties: AccessControl: PublicRead BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256将上述内容保存为serverless.yml后,直接运行:
checkov -d . --framework serverless可以看到两个层面的检查对象:
- 函数级检查:
myFunc的iamRoleStatements、environment(若存在)会被逐一评估。示例中iamRoleStatements声明了lambda:InvokeFunction的Allow语句,CloudFormation 风格的#{AWS::Region}、#{AWS::AccountId}伪参数由 serverless 解析器保留供后续替换。 - CloudFormation 资源检查:
resources.Resources节内的AWS::S3::Bucket会被提取出来,交给 CloudFormation 资源注册表(cfn_registry)扫描,因此 S3 相关的全部检查(加密、ACL、日志、版本控制、公有访问块等)都会生效。
四、解读扫描输出
对上述示例执行扫描,典型的控制台输出如下(篇幅所限,仅保留核心结构):
_ _ ___| |__ ___ ___| | _______ __ / __| '_ \ / _ \/ __| |/ / _ \ \ / / | (__| | | | __/ (__| < (_) \ V / \___|_| |_|\___|\___|_|\_\___/ \_/ By Prisma Cloud | version: x.x.x serverless scan results: Passed checks: 5, Failed checks: 7, Skipped checks: 0 Check: CKV_AWS_19: "Ensure the S3 bucket has server-side-encryption enabled" PASSED for resource: AWS::S3::Bucket.S3BucketPublicRead File:/serverless.yml:22-29 Check: CKV_AWS_57: "Ensure the S3 bucket does not allow WRITE permissions to everyone" PASSED for resource: AWS::S3::Bucket.S3BucketPublicRead File:/serverless.yml:22-29 Check: CKV_AWS_49: "Ensure no IAM policies documents allow "*" as a statement's actions" PASSED for resource: myFunc File:/serverless.yml:5-19 Check: CKV_AWS_41: "Ensure no hard coded AWS access key and secret key exists in provider" PASSED for resource: myFunc File:/serverless.yml:5-19 Check: CKV_AWS_1: "Ensure IAM policies that allow full "*-*" administrative privileges are not created" PASSED for resource: myFunc File:/serverless.yml:5-19 Check: CKV_AWS_20: "Ensure the S3 bucket does not allow READ permissions to everyone" FAILED for resource: AWS::S3::Bucket.S3BucketPublicRead File:/serverless.yml:22-29 22 | S3BucketPublicRead: 23 | Type: AWS::S3::Bucket 24 | Properties: 25 | AccessControl: PublicRead 26 | BucketEncryption: 27 | ServerSideEncryptionConfiguration: 28 | - ServerSideEncryptionByDefault: 29 | SSEAlgorithm: AES256 Check: CKV_AWS_18: "Ensure the S3 bucket has access logging enabled" FAILED for resource: AWS::S3::Bucket.S3BucketPublicRead File:/serverless.yml:22-29 Check: CKV_AWS_53: "Ensure S3 bucket has block public ACLS enabled" FAILED for resource: AWS::S3::Bucket.S3BucketPublicRead File:/serverless.yml:22-29 Check: CKV_AWS_54: "Ensure S3 bucket has block public policy enabled" FAILED for resource: AWS::S3::Bucket.S3BucketPublicRead File:/serverless.yml:22-29 Check: CKV_AWS_21: "Ensure the S3 bucket has versioning enabled" FAILED for resource: AWS::S3::Bucket.S3BucketPublicRead File:/serverless.yml:22-29 Check: CKV_AWS_55: "Ensure S3 bucket has ignore public ACLs enabled" FAILED for resource: AWS::S3::Bucket.S3BucketPublicRead File:/serverless.yml:22-29 Check: CKV_AWS_56: "Ensure S3 bucket has 'restrict_public_bucket' enabled" FAILED for resource: AWS::S3::Bucket.S3BucketPublicRead File:/serverless.yml:22-29输出中的几个关键要素:
- 汇总行:
Passed checks: 5, Failed checks: 7, Skipped checks: 0给出三类结果的数量统计(Skipped 通常来自checkov:skip=跳过注释)。 - 检查条目:每条输出包含检查 ID(如
CKV_AWS_20)、策略描述、针对的资源(AWS::S3::Bucket.S3BucketPublicRead或函数名myFunc)、命中的文件与行号区间,以及失败时附带的代码片段,方便直接定位到serverless.yml中对应的配置行。 - 结果对照:
myFunc的函数级检查(CKV_AWS_1、CKV_AWS_41、CKV_AWS_49)全部通过,说明 IAM 语句未使用*通配符、不存在硬编码凭据;而S3BucketPublicRead由于AccessControl: PublicRead且缺少日志、版本控制与公有访问块配置,导致 6 项 S3 检查失败。加密检查(CKV_AWS_19)通过,是因为示例中配置了SSEAlgorithm: AES256。
五、源码原理:Serverless 扫描引擎的四个阶段
理解了输出之后,我们再从 checkov/serverless 模块出发,看一次扫描在 Checkov 内部是如何完成的。
阶段一:文件发现与解析
Runner 通过get_scannable_file_paths在目标目录中按SLS_FILE_MASK匹配文件,随后调用get_files_definitions并行解析(见 checkov/serverless/runner.py)。解析器使用cfn_yaml.load(filename, cfn_yaml.ContentType.SLS)加载 YAML(见 checkov/serverless/parsers/parser.py),这一设计保证了与 CloudFormation 解析器共享底层实现,同时记录了每个节点的起始/结束行号,供后续输出代码片段使用。非 YAML、文件缺失、权限不足等情况都会被捕获并跳过,不会中断整个扫描。
阶段二:变量求值
Serverless Framework 的模板大量使用${...}变量语法。process_variables(见 checkov/serverless/parsers/parser.py)在解析后对模板做原地变量替换,支持:
${self:custom.xxx}:引用模板自身其他部分;${env:VAR_NAME}:引用环境变量;${file(./path/to/file.yml)}:引用外部 YAML/JSON 文件(${file(...)}也支持带路径内部属性,如${file(./conf.yml):deploy.bucket});- 逗号分隔的兜底值语法:如
${self:custom.env, 'dev'}。
变量替换采用循环求值以处理链式引用,上限为 25 轮防止振荡。从源码注释可以看到,若provider.variableSyntax被自定义,则使用自定义正则;否则使用默认模式SLS_DEFAULT_VAR_PATTERN。此外,环境变量CHECKOV_SERVERLESS_DISABLE_VARS=true可关闭${env:}与${file()}的解析(${self:}不受影响),用于需要在扫描中保留原始变量形态的场景(见 _load_var_data)。
阶段三:图构建
解析完成后,Runner 会构建一个 Serverless 局部图(ServerlessLocalGraph,见 checkov/serverless/graph_builder/local_graph.py)。图中为每个文件的 9 类顶层元素分别建立顶点:functions、params、provider、layers、custom、package、plugins、service、resources(枚举定义见 checkov/serverless/utils.py)。图构建完成后,顶点数据会被转换回定义结构,供后续检查阶段消费。
阶段四:检查分发
add_python_check_results(见 checkov/serverless/runner.py)按四类通道分发检查:
| 通道 | 覆盖节 | 注册表 | 说明 |
|---|---|---|---|
cfn_resources_checks | resources.Resources | cfn_registry | 将内嵌的 CloudFormation 资源交给 CloudFormation 检查体系(如全部 S3 检查) |
multi_item_sections_checks | functions、layers | function_registry、layer_registry | 多实体节,逐项检查每个函数/层 |
single_item_sections_checks | provider、custom、package、plugins、service | 对应注册表 | 单实体节,整体检查 |
complete_python_checks | 整个文件 | complete_registry | 针对整个模板的全局检查 |
其中SINGLE_ITEM_SECTIONS与MULTI_ITEM_SECTIONS的定义见 checkov/serverless/runner.py。
一个值得注意的细节是enrich_function_with_provider(见 checkov/serverless/parsers/context_parser.py):在扫描functions节时,Runner 会把provider块中的iamRoleStatements、environment、stackTags(映射为函数tags)、runtime、timeout、memorySize等属性下推合并到每个函数配置中,因为从 Serverless 运行时的语义看,函数会继承这些 provider 级配置。这样一来,函数级检查看到的配置就是"完整生效"的配置,而不是缺省的片段。
函数级检查的三份实现样例
Serverless 专属的serverless_aws实体检查位于 checkov/serverless/checks/function/aws,其中三份实现与示例输出中的检查一一对应:
- CKV_AWS_1(AdminPolicyDocument.py):遍历
iamRoleStatements,若存在Effect: Allow且Action与Resource均为*的语句,则判定 FAILED,防止创建"*-*"全管理权限策略。 - CKV_AWS_41(AWSCredentials.py):检查函数
environment中的字符串值是否命中 AWS access key / secret key 正则模式,用于发现硬编码凭据。 - CKV_AWS_49(StarActionPolicyDocument.py):检查 IAM 策略文档是否在
Allow语句的Action中使用*通配符。
这些检查类均继承自BaseFunctionCheck,通过scan_function_conf接收被"provider 属性充实过"的函数配置字典,返回CheckResult.PASSED / FAILED / UNKNOWN。此外,provider、service、custom、layer、package、plugin等节也有各自的基类与注册表,共同构成 checkov/serverless/checks 目录下的分层检查体系。
六、Serverless 检查与 CloudFormation 检查的关系
原文档明确指出:"Serverless 扫描利用的是 Checkov CloudFormation 扫描实现中的检查,因为 Serverless 资源定义扩展了 CloudFormation 定义。"这句话在源码层面有两处体现:
- 解析器复用:serverless 解析直接调用
cfn_yaml加载 YAML(checkov/serverless/parsers/parser.py),并复用 CloudFormation 的ContextParser提取代码行与收集跳过注释。 - 资源检查复用:
resources节内的资源以cfn_resources_checks通道交给cfn_registry扫描(checkov/serverless/runner.py),因此 S3、Lambda、IAM 等 AWS 资源的全部 CloudFormation 检查自动对 serverless 模板生效。
策略索引文档 docs/5.Policy Index/serverless.md 也印证了这一点:表格上半部分是serverless_aws实体专属的 3 项检查(CKV_AWS_1、CKV_AWS_41、CKV_AWS_49),下半部分则是大量标为 Cloudformation 的 AWS 资源检查(如 CKV_AWS_18、CKV_AWS_19、CKV_AWS_20、CKV_AWS_21、CKV_AWS_53~56 等 S3 检查),它们会通过 CloudFormation 通道作用于 serverless 模板中的内嵌资源。
七、测试验证与修复建议
仓库为 serverless 扫描提供了完整的单元测试,可作为验证行为与学习用法的参考:
- tests/serverless/runner/test_runner.py:验证 Runner 各注册表
report_type均为CheckType.SERVERLESS、记录路径的相对化、以及 enforcement rules 过滤等行为; - tests/serverless/runner/resources/serverless.yaml:一个真实的扫描样例,覆盖
${self:}/${file()}变量、provider 级environment继承、ECR 镜像、文件系统配置等复杂场景; - tests/serverless/checks/aws:针对
AdminPolicyDocument、AWSCredentials、StarActionPolicyDocument等检查的用例。
针对本文示例中失败的 7 项检查,修复思路非常明确:为 S3 存储桶补充版本控制(VersioningConfiguration)、访问日志(LoggingConfiguration)、以及完整四件套的公有访问块配置(PublicAccessBlockConfiguration下的BlockPublicAcls、IgnorePublicAcls、BlockPublicPolicy、RestrictPublicBuckets均设为true),并将AccessControl从PublicRead改为私有。修改后重新运行checkov -d . --framework serverless,即可看到 Failed 数量归零。
如果需要对个别策略做豁免,可以在serverless.yml相应资源块上方添加# checkov:skip=CKV_AWS_20:reason跳过注释,或结合--skip-check参数在命令行过滤(详见 docs/2.Basics/Suppressing and Skipping Policies.md)。将 serverless 扫描纳入 CI 流水线后,即可在每次构建阶段自动拦截公有存储桶、硬编码凭据、过度授权 IAM 等高风险配置,实现"基础设施即代码"的左移安全治理。
【免费下载链接】checkovPrevent cloud misconfigurations and find vulnerabilities during build-time in infrastructure as code, container images and open source packages with Checkov by Bridgecrew.项目地址: https://gitcode.com/GitHub_Trending/ch/checkov
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考