Fill84/procurve-webui

GitHub: Fill84/procurve-webui

为淘汰了 Java Applet 的 HP ProCurve 2810-24G 交换机提供单容器部署的现代浏览器 Web 管理界面。

Stars: 1 | Forks: 0

# procurve-webui 一个专为 **HP ProCurve Switch 2810-24G (J9021A)** 打造的现代、浏览器原生 Web UI —— 也是对原版 Java applet 所使用协议的一次彻底重写。这些交换机出厂自带的 Web UI 依赖于一个 Java applet (`agent.jar`),而它早已无法在任何现代浏览器中运行。本项目使用单个 Docker 容器将其取代:包含一个带类型的 Python 协议客户端、一个 FastAPI 服务,以及一个 React SPA —— 完全无需触及老旧的 Java 技术栈。 ## 为什么会有这个项目 HP ProCurve 2810-24G 是一款极其优秀的硬件,至今仍非常适合作为家庭、实验室和小型办公室中的可网管千兆交换机使用。遗憾的是,它的管理 UI 老化严重: - Web UI 是以 Java applet (`agent.jar`, ~183 KB) 形式提供的。现代浏览器多年前就移除了对 Java applet 的支持 —— Chrome 在 2015 年放弃了 NPAPI,Firefox 是在 2017 年,而 Edge 则从未支持过。 - 仅仅为了与交换机通信就去安装老旧的 Java,会带来安全风险并增加维护负担。 - CLI (telnet/SSH) 倒是还可以,但大多数用户还是希望有一个 GUI 来进行日常监控和临时的端口调整。 `procurve-webui` 让你可以保留硬件,同时抛弃那个 applet。 你只需在任意一台能够连接到该交换机的主机上运行 **一个** 容器,通过浏览器访问 `http://localhost:8080`,使用交换机已有的凭据登录,即可获得一个快速、现代的 UI。 ## 截图 | | | |---|---| | ![登录界面](https://raw.githubusercontent.com/Fill84/procurve-webui/main/docs/screenshots/01-login.png) | ![身份信息选项卡](https://raw.githubusercontent.com/Fill84/procurve-webui/main/docs/screenshots/02-identity.png) | | **登录** —— 仅需交换机凭据;无独立的用户数据库。 | **身份信息** —— 系统名称、型号、固件、运行时间、MAC、内存与 CPU。 | | ![状态概览](https://raw.githubusercontent.com/Fill84/procurve-webui/main/docs/screenshots/03-status.png) | ![支持选项卡](https://raw.githubusercontent.com/Fill84/procurve-webui/main/docs/screenshots/04-support.png) | | **状态** —— 实时交换机渲染图(带各端口 LED)、端口利用率图表,以及可确认/删除的告警日志。 | **支持** —— 用现代的 HPE 门户取代了老旧的 `procurve.com` 重定向,并支持复制粘贴型号和序列号以用于支持案例。 | | ![配置](https://raw.githubusercontent.com/Fill84/procurve-webui/main/docs/screenshots/05-configuration.png) | ![安全](https://static.pigsec.cn/wp-content/uploads/repos/cas/ba/baadc3fe5ca6beb90012e6af888647de09cf40914186f60be3883bfa6c598307.png) | | **配置** —— IP、端口、设备功能、故障检测、监控、QoS。 | **安全** —— Web 访问、Web 管理员、端口级安全、入侵日志、SSL 状态。 | | ![诊断](https://static.pigsec.cn/wp-content/uploads/repos/cas/1c/1c6d926f93e5e39567db9c874aef5e6902f97cd12bd279a4ca102d3cd3e94f2f.png) | ![备份](https://raw.githubusercontent.com/Fill84/procurve-webui/main/docs/screenshots/08-backups.png) | | **诊断** —— Ping、链路测试、配置报告、设备重启。 | **备份** —— 列表、与实时配置进行 diff、下载 `.pcc`、恢复、获取新快照。 | 如果截图缺失,请参阅下文的[本地生成截图](#generating-screenshots-locally) —— `tools/mock_demo/` 包提供了一个仅需一条命令即可运行的 demo 后端。 ## 亮点 - **彻底告别 Java。** 容器与交换机之间纯粹通过 HTTP 通信。 - **单容器部署。** 运行 `docker compose up -d` 即可大功告成。 - **默认只读。** `.env.example` 中预设了 `READ_ONLY=true`;在明确启用之前,写操作端点都会抛出 `403 WriteDisabledError` 异常。 - **每次写入前自动备份。** 在执行任何改变状态的操作之前,都会自动捕获一份当前运行配置的 `.pcc` 快照。 - **交换机即是身份提供者。** 应用端无用户数据库;UI 的登录表单直接向交换机本身验证凭据。 - **端到端类型安全。** 后端使用 Pydantic v2 模型,前端通过 OpenAPI 生成 TypeScript 类型。无需手写 API 类型。 - **对硬件友好。** 轮询频率特意设置得较低(实时仪表盘默认 2 秒,标签页隐藏时自动暂停);因为 2810-24G 的管理 CPU 性能有限,高频探测可能会导致其崩溃。 - **开放的文档。** 每一个逆向工程得出的交换机端点都有专属的 `research/protocol//.md`,详细描述了 URL、查询字符串、响应结构和验证规则。 ## 硬件支持 | | | |---|---| | **确认支持** | HP ProCurve Switch 2810-24G (J9021A),固件 **N.11.78** | | **可能支持** | 运行同系列固件的其他 2810 系列 (2810-48G / J9022A) —— 相同的 applet,相同的 CGI 接口。**未经测试** —— 请提交一个 issue 告知我们结果。 | | **不支持** | 2510、2610、2810-Plus (J9573A / J9574A) —— 它们运行不同的固件分支 (例如 K.x, R.x),且管理协议不同。 | | **将不会支持** | 任何由不同供应商/固件家族提供 `agent.jar` 的交换机。 | 如果你成功将其用于其他 ProCurve 硬件,非常欢迎提交 PR 为此表格添加一行记录。 ## 快速开始 ### 选项 A —— Docker(推荐) ``` git clone https://github.com//procurve-webui.git cd procurve-webui cp .env.example .env # 编辑 .env — 至少设置 SWITCH_HOST 和一个真实的 SESSION_SECRET。 # 使用以下命令生成 secret: python -c "import secrets; print(secrets.token_urlsafe(32))" docker compose up -d ``` 浏览访问 `http://localhost:8080`,使用你的交换机凭据登录(出厂固件下可使用 空白/空白)。compose 文件默认仅绑定至 `127.0.0.1`;如果你需要局域网访问,请更改 `docker-compose.yml` 中的 `ports:` (请先阅读[安全说明](#security-considerations))。 ### 选项 B —— 本地开发 ``` # Backend cd backend python -m venv .venv source .venv/Scripts/activate # Git Bash / WSL on Windows pip install -e ".[dev]" uvicorn app.main:app --reload --port 8080 # Frontend(单独的终端) cd frontend npm install npm run dev # Vite on http://localhost:5173, # proxies /api/v1 → 8080 ``` 开发环境前端会与本地后端通信,后者会连接到 `.env` 中指定的 真实交换机。在你完全信任当前环境下的写入路径之前,请务必设置 `READ_ONLY=true`。 ## 配置 (`.env`) | 变量 | 必需 | 默认值 | 描述 | |---|---|---|---| | `SWITCH_HOST` | 是 | — | ProCurve 交换机的 IP 或主机名。 | | `SWITCH_PORT` | 否 | `80` | 交换机管理端口 (HTTP)。 | | `SESSION_SECRET` | 是 | — | ≥32 字符的随机字符串,用于签名 session cookie。可通过 `python -c "import secrets; print(secrets.token_urlsafe(32))"` 生成。 | | `READ_ONLY` | 否 | `true` | 当为 `true` 时,所有 `@WRITE` 端点都会抛出 403。更改为 `false` 即可启用配置/安全/诊断的写入功能。 | | `SESSION_TTL_HOURS` | 否 | `8` | 空闲会话存活时间。超过此时间后,用户必须重新登录。 | | `POLL_INTERVAL_SECONDS` | 否 | `2.0` | 实时端口流量轮询频率 (WebSocket)。请勿低于 1.0 —— 交换机的管理 CPU 无法承受。 | | `HOST` | 否 | `127.0.0.1` | FastAPI 服务的绑定地址。设置为 `0.0.0.0` 可在局域网中公开;请务必先了解其[安全隐患](#security-considerations)。 | | `BACKUPS_DIR` | 否 | `/app/backups` | 存储 `.pcc` 备份的文件系统路径。compose 文件将 `./backups` 挂载到此处。 | | `METRICS_ENABLED` | 否 | `false` | 开放 Prometheus `/metrics` 端点。 | **交换机凭据绝对不会写入 `.env`。** 它们是在登录时于浏览器中输入的, 经过交换机验证后,仅保存在后端的 RAM 中(以 session cookie 为键)。 重启容器后,所有会话都必须重新进行身份验证。 ## 架构 单个 Docker 容器,三个逻辑层: ``` ┌────────────────────────────────────────────────────────────────┐ │ Docker container: procurve-webui │ │ │ │ ┌──────────────────────┐ ┌──────────────────────────┐ │ │ │ React 18 SPA │ ←→ │ FastAPI │ │ │ │ Vite + TS + Tailwind │ HTTP │ ┌──────────────────────┐ │ │ │ │ TanStack Router/Query│ /ws │ │ REST + WebSocket │ │ │ │ │ Recharts │ │ └──────────┬───────────┘ │ │ │ │ shadcn/ui │ │ │ │ │ │ │ (built ahead of time,│ │ ┌──────────▼───────────┐ │ │ │ │ served as static │ │ │ procurve_client │ │ │ │ │ assets by FastAPI) │ │ │ (typed Python, │ │ │ │ └──────────────────────┘ │ │ talks the switch's │ │ │ │ │ │ GET-based protocol) │ │ │ │ │ └──────────┬───────────┘ │ │ │ └────────────┼─────────────┘ │ └───────────────────────────────────────────────┼────────────────┘ │ HTTP/1.1 ▼ ┌──────────────────────────┐ │ ProCurve 2810-24G │ │ ($SWITCH_HOST:80) │ │ eHTTP v2.0 management │ └──────────────────────────┘ ``` ### 各层详情 - **`procurve_client/`** —— 纯 Python、异步、完全类型化 (Pydantic v2) 的协议库。每个 applet CGI 对应一个操作;函数接收一个 `ProcurveTransport` 并返回已验证的模型。**不依赖任何 FastAPI。** 可作为独立库在脚本和 Notebook 中复用。 - **`app/`** —— FastAPI 应用程序。作为轻量级层,负责将 HTTP 路由映射到 `procurve_client` 操作,处理身份验证/会话,管理 `BackupStore`,并提供预构建的 React bundle。WebSocket 端点 `/ws/port-traffic` 用于传输各端口的计数器。 - **`frontend/`** —— React 18 + TypeScript SPA。使用 TanStack Router 实现类型安全的路由,TanStack Query 获取数据,Tailwind + shadcn/ui 进行样式设计,Recharts 绘制图表。类型通过 `openapi-typescript` 从 FastAPI 的 `openapi.json` 生成 —— 无需手写 API 类型。 ### 为什么这样拆分? - **`procurve_client` 完全不依赖 FastAPI。** 你可以独立 `pip install` 该包,并在 Python 中编写与交换机通信的脚本。 - **前端完全不依赖交换机。** 它只知道内部的 REST 契约,这意味着同一个 UI 可以通过不同的传输方式工作(例如未来的 REST 代理、SNMP 网桥等)。 - **容器完全不接触凭据。** 交换机即是身份提供者;磁盘上唯一的 secret 是用于签发 cookie 的密钥 (`SESSION_SECRET`)。 完整设计请参阅 [`docs/specs/2026-04-23-procurve-webui-design.md`](docs/specs/2026-04-23-procurve-webui-design.md)。 ## 仓库结构 ``` procurve-webui/ ├── backend/ │ ├── app/ FastAPI routes, auth, backup store, settings │ │ ├── api/ one router per tab (status, security, …) │ │ ├── ws/ WebSocket endpoints (port-traffic) │ │ └── main.py app factory │ ├── procurve_client/ Standalone protocol client library │ │ ├── operations/ one module per tab; @READ / @WRITE decorators │ │ ├── models/ Pydantic models per domain │ │ ├── transport.py httpx wrapper; auth, error mapping │ │ └── parsing.py tilde/sentinel response parsers │ ├── tests/ unit + integration + byte-match write tests │ └── pyproject.toml ├── frontend/ │ ├── src/ │ │ ├── api/ generated TS types + React Query hooks │ │ ├── components/ layout shell, switch SVG, port LEDs │ │ ├── features/ one folder per tab (identity, status, …) │ │ ├── routes/ TanStack Router file-based routes │ │ └── lib/ formatting, alert helpers │ ├── openapi.json snapshot for type generation │ └── vite.config.ts ├── docker/ │ ├── Dockerfile multi-stage; Node 22 → Python 3.12-slim │ ├── entrypoint.sh │ └── healthcheck.sh ├── docker-compose.yml single-service convenience file ├── docs/ │ ├── specs/ design specifications per phase │ ├── plans/ step-by-step implementation plans │ └── screenshots/ PNGs used in this README ├── research/ Phase 0 reverse-engineering output │ ├── applet/agent.jar the original Java applet │ ├── mirror/ full HTTP mirror of the switch │ ├── decompiled/ CFR-decompiled .java sources (gitignored) │ ├── protocol/ one .md per CGI operation │ ├── fixtures/ live-captured response samples │ └── backups/ reference `.pcc` config snapshot ├── tools/ │ └── mock_demo/ mock backend for demos and screenshots └── README.md (this file) ``` ## 协议层工作原理 原版 applet 通过少量 CGI 端点与交换机通信(约 50 个 GET 处理程序, 外加 2 个用于配置下载和上传的 POST 端点)。所有响应体均为纯 ASCII, 字段以 `~` 分隔,并在第一行包含 `OK~…` / `error~…` 标识。 `procurve_client` 将该接口映射为一个扁平化的函数 API: ``` import asyncio from procurve_client import ProcurveTransport from procurve_client.operations.identity import read_identity_page from procurve_client.operations.status import get_port_status from procurve_client.operations.backup import download_config async def main(): async with ProcurveTransport(host="192.0.2.3") as t: identity = await read_identity_page(t) print(identity.system_name, identity.firmware_version) port = await get_port_status(t, port=1) print(port.link_state, port.speed_mbps, port.duplex) cfg = await download_config(t) print(f"running-config: {cfg.size} bytes, sha256={cfg.sha256[:16]}…") asyncio.run(main()) ``` 基于逆向工程得出的协议具有以下几个不变性: - **所有操作均为 `async`。** 一个 `ProcurveTransport` 包装了单个 `httpx.AsyncClient`;你为每个会话创建一个并将其传递给各项操作。 - **读取操作使用 `@READ` 装饰,写入操作使用 `@WRITE`。** 当 `READ_ONLY=true` 时,每个 `@WRITE` 都会在发起任何网络调用 *之前* 直接短路并引发 `WriteDisabledError`。 - **验证规则源自交换机固件,而非我们自定义的。** 每一个验证器(端口名长度、VLAN ID 范围、IP 格式等)均提取自反编译的 Java 代码,并与实际行为进行了交叉验证。如果交换机会拒绝某个值,Python 模型会先将其拦截并给出清晰的错误。 - **写入操作保持字节级精确。** 每一个设置器都有对应的单元测试,确保生成的 HTTP 请求与源自反编译 applet 的已知正确模板完全匹配。空格被编码为 `+`(applet 使用的是 `URLEncoder.encode`);查询键中的已知拼写错误(例如将 `indices` 写作 `indeces`)被原样保留,因为交换机固件就是按字面量去检查它们的。 ## 功能概览 ### 身份信息选项卡 — `/` 静态着陆页。从交换机获取 `/identity/identity.html` 并解析出型号、序列号、固件、基本 MAC、IPv4、运行时间、总/空闲内存以及 CPU 利用率。只读,无写操作。 ### 状态选项卡 — `/status` - 交换机机箱的实时 SVG 渲染图(24 个铜口 RJ45 分为 2 行各 12 个,右侧有 4 个 mini-GBIC 插槽),端口 LED 实时反映链路状态和活动情况。 - 通过 WebSocket `/ws/port-traffic` 按设定的 `POLL_INTERVAL_SECONDS` 频率刷新的各端口利用率图表。 - 包含详情对话框的告警日志,支持单条确认、批量确认、删除。 - `/status/ports/{port}` 深度链接,用于查看特定端口的详细信息。 ### 配置选项卡 — `/configuration` *(写入)* - 系统信息(名称、位置、联系人) - IP 配置 (DHCP / 静态) - 默认网关 - 端口级配置(管理状态、名称、速率/双工、流控) - 设备功能(巨型帧、IGMP Snooping 等) - 故障检测阈值 - 控(端口镜像) - "Bob" SFP/mini-GBIC 端口分配 - QoS —— CoS 应用/用户优先级/VLAN 优先级,DSCP,DiffServ ### 安全选项卡 — `/security` *(写入)* - Web 访问模式 (Web/SSL/WebSSL/禁用) - Web 管理员 IP 白名单 (`web-managers`) —— **存在锁定风险**,需通过额外的确认步骤 - 端口级安全(管理/学习/限制/入侵告警) - 入侵日志 + 重置标志 - SSL 状态检查 - 管理员 / 操作员密码 ### 诊断选项卡 — `/diagnostics` *(写入)* - ICMP Ping - 链路测试 (环回) - 配置报告(以人类可读格式转储完整的 running-config) - 设备重置 / 重新加载 ### 备份选项卡 — `/backups` - 已存储 `.pcc` 文件的列表(时间戳、标签、大小、SHA256、触发来源:手动 / 写入前 / 定时) - 立即获取新快照 - 将已存储的备份与交换机实时的 running-config 进行 diff - 下载为 `.pcc` - 恢复(会强制重启 —— 需明确的二次确认) - 删除 ### 支持选项卡 — `/support` 渲染交换机广播的、用户可配置的支持 URL(`set_support` 写入通过 配置 → 支持 页面进行)。该选项卡本身只是一个提供外部链接的简洁卡片。 ## 安全考量 本应用专为运行在受信任主机上的单一受信任用户设计(通常是托管 Docker daemon 的同一台机器)。在将其暴露给局域网之前,请仔细阅读本节内容。 - **无持久化用户数据库。** 交换机是身份提供者;凭据仅存在于浏览器 session 和后端 RAM 中,以签名 cookie 作为索引。它们绝不会写入磁盘,也绝不会被记录在日志中。 - **`SESSION_SECRET` 必须是真正的随机值。** 容器会 *有意* 拒绝使用 `.env.example` 中占位符字符串启动。请使用 `python -c "import secrets; print(secrets.token_urlsafe(32))"` 生成一个全新的值。 - **默认绑定地址为 `127.0.0.1`。** compose 文件将发布端口绑定至 `127.0.0.1:8080`,因此只能从本地主机访问。若要暴露给局域网,请在 `docker-compose.yml` 中将其更改为 `8080:8080`,*并* 添加一个处理 TLS 终结的反向代理 (Caddy / Traefik / nginx) —— 目前尚未内置 HTTPS。如果没有代理,交换机凭据将以明文形式在网络中传输。 - **针对写入操作的 CSRF 防护。** 前端在每次改变状态的请求中都会提交一个双重提交 cookie token;严格限制同源,并执行严格的 CORS。 - **默认只读。** 即使凭据有效,全新安装的应用也会拒绝所有写操作。如需启用,请在 `.env` 中设置 `READ_ONLY=false`。 - **每次写入前自动备份。** 在运行任何 `@WRITE` 之前都会捕获 `.pcc` 快照;生成的 `BackupMeta` 会记录 SHA256 和一个 `pre-write` 触发标签。 - **存在锁定风险的操作受到门控保护。** 更改管理 IP、Web 管理员白名单或管理员密码时,要求用户必须重新输入新值作为确认,然后才会发出调用。 - **危险操作不在范围内。** 固件上传、恢复出厂设置以及任何可能导致交换机变砖的操作都被刻意屏蔽了。它们被列在未来的开发计划中,并将需要专门的用户体验规范。 ## 本地生成截图 本 README 中的截图是由随仓库提供的一个小型模拟后端生成的,以便贡献者在没有真实交换机的情况下也能重新生成它们。 ``` # 1. 构建 frontend cd frontend && npm install && npm run build # 2. 运行 demo backend(提供 dist/ 服务 + 预置的 switch 响应) cd ../tools/mock_demo python demo_server.py # listens on http://127.0.0.1:8080 # 3. 浏览 http://127.0.0.1:8080 并手动截图, # 或运行 headless 捕获脚本: python capture_screenshots.py # writes PNGs to docs/screenshots/ ``` 该模拟后端提供的是硬编码的 fixtures —— 它绝不会与交换机建立 TCP 连接 —— 因此在任何主机上运行都是安全的。 ## 开发 ### 运行测试套件 ``` cd backend pytest # unit + parser + write-template tests pytest -m live # live read-only against the configured switch pytest -m roundtrip # opt-in: write → verify → restore loop # (requires user approval; never run in CI) ``` `live` 和 `roundtrip` **不会** 由 CI 运行。它们需要真实的交换机和凭据,并且 `roundtrip` 会在恢复参考基准之前执行一次写入操作。`procurve_client/` 的覆盖率目标是 ≥90%。 ### Lint 和类型检查 ``` cd backend ruff check . mypy . cd ../frontend npm run lint npm run typecheck ``` ### 重新生成 OpenAPI 类型 当你添加或更改后端端点时: ``` # Backend 在本地 :8080 上运行 curl http://localhost:8080/openapi.json > frontend/openapi.json cd frontend npm run gen:api # writes src/api/schema.d.ts ``` ### 逆向工程文档 每个交换机端点在 `research/protocol//.md` 下都有对应的协议文档。其格式记录在 [`research/protocol/_conventions.md`](research/protocol/_conventions.md) 中。 实时的响应样本位于 `research/fixtures/` 中并被原样保留(未作格式化),以便写入模板的字节匹配测试可以依赖它们。 ## 路线图 已实现: - [x] 阶段 0 —— JAR 反编译、资产镜像、协议文档、fixtures - [x] 阶段 1 —— Python `procurve_client` 库(读 + 写,覆盖率 ≥90%) - [x] 阶段 2 —— Docker 脚手架、只读的身份信息 + 状态 UI、WebSocket 端口流量 - [x] 阶段 3 —— 配置 / 安全 / 诊断选项卡(写入)、备份选项卡、告警确认/删除 计划中: - [ ] 通过 Caddy sidecar 实现 HTTPS(默认自签名,可替换为 ACME) - [ ] “危险级别”的操作(固件上传、恢复出厂设置)及其专属的 UX 规范 - [ ] 多交换机仪表盘 - [ ] 可选的“记住我”长效 token - [ ] SNMP 桥接模式(只读),以便同一 UI 能用于未开放 eHTTP 服务的交换机 ## 许可证 基于 MIT 许可证发布。完整文本请参阅 [`LICENSE`](LICENSE)。 ## 致谢 - 那些在论坛和 Reddit 帖子中让老旧 ProCurve 硬件继续焕发生机的社区成员 —— 特别是要感谢他们确认了 eHTTP v2 协议在整个 2810 固件系列中是一致的。 - [CFR](https://www.benf.org/other/cfr/) —— 用于阅读 `agent.jar` 的 Java 反编译工具。如果没有它,阶段 0 的工作将会艰难得多。 - [FastAPI](https://fastapi.tiangolo.com/)、[TanStack Router/Query](https://tanstack.com/)、[shadcn/ui](https://ui.shadcn.com/) 以及 [Recharts](https://recharts.org/) —— 技术栈中的每一层,正是它们使得我们能够在合理的时间内构建出一个精致的 UI。
标签:AV绕过, Docker, FastAPI, HP ProCurve, React, Syscalls, TCP SYN 扫描, Web UI, 安全防御评估, 网络设备管理, 请求拦截, 逆向工具