1
0

sshprox.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. package sshprox
  2. import (
  3. "errors"
  4. "fmt"
  5. "net/http"
  6. "net/url"
  7. "os"
  8. "os/exec"
  9. "path/filepath"
  10. "runtime"
  11. "strconv"
  12. "github.com/google/uuid"
  13. "imuslab.com/zoraxy/mod/reverseproxy"
  14. "imuslab.com/zoraxy/mod/utils"
  15. )
  16. /*
  17. SSH Proxy
  18. This is a tool to bind gotty into Zoraxy
  19. so that you can do something similar to
  20. online ssh terminal
  21. */
  22. type Manager struct {
  23. StartingPort int
  24. ReservedPorts map[string]int
  25. Instances []*Instance
  26. }
  27. type Instance struct {
  28. UUID string
  29. ExecPath string
  30. conn *reverseproxy.ReverseProxy //HTTP proxy
  31. tty *exec.Cmd //SSH connection ported to web interface
  32. }
  33. func NewSSHProxyManager() *Manager {
  34. return &Manager{
  35. StartingPort: 14810,
  36. ReservedPorts: map[string]int{},
  37. Instances: []*Instance{},
  38. }
  39. }
  40. //Get the next free port in the list
  41. func (m *Manager) GetNextPort() int {
  42. nextPort := m.StartingPort
  43. for {
  44. if _, exists := m.ReservedPorts[strconv.Itoa(nextPort)]; !exists {
  45. if !isPortInUse(nextPort) {
  46. return nextPort
  47. }
  48. }
  49. if nextPort == 65534 {
  50. return -1
  51. }
  52. nextPort++
  53. }
  54. }
  55. func (m *Manager) HandleHttpByInstanceId(instanceId string, w http.ResponseWriter, r *http.Request) {
  56. targetInstance, err := m.GetInstanceById(instanceId)
  57. if err != nil {
  58. http.Error(w, err.Error(), http.StatusNotFound)
  59. return
  60. }
  61. targetInstance.conn.ProxyHTTP(w, r)
  62. }
  63. func (m *Manager) GetInstanceById(instanceId string) (*Instance, error) {
  64. for _, instance := range m.Instances {
  65. if instance.UUID == instanceId {
  66. return instance, nil
  67. }
  68. }
  69. return nil, fmt.Errorf("instance not found: %s", instanceId)
  70. }
  71. func (m *Manager) NewSSHProxy(binaryRoot string) (*Instance, error) {
  72. //Check if the binary exists in system/gotty/
  73. binary := "gotty_" + runtime.GOOS + "_" + runtime.GOARCH
  74. if runtime.GOOS == "windows" {
  75. binary = binary + ".exe"
  76. }
  77. execPath := filepath.Join(binaryRoot, binary)
  78. if !utils.FileExists(execPath) {
  79. //Binary not found
  80. return nil, errors.New("binary not found at " + execPath)
  81. }
  82. //Convert the binary path to realpath
  83. realpath, err := filepath.Abs(execPath)
  84. if err != nil {
  85. return nil, err
  86. }
  87. thisInstance := Instance{
  88. UUID: uuid.New().String(),
  89. ExecPath: realpath,
  90. }
  91. m.Instances = append(m.Instances, &thisInstance)
  92. return &thisInstance, nil
  93. }
  94. //Create a new Connection to target address
  95. func (i *Instance) CreateNewConnection(listenPort int, remoteIpAddr string, remotePort int) error {
  96. //Create a gotty instance
  97. cmd := exec.Command(i.ExecPath, "-w", "-p", strconv.Itoa(listenPort), "--once", "ssh", remoteIpAddr, "-p", strconv.Itoa(remotePort))
  98. cmd.Stdout = os.Stdout
  99. cmd.Stderr = os.Stderr
  100. go func() {
  101. cmd.Run()
  102. }()
  103. i.tty = cmd
  104. //Create a new proxy agent for this root
  105. path, err := url.Parse("http://127.0.0.1:" + strconv.Itoa(listenPort))
  106. if err != nil {
  107. return err
  108. }
  109. //Create a new proxy object to the proxy
  110. proxy := reverseproxy.NewReverseProxy(path)
  111. i.conn = proxy
  112. return nil
  113. }