Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,13 @@ gotun --listen :8888 user@example.com
# 自动设置系统代理(默认开启)
# 若你希望启动时不修改系统代理,请显式关闭:
gotun --sys-proxy=false user@example.com

# 开启 SOCKS5 代理 (默认 :1080)
gotun --socks5 :1080 user@example.com
```

> **注意**: 使用 SOCKS5 代理配合自定义路由规则时,建议在客户端(如浏览器或代理插件)中开启 "Proxy DNS when using SOCKS5" (远程DNS解析) 选项。否则客户端可能会在本地解析域名为 IP,导致基于域名的路由规则失效。

### 在浏览器中使用

启动代理后,在浏览器中配置HTTP代理:
Expand All @@ -158,6 +163,7 @@ gotun --sys-proxy=false user@example.com
| `--identity_file` | `-i` | 用于认证的私钥文件路径 | |
| `--jump` | `-J` | 跳板机列表,用逗号分隔 (格式: user@host:port) | |
| `--target` | | 可选的目标网络覆盖 | |
| `--socks5` | | SOCKS5 代理监听地址 | `:1080` |
| `--timeout` | | 连接超时时间 | `10s` |
| `--verbose` | `-v` | 启用详细日志 | `false` |
| `--log` | | 日志文件路径 | 输出到标准输出 |
Expand Down Expand Up @@ -350,10 +356,10 @@ sudo gotun user@example.com
- [x] **跳板机 (Jump Host)**: 支持单级和多级SSH跳板机
- [x] **自定义路由规则**: 支持自定义的规则文件进行流量分流
- [x] **命令行自动补全**: 基于 Cobra 的智能提示
- [x] **SOCKS5 代理支持**: 更广泛的协议支持
- [ ] **RDP网关**:支持RDP远程桌面网关
- [ ] **托盘 GUI 界面**: 图形化用户界面
- [ ] **配置文件导出/导入**: 配置管理功能
- [ ] **SOCKS5 代理支持**: 更广泛的协议支持
- [ ] **连接池优化**: 提升性能和稳定性
- [ ] **统计和监控**: 流量统计和连接监控

Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Your machine SSH (tcp/22) Bastion I
- Rule-based traffic splitting via configuration file
- Shell completion support for Bash, Zsh, Fish, PowerShell
- Structured logging and verbose mode for debugging
- SOCKS5 proxy support

---

Expand Down Expand Up @@ -141,8 +142,13 @@ gotun --listen :8888 user@example.com

# Disable automatic system proxy configuration
gotun --sys-proxy=false user@example.com

# Enable SOCKS5 proxy (default listen on :1080)
gotun --socks5 :1080 user@example.com
```

> **Note**: When using SOCKS5 with custom routing rules, it is recommended to enable "Proxy DNS when using SOCKS5" (Remote DNS) in your client. Otherwise, the client might resolve domains to IPs locally, causing domain-based routing rules to fail.

### Browser configuration

By default, gotun listens on `127.0.0.1:8080` (unless changed by `--listen`).
Expand All @@ -166,6 +172,7 @@ If system proxy support is enabled, some platforms can be configured automatical
| `--identity_file` | `-i` | Private key file path | |
| `--jump` | `-J` | Comma-separated jump hosts (`user@host:port`) | |
| `--target` | | Optional target network scope/coverage | |
| `--socks5` | | SOCKS5 proxy bind address | `:1080` |
| `--timeout` | | SSH connection timeout | `10s` |
| `--verbose` | `-v` | Enable verbose logging | `false` |
| `--log` | | Log file path | stdout |
Expand Down Expand Up @@ -400,13 +407,13 @@ Implemented:
- [x] Single and multi-hop jump host support
- [x] Rule-based routing
- [x] Shell completion for common shells
- [x] SOCKS5 proxy support

Planned:

- [ ] RDP gateway support
- [ ] Tray/GUI frontend
- [ ] Export/import of configuration profiles
- [ ] SOCKS5 proxy support
- [ ] Connection pooling and performance tuning
- [ ] Traffic statistics and basic monitoring

Expand Down
64 changes: 58 additions & 6 deletions cmd/gotun/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/Sesame2/gotun/internal/config"
"github.com/Sesame2/gotun/internal/logger"
"github.com/Sesame2/gotun/internal/proxy"
"github.com/Sesame2/gotun/internal/router"
"github.com/Sesame2/gotun/internal/sysproxy"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -57,14 +58,43 @@ var rootCmd = &cobra.Command{
}
log.Infof("GoTun %s 启动中...", Version)

httpProxy, err := proxy.NewHTTPOverSSH(cfg, log)
// 1. 初始化 Router
var r *router.Router
if cfg.RuleFile != "" {
var err error
r, err = router.NewRouter(cfg.RuleFile)
if err != nil {
log.Warnf("加载规则文件失败: %v。将以全局代理模式运行。", err)
} else {
log.Infof("已加载规则文件: %s", cfg.RuleFile)
}
}

// 2. 初始化 SSHClient
sshClient, err := proxy.NewSSHClient(cfg, log)
if err != nil {
return fmt.Errorf("代理初始化失败: %w", err)
return fmt.Errorf("SSH连接失败: %w", err)
}
defer sshClient.Close()

// 3. 初始化 HTTP 代理
httpProxy, err := proxy.NewHTTPOverSSH(cfg, log, sshClient, r)
if err != nil {
return fmt.Errorf("HTTP代理初始化失败: %w", err)
}

// 4. 初始化 SOCKS5 代理
var socksProxy *proxy.SOCKS5OverSSH
if cfg.SocksAddr != "" {
socksProxy, err = proxy.NewSOCKS5OverSSH(cfg, log, sshClient, r)
if err != nil {
return fmt.Errorf("SOCKS5代理初始化失败: %w", err)
}
}

var proxyMgr *sysproxy.Manager
if cfg.SystemProxy {
proxyMgr = sysproxy.NewManager(log, cfg.ListenAddr)
proxyMgr = sysproxy.NewManager(log, cfg.ListenAddr, cfg.SocksAddr)
}

sigChan := make(chan os.Signal, 1)
Expand All @@ -77,12 +107,26 @@ var rootCmd = &cobra.Command{
}
}
if err := httpProxy.Start(); err != nil {
log.Errorf("代理服务启动失败: %v", err)
log.Errorf("HTTP代理服务启动失败: %v", err)
sigChan <- syscall.SIGTERM
}
}()

fmt.Println("\n代理服务已启动:", "http://"+cfg.ListenAddr)
if socksProxy != nil {
go func() {
if err := socksProxy.Start(); err != nil {
log.Errorf("SOCKS5代理服务启动失败: %v", err)
sigChan <- syscall.SIGTERM
}
}()
}

fmt.Println("\n代理服务已启动:")
fmt.Println("HTTP Proxy:", "http://"+cfg.ListenAddr)
if cfg.SocksAddr != "" {
fmt.Println("SOCKS5 Proxy:", "socks5://"+cfg.SocksAddr)
}

if len(cfg.JumpHosts) > 0 {
fmt.Printf("跳板机链: %s -> %s\n", fmt.Sprintf("%v", cfg.JumpHosts), cfg.SSHServer)
} else {
Expand All @@ -106,7 +150,14 @@ var rootCmd = &cobra.Command{
}

if err := httpProxy.Close(); err != nil {
log.Errorf("关闭代理服务失败: %v", err)
log.Errorf("关闭HTTP代理服务失败: %v", err)
}

// 新增关闭逻辑
if socksProxy != nil {
if err := socksProxy.Close(); err != nil {
log.Errorf("关闭SOCKS5代理服务失败: %v", err)
}
}

return nil
Expand All @@ -127,6 +178,7 @@ func init() {
rootCmd.PersistentFlags().StringVar(&cfg.LogFile, "log", "", "日志文件路径")
rootCmd.PersistentFlags().BoolVar(&cfg.SystemProxy, "sys-proxy", true, "自动设置/恢复系统代理")
rootCmd.PersistentFlags().StringVar(&cfg.RuleFile, "rules", "", "代理规则配置文件路径")
rootCmd.PersistentFlags().StringVar(&cfg.SocksAddr, "socks5", ":1080", "SOCKS5 代理监听地址")
}

func Execute(version string) {
Expand Down
2 changes: 2 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Config struct {
SSHKeyFile string
SSHTargetDial string
SSHPort string // 添加SSH端口配置
SocksAddr string // SOCKS5 监听地址
JumpHosts []string // 跳板机列表
Timeout time.Duration
Verbose bool
Expand All @@ -37,6 +38,7 @@ func NewConfig() *Config {
InteractiveAuth: true,
SystemProxy: true,
RuleFile: "",
SocksAddr: "",
}
}

Expand Down
23 changes: 1 addition & 22 deletions internal/proxy/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,9 @@ type HTTPOverSSH struct {
}

// NewHTTPOverSSH 创建HTTP代理
func NewHTTPOverSSH(cfg *config.Config, log *logger.Logger) (*HTTPOverSSH, error) {
func NewHTTPOverSSH(cfg *config.Config, log *logger.Logger, sshClient *SSHClient, r *router.Router) (*HTTPOverSSH, error) {
log.Info("初始化HTTP-over-SSH代理")

sshClient, err := NewSSHClient(cfg, log)
if err != nil {
return nil, err
}

var r *router.Router
if cfg.RuleFile != "" {
r, err = router.NewRouter(cfg.RuleFile)
if err != nil {
log.Warnf("加载规则文件失败: %v。将以全局代理模式运行。", err)
} else {
log.Infof("已加载规则文件: %s", cfg.RuleFile)
}
}

proxy := &HTTPOverSSH{
cfg: cfg,
ssh: sshClient,
Expand Down Expand Up @@ -220,12 +205,6 @@ func (p *HTTPOverSSH) Close() error {
defer cancel()
err = p.server.Shutdown(ctx)
}
if p.ssh != nil {
sshErr := p.ssh.Close()
if sshErr != nil && err == nil {
err = sshErr
}
}
return err
}

Expand Down
Loading
Loading