grepstrength/CICDefense
GitHub: grepstrength/CICDefense
这是一个使用 Terraform 在 Azure 上自动构建 CI/CD 与软件供应链安全测试实验室的项目。
Stars: 0 | Forks: 0

# CI/CD defense
**通过 CI/CDefense 实践软件供应链安全!**
如果你在过去几年关注过网络安全公告,就会知道软件供应链在相当长的一段时间内一直持续受到攻击。因为我觉得学习如何防御自己的 CI/CD pipeline 是至关重要的,所以我启动了这个项目。
借此,你可以构建自己的 Azure 实验室,用于 CI/CD pipeline 和软件供应链安全测试。
## 实验室架构
它在私有网络上拥有三台 VM,只能通过托管的堡垒机进行访问。没有任何 VM 使用公共 IP。
- **Ubuntu (最新 LTS)**:主要 CI 运行器,相当于自托管的 GitLab Runner 或 GitHub Actions runner
- **Windows Server 2022 Datacenter**:用于 Windows 二进制文件和 .NET 构建的辅助运行器
- **notkali**:基于 Ubuntu 的对抗/DAST 节点,预装了用于模拟供应链攻击和扫描暴露 secret 的攻击性工具
网络态势:
- VM 之间可以互相访问
- VM 可以访问互联网(允许流出以拉取 package 和模型)
- 互联网上的任何设备都无法访问 VM(无流入)
- 操作员访问通过 Azure Bastion 进行(**注意:**选择 Standard SKU 是一个*有意的*决定。如果你希望通过 Azure 门户中的 RDP 直接连接到 Linux 主机,则必须使用此项!)
## 构建内容
Azure(17 个资源):
- 资源组、虚拟网络、工作负载子网、AzureBastionSubnet
- 采用最小权限规则的安全组,以及子网关联
- Azure Bastion 主机及其静态公共 IP
- 3 个网络接口,无公共 IP
- 3 台虚拟机(除 Windows 运行器外,均为 Ubuntu 24.04)
- 3 个用于安装桌面环境和工具的 VM 扩展
## 前置条件
- Terraform CLI 1.9 或更高版本
- Azure CLI
- 启用了计费的 Azure 订阅
- Git
### 可选但推荐
**注意:**这些必须手动创建/配置。
- 带有 HashiCorp Terraform 扩展的 VS Code
- 多个虚拟账户
- GitHub
- Azure
- CircleCI
## 已验证的实验室
### notkali(对抗 / DAST 节点)

确认项:`labmin` 登录,`notkali-dast` 主机名,私有 IP `10.0.2.6`(无公共 IP),通过 Bastion (RDP) 连接的 XFCE 桌面,已安装 ZAP,并且攻击性工具链可在 PATH 中解析(nmap、sqlmap、nikto、gobuster、ffuf、trufflehog、gitleaks、amass)。
### Ubuntu(CI 运行器)

确认项:`ubuntu-runner`,私有 IP `10.0.2.4`,通过 Bastion (RDP) 连接的 XFCE 桌面。后台的 systemd 安装已完成 — Docker(注意正在运行的 `docker0` 网桥)、Syft、Grype、Trivy、.NET、Go、Rust 和 osquery 均可在 PATH 中解析,证明延迟安装模式有效。
### Windows(辅助运行器)

确认项:`win-runner`,私有 IP `10.0.2.5`,以及 Sysmon 正在通过 SwiftOnSecurity 配置主动将 Process Create (Event ID 1) 事件记录到操作日志中。
## 快速开始
### 1. 认证
```
az login
az account show --query id --output tsv
```
复制打印出的订阅 GUID。
### 2. 创建变量文件
`terraform.tfvars` 已被 gitignore,不在此 repo 中。从模板创建它:
```
copy terraform.tfvars.example terraform.tfvars
```
然后填写:
- `subscription_id` — 步骤 1 中的 GUID
- `location` — 默认为 `centralus`
- `admin_username` — 不能是 admin、administrator、root、guest、user 或 test
- `admin_password` — 12 个字符以上,包含大小写字母、数字、符号
为了防止密码留在磁盘上,请将其设置为环境变量并从文件中省略:
```
$env:TF_VAR_admin_password = "your-password-here"
```
### 3. 部署
```
terraform init
terraform validate
terraform plan
terraform apply
```
预计需要 20 到 40 分钟。仅 Bastion 本身就需要 5 到 10 分钟,而且 notkali 扩展会安装大量攻击性工具集。
### 4. 连接
```
terraform output
```
在 Azure 门户中,转到资源组,选择一台 VM,然后依次选择连接、Bastion。使用你的管理员用户名和密码登录。
所有三台 VM 均支持 RDP。Linux 节点上安装了 xrdp,因此它们提供图形化桌面,而不是纯 shell。对于 Linux VM,在 Bastion 连接窗格中选择协议:RDP,端口 3389(由 Standard SKU 启用)。通过 az network bastion tunnel + mstsc 进行的原生客户端隧道也可使用。
### 通过原生 RDP 客户端连接
门户中的 Bastion 连接窗格适用于 Windows (RDP) 和 Linux(通过 Standard SKU 协议选项的 RDP)。如果你更愿意使用本地 RDP 客户端 — 用于剪贴板共享、文件传输或多显示器 — 请从 PowerShell 打开 Bastion 隧道并从中进行连接。
需要 Bastion **Standard** SKU 且 `tunneling_enabled = true`(两者均
在 `bastion.tf` 中设置)。
打开到 VM 的隧道。保持此窗口运行 — 它用于维持隧道打开:
```
az network bastion tunnel `
--name cicdefense-bastion `
--resource-group cicdefense-rg `
--target-resource-id "" `
--resource-port 3389 `
--port 13389
```
使用以下命令获取 VM 的资源 ID:
```
az vm show --resource-group cicdefense-rg --name cicdefense-ubuntu --query id --output tsv
```
然后,在第二个窗口中,针对本地隧道端口启动远程桌面:
```
mstsc /v:localhost:13389
```
使用你的 `admin_username` 和 `admin_password` 登录。对于第二台 VM,请在不同的本地端口上打开另一个
隧道(例如 `--port 13390`),以便两者可以同时运行。
### 5. 销毁
```
terraform destroy
```
在每次会话结束时执行此操作。
## 已安装的工具
配置脚本位于 `scripts/` 中,并通过 VM 扩展自动运行。
在 Ubuntu 运行器上,较重的工具链和扫描器会在首次启动后通过 systemd 一次性单元在后台安装,因此部署会很快完成。如果在你连接后工具没有立即出现,说明它仍在安装中;请检查 `/var/log/cicdefense-detection-install.log`。
所有节点:
- git
- VS Code
- Ollama
Ubuntu(主要 CI 运行器):
- XFCE 桌面和 xrdp
- 构建工具链:build-essential (gcc/g++/make)、Python (pip、venv)
- 后台工具链:Docker CE、Node.js/npm、JDK + Maven、.NET SDK 8.0、Go、Rust
- 检测:auditd(带规则集)、osquery、Wireshark、mitmproxy
- SBOM 和漏洞扫描:Syft、Grype、Trivy
Windows Server 2022(用于 Windows 特定测试用例的辅助运行器,通过直接下载安装,无包管理器):
- git、VS Code、Ollama
- .NET SDK 8.0、Node.js
- 检测:Sysmon(带 SwiftOnSecurity 配置)、Procmon、Autoruns、Regshot
- IE 增强的安全配置已禁用
- C++ 构建工具 (MSVC) **未**进行配置 — Visual Studio Build Tools 的安装体积大且速度慢。如有需要,请手动安装:`winget install Microsoft.VisualStudio.2022.BuildTools`
notkali(对抗 / DAST 节点):
- XFCE 桌面和 xrdp
- 侦察和 OSINT:nmap、masscan、dnsrecon、amass(通过 snap)、whatweb、theHarvester
- Web 应用测试:sqlmap、nikto、gobuster、ffuf、wfuzz、OWASP ZAP
- Secrets 扫描:trufflehog、gitleaks
- 流量:Wireshark、mitmproxy
- Burp Suite Community 已下载到 Downloads 文件夹,但尚未安装。如果你需要使用它,必须自行安装
### 语言模型
Ollama 已安装,但在配置期间未拉取模型,因为下载量很大,可能会导致扩展超时。在首次登录后拉取它们:
```
ollama pull gemma4:e4b
ollama pull R4C3R/minicpm5-1b-fable5-heretic
```
我选择这些是因为它们符合我的预期用例,但当然你可以为这个实验室选择任何你想要的模型。
默认的 `Standard_D2s_v5` 规格(2 个 vCPU,8 GB RAM)可以轻松运行小型模型。如需更多空间,请在 `terraform.tfvars` 中调大 `vm_size`。
## 成本
此实验室按小时计费。为了你的理智,在不使用时请将其销毁。
在 centralus 地区大约每小时 0.55 到 0.65 美元,细分为:
- 3 台 D2s_v5 VM,每台约 $0.10/小时(取决于地区)
- Azure Bastion Standard SKU,约 $0.29
- OS 磁盘(Linux 节点上的 64 GB StandardSSD),每小时几美分
- 静态公共 IP,约 $0.005
免费:资源组、虚拟网络、子网和网络安全组
请针对你所在的地区和订阅通过 Azure 定价计算器进行验证。
注意:
- 无论你是否通过 Bastion 连接,它仅仅因为存在就会计费
- 即使 VM 已停止,OS 磁盘也会计费;停止并不等同于销毁
- `terraform destroy` 会删除所有内容,包括磁盘
四个小时的会话花费几美元。被遗忘一周的耗费大约为 100 美元。
## 安全说明
请记住这是一个实验室。我刻意在几件事情上选择了便利性而非强化安全性:
- Windows 配置脚本在部署时从公共 GitHub 原始 URL (raw.githubusercontent.com/.../win-setup.ps1) 获取并执行。它固定在一个分支而不是 commit 上,因此相同的配置随着时间的推移可能会拉取不同的脚本。这正是本实验室研究的远程获取供应链风险的生动实例。
- Linux VM 上已启用密码身份验证。SSH 密钥是普遍接受的安全做法。这之所以是可辩护的,仅仅是因为 VM 没有公共 IP,并且只能通过 Bastion 访问。如果要切换,请将 `disable_password_authentication = false` 替换为 `admin_ssh_key` 块。
- Terraform state 包含明文形式的 secret。将变量标记为 sensitive 只会将其从控制台输出中隐藏。State 文件已被 gitignore,但生产设置将使用带有静态加密和 state 锁定的远程 backend。
- 配置脚本使用了通过管道传递给 shell 的 curl。Ollama 安装程序执行了未经审查的远程脚本。这是标准的安装路径,坦白说,也是本实验室旨在研究的那类问题的生动实例,与本节中的其他配置一样。
- notkali 上的几个攻击性工具是从供应商安装脚本和 GitHub 发行版二进制文件安装的,而不是已签名的发行版 package。这既方便又常见,也正是本实验室旨在研究的未经核实的供应链步骤。
- 镜像版本设置为 `latest` 而非固定版本。这仅仅是为了方便。最终结果是,相同的配置随着时间的推移会产生不同的镜像。如果你想要完全可复现的构建,请固定你想要的精确版本。
- 出口不受限制。向 NSG 添加出站拒绝规则是顺理成章的下一步强化练习,这也是能够阻止受到破坏的运行器泄露 secret 的控制措施。
切勿提交 `terraform.tfvars` 或 `.tfstate` 文件。两者均在 repo 根目录下被 gitignore。
## 故障排除
**terraform plan 提示输入变量。** 某个必需的变量没有值。请检查 `terraform.tfvars` 是否存在并已填写完整。
**Azure CLI 不知道订阅 ID。** `terraform.tfvars` 中的 GUID 与你登录的会话不匹配。运行 `az account list --all --output table` 并进行比较。同时确认你登录的是正确的账户,特别是当你有多个账户时。
**SkuNotAvailable 或 NotAvailableForSubscription。** 你的区域 vCPU 配额可能为零或太低,或者该 VM 规格系列未向你所在地区的订阅提供。使用 `az vm list-usage --location --output table` 检查并查看 Limit 列。通过请求增加配额(在门户中搜索 Quotas、Compute)或选择你的订阅可用的区域或规格来解决此问题。
**Marketplace 套餐已移除,无法用于新购买。** 该镜像已从 Marketplace 撤回(这就是为什么对抗节点使用原生的 Ubuntu 加上工具,而不是官方的 Kali 镜像)。请使用原生镜像并通过配置脚本安装工具。
**PlatformImageNotFound。** Marketplace 镜像引用发生了偏移。请使用以下命令进行验证:
```
az vm image list --publisher --offer --location centralus --all --output table
```
**Bastion 连接窗格仅显示 Linux VM 的 SSH。** RDP 协议选项需要 Bastion **Standard** SKU(或更高版本)。在 SKU 上,门户仅为 Linux VM 提供 SSH,因此你获得的是一个终端而不是 XFCE 桌面。在 `azurerm_bastion_host` 资源上设置 `sku = "Standard"` 并重新应用。SKU 可以就地升级(无需重新构建)。
**`az network bastion tunnel` 失败:“Bastion Host SKU must be Standard or Premium and Native Client must be enabled.”** 根本原因与上述相同。原生客户端隧道需要 Standard(或 Premium)SKU **并且**在 `azurerm_bastion_host` 资源上设置 `tunneling_enabled = true`。同时设置两者并重新应用,然后重新运行隧道命令。
**扩展失败并显示“VM has reported a failure”。** Terraform 无法查看脚本内部。通过 Bastion 连接并阅读日志。在 Linux 上,查看 `/var/log/azure/custom-script/handler.log`。在 Windows 上,阅读 `C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\*\Downloads\` 下的 stderr。
**通过 RDP 连接到 Linux VM 后出现空白的灰屏。** `.xsession` 文件丢失或所有者错误。检查安装脚本是否为正确的用户编写了该文件。
**templatefile 错误:“vars map does not contain key ...”。** 配置脚本使用了带有花括号语法 (`${VAR}`) 的 bash 变量,这与 Terraform 的 `templatefile()` 插值发生冲突。请将其转义为 `$${VAR}`,以便 Terraform 将其传递给 bash。只有 `${admin_username}` 应该保留单美元符号(该项旨在由 Terraform 注入)。
**notkali 上缺少 OWASP ZAP。** ZAP 通过 snap 安装,而 snapd 在首次启动配置期间有时尚未就绪,因此安装可能会静默失败(根据设计,这不是致命错误)。如果 ZAP 不存在,请在连接后重新运行 `sudo snap install zaproxy --classic`。
**Windows 扩展失败并显示 404 / “failed to download the blob”。** Windows 扩展在部署时从公共 GitHub 原始 URL 获取 `scripts/win-setup.ps1`。如果你编辑了脚本但未推送,或者 `extensions.tf` 中的文件名(`fileUris` + `-File`)与实际文件不匹配,下载将返回 404。在应用之前,确认原始 URL 可在浏览器中解析,并确保文件、`fileUris` URL 和 `commandToExecute -File` 参数全都写明 `win-setup.ps1`。
**其他方法均无效。** 开启调试日志:
```
$env:TF_LOG = "DEBUG"
terraform plan
$env:TF_LOG = ""
```
## 路线图
- [x] 检测和日志层:Windows 上的 Sysmon;运行器上的 auditd 和 osquery;使用 Syft、Grype 和 Trivy 进行 SBOM 和漏洞扫描
- [ ] 用于攻击场景的预置漏洞 pipeline(这就是 CircleCI/GitHub 虚拟账户的用武之地)
- [ ] 带有锁定的远程 state backend
- [ ] 出口过滤练习
- [ ] 固定的镜像版本以实现可复现的构建
- [ ] 集中日志聚合,以便将来自所有三台 VM 的检测事件发送到同一个地方
## 许可证
完全免费,即 MIT。
标签:AI风险缓解, Azure, ECS, Terraform, 基础架构即代码, 安全实验环境, 安全测试, 攻击性安全, 请求拦截