calvin-quint/sentinel-content-as-code
GitHub: calvin-quint/sentinel-content-as-code
将 Microsoft Sentinel 的检测规则与 SOAR playbook 以代码形式管理,通过 GitHub Actions 实现自动校验和部署的安全运营工程化框架。
Stars: 1 | Forks: 1
# sentinel-content-as-code
以代码形式管理的 Microsoft Sentinel 内容:检测规则、狩猎查询和 SOAR playbook。每一条规则和 playbook 都经过了版本控制,在每次发起 pull request 时于 CI 中进行验证,并在合并到 `main` 分支时通过 Azure REST API 自动部署到 Sentinel 工作区。
此仓库将过去的三个独立仓库合并为一体——`kql-detection-rules`(丰富的单条规则文档)、原始的 `sentinel-content-as-code`(部署流水线)以及 `soar`/`sentinel-automation`(SOAR playbook/监视列表)——因此,检测内容及其部署流水线现在集中存放在同一个地方。
## 仓库布局
```
rules/
analytics/
/
.md # frontmatter (MITRE mapping, threat-intel context,
# validation status, ARM deploy config) + full narrative
# doc + embedded query — see TEMPLATE.md
versions/ # optional: archived prior query iterations (not deployed)
hunting/
/
.md # saved-search / enrichment queries — same template,
# no `analytics_rule` block, not deployed by this pipeline
sigma/
/
.yml # Sigma-authored source for rules where a Sigma→Kusto
# conversion path exists (ASIM-normalized tables today) —
# see sigma/README.md for which rules qualify and why
yara/
/
.yar # artifact-ID rules for named-threat detections —
# companion to a rule's threat_intel.yara_rule field,
# see yara/README.md
playbooks/
parents/ # Sentinel-triggered Logic Apps — orchestrate enrichment,
# write incident comments, trigger remediation
children/ # HTTP-triggered Logic Apps — one action each (enrich an
# IP/URL/hash/user, run a KQL query, notify Teams, disable
# an account, revoke sessions), return structured JSON only
automations/
/ # Timer-triggered Logic Apps, independent of incidents
watchlists/
*.csv # TrustedIPs, ServiceAccounts, PrivilegedAccounts,
# PlaybookOverride, SanctionedTools
schemas/
analytics-rule.schema.json # JSON Schema the derived ARM properties of every
# deployable rule are validated against
scripts/
rule_transform.py # parses a rule .md's frontmatter + Sentinel query block
# into a Sentinel alertRules REST API body
validate_rules.py # schema + duplicate-id + query-resolution validation
deploy_rules.py # deploys (PUTs) rules via `az rest`
deploy-playbook.py # deploys a single Logic App playbook/automation
sync-watchlist.sh # uploads watchlist CSVs to Sentinel
validate-watchlists.py # checks watchlist column headers
.github/workflows/
validate-rules.yml # PR: schema validation + dry-run deploy payload build
deploy-rules.yml # push to main: deploys changed/all analytics rules
deploy-playbooks.yml # push to main: deploys changed/all playbooks
sync-watchlists.yml # push to main: uploads watchlist CSVs
validate-watchlists.yml # PR: validates watchlist CSV headers
```
规则按类别(例如 `identity`、`credential-access`、`aitm-and-token-theft`、`ransomware`、`network`)分组到文件夹中,这纯粹是为了方便管理——文件夹名称没有任何功能性影响。可根据需要添加新的类别。
## 检测页面格式
每条规则都是一个单独的 Markdown 页面(参见 [`TEMPLATE.md`](TEMPLATE.md)):
YAML frontmatter 包含 MITRE ATT&CK 映射、命名威胁/威胁情报上下文(或明确的“无命名威胁”声明)、Atomic Red Team 验证状态,以及——对于任何作为活跃 Sentinel 分析规则部署的内容——一个与 ARM schema 几乎逐字段匹配的 `analytics_rule` 块。正文由一组固定的部分组成:摘要、假设、威胁情报上下文、查询(标注了 Defender XDR 和/或 Sentinel)、分析规则配置、命中表现、误报说明、检测盲区、验证、参考。
`rule_transform.py` 直接从该页面派生可部署的 ARM 属性:`name` 取自标题,`description` 取自摘要部分,`severity` 取自 frontmatter(首字母大写;`critical` 映射为 ARM 的 `High`,因为 ARM 没有 Critical 枚举值),而 `query` 取自查询部分中 **Sentinel** 标签下的 `kusto` 围栏代码块。没有 `analytics_rule` 块的页面(狩猎/查找查询、纯叙述页面)会被解析,但永远不会被部署。
## 编写规则
复制 [`TEMPLATE.md`](TEMPLATE.md) 作为起点。可部署规则的 `analytics_rule` 块所需的 frontmatter:
| 字段 | 说明 |
|---|---|
| `id` | GUID。使用 `uuidgen` 或 `[guid]::NewGuid()` 生成一次,并且切勿更改——更改它会创建一条新规则,而不是更新现有规则。 |
| `queryFrequency` / `queryPeriod` | ISO 8601 持续时间,例如 `PT1H`。 |
| `triggerOperator` / `triggerThreshold` | 例如 `gt` / `0`。 |
`name`、`description`、`severity` 和 `query` **不**直接在 `analytics_rule` 中设置——它们分别派生自页面的标题、摘要部分、顶级 `severity` 字段和查询部分。
可选的 ARM 字段(`tactics`、`relevantTechniques`、`entityMappings`、`incidentConfiguration`、`eventGroupingSettings`、`customDetails`、`alertDetailsOverride`、`requiredDataConnectors`)记录在 [`schemas/analytics-rule.schema.json`](schemas/analytics-rule.schema.json) 中。
目前仅支持 `Scheduled` 规则类型。
### 本地验证
```
cd scripts
pip install -r requirements.txt
python validate_rules.py ../rules/analytics
python deploy_rules.py ../rules/analytics --dry-run # prints what would deploy, makes no changes
```
## CI/CD
- 涉及 `rules/`、`schemas/` 或 `scripts/` 的 **Pull request** 会运行 [`validate-rules.yml`](.github/workflows/validate-rules.yml):执行 schema 验证以及部署 payload 构建的 dry run。不会向 Azure 发送任何内容。Playbook/自动化 JSON 由 push 时的 [`deploy-playbooks.yml` 中的 `validate-playbooks` 步骤](.github/workflows/deploy-playbooks.yml)进行验证,而监视列表标头由 [`validate-watchlists.yml`](.github/workflows/validate-watchlists.yml) 验证。
- **推送到 `main`** 会运行 [`deploy-rules.yml`](.github/workflows/deploy-rules.yml):首先进行验证,然后通过 OIDC 向 Azure 进行身份验证,并使用幂等的 `PUT` 请求将 `rules/analytics/` 下每个可部署的规则部署到 `Microsoft.SecurityInsights/alertRules` REST API。重复运行是安全的——规则由其 `id` 作为主键标识。[`deploy-playbooks.yml`](.github/workflows/deploy-playbooks.yml) 和 [`sync-watchlists.yml`](.github/workflows/sync-watchlists.yml) 同样会部署更改过的 playbook/自动化内容,并同步监视列表 CSV。
删除操作并未自动化:删除规则的 `.md` 文件**不会**从 Sentinel 中删除相应的规则。请在 Sentinel 中将其禁用(或设置 `analytics_rule.enabled: false` 并重新部署),如有需要再手动删除它。
## SOAR playbook 架构
```
Sentinel Incident
│
▼
┌─────────────┐ HTTP POST ┌──────────────────────┐
│ Parent (P) │ ─────────────────► │ Child (C) │
│ Orchestrates│ │ Enriches / Acts │
│ Comments │ ◄─── JSON body ─── │ Returns data only │
└─────────────┘ └──────────────────────┘
```
**父项**由 Sentinel 触发——它们负责协调富化调用、评估风险、编写事件评论、更新严重性,并触发补救措施。**子项**由 HTTP 触发——每一个只做一件事(调用外部 API、运行 KQL 查询、执行 Graph 操作)并返回结构化的 JSON;它们从不编写 Sentinel 评论。
**自动化**在计时器上运行,独立于事件。
| 子项 | 功能 |
|---|---|
| C1 Enrich-IP | GeoIP + AbuseIPDB + Sentinel TI + TrustedIPs watchlist → RiskTier |
| C2 Enrich-URL | urlscan.io 提交 → 结果 → ThreatLevel/IsMalicious/Score |
| C3 Enrich-Hash | VirusTotal v3 文件查询 → ThreatLevel |
| C4 Enrich-User | Graph 配置文件 + Entra 风险 + KQL SigninLogs/UEBA/watchlists/过往事件 |
| C5 KQL-GetURL | 并行 KQL(UrlClickEvents + EmailUrlInfo)查找被点击的 URL |
| C6 Notify-Teams | 向 Teams SOC 频道发帖,主题按严重性划分 |
| C7 Disable-Account | 通过 Managed Identity 执行 Graph `accountEnabled: false` |
| C10 Revoke-Sessions | 通过 Managed Identity 执行 Graph `revokeSignInSessions` |
| 父项 | 触发器 | 功能 |
|---|---|---|
| P1 Universal-Enrichment | 事件创建 | 并行富化所有 IP/账户实体;对高风险用户执行撤销+禁用 |
| P2 UrlClick-SignIn | 事件创建 | URL 查找 → 富化 + 点击后行为 + 用户配置文件;三级输出结果 |
| P3 URL-Enrichment | 事件创建 | 提取 URL 实体,扫描每一个,构建针对单个 URL 的评论 |
| P4 ImpossibleTravel | 事件创建 | 并行用户富化 + 不可能旅行 KQL;撤销+禁用 |
| P5 AccountCompromise | 事件创建 | 并行用户富化 + 登录失败 KQL;撤销+禁用 |
| 自动化 | 时间表 | 功能 |
|---|---|---|
| A1 Notify-SD-UserDisabled | 每 15 分钟 | 当自动同步应用禁用某个账户时,向 Service Desk 发送电子邮件 |
### 占位符 token
Playbook 定义使用在部署时由 `scripts/deploy-playbook.py` 替换的 `__PLACEHOLDER__` token——在首次部署之前,请查阅该脚本及其期望的 GitHub repository variables/secrets(`AZURE_SUBSCRIPTION_ID`、`SENTINEL_RESOURCE_GROUP`、`SENTINEL_WORKSPACE_NAME`、各 playbook 专属的 `C*_TRIGGER_URL` secrets 等)。
### 监视列表
| 监视列表 | SearchKey | 用途 |
|---|---|---|
| `TrustedIPs.csv` | CIDR 或 IP | 无论 AbuseIPDB 评分为多少,这些 IP 始终被评为 Trusted |
| `ServiceAccounts.csv` | UPN | 排除在异常升级之外的账户 |
| `PrivilegedAccounts.csv` | UPN | 在受到入侵时触发账户禁用的账户 |
| `PlaybookOverride.csv` | UPN 或 IP | 带过期日期的临时抑制 |
| `SanctionedTools.csv` | 工具名称/hash | 排除在 hash 告警之外的已批准软件 |
| `BrandDomains.csv` | Domain | 为仿冒域名规则提供种子品牌/供应商根域名(如不存在则创建) |
## 一次性 Azure/GitHub 设置
部署工作流使用 OIDC 联合凭据进行身份验证(不存储 client secret)。
1. **创建(或复用)Azure AD App Registration**,并记录其 Application (client) ID 和您的 Azure AD Tenant ID。
2. **授予其对 Sentinel 工作区的访问权限**:在相关的 resource group(s) 上分配 `Microsoft Sentinel Contributor`(用于规则)和 `Logic App Contributor`(用于 playbook)。
3. **为 GitHub Actions 添加联合凭据**(Azure Portal → App registration → *Certificates & secrets* → *Federated credentials*):
- Entity type:`Branch`,Branch:`main`(以及可选的 `Environment` = `production`,需与这些工作流引用的 `production` GitHub Environment 相匹配)。
4. **设置 repository variables/secrets**(Settings → Secrets and variables → Actions):
- Variables:`AZURE_TENANT_ID`、`AZURE_SUBSCRIPTION_ID`、`AZURE_RESOURCE_GROUP`、`AZURE_WORKSPACE_NAME`、`SENTINEL_RESOURCE_GROUP`、`SENTINEL_WORKSPACE_NAME`、`AZURE_LOCATION`、`PLAYBOOK_NAME_PREFIX`、`TENANT_DOMAIN`、`SYNC_APP_NAME`、`ORG_NAME`
- Secrets:`AZURE_CLIENT_ID`、各 playbook 专属的 `C*_TRIGGER_URL`、`AUTOMATION_SENDER_UPN`、`SERVICEDESK_EMAIL`
5. (推荐)创建一个名为 `production` 的 GitHub **Environment**,并设置必需的审查者。
配置完成后,将 `rules/analytics/**`、`playbooks/**`、`automations/**` 或 `watchlists/**` 下的更改合并到 `main` 即可自动部署。
## 来源说明
此处的检测内容以前重复存在于 `calvin-quint/docs`(`01-detection-engineering/kql/`)、`calvin-quint/kql-detection-rules` 和此仓库中;SOAR 内容重复存在于 `calvin-quint/soar` 和 `calvin-quint/sentinel-automation` 中。此仓库现在是两者的唯一事实来源——今后应将 `docs` 中的副本视为历史/已弃用的内容,而不是进行新编辑的地方。
标签:GitHub Actions, KQL, Microsoft Sentinel, SOAR, Yara, 代码化检测, 安全运营, 扫描框架, 自动笔记, 逆向工具