mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-08-12 16:09:20 +02:00
v2 init commit
This commit is contained in:
302
src/mod/sshprox/gotty/.gotty
Normal file
302
src/mod/sshprox/gotty/.gotty
Normal file
@@ -0,0 +1,302 @@
|
||||
// [string] Address to listen, all addresses will be used when empty
|
||||
// address = ""
|
||||
|
||||
// [string] Port to listen
|
||||
// port = "8080"
|
||||
|
||||
// [bool] Permit clients to write to the TTY
|
||||
// permit_write = false
|
||||
|
||||
// [bool] Enable basic authentication
|
||||
// enable_basic_auth = false
|
||||
|
||||
// [string] Default username and password of basic authentication (user:pass)
|
||||
// To enable basic authentication, set `true` to `enable_basic_auth`
|
||||
// credential = "user:pass"
|
||||
|
||||
// [bool] Enable random URL generation
|
||||
// enable_random_url = false
|
||||
|
||||
// [int] Default length of random strings appended to URL
|
||||
// To enable random URL generation, set `true` to `enable_random_url`
|
||||
// random_url_length = 8
|
||||
|
||||
// [bool] Enable TLS/SSL
|
||||
// enable_tls = false
|
||||
|
||||
// [string] Default TLS certificate file path
|
||||
// tls_crt_file = "~/.gotty.crt"
|
||||
|
||||
// [string] Default TLS key file path
|
||||
// tls_key_file = "~/.gotty.key"
|
||||
|
||||
// [bool] Enable client certificate authentication
|
||||
// enable_tls_client_auth = false
|
||||
|
||||
// [string] Certificate file of CA for client certificates
|
||||
// tls_ca_crt_file = "~/.gotty.ca.crt"
|
||||
|
||||
// [string] Custom index.html file
|
||||
// index_file = ""
|
||||
|
||||
// [string] Title format of browser window
|
||||
// Available variables are:
|
||||
// Command Command string
|
||||
// Pid PID of the process for the client
|
||||
// Hostname Server hostname
|
||||
// RemoteAddr Client IP address
|
||||
// title_format = "GoTTY - {{ .Command }} ({{ .Hostname }})"
|
||||
|
||||
// [bool] Enable client side reconnection when connection closed
|
||||
// enable_reconnect = false
|
||||
|
||||
// [int] Interval time to try reconnection (seconds)
|
||||
// To enable reconnection, set `true` to `enable_reconnect`
|
||||
// reconnect_time = 10
|
||||
|
||||
// [int] Timeout seconds for waiting a client (0 to disable)
|
||||
// timeout = 60
|
||||
|
||||
// [int] Maximum connection to gotty, 0(default) means no limit.
|
||||
// max_connection = 0
|
||||
|
||||
// [bool] Accept only one client and exit gotty once the client exits
|
||||
// once = false
|
||||
|
||||
// [bool] Permit clients to send command line arguments in URL (e.g. http://example.com:8080/?arg=AAA&arg=BBB)
|
||||
// permit_arguments = false
|
||||
|
||||
// [object] Client terminal (hterm) preferences
|
||||
// preferences {
|
||||
|
||||
// [enum(null, "none", "ctrl-alt", "left-alt", "right-alt")]
|
||||
// Select an AltGr detection hack^Wheuristic.
|
||||
// null: Autodetect based on navigator.language: "en-us" => "none", else => "right-alt"
|
||||
// "none": Disable any AltGr related munging.
|
||||
// "ctrl-alt": Assume Ctrl+Alt means AltGr.
|
||||
// "left-alt": Assume left Alt means AltGr.
|
||||
// "right-alt": Assume right Alt means AltGr.
|
||||
// alt_gr_mode = null
|
||||
|
||||
// [bool] If set, alt-backspace indeed is alt-backspace.
|
||||
// alt_backspace_is_meta_backspace = false
|
||||
|
||||
// [bool] Set whether the alt key acts as a meta key or as a distinct alt key.
|
||||
// alt_is_meta = false
|
||||
|
||||
// [enum("escape", "8-bit", "browser-key")]
|
||||
// Controls how the alt key is handled.
|
||||
// "escape"....... Send an ESC prefix.
|
||||
// "8-bit"........ Add 128 to the unshifted character as in xterm.
|
||||
// "browser-key".. Wait for the keypress event and see what the browser says.
|
||||
// (This won't work well on platforms where the browser performs a default action for some alt sequences.)
|
||||
// alt_sends_what = "escape"
|
||||
|
||||
// [string] URL of the terminal bell sound. Empty string for no audible bell.
|
||||
// audible_bell_sound = "lib-resource:hterm/audio/bell"
|
||||
|
||||
// [bool] If true, terminal bells in the background will create a Web Notification. http://www.w3.org/TR/notifications/
|
||||
// Displaying notifications requires permission from the user.
|
||||
// When this option is set to true, hterm will attempt to ask the user for permission if necessary.
|
||||
// Note browsers may not show this permission request
|
||||
// if it did not originate from a user action.
|
||||
// desktop_notification_bell = false
|
||||
|
||||
// [string] The background color for text with no other color attributes.
|
||||
// background_color = "rgb(16, 16, 16)"
|
||||
|
||||
// [string] CSS value of the background image. Empty string for no image.
|
||||
// For example:
|
||||
// "url(https://goo.gl/anedTK) linear-gradient(top bottom, blue, red)"
|
||||
// background_image = ""
|
||||
|
||||
// [string] CSS value of the background image size. Defaults to none.
|
||||
// background_size = ""
|
||||
|
||||
// [string] CSS value of the background image position.
|
||||
// For example:
|
||||
// "10% 10% center"
|
||||
// background_position = ""
|
||||
|
||||
// [bool] If true, the backspace should send BS ('\x08', aka ^H). Otherwise the backspace key should send '\x7f'.
|
||||
// backspace_sends_backspace = false
|
||||
|
||||
// [map[string]map[string]string]
|
||||
// A nested map where each property is the character set code and the value is a map that is a sparse array itself.
|
||||
// In that sparse array, each property is the received character and the value is the displayed character.
|
||||
// For example:
|
||||
// {"0" = {"+" = "\u2192"
|
||||
// "," = "\u2190"
|
||||
// "-" = "\u2191"
|
||||
// "." = "\u2193"
|
||||
// "0" = "\u2588"}}
|
||||
// character_map_overrides = null
|
||||
|
||||
// [bool] Whether or not to close the window when the command exits.
|
||||
// close_on_exit = true
|
||||
|
||||
// [bool] Whether or not to blink the cursor by default.
|
||||
// cursor_blink = false
|
||||
|
||||
// [2[int]] The cursor blink rate in milliseconds.
|
||||
// A two element array, the first of which is how long the cursor should be on, second is how long it should be off.
|
||||
// cursor_blink_cycle = [1000, 500]
|
||||
|
||||
// [string] The color of the visible cursor.
|
||||
// cursor_color = "rgba(255, 0, 0, 0.5)"
|
||||
|
||||
// [[]string]
|
||||
// Override colors in the default palette.
|
||||
// This can be specified as an array or an object.
|
||||
// Values can be specified as almost any css color value.
|
||||
// This includes #RGB, #RRGGBB, rgb(...), rgba(...), and any color names that are also part of the stock X11 rgb.txt file.
|
||||
// You can use 'null' to specify that the default value should be not be changed.
|
||||
// This is useful for skipping a small number of indicies when the value is specified as an array.
|
||||
// color_palette_overrides = null
|
||||
|
||||
// [bool] Automatically copy mouse selection to the clipboard.
|
||||
copy_on_select = true
|
||||
|
||||
// [bool] Whether to use the default window copy behaviour
|
||||
//use_default_window_copy = false
|
||||
|
||||
// [bool] Whether to clear the selection after copying.
|
||||
clear_selection_after_copy = false
|
||||
|
||||
// [bool] If true, Ctrl-Plus/Minus/Zero controls zoom.
|
||||
// If false, Ctrl-Shift-Plus/Minus/Zero controls zoom, Ctrl-Minus sends ^_, Ctrl-Plus/Zero do nothing.
|
||||
// ctrl_plus_minus_zero_zoom = true
|
||||
|
||||
// [bool] Ctrl+C copies if true, send ^C to host if false.
|
||||
// Ctrl+Shift+C sends ^C to host if true, copies if false.
|
||||
// ctrl_c_copy = true
|
||||
|
||||
// [bool] Ctrl+V pastes if true, send ^V to host if false.
|
||||
// Ctrl+Shift+V sends ^V to host if true, pastes if false.
|
||||
// ctrl_v_paste = true
|
||||
|
||||
// [bool] Set whether East Asian Ambiguous characters have two column width.
|
||||
// east_asian_ambiguous_as_two_column = false
|
||||
|
||||
// [bool] True to enable 8-bit control characters, false to ignore them.
|
||||
// We'll respect the two-byte versions of these control characters regardless of this setting.
|
||||
// enable_8_bit_control = false
|
||||
|
||||
// [enum(null, true, false)]
|
||||
// True if we should use bold weight font for text with the bold/bright attribute.
|
||||
// False to use the normal weight font.
|
||||
// Null to autodetect.
|
||||
// enable_bold = null
|
||||
|
||||
// [bool] True if we should use bright colors (8-15 on a 16 color palette) for any text with the bold attribute.
|
||||
// False otherwise.
|
||||
// enable_bold_as_bright = true
|
||||
|
||||
// [bool] Show a message in the terminal when the host writes to the clipboard.
|
||||
// enable_clipboard_notice = true
|
||||
|
||||
// [bool] Allow the host to write directly to the system clipboard.
|
||||
// enable_clipboard_write = true
|
||||
|
||||
// [bool] Respect the host's attempt to change the cursor blink status using DEC Private Mode 12.
|
||||
// enable_dec12 = false
|
||||
|
||||
// [map[string]string] The default environment variables, as an object.
|
||||
// environment = {"TERM" = "xterm-256color"}
|
||||
|
||||
// [string] Default font family for the terminal text.
|
||||
// font_family = "'DejaVu Sans Mono', 'Everson Mono', FreeMono, 'Menlo', 'Terminal', monospace"
|
||||
|
||||
// [int] The default font size in pixels.
|
||||
// font_size = 15
|
||||
|
||||
// [string] CSS font-smoothing property.
|
||||
// font_smoothing = "antialiased"
|
||||
|
||||
// [string] The foreground color for text with no other color attributes.
|
||||
// foreground_color = "rgb(240, 240, 240)"
|
||||
|
||||
// [bool] If true, home/end will control the terminal scrollbar and shift home/end will send the VT keycodes.
|
||||
// If false then home/end sends VT codes and shift home/end scrolls.
|
||||
// home_keys_scroll = false
|
||||
|
||||
// [map[string]string]
|
||||
// A map of key sequence to key actions.
|
||||
// Key sequences include zero or more modifier keys followed by a key code.
|
||||
// Key codes can be decimal or hexadecimal numbers, or a key identifier.
|
||||
// Key actions can be specified a string to send to the host, or an action identifier.
|
||||
// For a full list of key code and action identifiers, see https://goo.gl/8AoD09.
|
||||
// Sample keybindings:
|
||||
// {"Ctrl-Alt-K" = "clearScrollback"
|
||||
// "Ctrl-Shift-L"= "PASS"
|
||||
// "Ctrl-H" = "'HELLO\n'"}
|
||||
// keybindings = null
|
||||
|
||||
// [int] Max length of a DCS, OSC, PM, or APS sequence before we give up and ignore the code.
|
||||
// max_string_sequence = 100000
|
||||
|
||||
// [bool] If true, convert media keys to their Fkey equivalent.
|
||||
// If false, let the browser handle the keys.
|
||||
// media_keys_are_fkeys = false
|
||||
|
||||
// [bool] Set whether the meta key sends a leading escape or not.
|
||||
// meta_sends_escape = true
|
||||
|
||||
// [enum(null, 0, 1, 2, 3, 4, 5, 6]
|
||||
// Mouse paste button, or null to autodetect.
|
||||
// For autodetect, we'll try to enable middle button paste for non-X11 platforms.
|
||||
// On X11 we move it to button 3.
|
||||
// mouse_paste_button = null
|
||||
|
||||
// [bool] If true, page up/down will control the terminal scrollbar and shift page up/down will send the VT keycodes.
|
||||
// If false then page up/down sends VT codes and shift page up/down scrolls.
|
||||
// page_keys_scroll = false
|
||||
|
||||
// [enum(null, true, false)]
|
||||
// Set whether we should pass Alt-1..9 to the browser.
|
||||
// This is handy when running hterm in a browser tab, so that you don't lose Chrome's "switch to tab" keyboard accelerators.
|
||||
// When not running in a tab it's better to send these keys to the host so they can be used in vim or emacs.
|
||||
// If true, Alt-1..9 will be handled by the browser.
|
||||
// If false, Alt-1..9 will be sent to the host.
|
||||
// If null, autodetect based on browser platform and window type.
|
||||
// pass_alt_number = null
|
||||
|
||||
// [enum(null, true, false)]
|
||||
// Set whether we should pass Ctrl-1..9 to the browser.
|
||||
// This is handy when running hterm in a browser tab, so that you don't lose Chrome's "switch to tab" keyboard accelerators.
|
||||
// When not running in a tab it's better to send these keys to the host so they can be used in vim or emacs.
|
||||
// If true, Ctrl-1..9 will be handled by the browser.
|
||||
// If false, Ctrl-1..9 will be sent to the host.
|
||||
// If null, autodetect based on browser platform and window type.
|
||||
// pass_ctrl_number = null
|
||||
|
||||
// [enum(null, true, false)]
|
||||
// Set whether we should pass Meta-1..9 to the browser.
|
||||
// This is handy when running hterm in a browser tab, so that you don't lose Chrome's "switch to tab" keyboard accelerators.
|
||||
// When not running in a tab it's better to send these keys to the host so they can be used in vim or emacs.
|
||||
// If true, Meta-1..9 will be handled by the browser.
|
||||
// If false, Meta-1..9 will be sent to the host. If null, autodetect based on browser platform and window type.
|
||||
// pass_meta_number = null
|
||||
|
||||
// [bool] Set whether meta-V gets passed to host.
|
||||
// pass_meta_v = true
|
||||
|
||||
// [bool] If true, scroll to the bottom on any keystroke.
|
||||
// scroll_on_keystroke = true
|
||||
|
||||
// [bool] If true, scroll to the bottom on terminal output.
|
||||
// scroll_on_output = false
|
||||
|
||||
// [bool] The vertical scrollbar mode.
|
||||
// scrollbar_visible = true
|
||||
|
||||
// [int] The multiplier for the pixel delta in mousewheel event caused by the scroll wheel. Alters how fast the page scrolls.
|
||||
// scroll_wheel_move_multiplier = 1
|
||||
|
||||
// [bool] Shift + Insert pastes if true, sent to host if false.
|
||||
// shift_insert_paste = true
|
||||
|
||||
// [string] URL of user stylesheet to include in the terminal document.
|
||||
// user_css = ""
|
||||
|
||||
// }
|
21
src/mod/sshprox/gotty/LICENSE
Normal file
21
src/mod/sshprox/gotty/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2017 Iwasaki Yudai
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
BIN
src/mod/sshprox/gotty/gotty_linux_386
Normal file
BIN
src/mod/sshprox/gotty/gotty_linux_386
Normal file
Binary file not shown.
BIN
src/mod/sshprox/gotty/gotty_linux_amd64
Normal file
BIN
src/mod/sshprox/gotty/gotty_linux_amd64
Normal file
Binary file not shown.
BIN
src/mod/sshprox/gotty/gotty_linux_arm
Normal file
BIN
src/mod/sshprox/gotty/gotty_linux_arm
Normal file
Binary file not shown.
BIN
src/mod/sshprox/gotty/gotty_linux_arm64
Normal file
BIN
src/mod/sshprox/gotty/gotty_linux_arm64
Normal file
Binary file not shown.
222
src/mod/sshprox/sshprox.go
Normal file
222
src/mod/sshprox/sshprox.go
Normal file
@@ -0,0 +1,222 @@
|
||||
package sshprox
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"imuslab.com/zoraxy/mod/reverseproxy"
|
||||
"imuslab.com/zoraxy/mod/utils"
|
||||
"imuslab.com/zoraxy/mod/websocketproxy"
|
||||
)
|
||||
|
||||
/*
|
||||
SSH Proxy
|
||||
|
||||
This is a tool to bind gotty into Zoraxy
|
||||
so that you can do something similar to
|
||||
online ssh terminal
|
||||
*/
|
||||
|
||||
/*
|
||||
Bianry embedding
|
||||
|
||||
Make sure when compile, gotty binary exists in static.gotty
|
||||
*/
|
||||
var (
|
||||
//go:embed gotty/*
|
||||
gotty embed.FS
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
StartingPort int
|
||||
Instances []*Instance
|
||||
}
|
||||
|
||||
type Instance struct {
|
||||
UUID string
|
||||
ExecPath string
|
||||
RemoteAddr string
|
||||
RemotePort int
|
||||
AssignedPort int
|
||||
conn *reverseproxy.ReverseProxy //HTTP proxy
|
||||
tty *exec.Cmd //SSH connection ported to web interface
|
||||
Parent *Manager
|
||||
}
|
||||
|
||||
func NewSSHProxyManager() *Manager {
|
||||
return &Manager{
|
||||
StartingPort: 14810,
|
||||
Instances: []*Instance{},
|
||||
}
|
||||
}
|
||||
|
||||
//Get the next free port in the list
|
||||
func (m *Manager) GetNextPort() int {
|
||||
nextPort := m.StartingPort
|
||||
occupiedPort := make(map[int]bool)
|
||||
for _, instance := range m.Instances {
|
||||
occupiedPort[instance.AssignedPort] = true
|
||||
}
|
||||
for {
|
||||
if !occupiedPort[nextPort] {
|
||||
return nextPort
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
if targetInstance.tty == nil {
|
||||
//Server side already closed
|
||||
http.Error(w, "Connection already closed", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
r.Header.Set("X-Forwarded-Host", r.Host)
|
||||
requestURL := r.URL.String()
|
||||
if r.Header["Upgrade"] != nil && strings.ToLower(r.Header["Upgrade"][0]) == "websocket" {
|
||||
//Handle WebSocket request. Forward the custom Upgrade header and rewrite origin
|
||||
r.Header.Set("A-Upgrade", "websocket")
|
||||
requestURL = strings.TrimPrefix(requestURL, "/")
|
||||
u, _ := url.Parse("ws://127.0.0.1:" + strconv.Itoa(targetInstance.AssignedPort) + "/" + requestURL)
|
||||
wspHandler := websocketproxy.NewProxy(u)
|
||||
wspHandler.ServeHTTP(w, r)
|
||||
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"
|
||||
}
|
||||
|
||||
//Extract it from embedfs if not exists locally
|
||||
execPath := filepath.Join(binaryRoot, binary)
|
||||
|
||||
//Create the storage folder structure
|
||||
os.MkdirAll(filepath.Dir(execPath), 0775)
|
||||
|
||||
//Create config file if not exists
|
||||
if !utils.FileExists(filepath.Join(filepath.Dir(execPath), ".gotty")) {
|
||||
configFile, _ := gotty.ReadFile("gotty/.gotty")
|
||||
os.WriteFile(filepath.Join(filepath.Dir(execPath), ".gotty"), configFile, 0775)
|
||||
}
|
||||
|
||||
//Create web.ssh binary if not exists
|
||||
if !utils.FileExists(execPath) {
|
||||
//Try to extract it from embedded fs
|
||||
executable, err := gotty.ReadFile("gotty/" + binary)
|
||||
if err != nil {
|
||||
//Binary not found in embedded
|
||||
return nil, errors.New("platform not supported")
|
||||
}
|
||||
|
||||
//Extract to target location
|
||||
err = os.WriteFile(execPath, executable, 0777)
|
||||
if err != nil {
|
||||
//Binary not found in embedded
|
||||
log.Println("Extract web.ssh failed: " + err.Error())
|
||||
return nil, errors.New("web.ssh sub-program extract failed")
|
||||
}
|
||||
}
|
||||
|
||||
//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,
|
||||
AssignedPort: -1,
|
||||
Parent: m,
|
||||
}
|
||||
|
||||
m.Instances = append(m.Instances, &thisInstance)
|
||||
|
||||
return &thisInstance, nil
|
||||
}
|
||||
|
||||
//Create a new Connection to target address
|
||||
func (i *Instance) CreateNewConnection(listenPort int, username string, remoteIpAddr string, remotePort int) error {
|
||||
//Create a gotty instance
|
||||
connAddr := remoteIpAddr
|
||||
if username != "" {
|
||||
connAddr = username + "@" + remoteIpAddr
|
||||
}
|
||||
configPath := filepath.Join(filepath.Dir(i.ExecPath), ".gotty")
|
||||
title := username + "@" + remoteIpAddr
|
||||
if remotePort != 22 {
|
||||
title = title + ":" + strconv.Itoa(remotePort)
|
||||
}
|
||||
|
||||
sshCommand := []string{"ssh", "-t", connAddr, "-p", strconv.Itoa(remotePort)}
|
||||
cmd := exec.Command(i.ExecPath, "-w", "-p", strconv.Itoa(listenPort), "--once", "--config", configPath, "--title-format", title, "bash", "-c", strings.Join(sshCommand, " "))
|
||||
cmd.Dir = filepath.Dir(i.ExecPath)
|
||||
cmd.Env = append(os.Environ(), "TERM=xterm")
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
go func() {
|
||||
cmd.Run()
|
||||
i.Destroy()
|
||||
}()
|
||||
i.tty = cmd
|
||||
i.AssignedPort = listenPort
|
||||
i.RemoteAddr = remoteIpAddr
|
||||
i.RemotePort = remotePort
|
||||
|
||||
//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 new proxy objects to the proxy
|
||||
proxy := reverseproxy.NewReverseProxy(path)
|
||||
|
||||
i.conn = proxy
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Instance) Destroy() {
|
||||
// Remove the instance from the Manager's Instances list
|
||||
for idx, inst := range i.Parent.Instances {
|
||||
if inst == i {
|
||||
// Remove the instance from the slice by swapping it with the last instance and slicing the slice
|
||||
i.Parent.Instances[len(i.Parent.Instances)-1], i.Parent.Instances[idx] = i.Parent.Instances[idx], i.Parent.Instances[len(i.Parent.Instances)-1]
|
||||
i.Parent.Instances = i.Parent.Instances[:len(i.Parent.Instances)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
72
src/mod/sshprox/utils.go
Normal file
72
src/mod/sshprox/utils.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package sshprox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
//Rewrite url based on proxy root
|
||||
func RewriteURL(rooturl string, requestURL string) (*url.URL, error) {
|
||||
rewrittenURL := strings.TrimPrefix(requestURL, rooturl)
|
||||
return url.Parse(rewrittenURL)
|
||||
}
|
||||
|
||||
//Check if the current platform support web.ssh function
|
||||
func IsWebSSHSupported() bool {
|
||||
//Check if the binary exists in system/gotty/
|
||||
binary := "gotty_" + runtime.GOOS + "_" + runtime.GOARCH
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
binary = binary + ".exe"
|
||||
}
|
||||
|
||||
//Check if the target gotty terminal exists
|
||||
f, err := gotty.Open("gotty/" + binary)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
f.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
//Check if a given domain and port is a valid ssh server
|
||||
func IsSSHConnectable(ipOrDomain string, port int) bool {
|
||||
timeout := time.Second * 3
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ipOrDomain, port), timeout)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Send an SSH version identification string to the server to check if it's SSH
|
||||
_, err = conn.Write([]byte("SSH-2.0-Go\r\n"))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Wait for a response from the server
|
||||
buf := make([]byte, 1024)
|
||||
_, err = conn.Read(buf)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the response starts with "SSH-2.0"
|
||||
return string(buf[:7]) == "SSH-2.0"
|
||||
}
|
||||
|
||||
//Check if the port is used by other process or application
|
||||
func isPortInUse(port int) bool {
|
||||
address := fmt.Sprintf(":%d", port)
|
||||
listener, err := net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
listener.Close()
|
||||
return false
|
||||
}
|
Reference in New Issue
Block a user