jblukach/webmonitor
GitHub: jblukach/webmonitor
一个基于 AWS 无服务器架构的域名情报监控与告警平台,自动采集域名数据、智能匹配可疑域名并通过邮件及时通知安全团队。
Stars: 1 | Forks: 0
# webmonitor
webmonitor 是一个 AWS CDK 应用程序,用于下载域名情报源、构建带有日期的 SQLite 数据集、将匹配结果协调至 DynamoDB,并通过摘要队列发送电子邮件警报。
## 概述
计划工作流程如下:
1. 将 Domains Monitor 源下载到 S3 中,保存为 `YYYY-MM-DD-*.csv` 对象。
2. 将每个 CSV 转换为 `YYYY-MM-DD-*.sqlite3` 数据库。
3. 运行 `searchlist` 从共享的 `lunker` 表中读取受监控的搜索词,并为当天的每个 SQLite 对象调用 `search`。
4. 每小时运行 `osint` worker,将 `caretakerstaged` 中的 `dns.sqlite3` 复制到工作存储桶中,命名为 `YYYY-MM-DD-osint.sqlite3`,并分派搜索事件。
5. 通过插入新域名并删除过期的域名,将 SQLite 匹配结果与每个源的 DynamoDB 表进行协调。
6. 通过 `action` 处理选定表的 DynamoDB 流插入事件,该操作会从 `lunker` 中扩展收件人,并将待处理通知写入 `digest` 表。
7. 汇总每个收件人的待处理摘要项,将电子邮件负载排入 SQS 队列,并通过 `email` Lambda 发送 SES 电子邮件。
## 堆栈
### `WebmonitorStorage`
- 创建 S3 存储桶 `temporarywebmonitor`。
- 启用 S3 托管加密,阻止公共访问,强制使用 SSL,并在 1 天后使对象过期。
- 使用 SSM 参数 `/organization/id` 添加组织范围的 `s3:ListBucket` 和 `s3:GetObject` 存储桶策略。
### `WebmonitorDownload`
- 在 ARM64 上使用 Python 3.13 创建 Lambda `download`。
- 使用来自 `s3://packages-use2-lukach-io/requests.zip` 的预构建 Lambda 层。
- 创建 Secrets Manager 密钥 `webmonitor`,包含占位符 `token` 值。
- 将以下源类型下载到 `temporarywebmonitor`:
- `dailyupdate`、`weeklyupdate`、`monthlyupdate`
- `dailyremove`、`weeklyremove`、`monthlyremove`
- `detailed-update`、`malware`
- 每天 `01:00 UTC` 运行。
### `WebmonitorSqlite`
- 使用 `sqlite/` 包创建 Lambda `list` 和 `make`。
- `list` 扫描当天的 CSV 对象,并为每个文件异步调用 `make`。
- `make` 将 CSV 行转换为 SQLite 数据库,在 SQLite 支持的情况下为域名搜索构建 trigram FTS 索引,并上传 `YYYY-MM-DD-*.sqlite3` 对象。
- 每天 `01:15 UTC` 运行。
### `WebmonitorOsint`
- 使用 `osint/` 包创建 Lambda `osint`。
- 将 `caretakerstaged` 中的 `dns.sqlite3` 复制到 `temporarywebmonitor`,命名为 `YYYY-MM-DD-osint.sqlite3`。
- 查询共享的 `lunker` 表,并为每个受监控的值异步调用 `search`。
- 在每小时的第 `00` 分钟运行。
### `WebmonitorSearch`
- 创建 Lambda `search` 和 `searchlist`。
- `searchlist` 从共享的 `lunker` 表中读取受监控的值,在 `state` 中记录每日执行状态,从 `/account/webdb` 调用外部 `SEARCH_ATHENA_WEBDB` Lambda,并为当天的每个 SQLite 对象调用本地 `search`。
- `search` 对所有 SQLite 查询(域名和 OSINT 数据集)使用支持 SLD 的匹配:
- 对于短于 5 个字符的值,使用原始 SLD 加上共享的 `permutation` 值,并匹配不区分大小写的标签模式:`.%`、`%..%`、`.%` 和 `%..%`。
- 对于长度大于或等于 5 个字符的值,使用不区分大小写的 SQLite `LIKE` 包含匹配,搜索 `%sld%` 以及 `%permutation%` 项。
- `search` 从 `DYNAMODB_TABLE` 读取排列。
- 对于缺失的带日期的 SQLite 对象,当 S3 中存在前一天的键时,`search` 会通过复制前一天的键来回退到前一天。
- 每天 `01:45 UTC` 运行。
### `WebmonitorDynamoDB`
- 创建 DynamoDB 表:
- `dailyremove`、`dailyupdate`、`weeklyremove`、`weeklyupdate`
- `monthlyremove`、`monthlyupdate`
- `malware`、`osint`、`state`、`digest`
- 表使用按需计费、用于过期的 `ttl`、时间点恢复、DynamoDB 流和删除保护。
- 将每个表复制到 `us-east-1` 和 `us-west-2`。
- 将 `dailyremove`、`dailyupdate`、`malware` 和 `osint` 的流 ARN 发布到 `/webmonitor/stream/*` 下的 SSM 中。
### `WebmonitorAction`
- 使用 `action/` 包创建 Lambda `action`。
- 订阅 `dailyremove`、`dailyupdate`、`malware` 和 `osint` 的 DynamoDB 流。
- 通过 `pk-tk-index` GSI 从共享的 `lunker` 表中扩展收件人。
- 将每个收件人的待处理通知写入本地 `digest` 表。
### `WebmonitorDigest`
- 创建 SQS 队列 `webmonitor-email` 和 `webmonitor-email-dlq`。
- 使用 `digest/` 包创建 Lambda `digest`。
- `digest` 读取待处理的 `digest` 表项,按收件人分组,并将电子邮件负载排入队列。
- 使用 `email/` 包创建 Lambda `email`。
- `email` 使用 SQS 消息并从 `hello@lukach.io` 发送纯文本 SES 邮件,将域名行进行无害化处理,如 `example[.]com`。
- 使用直接附加到 `digest` Lambda 目标的 EventBridge 规则,每 15 分钟运行一次 `digest`。
### `WebmonitorGithub`
- 为 `https://token.actions.githubusercontent.com` 创建 GitHub OIDC 提供商。
- 为 `repo:jblukach/webmonitor:*` 创建 IAM 角色信任。
- 授予 GitHub Actions CDK 部署和资产发布所需的权限。
## 仓库布局
```
app.py CDK app entrypoint
webmonitor/ CDK stack definitions
download/download.py feed downloader Lambda
sqlite/list.py SQLite orchestration Lambda
sqlite/make.py CSV-to-SQLite builder Lambda
search/list.py search orchestration Lambda
search/search.py SQLite matcher and DynamoDB reconciler
osint/osint.py OSINT snapshot copy and search dispatcher
action/action.py DynamoDB stream to digest writer
digest/digest.py digest aggregator and SQS enqueuer
email/ses_sender.py SQS consumer and SES sender
.github/workflows/webmonitor.yaml GitHub Actions deployment workflow
```
## 要求
此仓库依赖于当前代码库以及必须已存在的共享基础设施。
### 本地工具
- Python `3.13`
- 已为目标账户配置的 AWS CLI
- 单独安装的 AWS CDK v2 CLI,例如通过 `npm install -g aws-cdk`
- 与 `codebuild-webmonitor-*` 标签匹配的 GitHub Actions 运行器容量(用于仓库 CI/CD)
### 引导 CDK 环境
在以下区域使用限定符 `lukach` 进行引导:
- `us-east-2` 用于主堆栈
- `us-east-1` 和 `us-west-2` 用于复制的 DynamoDB 资源和 CDK 资产
### 共享资源和参数
- 包含 `requests.zip` 的 S3 存储桶 `packages-use2-lukach-io`
- 包含 `dns.sqlite3` 的 S3 存储桶 `caretakerstaged`
- 带有拥有共享 `lunker` 和 `permutation` 表的 AWS 账户 ID 的 SSM 参数 `/account/lunker`
- 带有拥有被 `searchlist` 调用的外部 `search` Lambda 的 AWS 账户 ID 的 SSM 参数 `/account/webdb`
- 带有在存储桶和表资源策略中使用的 AWS 组织 ID 的 SSM 参数 `/organization/id`
### 共享 DynamoDB 表
由 `/account/lunker` 引用的账户必须已包含:
- `lunker`
- 分区键 `pk`
- 排序键 `sk`
- 名为 `pk-tk-index` 的 GSI,以 `pk` 作为哈希键,以 `tk` 作为范围键
- `permutation`
- 分区键 `pk`
- 排序键 `sk`
- 可选的 `perm` 属性,包含相关搜索词
- `tld`(可选,用于 `search` 兼容性回退逻辑)
- 分区键 `pk`
- 排序键 `sk`
- `tld` 属性和/或可解析的 domain/sk 值
### SES 先决条件
- 在 `us-east-2` 中已验证的 `hello@lukach.io` SES 身份和/或 `lukach.io` 域名身份
- `email` Lambda 的发送方默认值由堆栈配置设置,因此更改发件人身份需要修改堆栈/代码
## 本地设置
创建并激活虚拟环境,然后安装 Python 依赖项:
```
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
```
如果尚未安装 CDK CLI,请安装它:
```
npm install -g aws-cdk
```
引导所需区域:
```
cdk bootstrap --qualifier lukach aws:///us-east-1
cdk bootstrap --qualifier lukach aws:///us-east-2
cdk bootstrap --qualifier lukach aws:///us-west-2
```
## 部署
应用程序使用 `CDK_DEFAULT_ACCOUNT` 作为目标账户,为 `us-east-2` 综合堆栈。使用以下命令部署所有堆栈:
```
cdk deploy --all --profile
```
常用命令:
```
cdk ls
cdk synth
cdk diff --profile
cdk destroy --all --profile
```
## 部署后配置
使用您真实的 Domains Monitor token 更新 Secrets Manager 密钥 `webmonitor`:
```
{
"token": ""
}
```
如果没有此值,download Lambda 将无法获取上游源数据。
## 手动操作
调用 download Lambda:
```
DOWNLOAD_FN=$(aws lambda list-functions \
--query "Functions[?contains(FunctionName, 'WebmonitorDownload') && contains(FunctionName, 'download')].FunctionName | [0]" \
--output text)
aws lambda invoke \
--function-name "$DOWNLOAD_FN" \
--payload '{}' \
--cli-binary-format raw-in-base64-out \
/tmp/download.json
```
以计划模式为所有跟踪项运行 `searchlist`:
```
aws lambda invoke \
--function-name searchlist \
--payload '{}' \
--cli-binary-format raw-in-base64-out \
/tmp/searchlist.json
```
为单个状态或搜索词运行 `searchlist`:
```
aws lambda invoke \
--function-name searchlist \
--payload '{"Status":"example"}' \
--cli-binary-format raw-in-base64-out \
/tmp/searchlist-single.json
```
手动调用 OSINT Lambda:
```
OSINT_FN=$(aws lambda list-functions \
--query "Functions[?contains(FunctionName, 'WebmonitorOsint') && contains(FunctionName, 'osint')].FunctionName | [0]" \
--output text)
aws lambda invoke \
--function-name "$OSINT_FN" \
--payload '{}' \
--cli-binary-format raw-in-base64-out \
/tmp/osint.json
```
手动调用 digest Lambda:
```
aws lambda invoke \
--function-name digest \
--payload '{}' \
--cli-binary-format raw-in-base64-out \
/tmp/digest.json
```
## CI/CD
GitHub Actions 工作流 `.github/workflows/webmonitor.yaml`:
- 在每次推送到 `main` 时进行部署
- 在每月第一天的 `02:00 UTC` 按月计划运行
- 担当存储在 GitHub 机密 `ROLE` 中的 IAM 角色
- 全局安装 `aws-cdk` 和 `aws-cdk-lib`,安装 Python 依赖项,并运行 `cdk deploy --all --require-approval never`
## 运维说明
- 主部署区域在 `app.py` 中硬编码为 `us-east-2`。
- S3 存储桶名称 `temporarywebmonitor` 在代码中是固定的,并且必须在 AWS 全局唯一。
- `temporarywebmonitor` 是有意设置为短期存在的,并配置了 1 天的保留期。
- OSINT 摄取是每小时的(`WebmonitorOsint`),与每日的 CSV 到 SQLite 工作流是分开的。
- 域名 SQLite 搜索在可用时使用 trigram FTS,否则回退到不区分大小写的包含 `LIKE` 查询。
- 短 SLD 搜索包含排列项,并使用支持标签的 SQLite `LIKE` 模式(`term.%` 和 `%.term.%`)以减少嘈杂的子字符串匹配。
- 较长的 SLD 搜索包含来自共享表的排列项,并使用不区分大小写的包含 `LIKE` 匹配。
- 警报交付是一个多步骤管道:DynamoDB 流 -> `action` -> `digest` 表 -> SQS -> `email` -> SES。
- 某些 IAM 策略仍然很宽泛(`resources = ['*']`);为了更严格的最小权限原则,请对其进行收窄。
## 许可证
本项目采用 Apache License 2.0 授权。请参阅 `LICENSE`。
标签:AWS CDK, DevSecOps, DynamoDB, ESC4, ETL流水线, Lambda, OSINT, Python, S3, SES, SQLite, SQS, 上游代理, 事件驱动架构, 变更审计, 域名监控, 威胁情报, 实时处理, 开发者工具, 异常检测, 情报聚合, 数据清洗, 数据自动化, 无后门, 网络信息收集, 网络安全, 软件开发, 逆向工具, 邮件告警, 隐私保护