VladvonTranssylvanien/learningsteps-lockdown
GitHub: VladvonTranssylvanien/learningsteps-lockdown
一个记录Azure环境下FastAPI应用从不安全到端到端安全加固完整实战过程的项目。
Stars: 0 | Forks: 0
# LearningSteps 安全加固 - 端到端安全强化
## 项目概述
本项目记录了 LearningSteps API 的完整安全加固过程。该 FastAPI 应用运行在 Azure 上。我们从一个故意不安全的部署开始,遵循**纵深防御**原则实施了多层安全架构。
## 架构演进
### 加固前(不安全基线)
- 虚拟机通过公网 IP 直接暴露于互联网
- SSH 可从任意 IP 使用静态密钥文件访问
- API 完全匿名,无任何认证
- PostgreSQL 在 5432 端口可从任何位置访问
- 无日志、无监控、无告警
### 加固后(强化状态)
- 互联网 -> NSG (仅 443 端口) -> Nginx (TLS+WAF+速率限制) -> oauth2-proxy (JWT) -> FastAPI -> PostgreSQL (私有 VNet)
- 管理通道:Azure Bastion -> 虚拟机 (Entra ID 身份)
- 监控链路:Azure Monitor -> Log Analytics -> Microsoft Sentinel -> Logic App -> NSG (自动阻断)
```
graph TB
Internet((Internet))
subgraph Edge["Edge Layer"]
NSG["NSG - Port 443 only - Deny-SQLi-Attack auto-block"]
Nginx["Nginx - TLS 1.2/1.3 - WAF + Rate Limiting"]
end
subgraph App["Application Layer"]
OAuth["oauth2-proxy - JWT Validation"]
API["FastAPI - Port 8000 internal"]
end
subgraph Data["Data Layer"]
DB[("PostgreSQL - Private VNet")]
end
subgraph Mgmt["Management Layer"]
Bastion["Azure Bastion"]
EntraID["Microsoft Entra ID"]
end
subgraph SOC["SOC Layer"]
AMA["Azure Monitor Agent"]
LAW["Log Analytics"]
Sentinel["Microsoft Sentinel - SQLi Hunter"]
LogicApp["Logic App - Auto-block"]
end
Internet -->|HTTPS 443| NSG
NSG --> Nginx
Nginx -->|proxy_pass| OAuth
OAuth -->|verified| API
API --> DB
Bastion -->|Entra ID| EntraID
Nginx -->|JSON logs| AMA
AMA --> LAW
LAW --> Sentinel
Sentinel -->|incident| LogicApp
LogicApp -->|Deny rule| NSG
style Internet fill:#e0e0e0,stroke:#333,color:#000
style Edge fill:#fff3e0,stroke:#ff9800,color:#000
style App fill:#e3f2fd,stroke:#2196f3,color:#000
style Data fill:#fce4ec,stroke:#e91e63,color:#000
style Mgmt fill:#e8f5e9,stroke:#4caf50,color:#000
style SOC fill:#f3e5f5,stroke:#9c27b0,color:#000
```
## 安全层级
### 层级 1 - 边界与管理平面
**问题:** 22 端口向全球开放,静态 SSH 密钥存储于磁盘。
**解决方案:** 完全移除虚拟机公网 IP。仅通过使用 Microsoft Entra ID 身份的 Azure Bastion 进行访问。
| 组件 | 实施方案 |
|------|----------|
| 虚拟机访问 | Azure Bastion Standard (已启用隧道) |
| 认证方式 | Microsoft Entra ID (AADSSHLoginForLinux 扩展) |
| 授权策略 | RBAC 角色:虚拟机管理员登录 |
| 网络配置 | NSG 22 端口仅允许特定 IP + Bastion 子网访问 |
| 保护措施 | VNet 资源锁 (CanNotDelete) |

### 层级 2 - API 身份网关
**问题:** 任何匿名请求均可读取、修改或删除数据。
**解决方案:** 部署 oauth2-proxy 作为安全边车,在将任何请求转发至 FastAPI 前验证 Microsoft Entra ID JWT 令牌。
| 组件 | 实施方案 |
|------|----------|
| 身份提供商 | Microsoft Entra ID (单租户) |
| 令牌类型 | Bearer JWT (v2.0 访问令牌) |
| 代理工具 | oauth2-proxy v7.6.0 运行于 4180 端口 |
| 应用注册 | api://1a557f7c-0ac6-4232-8ecd-e3ed0fe7321f |
| 网络策略 | 移除 8000 端口,FastAPI 不可直接访问 |
**测试结果:**
- 匿名请求:302 重定向至 Microsoft 登录页
- 无效令牌:302 重定向至 Microsoft 登录页
- 有效 Entra ID 令牌:200 OK 并返回数据

### 层级 3 - 数据隔离
**问题:** PostgreSQL 拥有公网 IP 且防火墙规则允许所有连接。
**解决方案:** 将数据库迁移至 VNet 集成并配置私有 DNS 区域。实现零公网暴露。
| 组件 | 实施方案 |
|------|----------|
| 网络配置 | VNet 集成 (snet-db 10.0.3.0/24,已委托) |
| DNS 配置 | 私有 DNS 区域:vladvontranssylvanien.private.postgres.database.azure.com |
| 公网访问 | 已禁用 |
| 数据迁移 | 通过 pg_dump 导出,在 VNet 内使用 psql 恢复 |
**测试结果:**
- 从虚拟机连接:200 OK,返回 5 条记录
- 从互联网连接:DNS 解析失败,主机未知


### 层级 4 - 边缘安全
**问题:** oauth2-proxy 直接暴露于 80 端口,无加密,无 WAF。
**解决方案:** Nginx 边缘代理提供 TLS、安全头、速率限制和自定义 WAF 规则。
| 组件 | 实施方案 |
|------|----------|
| TLS 配置 | 自签名证书,仅启用 TLS 1.2/1.3 |
| HSTS | max-age=31536000; includeSubDomains |
| 安全头 | X-Content-Type-Options, X-Frame-Options: DENY |
| 速率限制 | 10 请求/秒,突发 20,返回 429 |
| WAF 规则 | 使用 Nginx map 指令的自定义规则阻断 SQL 注入和 XSS |
| Fail2Ban | 在 60 秒内触发 10 次以上 403 错误的 IP 将被封禁 1 小时 |
**WAF 说明:** 由于 Ubuntu 22.04 上的 Nginx 1.18 缺少 ModSecurity OWASP CRS 预编译包,我们使用 Nginx map 指令实现了自定义 WAF,功能等同于 OWASP CRS 规则 942100 (SQL 注入) 和 941100 (XSS)。
**测试结果:**
- HTTPS 请求:302 重定向,包含 HSTS 和安全头
- SQL 注入尝试:403 禁止访问
- 快速请求:429 请求过多


### 层级 5 - 监控与自动化事件响应
**问题:** 日志孤立存储于虚拟机。缺乏可见性。无自动化响应。
**解决方案:** 基于 Microsoft Sentinel 的云原生安全运营中心与自动化 IP 阻断。
| 组件 | 实施方案 |
|------|----------|
| 日志格式 | Nginx JSON 结构化日志 |
| 传输链路 | rsyslog -> Azure Monitor Agent -> Log Analytics |
| SIEM 平台 | Microsoft Sentinel 部署在 law-vladvontranssylvanien |
| DCR 规则 | 数据收集规则流式传输 local7 syslog |
| 分析规则 | KQL 查询检测单 IP 在 30 分钟内超过 10 次 SQL 注入尝试 |
| 自动化工具 | Logic App Playbook 配合托管身份 |
| 响应动作 | 自动在 NSG 中为攻击 IP 创建拒绝规则 |
**KQL 查询 (SQLi 猎手):**
```
Syslog
| where TimeGenerated >= ago(30m)
| where Facility == "local7"
| where SyslogMessage has "UNION" and SyslogMessage has "403"
| extend cleanMsg = extract(@'nginx: ({.*})', 1, SyslogMessage)
| extend payload = parse_json(cleanMsg)
| extend ClientIp = tostring(payload.remote_addr)
| where isnotempty(ClientIp)
| summarize AttemptCount = count() by ClientIp
| where AttemptCount > 10
```
**测试结果:**
- 发送 50 个 SQL 注入请求:Sentinel 生成事件 (高严重级别)
- Logic App 触发:自动创建 Deny-SQLi-Attack 的 NSG 规则




## 附加功能
| 功能 | 实施方案 |
|------|----------|
| 资源锁 | VNet 配置 CanNotDelete,删除操作因 ScopeLocked 错误而受阻 |
| 活动日志 | Azure Monitor 跟踪所有 NSG 修改,记录调用者和时间戳 |
| Fail2Ban | 基于 Nginx 403 模式的操作系统级 IP 封禁 |
| 自动化 SOC | 从攻击到阻断的全自动流水线,无需人工干预 |
| Key Vault | 数据库密码和 M2M 客户端密钥存储在 Azure Key Vault 中,通过托管身份获取 |
| 机器间认证 | 使用从 Key Vault 检索的密钥测试客户端凭据流 |
| 地理位置可视化 | Sentinel 工作簿通过世界地图展示 SQL 注入攻击来源 |
| 架构图 | 展示所有 5 个安全层级和数据流的 Mermaid 图 |



## 修改的文件
| 文件 | 变更内容 |
|------|----------|
| vm.tf | 从网卡移除公网 IP (符合 Azure Policy) |
| outputs.tf | 更新为私有 IP 和 az ssh 命令 |
## 部署
```
git clone https://github.com/VladvonTranssylvanien/learningsteps-lockdown
cd learningsteps-lockdown
python3 deploy.py
```
基础部署完成后,应用如上所述的安全加固措施。
标签:AppImage, AV绕过, Azure Bastion, Azure安全, Entra ID, FastAPI, JWT认证, Microsoft Sentinel, Nginx WAF, PostgreSQL, Streamlit, Web应用防火墙, 云计算安全, 多层防御, 测试用例, 私有数据库, 端到端安全加固, 网络安全, 自动事件响应, 访问控制, 逆向工具, 隐私保护, 零信任架构