Merge pull request #12 from lwch/dev

v0.7.0
This commit is contained in:
李文超
2021-11-19 11:08:08 +08:00
committed by GitHub
91 changed files with 227 additions and 726 deletions
+2 -2
View File
@@ -43,7 +43,7 @@ jobs:
- name: Build Client
run: |
go run contrib/bindata/main.go -pkg shell -o code/client/tunnel/shell/assets.go -prefix html/shell html/shell/...
go run contrib/bindata/main.go -pkg vnc -o code/client/tunnel/vnc/assets.go -prefix html/vnc html/vnc/...
go run contrib/bindata/main.go -pkg shell -o code/client/rule/shell/assets.go -prefix html/shell html/shell/...
go run contrib/bindata/main.go -pkg vnc -o code/client/rule/vnc/assets.go -prefix html/vnc html/vnc/...
go run contrib/bindata/main.go -pkg dashboard -o code/client/dashboard/assets.go -prefix html/dashboard html/dashboard/...
go build -v code/client/main.go
+10 -4
View File
@@ -33,7 +33,7 @@
# v0.3.0
1. 新增shell隧道的支持
1. 新增shell规则的支持
# v0.4.0
@@ -46,15 +46,21 @@
# v0.6.0
1. 新增vnc隧道支持
1. 新增vnc规则支持
# v0.6.1
1. 修正vnc隧道的fps参数上限不起作用的问题
1. 修正vnc规则的fps参数上限不起作用的问题
2. vnc页面增加全屏功能
# v0.6.2
1. vnc页面支持滚动
2. go版本升级到1.17.3
3. 文档补全
3. 文档补全
# v0.7.0
1. bootstrap降版到4.6.1
2. dashboard页面支持规则类型筛选
3. **为遵守中国法律,移除内网穿透功能**,保留shell和vnc功能不变
+8 -23
View File
@@ -7,10 +7,10 @@
[![platform](https://img.shields.io/badge/platform-linux%20%7C%20windows%20%7C%20macos-lightgrey.svg)](https://github.com/lwch/natpass)
[![QQ群711086098](https://img.shields.io/badge/QQ%E7%BE%A4-711086098-success)](https://jq.qq.com/?_wv=1027&k=6Fz2vkVE)
新一代NAT内网穿透工具,支持tcp隧道、shell隧道,[实现原理](docs/desc.md)
新一代主机管理工具,支持shell管理,支持远程桌面管理[实现原理](docs/desc.md)
1. [如何部署](docs/startup.md)
2. [隧道配置](docs/tunnel.md)
2. [规则配置](docs/rules.md)
功能与特性:
@@ -19,11 +19,10 @@
3. 支持多路异步IO
4. 支持虚拟链路层
5. 支持链路和终端会话监控
6. 支持tcp协议反向代理
7. 支持web shell
8. 支持web vnc
9. 支持多种操作系统
10. protobuf数据编码
6. 支持web shell
7. 支持web vnc
8. 支持多种操作系统
9. protobuf数据编码
## 效果图
@@ -51,29 +50,15 @@ windows11远程桌面
![win11-vnc](docs/imgs/vnc_win11.png)
## iperf3压测对比
使用相同参数,iperf3压测1分钟
# natpass10路复用,读写均为1s超时
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-60.00 sec 70.0 MBytes 9.79 Mbits/sec 22 sender
[ 5] 0.00-60.02 sec 57.9 MBytes 8.10 Mbits/sec receiver
# frp10路复用stcptls
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-60.00 sec 66.2 MBytes 9.26 Mbits/sec 31 sender
[ 5] 0.00-60.10 sec 57.7 MBytes 8.05 Mbits/sec receiver
## TODO
1. ~~支持include的yaml配置文件~~
2. ~~通用的connect、connect_response、disconnect消息~~
3. ~~所有隧道的dashboard页面~~
3. ~~dashboard页面~~
4. 文件传输
5. ~web远程桌面~
6. ~~流量监控统计页面,server还是client?~~
7. web端管理tunnel
7. web端管理规则
## 免责声明
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/sh
VERSION=0.6.2
VERSION=0.7.0
DOCKER_BUILDKIT=1 docker build -t natpass -f contrib/build/Dockerfile .
docker run --rm \
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/sh
VERSION=0.6.2
VERSION=0.7.0
DOCKER_BUILDKIT=1 docker build -t natpass -f contrib/build/Dockerfile .
docker run --rm \
+5 -12
View File
@@ -4,10 +4,9 @@ import (
"natpass/code/client/dashboard"
"natpass/code/client/global"
"natpass/code/client/pool"
"natpass/code/client/tunnel"
"natpass/code/client/tunnel/reverse"
"natpass/code/client/tunnel/shell"
"natpass/code/client/tunnel/vnc"
"natpass/code/client/rule"
"natpass/code/client/rule/shell"
"natpass/code/client/rule/vnc"
"natpass/code/network"
rt "runtime"
"time"
@@ -53,14 +52,10 @@ func (a *App) run() {
defer logging.Flush()
pl := pool.New(a.cfg)
mgr := tunnel.New()
mgr := rule.New()
for _, t := range a.cfg.Tunnels {
for _, t := range a.cfg.Rules {
switch t.Type {
case "tcp", "udp":
tn := reverse.New(t)
mgr.Add(tn)
go tn.Handle(pl)
case "shell":
sh := shell.New(t)
mgr.Add(sh)
@@ -89,8 +84,6 @@ func (a *App) run() {
switch msg.GetXType() {
case network.Msg_connect_req:
switch msg.GetCreq().GetXType() {
case network.ConnectRequest_tcp, network.ConnectRequest_udp:
a.connect(mgr, conn, msg)
case network.ConnectRequest_shell:
a.shellCreate(mgr, conn, msg)
case network.ConnectRequest_vnc:
+7 -46
View File
@@ -1,60 +1,21 @@
package app
import (
"fmt"
"natpass/code/client/global"
"natpass/code/client/pool"
"natpass/code/client/tunnel"
"natpass/code/client/tunnel/reverse"
"natpass/code/client/tunnel/shell"
"natpass/code/client/tunnel/vnc"
"natpass/code/client/rule"
"natpass/code/client/rule/shell"
"natpass/code/client/rule/vnc"
"natpass/code/network"
"net"
"strconv"
"github.com/lwch/logging"
)
func (a *App) connect(mgr *tunnel.Mgr, conn *pool.Conn, msg *network.Msg) {
req := msg.GetCreq()
// TODO: 创建tcp连接移到reverse包中实现
dial := "tcp"
if req.GetXType() == network.ConnectRequest_udp {
dial = "udp"
}
addr := req.GetCaddr()
link, err := net.Dial(dial, fmt.Sprintf("%s:%d", addr.GetAddr(), addr.GetPort()))
if err != nil {
logging.Error("connect to %s:%d failed, err=%v", addr.GetAddr(), addr.GetPort(), err)
conn.SendConnectError(msg.GetFrom(), msg.GetFromIdx(), msg.GetLinkId(), err.Error())
return
}
host, pt, _ := net.SplitHostPort(link.LocalAddr().String())
port, _ := strconv.ParseUint(pt, 10, 16)
tn := mgr.Get(req.GetName(), msg.GetFrom())
if tn == nil {
tn = reverse.New(global.Tunnel{
Name: req.GetName(),
Target: msg.GetFrom(),
Type: dial,
LocalAddr: host,
LocalPort: uint16(port),
RemoteAddr: addr.GetAddr(),
RemotePort: uint16(addr.GetPort()),
})
mgr.Add(tn)
}
lk := tn.NewLink(msg.GetLinkId(), msg.GetFrom(), msg.GetFromIdx(), link, conn).(*reverse.Link)
conn.SendConnectOK(msg.GetFrom(), msg.GetFromIdx(), msg.GetLinkId())
lk.Forward()
lk.OnWork <- struct{}{}
}
func (a *App) shellCreate(mgr *tunnel.Mgr, conn *pool.Conn, msg *network.Msg) {
func (a *App) shellCreate(mgr *rule.Mgr, conn *pool.Conn, msg *network.Msg) {
create := msg.GetCreq()
tn := mgr.Get(create.GetName(), msg.GetFrom())
if tn == nil {
tn = shell.New(global.Tunnel{
tn = shell.New(global.Rule{
Name: create.GetName(),
Target: msg.GetFrom(),
Type: "shell",
@@ -74,11 +35,11 @@ func (a *App) shellCreate(mgr *tunnel.Mgr, conn *pool.Conn, msg *network.Msg) {
lk.Forward()
}
func (a *App) vncCreate(confDir string, mgr *tunnel.Mgr, conn *pool.Conn, msg *network.Msg) {
func (a *App) vncCreate(confDir string, mgr *rule.Mgr, conn *pool.Conn, msg *network.Msg) {
create := msg.GetCreq()
tn := mgr.Get(create.GetName(), msg.GetFrom())
if tn == nil {
tn = vnc.New(global.Tunnel{
tn = vnc.New(global.Rule{
Name: create.GetName(),
Target: msg.GetFrom(),
Type: "vnc",
+4 -4
View File
@@ -4,7 +4,7 @@ import (
"fmt"
"natpass/code/client/global"
"natpass/code/client/pool"
"natpass/code/client/tunnel"
"natpass/code/client/rule"
"net/http"
)
@@ -12,12 +12,12 @@ import (
type Dashboard struct {
cfg *global.Configure
pl *pool.Pool
mgr *tunnel.Mgr
mgr *rule.Mgr
Version string
}
// New create dashboard object
func New(cfg *global.Configure, pl *pool.Pool, mgr *tunnel.Mgr, version string) *Dashboard {
func New(cfg *global.Configure, pl *pool.Pool, mgr *rule.Mgr, version string) *Dashboard {
return &Dashboard{
cfg: cfg,
pl: pl,
@@ -30,7 +30,7 @@ func New(cfg *global.Configure, pl *pool.Pool, mgr *tunnel.Mgr, version string)
func (db *Dashboard) ListenAndServe(addr string, port uint16) error {
mux := http.NewServeMux()
mux.HandleFunc("/api/info", db.Info)
mux.HandleFunc("/api/tunnels", db.Tunnels)
mux.HandleFunc("/api/rules", db.Rules)
mux.HandleFunc("/", db.Render)
svr := &http.Server{
Addr: fmt.Sprintf("%s:%d", addr, port),
+4 -4
View File
@@ -2,21 +2,21 @@ package dashboard
import (
"encoding/json"
"natpass/code/client/tunnel"
"natpass/code/client/rule"
"net/http"
)
// Info information data
func (db *Dashboard) Info(w http.ResponseWriter, r *http.Request) {
var ret struct {
Tunnels int `json:"tunnels"`
Rules int `json:"rules"`
PhysicalLinks int `json:"physical_links"`
VirtualLinks int `json:"virtual_links"`
Session int `json:"sessions"`
}
ret.Tunnels = len(db.cfg.Tunnels)
ret.Rules = len(db.cfg.Rules)
ret.PhysicalLinks = db.pl.Size()
db.mgr.Range(func(t tunnel.Tunnel) {
db.mgr.Range(func(t rule.Rule) {
n := len(t.GetLinks())
ret.VirtualLinks += n
if t.GetTypeName() == "shell" {
@@ -2,12 +2,12 @@ package dashboard
import (
"encoding/json"
"natpass/code/client/tunnel"
"natpass/code/client/rule"
"net/http"
)
// Tunnels get tunnels list
func (db *Dashboard) Tunnels(w http.ResponseWriter, r *http.Request) {
// Rules get rule list
func (db *Dashboard) Rules(w http.ResponseWriter, r *http.Request) {
type link struct {
ID string `json:"id"`
SendBytes uint64 `json:"send_bytes"`
@@ -23,7 +23,7 @@ func (db *Dashboard) Tunnels(w http.ResponseWriter, r *http.Request) {
Links []link `json:"links"`
}
var ret []item
db.mgr.Range(func(t tunnel.Tunnel) {
db.mgr.Range(func(t rule.Rule) {
var it item
it.Name = t.GetName()
it.Remote = t.GetRemote()
+10 -9
View File
@@ -2,6 +2,7 @@ package global
import (
"crypto/md5"
"fmt"
"natpass/code/utils"
"time"
@@ -9,8 +10,8 @@ import (
"github.com/lwch/yaml"
)
// Tunnel tunnel config
type Tunnel struct {
// Rule rule config
type Rule struct {
Name string `yaml:"name"`
Target string `yaml:"target"`
Type string `yaml:"type"`
@@ -39,7 +40,7 @@ type Configure struct {
DashboardEnabled bool
DashboardListen string
DashboardPort uint16
Tunnels []Tunnel
Rules []Rule
}
// LoadConf load configure file
@@ -63,16 +64,16 @@ func LoadConf(dir string) *Configure {
Listen string `yaml:"listen"`
Port uint16 `yaml:"port"`
} `yaml:"dashboard"`
Tunnel []Tunnel `yaml:"tunnel"`
Rules []Rule `yaml:"rules"`
}
runtime.Assert(yaml.Decode(dir, &cfg))
for i, t := range cfg.Tunnel {
for i, t := range cfg.Rules {
switch t.Type {
case "tcp", "shell", "vnc":
case "shell", "vnc":
default:
t.Type = "udp"
panic(fmt.Sprintf("unsupported type: %s", t.Type))
}
cfg.Tunnel[i] = t
cfg.Rules[i] = t
}
if cfg.Link.Connections <= 0 {
cfg.Link.Connections = 3
@@ -96,6 +97,6 @@ func LoadConf(dir string) *Configure {
DashboardEnabled: cfg.Dashboard.Enabled,
DashboardListen: cfg.Dashboard.Listen,
DashboardPort: cfg.Dashboard.Port,
Tunnels: cfg.Tunnel,
Rules: cfg.Rules,
}
}
+2 -2
View File
@@ -5,7 +5,7 @@ import (
"fmt"
"natpass/code/client/app"
"natpass/code/client/global"
"natpass/code/client/tunnel/vnc"
"natpass/code/client/rule/vnc"
"natpass/code/utils"
"os"
"path/filepath"
@@ -38,7 +38,7 @@ func main() {
conf := flag.String("conf", "", "configure file path")
ver := flag.Bool("version", false, "show version info")
act := flag.String("action", "", "install or uninstall")
name := flag.String("name", "", "tunnel name")
name := flag.String("name", "", "rule name")
vport := flag.Uint("vport", 6155, "vnc worker listen port")
vcursor := flag.Bool("vcursor", false, "vnc show cursor")
flag.Parse()
+2 -16
View File
@@ -9,26 +9,12 @@ import (
)
// SendConnectReq send connect request message
func (conn *Conn) SendConnectReq(id string, cfg global.Tunnel) {
func (conn *Conn) SendConnectReq(id string, cfg global.Rule) {
var msg network.Msg
msg.To = cfg.Target
msg.XType = network.Msg_connect_req
msg.LinkId = id
switch cfg.Type {
case "tcp":
msg.Payload = &network.Msg_Creq{
Creq: &network.ConnectRequest{
Name: cfg.Name,
XType: network.ConnectRequest_tcp,
Payload: &network.ConnectRequest_Caddr{
Caddr: &network.ConnectAddr{
Addr: cfg.RemoteAddr,
Port: uint32(cfg.RemotePort),
},
},
},
}
case "udp":
case "shell":
msg.Payload = &network.Msg_Creq{
Creq: &network.ConnectRequest{
@@ -68,7 +54,7 @@ func (conn *Conn) SendConnectReq(id string, cfg global.Tunnel) {
}
// SendConnectVnc send connect vnc request message
func (conn *Conn) SendConnectVnc(id string, cfg global.Tunnel, quality uint64, showCursor bool) {
func (conn *Conn) SendConnectVnc(id string, cfg global.Rule, quality uint64, showCursor bool) {
var msg network.Msg
msg.To = cfg.Target
msg.XType = network.Msg_connect_req
@@ -1,4 +1,4 @@
package tunnel
package rule
import (
"natpass/code/client/pool"
@@ -15,8 +15,8 @@ type Link interface {
GetPackets() (uint64, uint64)
}
// Tunnel tunnel interface
type Tunnel interface {
// Rule rule interface
type Rule interface {
NewLink(id, remote string, remoteIdx uint32, localConn net.Conn, remoteConn *pool.Conn) Link
GetName() string
GetRemote() string
@@ -26,29 +26,29 @@ type Tunnel interface {
GetLinks() []Link
}
// Mgr tunnel manager
// Mgr rule manager
type Mgr struct {
sync.RWMutex
tunnels []Tunnel
rules []Rule
}
// New new tunnel manager
// New new rule manager
func New() *Mgr {
return &Mgr{}
}
// Add add tunnel
func (mgr *Mgr) Add(tunnel Tunnel) {
// Add add rule
func (mgr *Mgr) Add(rule Rule) {
mgr.Lock()
defer mgr.Unlock()
mgr.tunnels = append(mgr.tunnels, tunnel)
mgr.rules = append(mgr.rules, rule)
}
// Get get tunnel by name
func (mgr *Mgr) Get(name, remote string) Tunnel {
// Get get rule by name
func (mgr *Mgr) Get(name, remote string) Rule {
mgr.RLock()
defer mgr.RUnlock()
for _, t := range mgr.tunnels {
for _, t := range mgr.rules {
if t.GetName() == name && t.GetRemote() == remote {
return t
}
@@ -56,11 +56,11 @@ func (mgr *Mgr) Get(name, remote string) Tunnel {
return nil
}
// Range range tunnels
func (mgr *Mgr) Range(fn func(Tunnel)) {
// Range range rules
func (mgr *Mgr) Range(fn func(Rule)) {
mgr.RLock()
defer mgr.RUnlock()
for _, t := range mgr.tunnels {
for _, t := range mgr.rules {
fn(t)
}
}
@@ -30,7 +30,7 @@ func (shell *Shell) New(pool *pool.Pool, w http.ResponseWriter, r *http.Request)
select {
case msg = <-ch:
case <-timeout:
logging.Error("create shell %s on tunnel %s failed, timtout", link.id, link.parent.Name)
logging.Error("create shell %s by rule %s failed, timtout", link.id, link.parent.Name)
http.Error(w, "timeout", http.StatusBadGateway)
return
}
@@ -42,7 +42,7 @@ func (shell *Shell) New(pool *pool.Pool, w http.ResponseWriter, r *http.Request)
}
rep := msg.GetCrep()
if !rep.GetOk() {
logging.Error("create shell %s on tunnel %s failed, err=%s",
logging.Error("create shell %s by rule %s failed, err=%s",
link.id, link.parent.Name, rep.GetMsg())
http.Error(w, rep.GetMsg(), http.StatusBadGateway)
return
@@ -85,7 +85,7 @@ func (shell *Shell) remoteForward(id string, local *websocket.Conn) {
logging.Debug("remote read %d bytes: name=%s, id=%s",
len(msg.GetSdata().GetData()), shell.Name, id)
case network.Msg_disconnect:
logging.Info("shell %s on tunnel %s closed by remote",
logging.Info("shell %s by rule %s closed by remote",
link.id, link.parent.Name)
return
}
@@ -4,7 +4,7 @@ import (
"fmt"
"natpass/code/client/global"
"natpass/code/client/pool"
"natpass/code/client/tunnel"
"natpass/code/client/rule"
"net"
"net/http"
"sync"
@@ -17,12 +17,12 @@ import (
type Shell struct {
sync.RWMutex
Name string
cfg global.Tunnel
cfg global.Rule
links map[string]*Link
}
// New new shell
func New(cfg global.Tunnel) *Shell {
func New(cfg global.Rule) *Shell {
return &Shell{
Name: cfg.Name,
cfg: cfg,
@@ -31,7 +31,7 @@ func New(cfg global.Tunnel) *Shell {
}
// NewLink new link
func (shell *Shell) NewLink(id, remote string, remoteIdx uint32, localConn net.Conn, remoteConn *pool.Conn) tunnel.Link {
func (shell *Shell) NewLink(id, remote string, remoteIdx uint32, localConn net.Conn, remoteConn *pool.Conn) rule.Link {
remoteConn.AddLink(id)
logging.Info("create link %s for shell %s on connection %d",
id, shell.Name, remoteConn.Idx)
@@ -48,24 +48,24 @@ func (shell *Shell) NewLink(id, remote string, remoteIdx uint32, localConn net.C
return link
}
// GetName get shell tunnel name
// GetName get shell rule name
func (shell *Shell) GetName() string {
return shell.Name
}
// GetTypeName get shell tunnel type name
// GetTypeName get shell rule type name
func (shell *Shell) GetTypeName() string {
return "shell"
}
// GetTarget get target of this tunnel
// GetTarget get target of this rule
func (shell *Shell) GetTarget() string {
return shell.cfg.Target
}
// GetLinks get tunnel links
func (shell *Shell) GetLinks() []tunnel.Link {
ret := make([]tunnel.Link, 0, len(shell.links))
// GetLinks get rule links
func (shell *Shell) GetLinks() []rule.Link {
ret := make([]rule.Link, 0, len(shell.links))
shell.RLock()
for _, link := range shell.links {
ret = append(ret, link)
@@ -88,7 +88,7 @@ func (shell *Shell) GetPort() uint16 {
func (shell *Shell) Handle(pl *pool.Pool) {
defer func() {
if err := recover(); err != nil {
logging.Error("close shell tunnel: %s, err=%v", shell.Name, err)
logging.Error("close shell: %s, err=%v", shell.Name, err)
}
}()
pf := func(cb func(*pool.Pool, http.ResponseWriter, *http.Request)) http.HandlerFunc {
@@ -7,7 +7,7 @@ import (
"strconv"
)
// Ctrl change vnc tunnel config
// Ctrl change vnc rule config
func (v *VNC) Ctrl(pool *pool.Pool, w http.ResponseWriter, r *http.Request) {
q := r.FormValue("quality")
s := r.FormValue("show_cursor")
@@ -50,7 +50,7 @@ func (v *VNC) New(pool *pool.Pool, w http.ResponseWriter, r *http.Request) {
select {
case msg = <-ch:
case <-timeout:
logging.Error("create vnc %s on tunnel %s failed, timtout", v.link.id, v.link.parent.Name)
logging.Error("create vnc %s by rule %s failed, timtout", v.link.id, v.link.parent.Name)
http.Error(w, "timeout", http.StatusBadGateway)
return
}
@@ -62,7 +62,7 @@ func (v *VNC) New(pool *pool.Pool, w http.ResponseWriter, r *http.Request) {
}
rep := msg.GetCrep()
if !rep.GetOk() {
logging.Error("create vnc %s on tunnel %s failed, err=%s",
logging.Error("create vnc %s by rule %s failed, err=%s",
v.link.id, v.link.parent.Name, rep.GetMsg())
http.Error(w, rep.GetMsg(), http.StatusBadGateway)
return
@@ -5,7 +5,7 @@ import (
"image"
"image/jpeg"
"natpass/code/client/pool"
"natpass/code/client/tunnel/vnc/process"
"natpass/code/client/rule/vnc/process"
"natpass/code/network"
"natpass/code/utils"
"time"
@@ -1,7 +1,7 @@
package process
import (
"natpass/code/client/tunnel/vnc/vncnetwork"
"natpass/code/client/rule/vnc/vncnetwork"
"natpass/code/network"
)
@@ -1,7 +1,7 @@
package process
import (
"natpass/code/client/tunnel/vnc/define"
"natpass/code/client/rule/vnc/define"
"syscall"
"github.com/lwch/logging"
@@ -3,7 +3,7 @@ package process
import (
"errors"
"fmt"
"natpass/code/client/tunnel/vnc/vncnetwork"
"natpass/code/client/rule/vnc/vncnetwork"
"natpass/code/utils"
"net"
"net/http"
@@ -5,8 +5,8 @@ import (
"fmt"
"image"
"image/jpeg"
"natpass/code/client/tunnel/vnc/define"
"natpass/code/client/tunnel/vnc/vncnetwork"
"natpass/code/client/rule/vnc/define"
"natpass/code/client/rule/vnc/vncnetwork"
"os"
"strings"
"syscall"
@@ -4,7 +4,7 @@ import (
"fmt"
"natpass/code/client/global"
"natpass/code/client/pool"
"natpass/code/client/tunnel"
"natpass/code/client/rule"
"net"
"net/http"
"sync"
@@ -17,12 +17,12 @@ import (
type VNC struct {
sync.RWMutex
Name string
cfg global.Tunnel
cfg global.Rule
link *Link
}
// New new vnc
func New(cfg global.Tunnel) *VNC {
func New(cfg global.Rule) *VNC {
return &VNC{
Name: cfg.Name,
cfg: cfg,
@@ -30,9 +30,9 @@ func New(cfg global.Tunnel) *VNC {
}
// NewLink new link
func (v *VNC) NewLink(id, remote string, remoteIdx uint32, localConn net.Conn, remoteConn *pool.Conn) tunnel.Link {
func (v *VNC) NewLink(id, remote string, remoteIdx uint32, localConn net.Conn, remoteConn *pool.Conn) rule.Link {
remoteConn.AddLink(id)
logging.Info("create link %s for tunnel %s on connection %d",
logging.Info("create link %s for rule %s on connection %d",
id, v.Name, remoteConn.Idx)
link := &Link{
parent: v,
@@ -48,25 +48,25 @@ func (v *VNC) NewLink(id, remote string, remoteIdx uint32, localConn net.Conn, r
return link
}
// GetName get vnc tunnel name
// GetName get vnc rule name
func (v *VNC) GetName() string {
return v.Name
}
// GetTypeName get vnc tunnel type name
// GetTypeName get vnc rule type name
func (v *VNC) GetTypeName() string {
return "vnc"
}
// GetTarget get target of this tunnel
// GetTarget get target of this rule
func (v *VNC) GetTarget() string {
return v.cfg.Target
}
// GetLinks get tunnel links
func (v *VNC) GetLinks() []tunnel.Link {
// GetLinks get rule links
func (v *VNC) GetLinks() []rule.Link {
if v.link != nil {
return []tunnel.Link{v.link}
return []rule.Link{v.link}
}
return nil
}
@@ -85,7 +85,7 @@ func (v *VNC) GetPort() uint16 {
func (v *VNC) Handle(pl *pool.Pool) {
defer func() {
if err := recover(); err != nil {
logging.Error("close shell tunnel: %s, err=%v", v.Name, err)
logging.Error("close shell: %s, err=%v", v.Name, err)
}
}()
pf := func(cb func(*pool.Pool, http.ResponseWriter, *http.Request)) http.HandlerFunc {
@@ -2,7 +2,7 @@ package vnc
import (
"fmt"
"natpass/code/client/tunnel/vnc/worker"
"natpass/code/client/rule/vnc/worker"
"github.com/gorilla/websocket"
"github.com/lwch/runtime"
@@ -2,8 +2,8 @@ package worker
import (
"errors"
"natpass/code/client/tunnel/vnc/define"
"natpass/code/client/tunnel/vnc/vncnetwork"
"natpass/code/client/rule/vnc/define"
"natpass/code/client/rule/vnc/vncnetwork"
"syscall"
"unsafe"
@@ -3,7 +3,7 @@
package worker
import "natpass/code/client/tunnel/vnc/vncnetwork"
import "natpass/code/client/rule/vnc/vncnetwork"
func (worker *Worker) runCapture() vncnetwork.ImageData {
return vncnetwork.ImageData{}
@@ -3,7 +3,7 @@
package worker
import "natpass/code/client/tunnel/vnc/vncnetwork"
import "natpass/code/client/rule/vnc/vncnetwork"
func runMouse(data *vncnetwork.MouseData) {
}
@@ -4,7 +4,7 @@
package worker
import (
"natpass/code/client/tunnel/vnc/vncnetwork"
"natpass/code/client/rule/vnc/vncnetwork"
"github.com/go-vgo/robotgo"
"github.com/lwch/logging"
@@ -3,7 +3,7 @@ package worker
import (
"image"
"image/jpeg"
"natpass/code/client/tunnel/vnc/vncnetwork"
"natpass/code/client/rule/vnc/vncnetwork"
"os"
"github.com/gorilla/websocket"
@@ -2,7 +2,7 @@ package worker
import (
"fmt"
"natpass/code/client/tunnel/vnc/define"
"natpass/code/client/rule/vnc/define"
"runtime"
"syscall"
-126
View File
@@ -1,126 +0,0 @@
package reverse
import (
"bytes"
"io"
"natpass/code/client/pool"
"natpass/code/network"
"natpass/code/utils"
"net"
"github.com/lwch/logging"
"google.golang.org/protobuf/proto"
)
// Link link object
type Link struct {
parent *Tunnel
id string // link id
target string // target id
targetIdx uint32 // target idx
local net.Conn
remote *pool.Conn
OnWork chan struct{}
closeFromRemote bool
// runtime
recvBytes uint64
sendBytes uint64
recvPacket uint64
sendPacket uint64
}
func (link *Link) close() {
logging.Info("close link %s on tunnel %s", link.id, link.parent.Name)
link.local.Close()
link.remote.RemoveLink(link.id)
link.parent.remove(link.id)
}
// GetID get link id
func (link *Link) GetID() string {
return link.id
}
// GetBytes get send and recv bytes
func (link *Link) GetBytes() (uint64, uint64) {
return link.recvBytes, link.sendBytes
}
// GetPackets get send and recv packets
func (link *Link) GetPackets() (uint64, uint64) {
return link.recvPacket, link.sendPacket
}
// Forward forward data
func (link *Link) Forward() {
go link.remoteRead()
go link.localRead()
}
func (link *Link) remoteRead() {
defer utils.Recover("remoteRead")
defer link.close()
ch := link.remote.ChanRead(link.id)
for {
msg := <-ch
if msg == nil {
return
}
data, _ := proto.Marshal(msg)
link.recvBytes += uint64(len(data))
link.recvPacket++
link.targetIdx = msg.GetFromIdx()
switch msg.GetXType() {
case network.Msg_forward:
_, err := io.Copy(link.local, bytes.NewReader(msg.GetXData().GetData()))
if err != nil {
logging.Error("write data on tunnel %s link %s failed, err=%v", link.parent.Name, link.id, err)
return
}
case network.Msg_connect_rep:
if msg.GetCrep().GetOk() {
link.OnWork <- struct{}{}
continue
}
logging.Error("create link %s on tunnel %s failed, err=%s",
link.id, link.parent.Name, msg.GetCrep().GetMsg())
return
case network.Msg_disconnect:
logging.Info("disconnect link %s on tunnel %s from remote",
link.id, link.parent.Name)
link.closeFromRemote = true
return
}
}
}
func (link *Link) localRead() {
defer utils.Recover("localRead")
defer link.close()
<-link.OnWork
buf := make([]byte, 16*1024)
for {
n, err := link.local.Read(buf)
if err != nil {
if !link.closeFromRemote {
n := link.remote.SendDisconnect(link.target, link.targetIdx, link.id)
link.sendBytes += n
link.sendPacket++
}
logging.Error("read data on tunnel %s link %s failed, err=%v", link.parent.Name, link.id, err)
return
}
if n == 0 {
continue
}
logging.Debug("link %s on tunnel %s read from local %d bytes", link.id, link.parent.Name, n)
send := link.remote.SendData(link.target, link.targetIdx, link.id, buf[:n])
link.sendBytes += send
link.sendPacket++
}
}
// SetTargetIdx set link remote index
func (link *Link) SetTargetIdx(idx uint32) {
link.targetIdx = idx
}
-139
View File
@@ -1,139 +0,0 @@
package reverse
import (
"natpass/code/client/global"
"natpass/code/client/pool"
"natpass/code/client/tunnel"
"net"
"sync"
"github.com/lwch/logging"
"github.com/lwch/runtime"
)
// Tunnel tunnel object
type Tunnel struct {
sync.RWMutex
Name string
cfg global.Tunnel
links map[string]*Link
}
// New new tunnel
func New(cfg global.Tunnel) *Tunnel {
return &Tunnel{
Name: cfg.Name,
cfg: cfg,
links: make(map[string]*Link),
}
}
// NewLink new link
func (tn *Tunnel) NewLink(id, remote string, remoteIdx uint32, localConn net.Conn, remoteConn *pool.Conn) tunnel.Link {
remoteConn.AddLink(id)
logging.Info("create link %s for reverse %s on connection %d",
id, tn.Name, remoteConn.Idx)
link := &Link{
parent: tn,
id: id,
target: remote,
targetIdx: remoteIdx,
local: localConn,
remote: remoteConn,
OnWork: make(chan struct{}),
}
tn.Lock()
tn.links[link.id] = link
tn.Unlock()
return link
}
// GetName get reverse tunnel name
func (tn *Tunnel) GetName() string {
return tn.Name
}
// GetTypeName get reverse tunnel type name
func (tn *Tunnel) GetTypeName() string {
return "reverse"
}
// GetTarget get target of this tunnel
func (tn *Tunnel) GetTarget() string {
return tn.cfg.Target
}
// GetLinks get tunnel links
func (tn *Tunnel) GetLinks() []tunnel.Link {
ret := make([]tunnel.Link, 0, len(tn.links))
tn.RLock()
for _, link := range tn.links {
ret = append(ret, link)
}
tn.RUnlock()
return ret
}
// GetRemote get remote target name
func (tn *Tunnel) GetRemote() string {
return tn.cfg.Target
}
// GetPort get listen port
func (tn *Tunnel) GetPort() uint16 {
return tn.cfg.LocalPort
}
// Handle handle tunnel
func (tn *Tunnel) Handle(pool *pool.Pool) {
if tn.cfg.Type == "tcp" {
tn.handleTCP(pool)
} else {
// TODO
func() {}()
}
}
func (tn *Tunnel) handleTCP(pool *pool.Pool) {
defer func() {
if err := recover(); err != nil {
logging.Error("close tcp tunnel: %s, err=%v", tn.cfg.Name, err)
}
}()
l, err := net.ListenTCP("tcp", &net.TCPAddr{
IP: net.ParseIP(tn.cfg.LocalAddr),
Port: int(tn.cfg.LocalPort),
})
runtime.Assert(err)
defer l.Close()
for {
conn, err := l.Accept()
if err != nil {
logging.Error("accept from %s tunnel, err=%v", tn.cfg.Name, err)
continue
}
id, err := runtime.UUID(16, "0123456789abcdef")
if err != nil {
conn.Close()
logging.Error("generate link id failed, err=%v", err)
continue
}
remote := pool.Get(id)
if remote == nil {
conn.Close()
logging.Error("no connection available")
continue
}
link := tn.NewLink(id, tn.cfg.Target, 0, conn, remote).(*Link)
remote.SendConnectReq(id, tn.cfg)
link.Forward()
}
}
func (tn *Tunnel) remove(id string) {
tn.Lock()
delete(tn.links, id)
tn.Unlock()
}
+2 -2
View File
@@ -5,5 +5,5 @@ dashboard: # web面板
listen: 0.0.0.0 # 监听地址
port: 8080 # 监听端口号
#include conf.d/*.yaml
tunnel: # tunnel列表
#include tunnel.d/*.yaml
rules: # rule列表
#include rule.d/*.yaml
-7
View File
@@ -1,7 +0,0 @@
- name: rdp # 链路名称
target: that # 目标客户端ID
type: tcp # 连接类型tcp或udp
local_addr: 0.0.0.0 # 本地监听地址
local_port: 3389 # 本地监听端口号
remote_addr: 127.0.0.1 # 目标客户端连接地址
remote_port: 3389 # 目标客户端连接端口号
+2 -2
View File
@@ -17,9 +17,9 @@ LDFLAGS="$LDFLAGS
--extldflags '-static -fpic -lssp'"
fi
go run contrib/bindata/main.go -pkg shell -o code/client/tunnel/shell/assets.go \
go run contrib/bindata/main.go -pkg shell -o code/client/rule/shell/assets.go \
-prefix html/shell "$@" html/shell/...
go run contrib/bindata/main.go -pkg vnc -o code/client/tunnel/vnc/assets.go \
go run contrib/bindata/main.go -pkg vnc -o code/client/rule/vnc/assets.go \
-prefix html/vnc "$@" html/vnc/...
go run contrib/bindata/main.go -pkg dashboard -o code/client/dashboard/assets.go \
-prefix html/dashboard "$@" html/dashboard/...
+2 -2
View File
@@ -118,7 +118,7 @@ func main() {
func bindata() {
cmd := exec.Command("go", "run", "contrib/bindata/main.go",
"-pkg", "shell",
"-o", "code/client/tunnel/shell/assets.go",
"-o", "code/client/rule/shell/assets.go",
"-prefix", "html/shell",
"html/shell/...")
cmd.Stdout = os.Stdout
@@ -127,7 +127,7 @@ func bindata() {
cmd = exec.Command("go", "run", "contrib/bindata/main.go",
"-pkg", "vnc",
"-o", "code/client/tunnel/vnc/assets.go",
"-o", "code/client/rule/vnc/assets.go",
"-prefix", "html/vnc",
"html/vnc/...")
cmd.Stdout = os.Stdout
+15 -19
View File
@@ -1,9 +1,8 @@
# 实现原理
基于tls链接,protobuf进行数据传输,下面举例在办公网络穿透到家庭网络,
并通过rdp进行连接家庭网络下的某台windows设备
基于tls链接,protobuf进行数据传输,下面举例远程连接服务器集群内的某台主机
![rdp](imgs/example.jpg)
![shell](imgs/example.jpg)
server端配置(10.0.1.1)
@@ -17,9 +16,9 @@ server端配置(10.0.1.1)
key: /dir/to/tls/key/file # tls密钥
crt: /dir/to/tls/crt/file # tls证书
家庭网络client配置(192.168.1.100)
服务器client配置(192.168.1.100)
id: home # 客户端ID
id: server # 客户端ID
server: 10.0.1.1:6154 # 服务器地址
secret: 0123456789 # 预共享密钥,必须与server端相同,否则握手失败
log:
@@ -36,29 +35,26 @@ server端配置(10.0.1.1)
dir: /opt/natpass/logs # 路径
size: 50M # 单个文件大小
rotate: 7 # 保留数量
tunnel: # 远端tunnel列表可为空
rules: # 远端rule列表可为空
- name: rdp # 链路名称
target: home # 目标客户端ID
type: tcp # 连接类型tcp或udp
target: server # 目标客户端ID
type: shell # 连接类型tcp或udp
local_addr: 0.0.0.0 # 本地监听地址
local_port: 3389 # 本地监听端口号
remote_addr: 192.168.1.101 # 目标客户端连接地址
remote_port: 3389 # 目标客户端连接端口号
工作流程如下:
1. 办公网络与家庭网络中的np-cli创建tls连接到np-svr
2. np-cli服务发送握手包,并将配置文件中的secret字段进行md5哈希
3. np-svr等待握手报文,若等待超时则为非法链接,直接断开
4. 办公网络客户机创建新连接到172.16.1.100的3389端口
5. 172.16.1.100上的np-cli接收到新请求后创建新的link并生成链接id
6. 172.16.1.100上的np-cli发送connect_request消息,告知连接类型和链接目标地址和端口
7. np-svr转发connect_request消息至192.168.1.100上的np-cli
8. 192.168.1.100上的np-cli接收到connect_request消息,根据请求信息创建链接到目标地址和端口
9. 192.168.1.100上的np-cli根据链接创建结果返回connect_response消息
10. np-svr转发connect_response消息至172.16.1.100上的np-cli
11. 172.168.1.100上的np-cli接收到connect_response消息后根据是否成功来决定是否需要断开rdp客户端链接
12. 链路打通,两端各自发送data消息到对应链路
4. 用户打开办公网络主机172.16.1.100上的终端页面,并连接到服务器集群中的主机server
5. 172.16.1.100上的np-cli发送connect_request消息,并将连接类型设置为shell
6. np-svr转发connect_request消息至192.168.1.100上的np-cli
7. 192.168.1.100上的np-cli接收到connect_request消息,创建/bin/bash进程
8. 192.168.1.100上的np-cli根据链接创建结果返回connect_response消息
9. np-svr转发connect_response消息至172.16.1.100上的np-cli
10. 172.168.1.100上的np-cli接收connect_response消息
11. 开始转发网页上的输入输出内容
## 软件架构
+1 -1
View File
@@ -1 +1 @@
<mxfile host="Chrome" modified="2021-11-15T16:37:12.268Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36" etag="TKZKltqgxWfshnQa0pVI" version="15.5.6" type="device"><diagram id="LZ-Lv04eS_hwo8OXVvyP" name="第 1 页">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</diagram></mxfile>
<mxfile host="Chrome" modified="2021-11-18T16:09:14.322Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36" etag="I6b9FnXFCRl5Ulcx4P_B" version="15.5.6" type="device"><diagram id="LZ-Lv04eS_hwo8OXVvyP" name="第 1 页">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</diagram></mxfile>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 80 KiB

+1 -158
View File
@@ -1,158 +1 @@
<mxfile host="app.diagrams.net" modified="2021-08-19T03:29:42.029Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36" etag="kKNkbF7So22PkGp4eWOc" version="14.9.7" type="github">
<diagram id="Kjf8sS8rRDm6eebZkxfj" name="第 1 页">
<mxGraphModel dx="1038" dy="579" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="WTAgDNwTBJ27G1_lSc48-10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="290" y="219" as="sourcePoint" />
<mxPoint x="390" y="130" as="targetPoint" />
<Array as="points">
<mxPoint x="290" y="219" />
<mxPoint x="290" y="130" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.71;exitY=1.066;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="WTAgDNwTBJ27G1_lSc48-3" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="320" y="356" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-3" value="" style="points=[];aspect=fixed;html=1;align=center;shadow=0;dashed=0;image;image=img/lib/allied_telesis/security/Router_VPN.svg;" parent="1" vertex="1">
<mxGeometry x="280" y="220" width="55.800000000000004" height="39.6" as="geometry" />
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.694;entryY=-0.054;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0.717;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="WTAgDNwTBJ27G1_lSc48-4" target="WTAgDNwTBJ27G1_lSc48-8" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-13" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="WTAgDNwTBJ27G1_lSc48-4" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="436" y="150" as="targetPoint" />
<Array as="points">
<mxPoint x="500" y="150" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-4" value="" style="points=[];aspect=fixed;html=1;align=center;shadow=0;dashed=0;image;image=img/lib/allied_telesis/security/Router_VPN.svg;" parent="1" vertex="1">
<mxGeometry x="490" y="221" width="55.800000000000004" height="39.6" as="geometry" />
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-7" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="WTAgDNwTBJ27G1_lSc48-5" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="290" y="260" as="targetPoint" />
<Array as="points">
<mxPoint x="290" y="330" />
<mxPoint x="290" y="330" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-23" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=-0.005;exitY=0.676;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="WTAgDNwTBJ27G1_lSc48-5" target="WTAgDNwTBJ27G1_lSc48-18" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="230" y="402" />
<mxPoint x="230" y="402" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-5" value="内网设备&lt;br&gt;172.16.1.100" style="points=[];aspect=fixed;html=1;align=center;shadow=0;dashed=0;image;image=img/lib/allied_telesis/computer_and_terminals/Personal_Computer_with_Server.svg;" parent="1" vertex="1">
<mxGeometry x="276.7" y="360" width="62.400000000000006" height="62.400000000000006" as="geometry" />
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="440" y="130" as="sourcePoint" />
<mxPoint x="530" y="222" as="targetPoint" />
<Array as="points">
<mxPoint x="530" y="130" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-14" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.681;entryY=0.015;entryDx=0;entryDy=0;entryPerimeter=0;exitX=-0.014;exitY=0.759;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="WTAgDNwTBJ27G1_lSc48-6" target="WTAgDNwTBJ27G1_lSc48-3" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-6" value="np-svr&lt;br&gt;公网服务&lt;br&gt;10.0.1.1" style="points=[];aspect=fixed;html=1;align=center;shadow=0;dashed=0;image;image=img/lib/allied_telesis/computer_and_terminals/Server_Desktop.svg;" parent="1" vertex="1">
<mxGeometry x="393" y="110" width="42.599999999999994" height="54" as="geometry" />
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="500" y="357" as="sourcePoint" />
<mxPoint x="500" y="250" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-33" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1.053;exitY=0.33;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="WTAgDNwTBJ27G1_lSc48-8" target="WTAgDNwTBJ27G1_lSc48-28" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="590" y="381" />
<mxPoint x="590" y="381" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-8" value="内网设备&lt;br&gt;192.168.1.100" style="points=[];aspect=fixed;html=1;align=center;shadow=0;dashed=0;image;image=img/lib/allied_telesis/computer_and_terminals/Personal_Computer_with_Server.svg;" parent="1" vertex="1">
<mxGeometry x="486.7" y="360" width="62.400000000000006" height="62.400000000000006" as="geometry" />
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-16" value="办公网络&lt;br&gt;172.16.1.1/24" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
<mxGeometry x="180" y="221" width="90" height="30" as="geometry" />
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-17" value="家庭网络&lt;br&gt;192.168.1.1/24" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
<mxGeometry x="560" y="224.8" width="100" height="30" as="geometry" />
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-22" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="WTAgDNwTBJ27G1_lSc48-18" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="276" y="380" as="targetPoint" />
<Array as="points">
<mxPoint x="276" y="380" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="200" y="410" as="sourcePoint" />
<mxPoint x="200" y="480" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-18" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;aspect=fixed;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;align=center;outlineConnect=0;shape=mxgraph.vvd.cloud_computing;fillColor=#066A90;" parent="1" vertex="1">
<mxGeometry x="160" y="375" width="50" height="32" as="geometry" />
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-26" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="170" y="410" as="targetPoint" />
<mxPoint x="170" y="470" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-19" value="rdp客户端" style="pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#434445;aspect=fixed;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;align=center;outlineConnect=0;shape=mxgraph.vvd.administrator;" parent="1" vertex="1">
<mxGeometry x="174.25" y="480" width="21.5" height="50" as="geometry" />
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-21" value="np-cli&lt;br&gt;监听3389端口" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
<mxGeometry x="70" y="375" width="90" height="30" as="geometry" />
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-27" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="650" y="410" as="sourcePoint" />
<mxPoint x="650" y="480" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-35" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="WTAgDNwTBJ27G1_lSc48-28" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="550" y="400" as="targetPoint" />
<Array as="points">
<mxPoint x="560" y="400" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-28" value="" style="pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;aspect=fixed;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;align=center;outlineConnect=0;shape=mxgraph.vvd.cloud_computing;fillColor=#066A90;" parent="1" vertex="1">
<mxGeometry x="610" y="375" width="50" height="32" as="geometry" />
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-29" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="620" y="410" as="targetPoint" />
<mxPoint x="620" y="470" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-31" value="np-cli&lt;br&gt;连接到3389端口" style="text;html=1;resizable=0;autosize=1;align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;" parent="1" vertex="1">
<mxGeometry x="665" y="377" width="100" height="30" as="geometry" />
</mxCell>
<mxCell id="WTAgDNwTBJ27G1_lSc48-32" value="内网设备&lt;br&gt;192.168.1.101" style="points=[];aspect=fixed;html=1;align=center;shadow=0;dashed=0;image;image=img/lib/allied_telesis/computer_and_terminals/Personal_Computer_with_Server.svg;" parent="1" vertex="1">
<mxGeometry x="603.8" y="480" width="62.400000000000006" height="62.400000000000006" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
<mxfile host="Chrome" modified="2021-11-18T15:55:23.638Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36" etag="KKjYPjoS9srZirTPQqsC" version="15.5.6" type="device"><diagram id="Kjf8sS8rRDm6eebZkxfj" name="第 1 页">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</diagram></mxfile>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 KiB

After

Width:  |  Height:  |  Size: 393 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 102 KiB

+10 -30
View File
@@ -1,30 +1,10 @@
# 隧道配置
# 规则配置
所有隧道均为正向隧道,由连接发起方进行配置
所有链接均为正向配置,由连接发起方进行配置
## tcp隧道
## shell规则
tcp隧道用于反向代理远程的任意服务,如rdp、ssh、http等
- name: rdp # 链路名称
target: that # 目标客户端ID
type: tcp # 连接类型tcp或udp
local_addr: 0.0.0.0 # 本地监听地址
local_port: 3389 # 本地监听端口号
remote_addr: 127.0.0.1 # 目标客户端连接地址
remote_port: 3389 # 目标客户端连接端口号
1. `name`: 该隧道名称,必须全局唯一
2. `target`: 对端客户端ID
3. `type`: tcp
4. `local_addr`: 本地监听地址,如只允许局域网访问可绑定在局域网IP地址上
5. `local_port`: 本地监听端口号
6. `remote_addr`: 目标客户端连接地址,该地址为127.0.0.1时表示连接本机服务,也可连接局域网或广域网上的其他地址
7. `remote_port`: 目标客户端连接端口号
## shell隧道
shell隧道用于创建一个网页端的命令行操作页面
shell规则用于创建一个网页端的命令行操作页面
- name: shell # 链路名称
target: that # 目标客户端ID
@@ -37,7 +17,7 @@ shell隧道用于创建一个网页端的命令行操作页面
env: # 环境变量设置
- TERM=xterm
1. `name`: 该隧道名称,必须全局唯一
1. `name`: 该规则名称,必须全局唯一
2. `target`: 对端客户端ID
3. `type`: shell
4. `local_addr`: 本地监听地址,如只允许局域网访问可绑定在局域网IP地址上
@@ -48,11 +28,11 @@ shell隧道用于创建一个网页端的命令行操作页面
- windows系统:优先查找powershell命令,若没有则查找cmd命令,否则报错
7. `env`: 进程启动时的环境变量设置
连接成功后即可使用浏览器访问`local_port`所对应的端口来创建shell隧道,如http://127.0.0.1:8080
连接成功后即可使用浏览器访问`local_port`所对应的端口来创建shell,如http://127.0.0.1:8080
## vnc隧道
## vnc规则
vnc隧道用于创建一个网页端的远程桌面操作页面,目前仅支持*windows*操作系统
vnc规则用于创建一个网页端的远程桌面操作页面,目前仅支持*windows*操作系统
- name: vnc # 链路名称
target: that # 目标客户端ID
@@ -61,14 +41,14 @@ vnc隧道用于创建一个网页端的远程桌面操作页面,目前仅支
local_port: 5900 # 本地监听端口号
fps: 10 # 刷新频率
1. `name`: 该隧道名称,必须全局唯一
1. `name`: 该规则名称,必须全局唯一
2. `target`: 对端客户端ID
3. `type`: shell
4. `local_addr`: 本地监听地址,如只允许局域网访问可绑定在局域网IP地址上
5. `local_port`: 本地监听端口号
6. `fps`: 每秒钟截屏多少次,最高50
连接成功后即可使用浏览器访问`local_port`所对应的端口来创建vnc隧道,如http://127.0.0.1:5900
连接成功后即可使用浏览器访问`local_port`所对应的端口来创建vnc,如http://127.0.0.1:5900
注意:
+2 -2
View File
@@ -25,7 +25,7 @@
1. 在远端机器上[下载](https://github.com/lwch/natpass/releases)并解压到任意目录
2. 修改client.yaml配置文件,设置*id*为remote,设置*server*地址,并删除以下配置
#include tunnel.d/*.yaml
#include rule.d/*.yaml
3. 修改conf.d/conn.yaml配置文件,修改*secret*密钥,该密钥必须与服务器端保持一致
4. 使用以下命令将np-cli注册为系统服务,其中-conf参数后跟配置文件所在路径,-user参数后为程序启动身份(建议使用nobody身份启动)
@@ -40,7 +40,7 @@
1. 在本地机器上[下载](https://github.com/lwch/natpass/releases)并解压到任意目录
2. 修改client.yaml配置文件,设置*id*为local,设置*server*地址
3. 修改conf.d/conn.yaml配置文件,修改*secret*密钥,该密钥必须与服务器端保持一致
4. 修改tunnel.d目录下的tunnel配置文件,[tunnel配置方法](tunnel.md)
4. 修改rule.d目录下的规则配置文件,[rule配置方法](rules.md)
5. 使用以下命令将np-cli注册为系统服务,其中-conf参数后跟配置文件所在路径,-user参数后为程序启动身份(建议使用nobody身份启动)
sudo ./np-cli -conf server.yaml -action install -user nobody
+2 -2
View File
@@ -7,7 +7,7 @@ require (
github.com/dustin/go-humanize v1.0.0
github.com/go-bindata/go-bindata/v3 v3.1.3
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-vgo/robotgo v0.100.7
github.com/go-vgo/robotgo v0.100.8
github.com/gorilla/websocket v1.4.2
github.com/kardianos/service v1.2.0
github.com/kisielk/errcheck v1.6.0 // indirect
@@ -18,7 +18,7 @@ require (
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/mod v0.5.1 // indirect
golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1
golang.org/x/text v0.3.7
golang.org/x/tools v0.1.7 // indirect
google.golang.org/protobuf v1.27.1
+4 -4
View File
@@ -14,8 +14,8 @@ github.com/go-bindata/go-bindata/v3 v3.1.3/go.mod h1:1/zrpXsLD8YDIbhZRqXzm1Ghc7N
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-vgo/robotgo v0.100.7 h1:tkb/Sd2Drz3uyKGzlhjhNIbLEtV7nkzyGK0RxsuG418=
github.com/go-vgo/robotgo v0.100.7/go.mod h1:GCjwxRFoUkuekzm02WexYBV0paDLCbrrlKEfxt/CB10=
github.com/go-vgo/robotgo v0.100.8 h1:fBcmqPoQq/0vOr0C45T6SSScKwuYgAEJr9+T1PulHOA=
github.com/go-vgo/robotgo v0.100.8/go.mod h1:GCjwxRFoUkuekzm02WexYBV0paDLCbrrlKEfxt/CB10=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -100,8 +100,8 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02 h1:7NCfEGl0sfUojmX78nK9pBJuUlSZWEJA/TwASvfiPLo=
golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 h1:kwrAHlwJ0DUBZwQ238v+Uod/3eZ8B2K5rYsUHBQvzmI=
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
../bootstrap-5.1.3
../bootstrap-4.6.1
+22 -5
View File
@@ -3,6 +3,15 @@
<script src="/js/index.js"></script>
{{template "aside" .}}
<script>aside.active_dashboard();</script>
<style>
#label-rule-type {
margin-right: 5px;
}
#rule-type {
width: 200px;
display: inline-block;
}
</style>
<div class="content-wrapper">
<div class="content-header">
<div class="container-fluid">
@@ -23,10 +32,18 @@
<div class="row"><div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">隧道列表</h3>
<h3 class="card-title">终端列表</h3>
</div>
<div class="card-body p-0">
<table id="tunnels" class="table table-hover">
<div class="card-body">
<div class="form-group">
<label id="label-rule-type" for="rule-type">类型:</label>
<select id="rule-type" class="form-control">
<option value="all">全部</option>
<option value="shell">shell</option>
<option value="vnc">vnc</option>
</select>
</div>
<table id="rules" class="table table-hover">
<thead><tr>
<th>名称</th>
<th style="width:200px">对端</th>
@@ -34,7 +51,7 @@
<th style="width:120px">虚拟连接数</th>
<th style="width:200px">接收/发送(字节数)</th>
<th style="width:200px">接收/发送(数据包)</th>
<th style="width:200px">操作</th>
<th style="width:120px">操作</th>
</tr></thead>
<tbody></tbody>
</table>
@@ -43,7 +60,7 @@
</div>
<!-- card -->
</div></div>
<!-- 隧道列表 -->
<!-- 终端列表 -->
</div>
<!-- container -->
</section>
+21 -16
View File
@@ -1,58 +1,63 @@
var page = {
init: function() {
$('#rule-type').change(page.render);
page.render();
setInterval(page.render, 5000);
},
render: function() {
page.render_cards();
page.render_tunnels();
page.render_rules();
},
render_cards: function() {
$.get('/api/info', function(ret) {
$('#cards').empty();
page.add_card('隧道总数', ret.tunnels);
page.add_card('规则总数', ret.rules);
page.add_card('物理连接数', ret.physical_links);
page.add_card('虚拟连接数', ret.virtual_links);
page.add_card('终端会话', ret.sessions);
});
},
render_tunnels: function() {
$.get('/api/tunnels', function(ret) {
$('#tunnels tbody').empty();
$.each(ret, function(_, tunnel) {
render_rules: function() {
$.get('/api/rules', function(ret) {
$('#rules tbody').empty();
var type = $('#rule-type').val();
$.each(ret, function(_, rule) {
if (type != 'all' && rule.type != type) {
return;
}
var send_bytes = 0;
var send_packet = 0;
var recv_bytes = 0;
var recv_packet = 0;
$.each(tunnel.links, function(_, link) {
$.each(rule.links, function(_, link) {
send_bytes += link.send_bytes;
send_packet += link.send_packet;
recv_bytes += link.recv_bytes;
recv_packet += link.recv_packet;
});
var op = '';
switch (tunnel.type) {
switch (rule.type) {
case 'reverse':
op = `
<a href="http://${location.hostname}:${tunnel.port}" target="_blank">http</a>
<a href="https://${location.hostname}:${tunnel.port}" target="_blank">https</a>`;
<a href="http://${location.hostname}:${rule.port}" target="_blank">http</a>
<a href="https://${location.hostname}:${rule.port}" target="_blank">https</a>`;
break;
case 'shell':
case 'vnc':
op = `<a href="http://${location.host}/terminal.html?name=${tunnel.name}" target="_blank">连接</a>`;
op = `<a href="http://${location.host}/terminal.html?name=${rule.name}" target="_blank">连接</a>`;
break;
}
var str = `
<tr>
<td>${tunnel.name}</td>
<td>${tunnel.remote}</td>
<td>${tunnel.type}</td>
<td>${tunnel.links?tunnel.links.length:0}</td>
<td>${rule.name}</td>
<td>${rule.remote}</td>
<td>${rule.type}</td>
<td>${rule.links?rule.links.length:0}</td>
<td>${humanize.bytes(recv_bytes)}/${humanize.bytes(send_bytes)}</td>
<td>${recv_packet}/${send_packet}</td>
<td>${op}</td>
</tr>`;
$('#tunnels tbody').append($(str));
$('#rules tbody').append($(str));
});
});
},
+5 -5
View File
@@ -11,14 +11,14 @@ var page = {
},
load: function(cb) {
if (!cb) cb = function(){};
$.get('/api/tunnels', function(ret) {
$.get('/api/rules', function(ret) {
$('#terms').empty();
$.each(ret, function(_, tunnel) {
if (tunnel.type != 'shell' &&
tunnel.type != 'vnc') {
$.each(ret, function(_, rule) {
if (rule.type != 'shell' &&
rule.type != 'vnc') {
return;
}
$('#terms').append($(`<option value="${tunnel.port}">${tunnel.name}</option>`));
$('#terms').append($(`<option value="${rule.port}">${rule.name}</option>`));
});
cb();
});
+1 -1
View File
@@ -20,7 +20,7 @@
<div class="container-fluid">
<div class="row"><div class="col-12">
<div class="input-group mb-3">
<select id="terms" class="form-select"></select>
<select id="terms" class="form-control"></select>
<div class="input-group-append">
<button id="connect" class="btn btn-primary" type="button">连接</button>
</div>
+1 -1
View File
@@ -1 +1 @@
../bootstrap-5.1.3
../bootstrap-4.6.1
+1 -1
View File
@@ -20,7 +20,7 @@
<div class="container-fluid">
<div>
<label>图像质量:</label>
<select id="quality" class="form-select">
<select id="quality" class="form-control">
<option value="100">原始</option>
<option value="90" selected></option>
<option value="70"></option>