vegazbabz/CIS-Azure-Benchmark-Audit-Tool

GitHub: vegazbabz/CIS-Azure-Benchmark-Audit-Tool

基于 CIS Azure 基准的轻量级自动化合规审计工具,无需外部依赖即可生成带修复指引的交互式 HTML 报告。

Stars: 3 | Forks: 0

# CIS Microsoft Azure Foundations Benchmark v5.0.0 — 审计工具 [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) [![CIS Benchmark](https://img.shields.io/badge/CIS%20Benchmark-v5.0.0-orange.svg)](https://www.cisecurity.org/benchmark/azure) [![Python](https://img.shields.io/badge/Python-3.10%2B-blue.svg)](https://www.python.org/) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/12bf16193b205541.svg)](https://github.com/vegazbabz/CIS-Azure-Benchmark-Audit-Tool/actions/workflows/ci.yml) ![Sample report dashboard](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/0bd4391bb1205552.png) **版本:** 1.0.0-beta3 **基准:** [CIS Microsoft Azure Foundations Benchmark v5.0.0](https://www.cisecurity.org/benchmark/azure) (2025年9月) **覆盖范围:** 94项自动化控制 · 输出中注明了2项手动控制 · 1项控制待定 (2.1.1) ## 概述 一个针对 **[CIS Microsoft Azure Foundations Benchmark v5.0.0](https://www.cisecurity.org/benchmark/azure)** 对 Azure 租户进行审计的 Python 工具 —— 这是由 [Center for Internet Security (CIS)](https://www.cisecurity.org/) 发布的 Azure 环境行业标准加固指南。 除标准库外,它不需要任何 pip 安装 —— 仅需 Python 3.10+ 和 Azure CLI。 结果会在每个订阅完成后作为检查点保存,因此失败或中断的运行 可以在不重新运行已完成工作的情况下恢复。输出是一个包含 过滤、合规性评分、图表和针对每项发现的修复指南的独立 HTML 报告。JSON 和 CSV 导出文件 会随 HTML 自动生成。 ## 系统要求 ### 运行时 | 要求 | 详情 | | --- | --- | | Python | [3.10 或更高版本](https://www.python.org/downloads/) | | Azure CLI | 任何最新版本 — | | resource-graph 扩展 | 首次运行时自动安装 | | Azure 登录 | 运行前完成 `az login` | | msal | `pip install -r requirements.txt` — 检查项 5.1.1 必需(可选,见下文) | ### Azure 权限 | 范围 | 角色 | 用途 | | --- | --- | --- | | 每个订阅 | Reader | 枚举所有资源 | | 每个订阅 | Security Reader | Defender 计划,安全联系人 | | Microsoft Entra ID (租户) | Global Reader | 身份检查 (5.x) | | Key Vaults (可选) | Key Vault Reader | 列出 8.3.x 检查的密钥、机密、证书 | ### 依赖项 该工具有一个运行时依赖项 —— `msal` —— 仅用于检查项 5.1.1 (安全默认值)。 使用以下命令安装: ``` pip install -r requirements.txt ``` 如果未安装 `msal`,检查项 5.1.1 仍将通过 `az` CLI 路径运行,但将返回 ERROR,因为 `az` CLI 应用无法获取 `Policy.Read.All`。所有其他检查没有 pip 依赖项。 开发依赖项(`black`, `flake8`, `mypy`, 以及可选的 `rich`)列在 `requirements-dev.txt` 中: ``` pip install -r requirements-dev.txt ``` ## 快速开始 ``` # 登录 Azure az login # 运行审计(审计所有已启用的 subscriptions) python cis_azure_audit.py ``` 审计完成后,报告将自动在您的浏览器中打开。该脚本还将 在缺失时自动安装 `resource-graph` 扩展,枚举所有已启用的 订阅,运行所有检查,并将报告文件保存到 `reports/` 子目录。 ## 入门指南(分步说明) 不熟悉 Python 或 Azure CLI?按照以下步骤在您的机器上运行该工具。 ### 步骤 1 — 安装 Python 1. 前往 [https://www.python.org/downloads/](https://www.python.org/downloads/) 并下载 适用于您操作系统的最新 **Python 3.10+** 安装程序。 2. 运行安装程序。在 Windows 上,在点击 Install 之前勾选 **"Add Python to PATH"**。 3. 验证安装: python --version ### 步骤 2 — 安装 Azure CLI 1. 按照 上适用于您操作系统的官方说明进行操作。 2. 验证: az --version ### 步骤 3 — 获取工具 **选项 A — 使用 Git 克隆**(推荐 —— 便于更新): ``` git clone https://github.com/vegazbabz/CIS-Azure-Benchmark-Audit-Tool.git cd CIS-Azure-Benchmark-Audit-Tool ``` **选项 B — 下载 ZIP**(无需 Git): 1. 在 [GitHub 仓库页面](https://github.com/vegazbabz/CIS-Azure-Benchmark-Audit-Tool), 点击绿色的 **Code** 按钮 → **Download ZIP**。 2. 解压 ZIP 并在解压后的文件夹中打开终端。 ### 步骤 4 — 安装 Python 依赖项 唯一的运行时依赖是 `msal`(用于一项可选检查)。使用以下命令安装: ``` pip install -r requirements.txt ``` ### 步骤 5 — 登录 Azure ``` az login ``` 将打开一个浏览器窗口供您登录。您使用的帐户至少需要对 您要审计的订阅拥有 **Reader** 和 **Security Reader** 权限。 ### 步骤 6 — 运行审计 ``` python cis_azure_audit.py ``` 该工具将枚举您所有已启用的订阅,运行所有检查,并在完成后 自动在您的浏览器中打开 HTML 报告。报告将保存到 `reports/` 子文件夹。 ## 项目结构 ``` cis_azure_audit.py Main entry point and CLI cis_audit.toml Optional configuration file (parallel, timeouts, etc.) requirements.txt Runtime pip dependency (msal) azure/ client.py az CLI wrappers, retry logic, error classification helpers graph_auth.py MSAL-based Graph auth for Policy.Read.All (check 5.1.1) helpers.py Shared Azure utilities identity.py Permission preflight and role helpers checks/ s2.py Section 2 — Databricks s5.py Section 5 — Identity s6.py Section 6 — Monitoring s7.py Section 7 — Networking s8.py Section 8 — Security Services s9.py Section 9 — Storage cis/ checkpoint.py Checkpoint read/write, result reclassification on load check_helpers.py Shared check utilities (port ranges, NSG rules, etc.) config.py Config file loader (cis_audit.toml) helpers.py Logging setup, console output history.py Run history for compliance trend tracking models.py Result dataclass (R) report.py HTML report generation, JSON/CSV export suppressions.py Finding suppression (accepted risks) scripts/ preflight_check.py Standalone permission check script tests/ Unit test suite (no Azure connection required) ``` ## 配置文件 `cis_audit.toml` 从 `cis_azure_audit.py` 所在的目录自动加载。 所有设置都是可选的 —— 省略任何行以保留内置默认值。 可以使用 `CIS_AUDIT_CONFIG` 环境变量覆盖该路径。 ``` [audit] parallel = 3 # Concurrent subscription workers (1–20) executor = "thread" # "thread" (recommended on Windows) or "process" checkpoint_dir = "cis_checkpoints" [timeouts] default = 20 # Most az CLI calls (seconds) storage_list = 30 # az storage account list storage_svc = 15 # Per-account blob/file/table service queries activity_log = 25 # Activity log queries graph = 120 # Resource Graph bulk queries # 可选 — 启用 5.1.1 (Security defaults) 的自动化评估。 # 请参阅下方的“Check 5.1.1 setup”了解一次性的 app registration 步骤。 [graph_auth] # client_id = "00000000-0000-0000-0000-000000000000" # tenant_id = "" # 可选 — 通过 az account show 自动检测 # client_secret = "" # 仅限 SP / CI;交互式用户认证请省略 ``` 当 CLI 标志和 `cis_audit.toml` 值同时设置时,前者优先。 ## 使用方法 ``` python cis_azure_audit.py [options] ``` ### 所有选项 | 选项 | 描述 | | --- | --- | | `-s`, `--subscription` | 按名称或 GUID 审计一个或多个订阅。多个值跟在单个标志后:`-s Sub1 Sub2 Sub3` | | `-o`, `--output` | 输出 HTML 文件名(默认:`reports/cis_azure_audit_report_.html`) | | `--output-dir` | 所有输出文件的目录(HTML, JSON, CSV, 检查点) | | `-p`, `--parallel` | 并发订阅工作线程数(默认:来自配置或 3) | | `--executor` | 工作线程后端:`thread`(默认)或 `process` | | `--no-adaptive-concurrency` | 检测到限制时禁用动态工作线程调整 | | `-l`, `--level` | 仅过滤输出为 Level `1` 或 `2` 的控制项 | | `--fresh` | 清除所有检查点并开始完全重新审计 | | `--report-only` | 从现有检查点重新生成 HTML/JSON/CSV —— 无 API 调用 | | `--suppressions` | 屏蔽规则 TOML 文件的路径(默认:脚本旁的 `suppressions.toml`) | | `--list-suppressions` | 打印所有活动的屏蔽规则并退出 | | `--no-open` | 审计完成后不要在浏览器中自动打开报告 | | `--skip-preflight` | 跳过权限预检 | | `-q`, `--quiet` | 抑制每项检查的进度行;仅显示摘要 | | `--log-level` | 基础日志级别:`TRACE`, `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` | | `-v`, `--verbose` | 详细日志记录(设置 `DEBUG`) | | `--debug` | 跟踪日志记录(设置 `TRACE`) | | `--log-file` | 将完整日志写入文件,除了控制台外 | ### 示例 ``` # 审计所有 subscriptions python cis_azure_audit.py # 按名称审计单个 subscription python cis_azure_audit.py -s "Production" # 审计多个 subscriptions python cis_azure_audit.py -s "Production" "Staging" # 使用更多 parallel workers 运行得更快 python cis_azure_audit.py --parallel 5 # 使用 thread workers(Windows 上推荐) python cis_azure_audit.py --executor thread --parallel 4 # 将所有内容保存到特定文件夹 python cis_azure_audit.py --output-dir C:\AuditResults # 运行中断?只需重新运行 — 它会自动恢复 python cis_azure_audit.py # 完全从头开始,忽略之前的 checkpoints python cis_azure_audit.py --fresh # 重新生成 HTML report,无需重新运行任何 checks python cis_azure_audit.py --report-only # 仅限 Level 1 controls python cis_azure_audit.py --level 1 # 安静模式 — 隐藏每个 check 的输出行,仅显示摘要 python cis_azure_audit.py --quiet # 写入 log file 的 Trace 级别诊断信息 python cis_azure_audit.py --debug --log-file cis_audit.log # 查看当前被屏蔽的 findings python cis_azure_audit.py --list-suppressions # 编辑 suppressions.toml 后重新生成 report(无需重新审计) python cis_azure_audit.py --report-only ``` ### 并发调优 当检测到 Azure API 限制 (HTTP 429) 时,审计会自动调整工作线程数: - 从 `--parallel` 个工作线程开始(最少 1 个)。 - 当暂时性限制重试激增时减少工作线程。 - 在批次稳定后逐渐恢复工作线程。 使用 `--no-adaptive-concurrency` 保持工作线程数固定。 #### 对您自己的默认值进行基准测试 ``` $py = "python" $sub = "" $runs = @( @{executor="thread"; parallel=2}, @{executor="thread"; parallel=4}, @{executor="process"; parallel=2} ) foreach ($r in $runs) { $label = "$($r.executor)-p$($r.parallel)" $log = "bench_$label.log" $elapsed = Measure-Command { & $py cis_azure_audit.py --subscription "$sub" --executor $r.executor --parallel $r.parallel ` --level 1 --fresh --skip-preflight --output "bench_$label.html" --log-file $log *> $null } $retries = (Select-String -Path $log -Pattern "transient error" -SimpleMatch | Measure-Object).Count Write-Output "$label : $([Math]::Round($elapsed.TotalSeconds,2))s retries=$retries" } ``` 每个候选方案运行 2-3 次并比较中位运行时间,而不是单次运行。 ## 工作原理 ### 数据收集 —— 三种方法 #### 1. Azure Resource Graph(批量预取 —— 每次审计一次) 在任何针对每个订阅的工作开始之前,Kusto 查询会在 单次往返中获取整个租户的所有相关资源: - 网络安全组和安全规则 - 存储帐户和安全属性 - Key Vault —— 访问配置和网络设置 - 虚拟网络、子网和 NSG 关联 - 应用程序网关和 WAF 设置 - Databricks 工作区 - Bastion Host - Network Watcher 和资源位置 - 角色分配(所有者和用户访问管理员) - WAF 策略 #### 2. 每个订阅的 Azure CLI 调用 用于 Resource Graph 无法公开的实时服务配置和数据: - `az security pricing show` — Defender 计划状态 (8.1.x) - `az security contact list` — 通知设置 (8.1.12–8.1.15) - `az monitor diagnostic-settings list` — Key Vault 和 App Service 日志记录 - `az monitor activity-log alert list` — 所有 11 项警报检查 (6.1.2.x) - `az keyvault key/secret list` — 每个密钥和机密的到期日期 - `az keyvault key rotation-policy show` — 自动轮换配置 - `az keyvault certificate show` — 证书有效期 - `az storage account blob-service-properties show` — 软删除、版本控制 - `az storage account file-service-properties show` — 文件软删除、SMB 设置 - `az network watcher flow-log list` — 流日志保留 (7.5, 7.8) - `az role definition list` — 自定义管理员角色 (5.23) #### 3. 通过 `az rest` 的 Azure REST API 用于无法通过 az CLI 获得的租户级身份检查: - `graph.microsoft.com/v1.0/policies/authorizationPolicy` — 涵盖 5.4, 5.14, 5.15, 5.16 - ARM REST 用于 WDATP 集成设置 (8.1.3.3) 和攻击路径通知 (8.1.15) ### 权限预检 在审计开始之前,该工具会检查运行帐户是否在每个 订阅上拥有所需角色。在大型租户上,这会并行运行(一次最多 8 个订阅),因此 检查在几秒钟而不是几分钟内完成。 ### 检查点和恢复 在每个订阅完成后,结果将写入 `cis_checkpoints/.json`。 如果脚本在运行中途停止或崩溃,重新运行它将跳过已完成的订阅并 从中断处继续。使用 `--fresh` 丢弃所有检查点并重新开始。 按一次 **Ctrl+C** 即可干净退出:进行中的 Azure CLI 子进程立即被终止, 打印一条消息告诉您如何恢复或重新开始,并且进程退出而不会 留下孤立的后台进程或冻结的终端。 ### 并行执行 订阅通过 Python 的 `concurrent.futures` 执行器并发运行。默认为 3 个并行 工作线程(可在 `cis_audit.toml` 中配置或通过 `--parallel`)。Resource Graph 预取始终 在并行循环开始前运行一次。 ## HTML 报告 生成的报告是一个没有外部依赖项的独立 HTML 文件。 - **摘要卡片** — 合规性评分(PASS / 总数,不包括 INFO 和 MANUAL),以及每种状态的计数。 - **合规性圆环图** — 三个环形图,分别显示整体、Level 1 和 Level 2 的 PASS/FAIL/ERROR 比例。 - **部分细分** — 每个 CIS 部分的水平堆叠条,从最差到最好排序。 - **每个订阅的摘要** — 显示每个订阅的通过/失败/错误计数的堆叠条表格;单击一行以将结果表过滤到该订阅。 - **可过滤表格** — 同时按自由文本搜索、订阅、状态和级别 (L1/L2) 过滤。当所有结果都被过滤掉时,部分标题会折叠。 - **每个资源的结果** — 每个 NSG、存储帐户、Key Vault、子网和 Databricks 工作区都单独报告,而不是聚合为每个控制项的单个通过/失败。 - **修复提示** — 每个 FAIL 结果都包含用于解决问题的 Azure 门户导航路径。ERROR 结果包含关于缺少什么访问权限的可操作说明。 - **导出** — JSON 和 CSV 文件在报告时随 HTML 自动生成。在报告中单击 **Export JSON** 或 **Export CSV** 下载它们。 - **合规性趋势** — 在两次或更多次完整租户审计运行后,结果表上方会出现一个可折叠图表,显示随时间变化的合规性评分。默认折叠,以免分散对当前发现的注意力。使用 `--subscription` 过滤器运行时不显示,因为过滤后的运行反映的是租户的一个子集,无法与完整租户基线进行比较。 - **回到顶部** — 用于长报告的右下角固定按钮。 ### 状态类型 | 状态 | 含义 | | --- | --- | | PASS | 控制项合规 | | FAIL | 控制项不合规 —— 提供修复提示 | | ERROR | 检查无法完成 —— 审计缺口(权限缺失、超时或 API 错误)。合规性**未知**;请勿视为正常。 | | INFO | 不适用 —— 资源类型不存在或帐户类型不支持该功能(例如 ADLS Gen2 没有 blob/file 服务) | | MANUAL | 无法自动化 —— 需要根据 CIS PDF 进行手动验证 | | SUPPRESSED | 已接受风险 —— 在 `suppressions.toml` 中定义,带有理由和到期日 | ### `--report-only`:重新生成报告而不重新审计 ``` python cis_azure_audit.py --report-only ``` 加载所有现有检查点并重新生成 HTML、JSON 和 CSV —— 不进行任何 Azure API 调用。 在以下情况后很有用: - 编辑 `suppressions.toml` 以接受一项发现 - 升级工具(加载时应用新的错误分类和消息格式) - 更改 `--level` 过滤器 第 5 部分租户级身份检查(5.1.1, 5.1.2, 5.1.3, 5.4, 5.14, 5.15, 5.16)在 报告时**重新运行**,因为它们不存储在订阅检查点中。实时 Graph API 结果(5.4, 5.14, 5.15, 5.16)将反映您租户的当前状态。MANUAL 发现 (5.1.3)将始终出现。如果 Graph 无法访问,这些检查将被跳过并发出警告,并且 报告仍从检查点生成。 从检查点加载的结果会使用当前工具逻辑自动重新分类: 旧检查点中记录为 ERROR 的 `FeatureNotSupportedForAccount` 存储错误显示为 INFO,Key Vault 权限错误被重写为清晰的可操作消息,并且工具更新版本中引入的 任何部分名称更正都会在加载时应用。 ### 输出文件 每次运行生成三个报告文件并更新运行历史记录: | 文件 | 内容 | | --- | --- | | `reports/cis_azure_audit_report_.html` | 独立交互式报告(在浏览器中自动打开) | | `reports/cis_azure_audit_report_.json` | 作为 JSON 数组的所有结果 | | `reports/cis_azure_audit_report_.csv` | 作为扁平 CSV 的所有结果 | | `reports/cis_run_history.json` | 趋势图的合规性评分历史(最近 30 次完整租户运行) | 使用 `--output` 设置自定义文件名,或使用 `--output-dir` 将所有输出重定向到不同的目录。 历史文件始终写入与 HTML 报告相同的目录. ## 屏蔽发现(已接受风险) 有些发现是有意的 —— 例如,故意开放 RDP 的跳转主机,或 特定环境不需要的 DDoS 计划。如果没有确认这些的方法, 每个报告都包含相同的已知发现,审查者就会不再信任输出。 `cis_azure_audit.py` 旁边的 `suppressions.toml` 文件允许您将特定发现标记为 已接受风险。被屏蔽的发现仍以灰色显示在报告中,并显示您的理由 —— 它们永远不会被隐藏。合规性评分将其排除(像 INFO 和 MANUAL 一样)。 屏蔽规则**仅在报告生成时**应用。检查点始终存储原始 FAIL,因此删除屏蔽规则并运行 `--report-only` 会立即恢复该发现。 ### suppressions.toml 格式 ``` [[suppressions]] control_id = "7.1" resource = "jumphost-nsg" # optional — exact match; omit to match all resources subscription = "Production" # optional — exact match; omit to match all subscriptions justification = "Intentional RDP jump host — restricted by Azure Firewall IP allowlist" expires = "2026-12-31" # required — ISO date, maximum 1 year from today [[suppressions]] control_id = "8.5" justification = "DDoS protection not required — no public-facing workloads in this tenant" expires = "2026-06-01" ``` ### 屏蔽规则 | 字段 | 必需 | 行为 | | --- | --- | --- | | `control_id` | 是 | 与 CIS 控制编号完全匹配 | | `resource` | 否 | 与资源名称不区分大小写完全匹配;省略以匹配所有 | | `subscription` | 否 | 与订阅名称不区分大小写完全匹配;省略以匹配所有 | | `justification` | 是 | 自由文本 —— 在发现旁边的报告中显示 | | `expires` | 是 | ISO 日期(`YYYY-MM-DD`);距今天最多 1 年 | - 只有 `FAIL` 和 `ERROR` 发现可以被屏蔽 - 过期条目将被跳过,发现会恢复为 `FAIL`/`ERROR` 并记录警告 - 到期时间超过 1 年的条目将被限制为 1 年并发出警告 ### 屏蔽工作流 ``` # 1. 检查当前被屏蔽的内容 python cis_azure_audit.py --list-suppressions # 2. 编辑 suppressions.toml,然后在不重新审计的情况下重新生成 report python cis_azure_audit.py --report-only # 3. 使用自定义 suppressions 文件(例如按环境配置) python cis_azure_audit.py --suppressions prod-suppressions.toml ``` ## 覆盖的控制项 ### 第 2 部分 — Azure Databricks(6 项中有 5 项自动化) | 控制项 | 标题 | 级别 | | --- | --- | --- | | 2.1.2 | 为 Databricks 子网配置 NSG | L1 | | 2.1.7 | 已配置诊断日志记录 | L1 | | 2.1.9 | 未启用公共 IP | L1 | | 2.1.10 | 已禁用公共网络访问 | L1 | | 2.1.11 | 使用专用终结点访问工作区 | L2 | ### 第 5 部分 — 身份服务(9 项自动化 · 2 项手动) | 控制项 | 标题 | 级别 | 备注 | | --- | --- | --- | --- | | 5.1.1 | 已启用安全默认值 | L1 | 需要 `[graph_auth]` 设置 —— 见下文 | | 5.1.2 | 为所有用户启用 MFA | L1 | | | 5.1.3 | 禁止用户在受信任的设备上记住 MFA | L1 | **手动** —— 无 API 可用 | | 5.3.3 | 限制用户访问管理员角色 | L1 | | | 5.4 | 限制非管理员用户创建租户 | L1 | | | 5.14 | 用户不能注册应用程序 | L1 | | | 5.15 | 来宾访问仅限于自己的目录对象 | L1 | | | 5.16 | 来宾邀请限制设置为管理员或无人 | L2 | | | 5.23 | 无自定义订阅管理员角色 | L1 | | | 5.27 | 订阅所有者在 2 到 3 人之间 | L1 | | | 5.28 | 特权用户受防钓鱼 MFA 保护 | L1 | **手动** —— Entra ID 门户审查 | #### 检查项 5.1.1 设置(一次性) 1. 在 Entra ID 中,前往 **App registrations** → **New registration**(任意名称,单租户)。 2. **API permissions** → Add → Microsoft Graph → **Delegated** → `Policy.Read.All` → **Grant admin consent**。 3. 对于服务主体 / CI:还需添加 **Application** 权限 `Policy.Read.All` 并同意;通过 `CIS_GRAPH_CLIENT_SECRET` **环境变量**设置机密(推荐 —— 避免在 `cis_audit.toml` 中存储凭据),或仅在本地使用时在 `[graph_auth]` 中设置 `client_secret`。 4. 将 **Application (client) ID** 复制到 `cis_audit.toml`: [graph_auth] client_id = "your-app-client-id" 5. `pip install -r requirements.txt` 6. 运行工具 —— 浏览器窗口打开一次用于交互式登录;后续运行使用 缓存在 `~/.cis_audit/msal_token_cache.json` 中的令牌。 ### 第 6 部分 — 管理和治理(16 项自动化) | 控制项 | 标题 | 级别 | | --- | --- | --- | | 6.1.1.1 | 订阅活动日志存在诊断设置 | L1 | | 6.1.1.2 | 诊断设置捕获所需类别 | L1 | | 6.1.1.4 | Key Vault 诊断日志记录已启用 | L1 | | 6.1.1.6 | Azure AppService HTTP 日志已启用 | L2 | | 6.1.2.1 | 活动日志警报:创建策略分配 | L1 | | 6.1.2.2 | 活动日志警报:删除策略分配 | L1 | | 6.1.2.3 | 活动日志警报:创建或更新 NSG | L1 | | 6.1.2.4 | 活动日志警报:删除 NSG | L1 | | 6.1.2.5 | 活动日志警报:创建或更新安全解决方案 | L1 | | 6.1.2.6 | 活动日志警报:删除安全解决方案 | L1 | | 6.1.2.7 | 活动日志警报:创建或更新 SQL 防火墙规则 | L1 | | 6.1.2.8 | 活动日志警报:删除 SQL 防火墙规则 | L1 | | 6.1.2.9 | 活动日志警报:创建或更新公共 IP | L1 | | 6.1.2.10 | 活动日志警报:删除公共 IP | L1 | | 6.1.2.11 | 活动日志警报:服务运行状况 | L1 | | 6.1.3.1 | 已配置 Application Insights | L2 | ### 第 7 部分 — 网络服务(13 项自动化) | 控制项 | 标题 | 级别 | | --- | --- | --- | | 7.1 | RDP (3389) 未对互联网开放 | L1 | | 7.2 | SSH (22) 未对互联网开放 | L1 | | 7.3 | 来自互联网的 UDP 访问受限制 | L1 | | 7.4 | 来自互联网的 HTTP/HTTPS (80/443) 已评估并受限制 | L1 | | 7.5 | NSG 流日志保留期 >= 90 天 | L2 | | 7.6 | 为所有使用中的区域启用 Network Watcher | L2 | | 7.8 | VNet 流日志保留期 >= 90 天 | L2 | | 7.10 | Azure Application Gateway 上启用了 WAF | L2 | | 7.11 | 子网与 NSG 关联 | L1 | | 7.12 | App Gateway SSL 策略最低 TLS 1.2+ | L1 | | 7.13 | Application Gateway 上启用了 HTTP2 | L1 | | 7.14 | 启用了 WAF 请求正文检查 | L2 | | 7.15 | 启用了 WAF 机器人保护 | L2 | ### 第 8 部分 — 安全服务(30 项自动化) | 控制项 | 标题 | 级别 | | --- | --- | --- | | 8.1.1.1 | Microsoft Defender CSPM | L2 | | 8.1.2.1 | Microsoft Defender for APIs | L2 | | 8.1.3.1 | Microsoft Defender for Servers | L2 | | 8.1.3.3 | 端点保护 (WDATP) 组件 | L2 | | 8.1.4.1 | Microsoft Defender for Containers | L2 | | 8.1.5.1 | Microsoft Defender for Storage | L2 | | 8.1.6.1 | Microsoft Defender for App Services | L2 | | 8.1.7.1 | Microsoft Defender for Azure Cosmos DB | L2 | | 8.1.7.2 | Microsoft Defender for Open-Source Relational DBs | L2 | | 8.1.7.3 | Microsoft Defender for SQL (托管实例) | L2 | | 8.1.7.4 | Microsoft Defender for SQL Servers on Machines | L2 | | 8.1.8.1 | Microsoft Defender for Key Vault | L2 | | 8.1.9.1 | Microsoft Defender for Resource Manager | L2 | | 8.1.10 | Defender 配置为检查 VM 操作系统更新 | L1 | | 8.1.12 | 安全警报通知订阅所有者 | L1 | | 8.1.13 | 安全联系人的其他电子邮件地址 | L1 | | 8.1.14 | 已配置警报严重性通知 | L1 | | 8.1.15 | 已配置攻击路径通知 | L1 | | 8.3.1 | 已设置密钥过期 —— RBAC Key Vault | L1 | | 8.3.2 | 已设置密钥过期 —— 非 RBAC Key Vault | L1 | | 8.3.3 | 已设置机密过期 —— RBAC Key Vault | L1 | | 8.3.4 | 已设置机密过期 —— 非 RBAC Key Vault | L1 | | 8.3.5 | Key Vault 清除保护已 | L1 | | 8.3.6 | Key Vault RBAC 授权已启用 | L2 | | 8.3.7 | Key Vault 公共网络访问已禁用 | L1 | | 8.3.8 | 使用专用终结点访问 Key Vault | L2 | | 8.3.9 | 已启用自动密钥轮换 | L2 | | 8.3.11 | 证书有效期 <= 12 个月 | L1 | | 8.4.1 | Azure Bastion Host 存在 | L2 | | 8.5 | VNet 上启用了 DDoS Network Protection | L2 | ### 第 9 部分 — 存储服务(21 项自动化) | 控制项 | 标题 | 级别 | | --- | --- | --- | | 9.1.1 | Azure Files 软删除已启用 | L1 | | 9.1.2 | SMB 协议版本 >= 3.1.1 | L1 | | 9.1.3 | SMB 通道加密 AES-256-GCM 或更高 | L1 | | 9.2.1 | Blob 软删除已启用 | L1 | | 9.2.2 | 容器软删除已启用 | L1 | | 9.2.3 | Blob 版本控制已启用 | L2 | | 9.3.1.1 | 密钥轮换提醒已启用 | L1 | | 9.3.1.2 | 访问密钥在 90 天内重新生成 | L1 | | 9.3.1.3 | 存储帐户密钥访问已禁用 | L1 | | 9.3.2.1 | 使用专用终结点访问存储帐户 | L2 | | 9.3.2.2 | 公共网络访问已禁用 | L1 | | 9.3.2.3 | 默认网络访问规则为拒绝 | L1 | | 9.3.3.1 | Azure 门户中默认使用 Microsoft Entra 授权 | L1 | | 9.3.4 | 需要安全传输 (HTTPS) | L1 | | 9.3.5 | 允许 Azure 受信任服务访问存储 | L2 | | 9.3.6 | 最低 TLS 版本 1.2 | L1 | | 9.3.7 | 跨租户复制已禁用 | L1 | | 9.3.8 | Blob 匿名访问已禁用 | L1 | | 9.3.9 | 存储帐户具有 CanNotDelete 资源锁 | L1 | | 9.3.10 | 存储帐户具有 ReadOnly 资源锁 | L2 | | 9.3.11 | 冗余设置为异地冗余 (GRS) | L2 | ## 测试 测试套件使用 Python 内置的 `unittest` —— 不需要额外的包。 所有测试都模拟 Azure CLI 调用,因此不需要真正的 Azure 连接。 ### 运行所有 ``` python -m unittest discover -s tests -p "test_*.py" -v ``` ### 运行一个测试文件 ``` python -m unittest tests.test_checks -v python -m unittest tests.test_report -v ``` ### 运行一个测试类 ``` python -m unittest tests.test_permissions.TestPreflight -v ``` ### 持续集成 GitHub Actions 管道在每次推送和拉取请求时运行: - `python -m unittest` (Python 3.10, 3.11, 3.12, 3.13) - `black --check` 格式化 - `flake8` 代码检查 - `mypy` 静态类型检查 工作流文件位于 `.github/workflows/ci.yml`。 ## 检查点文件 ``` cis_checkpoints/ |- xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json <- completed |- yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy.json <- completed `- zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz.json <- failed (retried on next run) ``` 每个文件包含该订阅的完整结果集、UTC 时间戳和完成 状态。删除 `cis_checkpoints/` 文件夹或使用 `--fresh` 丢弃所有检查点。 使用 `--report-only` 从现有检查点重新生成 HTML 报告,而不运行任何检查。 加载检查点时,结果会使用当前工具逻辑自动重新分类。 这意味着升级工具并运行 `--report-only` 会将新版本中修正的错误分类、 消息改进和控制元数据修复应用于您现有的检查点 数据 —— 在大多数情况下,不需要重新审计。 ## 已知限制 **只读** —— 脚本仅进行审计。它不会对您的环境进行任何更改。 **时间点** —— 结果反映脚本运行时的状态。 **Key Vault 数据平面访问** —— 列出密钥、机密和证书除了订阅 Reader 外还需要数据平面 权限。分配 **Key Vault Reader**(RBAC 保管库)或将 运行帐户添加到保管库的访问策略(非 RBAC 保管库)。没有此项,受影响的检查 将返回 ERROR 并附带说明性消息。报告清楚地将此与正常 结果区分开来 —— 合规性未知,而非假定正常。 **检查项 5.1.1 返回 ERROR (Policy.Read.All)** `az` CLI 应用无法获取 `Policy.Read.All`。这是 Microsoft 施加的限制 — az CLI 应用(`04b07795-...`)未预授权该作用域。在 `cis_audit.toml` 中使用您自己的应用注册配置 `[graph_auth]` 以解决此问题(见上文“检查项 5.1.1 设置”)。 **用于身份检查的 Graph API** —— 控制项 5.4, 5.14, 5.15, 和 5.16 通过 `az rest` 调用 Graph API。如果 Azure CLI 应用尚未同意所需的 Graph 权限,这些 将返回 ERROR。使用以下命令测试: ``` az rest --method get --url "https://graph.microsoft.com/v1.0/policies/authorizationPolicy" ``` **条件访问策略 (5.2.x)** —— 在基准中标记为手动,此工具不检查。它们需要在 Entra ID 门户中进行审查。 **大型租户** —— Resource Graph 高效处理批量数据,权限预检 并行运行订阅。规模上的主要瓶颈是每个订阅的 CLI 调用。 使用 `--parallel 5` 或更高,或通过 `cis_audit.toml` 进行调整。 ## 故障排除 **Windows 上找不到 `az`** 该脚本在 Windows 上自动使用 `az.cmd`。确保已安装 Azure CLI 并在您的 PATH 中,然后重新启动终端。 **身份检查返回 ERROR (AccessDenied)** 您的帐户在 Entra ID 中需要 Global Reader。直接测试 Graph 调用: ``` az rest --method get --url "https://graph.microsoft.com/v1.0/policies/authorizationPolicy" ``` 如果失败,请让您的 Entra ID 管理员授予 Global Reader 或同意 Azure CLI 应用所需的 Graph API 权限(应用 ID:`04b07795-8ddb-461a-bbee-02f9e1bf7b46`)。 **5.1.1 特别返回 ERROR (Policy.Read.All)** 如果未配置 `[graph_auth]`,这是预期的。az CLI 应用无法获取此作用域。 见第 5 部分控制表下的“检查项 5.1.1 设置”。 **Key Vault 检查返回 ERROR(审计不完整)** 运行帐户需要 Key Vault 数据平面访问权限。对于启用 RBAC 的保管库,分配 **Key Vault Reader** 角色;对于访问策略保管库,将帐户添加到保管库的访问策略。 报告中的 ERROR 消息确切说明了缺少什么。 **某项检查持续超时** 所有 `az` CLI 调用都有可配置的超时(默认 20 秒)。在 `cis_audit.toml` 中增加它们: ``` [timeouts] default = 40 ``` 超时的检查记录为 ERROR,审计继续。 **找不到订阅** 订阅名称完全匹配(区分大小写)。运行 `az account list --output table` 以 查看您的帐户可用的确切名称。 **运行中断** 按一次 Ctrl+C。进行中的 Azure CLI 调用立即被终止, 已完成订阅的检查点被保留,并且进程干净退出。重新运行相同的命令以 从中断处恢复,或添加 `--fresh` 重新开始。 ## 免责声明 本工具**按“原样”提供,不附带任何形式的保证**。维护者和贡献者 不提供 SLA,不保证准确性或完整性,也不对 本工具的使用或基于其输出做出的任何决定承担法律责任。使用风险完全 由您自己承担。有关完整的 MIT 免责声明,请参阅 [LICENSE](LICENSE)。 ## 致谢 这是一个引用公开可用的 **[CIS Microsoft Azure Foundations Benchmark v5.0.0](https://www.cisecurity.org/benchmark/azure)** 的独立社区实现。 CIS Benchmarks 是 Center for Internet Security () 的财产, 根据 [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) 使用。 本工具不隶属于 CIS、未经其认可或批准。 **版本:** 1.0.0-beta3 **基准:** CIS Microsoft Azure Foundations Benchmark v5.0.0 (2025年9月) **覆盖范围:** 94项自动化控制 · 输出中注明了2项手动控制 · 1项控制待定 (2.1.1)
标签:ATTACK-Python-Client, Azure, Azure CLI, Azure租户, CIS Benchmark, DevSecOps, GPT, HTML报告, IT治理, Python, Web报告查看器, 上游代理, 云基础设施, 关系图谱, 反取证, 合规性扫描, 基线检查, 安全评估, 对称加密, 无后门, 漏洞管理, 自动化审计, 评分系统, 足迹分析, 逆向工具, 配置加固