not-narleeek/pi-vpn

GitHub: not-narleeek/pi-vpn

一款为 CTF 和渗透测试场景设计的 OpenVPN 驱动工具,提供 pi 智能体扩展和独立 TUI 两种模式管理持久化的 VPN 隧道。

Stars: 0 | Forks: 0

# pi-vpn `pi-vpn` 提供**基于相同逻辑的两个接口**: - 一个 **pi 扩展** (`extensions/vpn.ts`),将 OpenVPN 暴露为四个 agent 工具 + 一个 `/vpn` 命令 + 一个实时页脚,以及 - 一个**独立终端 UI** (`pi-vpn`),你可以在任何 shell 或 tmux 面板中运行 —— 无需 agent。 两者都为你处理权限、凭证和日志解析,并以**分离的 daemon** 形式运行 tunnel,因此长时间的渗透测试会话不会在进程重启时中断。零运行时依赖。 ## 目录 - [功能介绍](#what-it-does) - [环境要求](#requirements) - [安装说明](#install) - [独立 TUI](#standalone-tui) - [快速开始](#quick-start) - [架构设计](#architecture) - [工作原理](#how-it-works) - [配置说明](#configuration) - [安全提示](#security-notes) - [故障排除](#troubleshooting) - [开源许可](#license) ## 功能介绍 将 agent 指向一个 `.ovpn` 文件(或让它自行查找),它可以: - 通过一次调用**连接**到 OpenVPN 服务器,仅在配置需要时提示输入凭证, - 显示一个**实时状态页脚** —— 名称 · 状态 · VPN IP · 经过时间 —— 每秒更新, - **监控连接**状态以确认成功或显示可读的失败信息(证书无效、认证失败、tun-device 冲突、超时), - **列出**工作区和常见目录中的 `.ovpn`/`.opvn` 文件, - **断开连接**,以及 - **重新附加**到上一次会话中仍然保持连接的 tunnel。 该包为 agent 提供了以下工具: | 工具 | agent 的用途 | |------|----------------------------| | `vpn_connect` | 从 `.ovpn` 文件连接(或切换)到 OpenVPN tunnel。仅在用户提供时传入用户名/密码;否则让扩展进行提示。 | | `vpn_disconnect` | 断开当前的 tunnel。 | | `vpn_status` | 报告当前状态:名称、状态、VPN IP、设备、PID、经过时间。 | | `vpn_list` | 发现工作区和常见目录中的 `.ovpn`/`.opvn` 文件。 | 并为你提供了一个 `/vpn` 命令: ``` /vpn interactive status panel (TUI) /vpn connect connect (prompts for creds if the profile needs them) /vpn shorthand for connect /vpn disconnect tear the current tunnel down /vpn status one-line status notification /vpn list pick an .ovpn and connect ``` 一个实时页脚 (🔒 `htb · ● · 10.10.0.5 · 12:03`) 可以让你一眼看出准备就绪状态。 ## 环境要求 | 组件 | 版本 | 说明 | |-----------|---------|-------| | **pi** | 任意较新版本 | 加载此包的 agent —— `npm i -g @earendil-works/pi-coding-agent` | | **OpenVPN** | 2.5+ | `PATH` 中的 `openvpn` 二进制文件。在 Kali/Debian 上:`apt install openvpn`。 | | **`sudo` / root** | — | `openvpn` 需要 root 权限来创建 tun 设备。参见[权限模型](#how-it-works)。 | | **`ip` (iproute2)** | — | 用于只读的 tunnel IP 检测;几乎所有 Linux 上均存在。 | | **Node** | 18+ | 用于 TypeScript 扩展 | ## 安装说明 从三种来源中选择**一种**。`pi install` 默认写入用户设置 (`~/.pi/agent/settings.json`);添加 `-l` 可写入项目本地设置。 ### 1. 从 git 安装(推荐 —— 始终最新) ``` pi install git:github.com/not-narleeek/pi-vpn ``` 如果需要可重现性,可以固定 tag/commit: ``` pi install git:github.com/not-narleeek/pi-vpn@v0.1.0 ``` ### 2. 从 npm 安装(一旦发布) ``` pi install npm:pi-vpn ``` ### 3. 从本地克隆安装(用于开发) ``` git clone https://github.com/not-narleeek/pi-vpn cd pi-vpn npm install # installs TypeScript peer deps for local type-checking pi install . # or: pi install ./pi-vpn (absolute or relative path) ``` 验证是否已加载: ``` pi list # pi-vpn should appear under packages /vpn status # inside a pi session ``` 无需将其提交到设置即可尝试: ``` pi -e git:github.com/not-narleeek/pi-vpn # ephemeral, current run only ``` ## 独立 TUI 不想牵扯到 agent?直接运行 `pi-vpn` —— 这是一个键盘驱动、对 tmux 友好的 VPN 管理器,驻留在一个面板中: ``` ┌ pi-vpn ─ 14:32:07 ──────────────────────────────────────────────────┐ │🔒 htb ● connected 10.10.14.23 tun0 12:03 pid 4821 │ ├──────────────────────────────┬────────────────────────────────────┤ │ Configs │ Log │ │ ▸ ● htb.ovpn │ … Initialization Sequence Compl… │ │ ○ thm.ovpn │ │ ├──────────────────────────────┴────────────────────────────────────┤ │ ↑↓/jk select ⏎ connect d disconnect r refresh q quit │ └───────────────────────────────────────────────────────────────────┘ ``` ``` git clone https://github.com/not-narleeek/pi-vpn && cd pi-vpn npm install # builds dist/pi-vpn.cjs via the `prepare` script npm link # optional: puts `pi-vpn` on PATH pi-vpn # launch the TUI ``` 它是一个零依赖的独立打包文件,响应式渲染(在 ≥100 列时并排显示,否则在下方堆叠),在面板大小改变时立即重新渲染,使用 alternate screen,并在退出时进行清理。还有一个用于脚本编写的 CLI: `pi-vpn connect `, `disconnect`, `status`, `list`。 ➡️ **完整的快捷键、标志和 tmux 技巧:[docs/TUI.md](docs/TUI.md)。** ## 快速开始 在 pi 会话中,只要有 `.ovpn` 文件的地方: ``` > connect to the htb vpn using ~/Downloads/lab_norlek.ovpn # Agent 调用 vpn_connect;如果配置文件需要用户名/密码,且 # 您没有提供它们,扩展会提示您。页脚亮起: # 🔬 htb · ● · 10.10.14.23 · 0:04 ``` 不记得文件名?让 agent 帮你找: ``` > list my vpn configs and connect to the tryhackme one # Agent 运行 vpn_list(扫描 cwd, ~/ctf, ~/Downloads, ~/hackerone, ~/security, ~), # 然后根据您的选择运行 vpn_connect。 ``` 即使 pi 退出,tunnel 也会继续运行: ``` > disconnect from the vpn when you're done # Agent 调用 vpn_disconnect。在此之前,后台守护进程化的 openvpn 会持续运行, # 即使 pi 退出也是如此 —— 稍后重新打开 pi,它会自动重新附加。 ``` ## 架构设计 两层结构,没有辅助脚本。(完整深入剖析: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)。) ``` ┌──────────────────────────────────────────────────────────────────────┐ │ pi (the agent) │ │ ┌────────────────────────────────────────────────────────────────┐ │ │ │ extensions/vpn.ts ←── this package (TypeScript, stdlib only) │ │ │ │ • 4 tools + /vpn command + interactive status panel + footer │ │ │ │ • privilege mgmt · credential handling · log monitoring │ │ │ └───────────────────┬────────────────────────────────────────────┘ │ └───────────────────────┼────────────────────────────────────────────────┘ │ spawnSync("sudo", ["-n","openvpn", …]) (or openvpn if root) ┌───────────────────────▼────────────────────────────────────────────────┐ │ openvpn --daemon (detached — not a child of pi) │ │ writes pid + log · opens tun/tap · SURVIVES pi quitting │ └────────────────────────────────────────────────────────────────────────┘ ``` **为什么没有辅助脚本?** 与 `pi-ghidra`/`pi-caido` 不同,没有任何东西需要 桥接 —— `openvpn` 是一个普通的 CLI。扩展仅负责编排:解析 配置、整理凭证、提升权限、启动 daemon,并 解析返回的日志。这使得整个包只是一个可审计的 TypeScript 文件。 ### 文件布局 ``` pi-vpn/ ├── extensions/ │ └── vpn.ts # pi extension: tools, /vpn command, panel, footer ├── tui/ │ ├── vpn-core.ts # framework-agnostic OpenVPN core (manager, sudo, monitor) │ └── vpn-tui.ts # standalone TUI renderer + CLI entry (→ dist/pi-vpn.cjs) ├── docs/ │ ├── ARCHITECTURE.md # lifecycle, privilege model, log parsing, reattach │ └── TUI.md # standalone TUI: keys, flags, tmux tips ├── package.json # pi manifest (pi.extensions) + `bin` + npm metadata ├── tsconfig.json # extension type-checking ├── tsconfig.tui.json # TUI type-checking ├── README.md ├── CHANGELOG.md └── LICENSE ``` ## 工作原理 ### 1. Daemon 模型 连接通过以下方式启动: ``` sudo -n openvpn --config --daemon ovpn-pi- \ --writepid /.pid --log /.log \ [--auth-user-pass ] ``` `--daemon` 将 openvpn 从 pi 的进程树中分离。**这是刻意为之且至关重要的**: CTF / 实验室 VPN 必须比任何单次 agent 运行的时间更长。你 可以通过 `/vpn disconnect`(或通过杀死 pid)显式断开连接。 ### 2. 权限模型 `openvpn` 需要 root 权限来操作 tun 设备。扩展绝不会要求超出必要的权限 —— 它会按顺序尝试以下方法,并在第一种有效的方法处停止: | # | 方法 | 说明 | |---|--------|-------| | 1 | pi 已经以 root 身份运行 | 直接调用 `openvpn` | | 2 | 免密 `sudo -n` | 在专用渗透测试机器上很常见 | | 3 | `$PI_VPN_SUDO_PASS` | 用于自动化;通过 `sudo -S` 传入 | | 4 | 会话内 UI 提示 | **仅在内存中**缓存,绝不写入磁盘 | 每次特权调用之前都会重新检查 `sudo -v` 以刷新时间戳。 ### 3. 凭证处理 按优先级顺序: - 显式 `authFile` → 按原样使用。 - 显式 `username`/`password` → 写入一个模式为 `0600` 的临时文件。 - 配置文件包含裸露的 `auth-user-pass`(通过扫描配置检测到) 且未传递凭证 → UI 交互式提示(在没有 UI 的 headless 模式下则会报错)。 临时认证文件会在 `finally` 块中删除,因此即使 连接失败或被取消,它们也会被移除。密码保存在进程作用域的变量中,**绝不**持久化到磁盘。 ### 4. 日志监控 启动后,扩展每 500 毫秒轮询一次日志文件,持续最多 60 秒, 仅读取自上次轮询以来的新字节(处理轮转): - **成功:** `Initialization Sequence Completed` → 同时捕获 `TUN/TAP device opened` 以获取接口名称。 - **失败**(第一个匹配项优先,映射为清晰的信息): `AUTH_FAILED`, `private key password verification failed`, `Options error:`, `Exiting due to fatal error`, `Cannot ioctl TUNSETIFF`, `Inactivity timeout`。 - **进程死亡:** 如果 pid 在成功之前死亡 → 失败并显示匹配到的 原因或日志末尾。 ### 5. 重启后重新附加 在 `session_start` 时,扩展会读取 `~/.pi/vpn/state.json`(仅在 连接时持久化)。如果记录的 pid 仍然存活(`kill -0`;对于 root 拥有的 pid,`EPERM` 也算作存活),它会恢复完整的 `connected` 状态 —— 包括 `startTime`,因此经过的时间计数器会从上次中断的地方继续——并 重启页脚计时器。如果 pid 已死,它会清除状态并进入空闲。 这就是让 tunnel 在 agent 重启时感觉持续存在的原因。 ### 6. Tunnel-IP 检测 只读(无需 root):首选 `ip -j -4 addr show` (JSON),回退到 文本解析 `ip -4 addr show`,以找到第一个带有 IPv4 地址的 `tun|tap|ppp|ovpn*` 接口。 ## 配置说明 全部可选。对于常见情况(交互式 UI + 免密 sudo,或以 root 身份运行 pi)无需任何配置。 | 环境变量 | 默认值 | 用途 | |---------|---------|---------| | `PI_VPN_SUDO_PASS` | *(未设置 → 提示)* | 用于非交互式 / 自动化的 sudo 密码。通过 `sudo -S` 传入;仅在内存中缓存。 | 生成的状态(绝不提交,在运行时创建于 `~/.pi/vpn/` 下): ``` state.json # persisted ONLY while connected (name, configPath, pid, # startTime, dev) — mode 0600, deleted on disconnect logs/.log # openvpn log for the current/last connection pid/.pid # openvpn pid file auth-.txt # TEMP auth-user-pass file (0600), deleted after connect ``` ## 安全提示 - **设计上,tunnel 的寿命比 pi 更长。** 这对于长时间 会话来说是全部意义所在,但这意味着被遗忘的 tunnel 会一直保持连接,直到你执行 `/vpn disconnect` 或杀死 pid。页脚和 `state.json` 会使其保持可见。 - **密码是短暂的。** 保存在进程作用域的变量中;唯一的磁盘 产物是一个 `0600` 认证文件,仅在 `connect` 调用期间存在 并在 `finally` 中删除。 - **`state.json` 不携带任何机密** —— 只有 `name`、`configPath`、`pid`、 `startTime`、`dev`。以 `0600` 模式写入,断开连接时删除。 - **以你的权限运行。** 像所有 pi 包一样,这会执行代码 (`openvpn`、`sudo`、`ip`、`kill`)。正好只有一个源文件需要审计: `extensions/vpn.ts`。 - **OpenVPN 配置文件**由你负责 —— 在连接前审查 `.ovpn` 文件,尤其是 `redirect-gateway` 和任何内嵌的脚本。 ## 故障排除 | 症状 | 解决方法 | |---------|-----| | `sudo password required` / `sudo authentication failed` | 以 root 身份运行 pi,为 `openvpn` 添加免密 sudoers 规则,或者 `export PI_VPN_SUDO_PASS=…`。在交互式会话中,你会直接收到提示。 | | `authentication failed (check VPN credentials)` | 用户名/密码错误。配置文件具有裸露的 `auth-user-pass`;请使用正确的凭证重新连接。 | | `cannot create tun device` | 另一个 tunnel 已经连接,或者没有权限。先执行 `/vpn disconnect`,或检查 sudo。 | | `connection timed out` (60 秒) | 服务器不可达,或握手速度极慢。检查位于 `~/.pi/vpn/logs/.log` 的日志。 | | 页脚显示已连接但没有 IP | Tunnel IP 检测使用 `ip`;确保已安装 iproute2。`dev`/`pid` 仍然会显示。 | | pi 重启后 Tunnel 消失 | 在 pi 离开时 openvpn 进程死掉了(服务器将你踢下线、重启等)。请重新连接。 | | `openvpn: command not found` | `apt install openvpn`(或你的发行版的等效命令)。 | | 想要完全重置 | `/vpn disconnect`,然后 `rm -rf ~/.pi/vpn/` 以清除日志/pid/状态。 | ## 开源许可 [MIT](LICENSE) — 随意使用、修改和分发。感谢注明出处,但不作强制要求。
标签:MITM代理, OpenVPN, TUI, VPN, 网络调试, 自动化, 自动化攻击, 运维工具