基础设施即代码(IaC) 已经吞噬了世界。它有助于自动管理和配置计算机资源,避免手动工作或 UI 表单工作流。IaC 的生命周期管理从声明性和幂等配置、包和工具安装开始。在云提供商时代,IaC 工具还有助于抽象云供应。他们可以自动创建定义的资源(网络、存储、数据库等)并应用配置(DNS 条目、防火墙规则等)。
像其他一切一样,它也有缺陷。IaC 工作流程在开发生命周期中向左移动,使其更加高效。开发人员和 DevOps 工程师需要学习新工具和最佳实践。错误可能导致凭据泄露或供应链攻击。现有的安全评估工具可能无法检测到这些新漏洞。
在这篇文章中,我们将深入探讨这些特定风险,并重点关注 IaC 管理工具,例如 Terraform、云提供商以及涉及容器和 Kubernetes 的部署平台。
对于每种情况,我们将研究威胁、工具、集成和最佳实践以降低风险。
您可以自上而下阅读博客文章或单独导航到各个章节。
首先确定负责管理 IAC 任务的项目/组。.tf
对特定 IaC 工具、文件后缀(例如 Terraform 使用)和语言的清单搜索会很有帮助。本博文中讨论的安全扫描工具将自动发现所有支持的类型。确定项目后,您可以使用其中一种工具运行扫描并识别检测到的可能漏洞。
可能没有任何扫描结果,因为此时您的基础架构是安全的。不过,您的流程可能需要您为将来最终发现的漏洞创建文档、运行手册和操作项。很难预测要防御的可能场景,所以让我们暂时将角色从防御者转变为攻击者。作为恶意攻击者可以利用哪些安全漏洞?也许可以通过运行安全扫描来创建易受攻击的场景并模拟攻击者角色。
配置中可能存在明显的潜在漏洞,例如明文密码。其他场景涉及您永远不会想到的情况或导致安全问题的一系列项目。
让我们通过在 AWS 中使用 Terraform 预置 S3 存储桶来为攻击者创建一个场景。我们打算在此 S3 存储桶中存储日志、数据库转储或凭证保险库。
以下示例aws_s3_bucket
使用 AWS 提供程序在 Terraform 中创建资源。
# Create the bucket
resource "aws_s3_bucket" "demobucket" {
bucket = "terraformdemobucket"
acl = "private"
}
在第一次配置 S3 存储桶后,有人决定让 S3 存储桶默认可访问。下面的示例使用 授予对存储桶的公共访问权限aws_s3_bucket_public_access_block
。block_public_acls
并block_public_policy
设置为false
允许任何公共访问。
# Grant bucket access: public
resource "aws_s3_bucket_public_access_block" "publicaccess" {
bucket = aws_s3_bucket.demobucket.id
block_public_acls = false
block_public_policy = false
}
S3 存储桶现在是公开可读的,任何知道 URL 或扫描网络范围以查找开放端口的人都可以找到 S3 存储桶及其数据。恶意行为者不仅可以捕获凭据,还可以从存储在 S3 存储桶中的日志、备份和数据库转储中了解您的基础设施、IP 地址、内部服务器 FQDN 等。
我们需要减轻和检测此安全问题的方法。以下部分描述了您可以使用的不同工具。完整的 Terraform 代码位于此项目中,允许您测试此博客文章中描述的所有工具。
在“不是最坏的情况”情况下,用于管理基础设施的 Terraform 代码保存在中央 Git 服务器上,而不是隐藏在主机或本地桌面的某个地方。也许您已经terraform init, plan, apply
在 CI/CD 管道中使用作业。让我们研究有助于检测公共 S3 存储桶漏洞的方法和工具。稍后,我们将讨论 CI/CD 集成和自动化 IaC 安全扫描。
在我们深入研究这些工具之前,请确保在本地克隆演示项目以自己遵循示例。
$ cd /tmp
$ git clone https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning.git && cd infrastructure-as-code-scanning/
本博文中的工具安装步骤以macOS 上的 Homebrew 进行说明。请参阅工具文档以了解替代安装方法和支持的平台。
您可以自上而下地阅读 Terraform 安全扫描工具,或直接导航到工具部分:
Aqua Security 的tfsec可以帮助检测 Terraform 漏洞。有Docker 镜像可用于在 CLI 上快速测试扫描仪,或二进制文件来安装 tfsec。tfsec
在本地项目路径上运行terraform/aws/
以获取漏洞列表。
$ brew install tfsec
$ tfsec terraform/aws/
默认扫描在 CLI 上提供表概览,这可能需要额外的过滤器。检查tfsec –help
以获取所有可用参数的列表,并尝试生成 JSON 和 JUnit 输出文件以进一步处理。
$ tfsec terraform/aws --format json --out tfsec-report.json
1 file(s) written: tfsec-report.json
$ tfsec terraform/aws --format junit --out tfsec-junit.xml
1 file(s) written: tfsec-junit.xml
完整示例位于该项目的 terraform/aws 目录中。
在之前的博客文章中,我们分享了如何检测 JSON 数据结构并使用链式 jq 命令进行过滤。tfsec 报告是一个很好的做法:提取results
键,遍历所有数组列表项并通过 be 过滤rule_service
,并且s3
仅打印severity
和。description``location.filename
$ jq < tfsec-report.json | jq -c '.["results"]' | jq -c '.[] | select (.rule_service == "s3") | [.severity, .description, .location.filename]'
kics是另一个 IaC 扫描程序,为许多不同的工具(Ansible、Terraform、Kubernetes、Dockerfile 和云配置 API,如 AWS CloudFormation、Azure 资源管理器和 Google 部署管理器)提供支持。
让我们尝试一下:安装 kics并在易受攻击的项目上运行它。--report-formats
,--output-path
并--output-name
允许您创建可以使用其他工具自动解析的 JSON 报告。
$ kics scan --path .
$ kics scan --path . --report-formats json --output-path kics --output-name kics-report.json
使用 jq解析 JSON 报告kics
的方式与上面的 tfsec 示例相同。检查数据结构和嵌套对象,并按 AWS 过滤为cloud_provider
. 该files
条目是一个字典数组,结果发现提取起来有点棘手,还(.files[] | .file_name )
需要添加一个额外的内容:
$ jq < kics/kics-report.json | jq -c '.["queries"]' | jq -c '.[] | select (.cloud_provider == "AWS") | [.severity, .description, (.files[] | .file_name ) ]'
kics
根据找到的不同严重性的数量返回不同的退出代码。50
指示HIGH
严重性并导致 CI/CD 管道失败。
Checkov支持 Terraform(适用于 AWS、GCP、Azure 和 OCI)、CloudFormation、ARM、Severless 框架、Helm 图表、Kubernetes 和 Docker。
$ brew install checkov
$ checkov --directory .
Terrascan支持 Terraform,以及针对云提供商、Docker 和 Kubernetes 的更多策略。
$ brew install terrascan
$ terrascan scan .
Semgrep 正在研究Terraform 支持,目前处于 Beta 阶段。它还检测 Dockerfile 错误——例如无效的端口范围和多个范围,类似于 kics。
$ brew install semgrep
$ semgrep --config auto .
tflint也是一种替代扫描仪。
第一次测试 IaC 安全扫描器时,我一直在寻找演示项目和示例。Terraform的kics 查询列表提供了所有漏洞的详尽列表和链接的文档。从那里,您可以为演示和展示构建和创建潜在的攻击向量,而不会泄露您的公司代码和工作流程。
Terragoat也是一个很好的学习资源,可以测试各种扫描仪并查看现实生活中的漏洞示例。
$ cd /tmp && git clone https://github.com/bridgecrewio/terragoat.git && cd terragoat$ tfsec .
$ kics scan --path .
$ checkov --directory .
$ semgrep --config auto .
$ terrascan scan .
验证报告的漏洞并为团队所需的操作创建文档也很重要。并非所有检测到的漏洞在您的环境中都同样重要。随着 IaC、GitOps 和云原生环境的快速发展,使用 2 个以上的扫描程序来查看其中一个或另一个是否缺少漏洞也是一个好主意。
以下部分将详细讨论更多场景。
可重用的 IaC 工作流还可能引入您不知道的安全漏洞。本项目提供注册表中的模块文件和包,可以main.tf
在demo项目中使用。
module "my_module_name" {
source = "gitlab.com/gitlab-de/iac-tf-vuln-module/aws"
version = "1.0.0"
}
kics对官方 Terraform 模块注册表的支持有限,用于模块依赖性检查的checkov`` failed to download private modules,
terrascan and
tfsec work when
terraform init is run before the scan. Depending on your requirements, running
kics for everything and
tfsec` 可能是一个解决方案,建议添加在这里。
容器中的安全问题可能导致应用程序部署漏洞。kics查询数据库有助于对更易受攻击的示例进行逆向工程:使用最新标签、在容器中调用 sudo 来提升权限、超出范围的端口以及多个入口点只是一些不好的做法。
以下Dockerfile实现了扫描程序检测的示例漏洞:
# Create vulnerabilities based on kics queries in https://docs.kics.io/latest/queries/dockerfile-queries/
FROM debian:latest# kics: Run Using Sudo
# kics: Run Using apt
RUN sudo apt install git
# kics: UNIX Ports Out Of Range
EXPOSE 99999
# kics: Multiple ENTRYPOINT Instructions Listed
ENTRYPOINT ["ex1"]
ENTRYPOINT ["ex2"]
Kics、tfsec 和 terrascan 可以检测Dockerfile
漏洞,类似于 semgrep 和 checkov。作为示例扫描程序,terrascan 可以使用--iac-type docker
允许过滤扫描类型的参数来检测漏洞。
$ terrascan scan --iac-type docker
您可以运行 kics 和 tfsec 作为练习来验证结果。
保护 Kubernetes 集群可能是一项具有挑战性的任务。Open Policy Agent、Kyverno、RBAC 等以及许多不同的 YAML 配置属性都需要在生产部署之前进行审查和自动检查。集群镜像扫描是缓解安全威胁的一种方法,仅次于对正在部署的应用程序的容器扫描。如果您想深入了解 Kubernetes 安全性和攻击向量,建议阅读 Andrew Martin 和 Michael Hausenblas 所著的“Hacking Kubernetes”一书。
例如,在复制 YAML 示例配置并继续使用它时,可能会出错。我为Kubernetes 监控研讨会创建了一个部署和服务,它提供了一个实际示例来学习,但也使用了一些不太好的实践。
ecc-demo-service.yml中的以下配置引入了漏洞和潜在的生产问题:
---
# A deployment for the ECC Prometheus demo service with 3 replicas.
apiVersion: apps/v1
kind: Deployment
metadata:
name: ecc-demo-service
labels:
app: ecc-demo-service
spec:
replicas: 3
selector:
matchLabels:
app: ecc-demo-service
template:
metadata:
labels:
app: ecc-demo-service
spec:
containers:
- name: ecc-demo-service
image: registry.gitlab.com/everyonecancontribute/observability/prometheus_demo_service:latest
imagePullPolicy: IfNotPresent
args:
- -listen-address=:80
ports:
- containerPort: 80
---
# A service that references the demo service deployment.
apiVersion: v1
kind: Service
metadata:
name: ecc-demo-service
labels:
app: ecc-demo-service
spec:
ports:
- port: 80
name: web
selector:
app: ecc-demo-service
让我们用 kics 扫描 Kubernetes 清单,然后用 jq 再次解析结果。可以在kics 文档中找到 Kubernetes 的 kics 查询列表。
$ kics scan --path kubernetes --report-formats json --output-path kics --output-name kics-report.json$ jq < kics/kics-report.json | jq -c '.["queries"]' | jq -c '.[] | select (.platform == "Kubernetes") | [.severity, .description, (.files[] | .file_name ) ]'
Checkov检测到 Kubernetes 的类似漏洞。
$ checkov --directory kubernetes/
$ checkov --directory kubernetes -o json > checkov-report.json
kube-linter分析 Kubernetes YAML 文件和 Helm 图表以确保生产就绪和安全。
$ brew install kube-linter
$ kube-linter lint kubernetes/ecc-demo-service.yml --format json > kube-linter-report.json
kubesec为 Kubernetes 资源提供安全风险分析。kubesec
也集成到GitLab SAST 扫描仪中。
$ docker run -i kubesec/kubesec:512c5e0 scan /dev/stdin < kubernetes/ecc-demo-service.yml
那里有许多扫描仪,其中大多数以 JSON 格式返回结果,这些结果可以被解析并集成到您的 CI/CD 管道中。您可以在本期了解更多关于 GitLab IaC 扫描仪的评估。问题中的表格包括许可证、语言、输出和示例。
checkov
并tfsec
提供 JUnit XML 报告作为输出格式,可以解析并集成到 CI/CD 中。漏洞报告将需要不同的格式,但不会将它们与单元测试结果混淆。在 GitLab 中集成 SAST 扫描器需要您提供artifacts:reports:sast作为指定的输出格式和 API。然后,GitLab 集成可以使用此报告,例如 MR 小部件和漏洞仪表板,在 Ultimate 层中可用。以下屏幕截图显示了在此 MR中添加具有潜在漏洞的 Kubernetes 部署和服务。
在 CI/CD 管道或计划运行中收集 JSON 报告的方法有多种。其中一个想法可以是使用 Markdown 表创建合并请求评论。它需要更多的工作来解析报告、格式化评论文本以及与 GitLab REST API 交互,如 Python 脚本中的以下步骤所示。您可以按照实施步骤以扫描仪类型的首选语言重新创建它们,并使用GitLab API 客户端。
首先,读取 JSON 格式的报告,检查是否kics_version
设置为继续。然后提取queries
密钥,并准备comment_body
带有降价表标题列。
FILE="kics/kics-report.json"f = open(FILE)
report = json.load(f)
# Parse the report: kics
if "kics_version" in report:
print("Found kics '%s' in '%s'" % (report["kics_version"], FILE))
queries = report["queries"]
else:
raise Exception("Unsupported report format")
comment_body = """### kics vulnerabilities report| Severity | Description | Platform | Filename |
|----------|-------------|----------|----------|
"
""
接下来,我们需要循环解析所有查询,并收集所有列值。它们被收集到一个新列表中,然后与|
角色一起加入。键需要一个嵌套集合,因为这files
是一个字典列表,其中只有file_name
演示感兴趣。
# Example query to parse: {'query_name': 'Service Does Not Target Pod', 'query_id': '3ca03a61-3249-4c16-8427-6f8e47dda729', 'query_url': 'https://kubernetes.io/docs/concepts/services-networking/service/', 'severity': 'LOW', 'platform': 'Kubernetes', 'category': 'Insecure Configurations', 'description': 'Service should Target a Pod', 'description_id': 'e7c26645', 'files': [{'file_name': 'kubernetes/ecc-demo-service.yml', 'similarity_id': '9da6166956ad0fcfb1dd533df17852342dcbcca02ac559becaf51f6efdc015e8', 'line': 38, 'issue_type': 'IncorrectValue', 'search_key': 'metadata.name={{ecc-demo-service}}.spec.ports.name={{web}}.targetPort', 'search_line': 0, 'search_value': '', 'expected_value': 'metadata.name={{ecc-demo-service}}.spec.ports={{web}}.targetPort has a Pod Port', 'actual_value': 'metadata.name={{ecc-demo-service}}.spec.ports={{web}}.targetPort does not have a Pod Port'}]}for q in queries:
#print(q) # DEBUG
l = []
l.append(q["severity"])
l.append(q["description"])
l.append(q["platform"])
if "files" in q:
l.append(",".join((f["file_name"] for f in q["files"])))
comment_body += "| " + " | ".join(l) + " |\n"
f.close()
降价表已经准备好,现在是时候与 GitLab API 进行通信了。python-gitlab提供了一个很棒的抽象层和编程接口。
GitLab API 需要具有 API 权限的项目/组访问令牌。CI_JOB_TOKEN
是不够的。
从环境中读取GITLAB_TOKEN
,并实例化一个新Gitlab
对象。
GITLAB_URL='https://gitlab.com'if 'GITLAB_TOKEN' in os.environ:
gl = gitlab.Gitlab(GITLAB_URL, private_token=os.environ['GITLAB_TOKEN'])
else:
raise Exception('GITLAB_TOKEN variable not set. Please provide an API token to update the MR!')
接下来,使用CI_PROJECT_ID
环境中的 CI/CD 变量来选择包含我们要定位的合并请求的项目对象。
project = gl.projects.get(os.environ['CI_PROJECT_ID'])
棘手的部分是从 CI/CD 管道中获取合并请求ID,它并不总是可用的。一种解决方法是读取CI_COMMIT_REF_NAME
变量并将其与项目中的所有 MR 进行匹配,查看是否source_branch
匹配。
real_mr = Noneif 'CI_MERGE_REQUEST_ID' in os.environ:
mr_id = os.environ['CI_MERGE_REQUEST_ID']
real_mr = project.mergerequests.get(mr_id)
# Note: This workaround can be very expensive in projects with many MRs
if 'CI_COMMIT_REF_NAME' in os.environ:
commit_ref_name = os.environ['CI_COMMIT_REF_NAME']
mrs = project.mergerequests.list()
for mr in mrs:
if mr.source_branch in commit_ref_name:
real_mr = mr
# found the MR for this source branch
# print(mr) # DEBUG
if not real_mr:
print("Pipeline not run in a merge request, no reports sent")
sys.exit(0)
最后但同样重要的是,使用 MR 对象创建一个包含之前创建的Markdown 表的新笔记。comment_body
mr_note = real_mr.notes.create({'body': comment_body})
每次推送新的提交时,此工作流程都会创建一个新的 MR 评论。考虑自行评估脚本并优化更新频率。该脚本可以通过运行 kics 集成到 CI/CD 中,然后生成以下示例配置所示的报告.gitlab-ci.yml
:
# Full RAW example for kics reports and scans
kics-scan:
image: python:3.10.2-slim-bullseye
variables:
# Visit for new releases
# https://github.com/Checkmarx/kics/releases
KICS_VERSION: "1.5.1"
script:
- echo $CI_PIPELINE_SOURCE
- echo $CI_COMMIT_REF_NAME
- echo $CI_MERGE_REQUEST_ID
- echo $CI_MERGE_REQUEST_IID
- apt-get update && apt-get install wget tar --no-install-recommends
- set -ex; wget -q -c "https://github.com/Checkmarx/kics/releases/download/v${KICS_VERSION}/kics_${KICS_VERSION}_linux_x64.tar.gz" -O - | tar -xz --directory /usr/bin &>/dev/null
# local requirements
- pip install -r requirements.txt
- kics scan --no-progress -q /usr/bin/assets/queries -p $(pwd) -o $(pwd) --report-formats json --output-path kics --output-name kics-report.json || true
- python ./integrations/kics-scan-report-mr-update.py
您可以在此项目中找到.gitlab-ci.yml 配置和完整脚本,包括更多内联注释和调试输出。您可以在此评论中看到实现 MR 测试本身。
上一节中的步骤显示了原始kics
命令执行,包括需要您创建自己的解析逻辑的 JSON 报告解析。或者,您可以依赖GitLab 中的 IaC 扫描仪并将 SAST JSON 报告解析为标准化格式。这适用于所有 GitLab 层。
下载gl-sast-report.json 示例,将其保存gl-sast-report.json
在与脚本相同的目录中,并以与上所示类似的方式解析报告。
FILE="gl-sast-report.json"f = open(FILE)
report = json.load(f)
# Parse the report: kics
if "scan" in report:
print("Found scanner '%s' in '%s'" % (report["scan"]["scanner"]["name"], FILE))
queries = report["vulnerabilities"]
else:
raise Exception("Unsupported report format")
漏洞报告中的参数还包括 CVE 编号。这location
是使用嵌套字典,因此更容易解析。
comment_body = """### IaC SAST vulnerabilities report| Severity | Description | Category | Location | CVE |
|----------|-------------|----------|----------|-----|
"
""for q in queries:
#print(q) # DEBUG
l = []
l.append(q["severity"])
l.append(q["description"])
l.append(q["category"])
l.append(q["location"]["file"])
l.append(q["cve"])
comment_body += "| " + " | ".join(l) + " |\n"
f.close()
包含 Markdown表comment_body
,并且可以使用相同的代码使用 GitLab API Python 绑定使用注释更新 MR。此 MR 评论中显示了一个示例运行。
您可以使用以下步骤将脚本集成到 CI/CD 工作流中:1) 覆盖模板创建的kics-iac-sast
作业,以及 2) 添加解析 JSON 报告并调用脚本以发送 MR 评论的作业。artifacts``Security/SAST-IaC.latest.gitlab-ci.yml``iac-sast-parse
# GitLab integration with SAST reports spec
include:
- template: Security/SAST-IaC.latest.gitlab-ci.yml# Override the SAST report artifacts
kics-iac-sast:
artifacts:
name: sast
paths:
- gl-sast-report.json
reports:
sast: gl-sast-report.json
iac-sast-parse:
image: python:3.10.2-slim-bullseye
needs: ['kics-iac-sast']
script:
- echo "Parsing gl-sast-report.json"
- pip install -r requirements.txt
- python ./integrations/sast-iac-report-mr-update.py
artifacts:
paths:
- gl-sast-report.json
CI/CD 管道测试本身可以在这个 MR 评论中找到。请查看sast-iac-report-mr-update.py脚本并评估它是否对您的工作流程有用。
评估扫描仪的一种方法是查看它们的可扩展性。例如,kics调用它们queries
,semgrep使用rules
,[checkov](https://www.checkov.io/3.Custom Policies/Custom Policies Overview.html)说policies
,tfsec用作custom checks
名称。这些规范允许您使用广泛的教程指南创建和贡献自己的检测方法。
许多显示的扫描仪都提供了要使用的容器映像,或 CI/CD 集成文档。确保在您的评估中包含此要求。对于完全集成和测试的解决方案,请使用GitLab 中的 IaC 安全扫描功能,目前基于kics
扫描仪。如果您已经有使用其他扫描仪的经验,或者更喜欢自己的自定义集成,请评估您的解决方案的替代方案。本博文中讨论的所有扫描器都提供 JSON 作为输出格式,这有助于程序化解析和自动化。
也许您想贡献一个新的 IaC 扫描器或帮助改进开源扫描器的检测规则和功能:-)
Sawyer Bengtson在Unsplash上的封面图片