Skip to content

Repository files navigation

dsh-plugin-tunnel

把任意 dsh 插件的 loopback 看板,按需、限时、可撤销地暴露到公网——并且只做这一件事。

能力边界

插件侧持有的只有一份目录(哪些看板存在、叫什么)和一个受限的调用边界。 真正的事实全部在 root 手里:

事实 谁说了算
哪些目标可达 /etc/dsh-tunnel/targets/<name>.conf 是否存在且归 root
最大暴露时长 描述文件的 ttl_seconds + systemd RuntimeMaxSec drop-in,双重强制
源站是哪个端口 描述文件的 origin_port,模型不可见
公网是否真的被 Access 保护 每次开启做匿名探测,未被重定向到登录域即立即停止
运行期是否仍然安全 看门狗每 10 秒复核连接器/入口/源站/边缘,漂移即关闭

即使插件配置被完全篡改,可达目标集合与最大暴露时长也不会变化——配置里写一个 未部署的目标,只会让开启请求被 helper 拒绝。

使用

在 profile 里登记目标

- id: tunnel
  config:
    targets:
      - name: console
        title: 运行状态看板
        description: 实时指标、决策记录与运行日志
      - name: metrics
        title: 指标面板
        description: 只读的聚合视图

name 必须与生产机上 /etc/dsh-tunnel/targets/<name>.conf 同名。

由插件自己登记(推荐)

看板归谁,登记就由谁做——这样标题和描述不会和实现漂移:

export const inject = ['tunnel']

export function apply(ctx: Context) {
  const dispose = ctx.tunnel.register({
    name: 'console',
    title: '运行状态看板',
    description: '实时指标、决策记录与运行日志',
  })
  ctx.on('dispose', dispose)
}

在 IM 里用

模型拿到的是一个 tunnel_control 工具:

  • list —— 有哪些看板(本地读目录,不触发任何特权调用)
  • status —— 每条隧道的状态与 expires_at
  • open <target> —— 开一条限时隧道
  • close <target> —— 关掉某一条,其余不受影响
  • close-all —— 全关

典型对话:「打开状态看板」→ open console → 返回公网地址与到期时间; 「看完了,关掉」→ close console

编程调用

await ctx.tunnel.open('console')
await ctx.tunnel.status()
await ctx.tunnel.closeAll()

部署

# 1. 装特权层(可执行文件、模板单元、tmpfiles、sudoers)。不创建任何目标。
sudo ./deploy/install.sh

# 2. 逐个下发目标。凭据一律文件到文件,绝不经过命令行参数。
sudo ./deploy/provision-target.sh console \
  ./console.conf ./token ./uuid ./hostname ./aud

描述文件的字段说明见 deploy/targets/example.conf

sudoers 里把调用用户改成实际运行 dsh Host 的那个用户。 sudoers 只钉死 helper 路径与四个动词;目标名是可变参数,由 root 侧用 「字面形状 + 必须存在同名 root 持有描述文件」两道校验收敛。

开发

npm ci
npm run check && npm test     # 类型检查 + 28 项测试
npm run build
npm run lint:sh               # 特权脚本语法检查

测试不触网、不需要真实凭据:特权 helper 全部由注入的假执行器代替。

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages