nagent-remote:内网外网互通工具:Node.js 原生库实现,零第三方依赖,Agent/Server 双角色,支持多通道映射

内网外网互通工具:Node.js 原生库实现,零第三方依赖,Agent/Server 双角色,支持多通道映射

Branch1Tags1
FilesLast commitLast update
18 days ago
1 month ago
2 months ago
2 months ago
18 days ago
2 months ago
1 month ago
18 days ago
2 months ago
2 months ago
18 days ago
1 month ago

nagent-remote

轻量级内网穿透工具,基于 Node.js 原生库实现,零第三方依赖。

同一程序通过配置切换 内网 Agent(server 模式)和 ECS 公网服务端(client 模式)两种角色。适合个人/小团队将家中电脑、办公内网的服务暴露到公网。

当前版本:v1.7.3

✨ 特性

  • 零依赖 — 仅使用 Node.js 内置 net / fs / path / crypto 等模块
  • 单一程序双角色 — 通过 --role server|client 切换,一套配置文件部署两台机器
  • Token 认证 — 控制通道握手验证,防止未授权隧道注册
  • 断线自动重连 — 指数退避(2s → 60s),网络恢复后自动重建隧道
  • 心跳检测 — 30s 间隔,3 次超时自动断开死连接
  • 并发限制 — 单隧道最大 500 并发连接
  • 日志按天分割 — 每日一个日志文件,自动清理超过保留天数的旧日志
  • 优雅退出 — SIGINT/SIGTERM 信号触发时清理所有连接
  • PM2 兼容 — 与 PM2 进程管理无缝配合
  • IP 封禁 — 控制口 token 认证连续失败自动封禁来源 IP
  • TLS 支持(v1.7.0)— 控制通道可走 TLS,支持 servername 指定 SNI,解决用 IP 连接域名证书时的 ERR_TLS_CERT_ALTNAME_INVALID
  • RDP 认证失败封禁(v1.7.0)— 按认证是否失败判定,密码正确永远不计数
  • 封禁加固(v1.7.1)— 封禁白名单(IP / CIDR / fail2ban ignoreip 文件)、封禁时长递增、封禁状态持久化落盘、fail2ban 事件文件对接
  • 准入与封禁解耦(v1.7.3)— allowAnySource(放谁进来)与 enableFailBan(封谁)拆成两个独立开关,动态 IP 场景既能不限来源又能保持封禁生效

🔐 RDP 认证失败封禁(v1.7.0)

传统做法按「连接频率」封禁,无法区分正常访问与爆破 —— 一旦降阈值必然误伤自己。 v1.7.0 改为按「认证是否失败」判定:密码正确永远不计数,从根本上消除误伤。

工作方式

  1. 隧道配置加 "service": "rdp"
  2. 内网 Agent 为每条隧道连接分配一个独立回环源地址 127.0.0.x 出站; 该地址会原样出现在 Windows 安全日志的 IpAddress 字段
  3. Agent 轮询安全日志 4625(登录失败)/ 4624(登录成功), 用回环地址把认证结果关联回具体连接,只回传 connId、不回传 IP (IP 由公网侧自行认定,即使 Agent 被攻陷也无法伪造封禁目标)
  4. 公网侧按 (真实IP × 隧道) 计数,连续失败达阈值即封禁该 IP 对该隧道的访问

配置(公网侧 client 段)

字段 默认 说明
evidenceEnforce false false = 观察模式(只记日志不封禁);建议先观察再开启
maxEvidenceFails 5 窗口内认证失败次数阈值
evidenceWindow 600 计数窗口(秒)
evidenceBanDuration 1800 封禁时长(秒)

前置条件

  • 内网机为 Windows,且 RDP 启用 NLA(UserAuthentication=1)
  • 开启登录失败审计:auditpol /set /subcategory:"Logon" /failure:enable
  • 运行 Agent 的账号需能读取安全日志(服务账号 LocalSystem 即可)
  • service 字段缺省为 tcp,不影响其他类型隧道,向后兼容

专用日志行(纯 ASCII,可直接接入 fail2ban)

[EVIDENCE] RDP auth failed src=<IP> tunnel=<name> count=<n>
[EVIDENCE] IP <IP> 已封禁 <n>s(隧道: <name>,RDP 认证连续失败 <n> 次)

🛡️ 封禁加固(v1.7.1)

v1.7.1 在公网侧(client 角色)新增三项封禁增强,全部可向后兼容关闭:

  • 封禁白名单 — banWhitelist(静态 IP/CIDR 数组)+ banWhitelistFrom(fail2ban jail.local 的 ignoreip 文件路径,自动解析)。白名单 IP 永不计入失败、永不封禁,杜绝误伤自己/内网。
  • 封禁时长递增 — 同一 IP 反复触发时,封禁时长按 banFactor(默认 2)倍增,封顶 banMax(默认 604800s = 7 天)。
  • 封禁持久化 — banStateFile 把封禁状态落盘,程序重启后不丢;banEventFile 写入纯 ASCII 事件行,可直接对接 fail2ban 的 actionban。

配套回归测试见 test/test_v171.js(覆盖 CIDR 匹配、白名单、递增封禁、持久化、事件文件,含 v1.7.3 准入/封禁解耦用例)。

🔀 准入与封禁解耦(v1.7.3)

v1.7.1 的白名单本质是「免封名单」,v1.7.2 为了支持动态 IP 引入了 ipPolicy, 却把「允许谁来连」和「要不要封失败 IP」绑在同一个开关上 —— 设成 any 时整套封禁一起失效, 实测单日数万次爆破全部放行。v1.7.3 把两件事拆开:

字段 默认 作用
allowAnySource true 是否允许任意来源 IP 接入。true = 只校验端口/协议/token;false = 仅放行白名单 IP(白名单为空时自动回退 true,避免全拒)
enableFailBan false 是否对认证失败达阈值的 IP 临时封禁。与准入完全无关,开启不会挡住任何新来源

兼容:两字段任一缺省时回退旧 ipPolicy("guard" = 准入不限 + 封禁开启)。 旧配置想保留封禁,加一行 "enableFailBan": true 即可,不用碰准入。

注意:升级本版本后封禁默认是关闭的,动态 IP 场景请显式配置 "enableFailBan": true。 启动日志会分别打印准入策略与封禁状态,便于确认。

📁 目录结构

nagent-remote/
├── nagent.js              # 主程序(--role server|client),当前 v1.7.3
├── nagent.json            # 内网端配置示例(role: server)
├── nagent-client.json     # ECS 公网端配置示例(role: client)
├── ecosystem.config.json  # PM2 进程配置
├── install-service.ps1    # Windows 服务安装脚本(nssm,需管理员运行)
├── nagent-deploy-ecs.html # ECS 部署可视化页
├── nagent-deploy-pc.html  # PC 部署可视化页
├── test/
│   └── test_v171.js       # v1.7.1 封禁加固单测
└── README.md

🚀 快速开始

1. 准备配置文件

# 内网电脑:编辑 nagent.json,修改 token 与 remoteHost(ECS 公网 IP)
# ECS 服务器:编辑 nagent-client.json,修改 token(双方 token 必须一致)

2. ECS(公网服务器)启动

node nagent.js --role client
# 或使用 PM2
pm2 start nagent.js --name nagent-client -- --role client

3. 内网电脑启动

# 先确认 nagent.json 中 remoteHost 为 ECS 公网 IP
node nagent.js --role server
# 或使用 PM2
pm2 start nagent.js --name nagent-server -- --role server

4. Windows 安装为系统服务(可选)

将 nagent.js 与本仓库的 install-service.ps1、nssm.exe 放在同一目录, 右键 install-service.ps1 → 以管理员身份运行。脚本会自动发现 node.exe、 注册并启动 nagent-server 服务,并验证隧道是否真正建立。

⚙️ 配置说明

内网端(role: server)

{
  "role": "server",
  "token": "双方一致的认证令牌",
  "server": {
    "remoteHost": "你的ECS公网IP",
    "remotePort": 7000,
    "tunnels": [
      { "name": "rdp", "localHost": "127.0.0.1", "localPort": 3389, "remotePort": 63389, "service": "rdp" },
      { "name": "web", "localHost": "127.0.0.1", "localPort": 80, "remotePort": 8080 }
    ],
    "heartbeatInterval": 30000,
    "reconnectInitialDelay": 2000,
    "tls": { "enabled": true, "rejectUnauthorized": true, "servername": "你的证书域名" }
  },
  "log": { "dir": "./logs", "level": "info", "maxDays": 30 }
}

公网端(role: client)

{
  "role": "client",
  "token": "双方一致的认证令牌",
  "client": {
    "bindHost": "0.0.0.0",
    "bindPort": 7000,
    "maxFailures": 5,
    "banDuration": 3600,
    "tls": { "enabled": true, "rejectUnauthorized": true },
    "maxEvidenceFails": 5,
    "evidenceWindow": 600,
    "evidenceBanDuration": 1800,
    "evidenceEnforce": false,
    "banFactor": 2,
    "banMax": 604800,
    "banWhitelist": [],
    "banWhitelistFrom": "",
    "banStateFile": "./ban-state.json",
    "banEventFile": "./ban-events.log"
  },
  "log": { "dir": "./logs", "level": "info", "maxDays": 30 }
}

命令行参数

参数 说明
--role server 以内网 Agent 模式运行
--role client 以 ECS 服务端模式运行
--config <path> 指定配置文件路径(默认 ./nagent.json)
--version / -v 显示版本号
--help / -h 显示帮助

🧪 运行测试

node test/test_v171.js

🔒 安全建议

  • Token 使用 32 位以上随机字符串
  • ECS 安全组对隧道端口(如 63389)做 IP 白名单限制
  • 建议搭配 Nginx/HAProxy 做 TLS 终止(如传输敏感数据)
  • 公网端 evidenceEnforce 先设 false 观察一段时间,确认无误后再开启执行封禁
  • 把内网/自家 IP 加入 banWhitelist,避免被自己的探测误封

📜 许可证 / License

本项目基于 Apache License 2.0 开源 — 详见 LICENSE 与 NOTICE。

Copyright 2026 白志明 (Bai Zhiming)

Introduction

内网外网互通工具:Node.js 原生库实现,零第三方依赖,Agent/Server 双角色,支持多通道映射

Customize your domain