package sshprox import ( "errors" "fmt" "net/http" "net/url" "os" "os/exec" "path/filepath" "runtime" "strconv" "github.com/google/uuid" "imuslab.com/zoraxy/mod/reverseproxy" "imuslab.com/zoraxy/mod/utils" ) /* SSH Proxy This is a tool to bind gotty into Zoraxy so that you can do something similar to online ssh terminal */ type Manager struct { StartingPort int ReservedPorts map[string]int Instances []*Instance } type Instance struct { UUID string ExecPath string conn *reverseproxy.ReverseProxy //HTTP proxy tty *exec.Cmd //SSH connection ported to web interface } func NewSSHProxyManager() *Manager { return &Manager{ StartingPort: 14810, ReservedPorts: map[string]int{}, Instances: []*Instance{}, } } //Get the next free port in the list func (m *Manager) GetNextPort() int { nextPort := m.StartingPort for { if _, exists := m.ReservedPorts[strconv.Itoa(nextPort)]; !exists { if !isPortInUse(nextPort) { return nextPort } } if nextPort == 65534 { return -1 } nextPort++ } } func (m *Manager) HandleHttpByInstanceId(instanceId string, w http.ResponseWriter, r *http.Request) { targetInstance, err := m.GetInstanceById(instanceId) if err != nil { http.Error(w, err.Error(), http.StatusNotFound) return } targetInstance.conn.ProxyHTTP(w, r) } func (m *Manager) GetInstanceById(instanceId string) (*Instance, error) { for _, instance := range m.Instances { if instance.UUID == instanceId { return instance, nil } } return nil, fmt.Errorf("instance not found: %s", instanceId) } func (m *Manager) NewSSHProxy(binaryRoot string) (*Instance, error) { //Check if the binary exists in system/gotty/ binary := "gotty_" + runtime.GOOS + "_" + runtime.GOARCH if runtime.GOOS == "windows" { binary = binary + ".exe" } execPath := filepath.Join(binaryRoot, binary) if !utils.FileExists(execPath) { //Binary not found return nil, errors.New("binary not found at " + execPath) } //Convert the binary path to realpath realpath, err := filepath.Abs(execPath) if err != nil { return nil, err } thisInstance := Instance{ UUID: uuid.New().String(), ExecPath: realpath, } m.Instances = append(m.Instances, &thisInstance) return &thisInstance, nil } //Create a new Connection to target address func (i *Instance) CreateNewConnection(listenPort int, remoteIpAddr string, remotePort int) error { //Create a gotty instance cmd := exec.Command(i.ExecPath, "-w", "-p", strconv.Itoa(listenPort), "--once", "ssh", remoteIpAddr, "-p", strconv.Itoa(remotePort)) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr go func() { cmd.Run() }() i.tty = cmd //Create a new proxy agent for this root path, err := url.Parse("http://127.0.0.1:" + strconv.Itoa(listenPort)) if err != nil { return err } //Create a new proxy object to the proxy proxy := reverseproxy.NewReverseProxy(path) i.conn = proxy return nil }