ssh backend (mostly)
This commit is contained in:
parent
aa16549667
commit
79389d37b8
3 changed files with 219 additions and 1 deletions
|
@ -13,6 +13,7 @@ require (
|
|||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.15.2 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
golang.org/x/crypto v0.29.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
)
|
||||
|
|
|
@ -21,8 +21,12 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
|
|||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
|
|
213
api/gosrc/sshbackend/main.go
Normal file
213
api/gosrc/sshbackend/main.go
Normal file
|
@ -0,0 +1,213 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.greysoh.dev/imterah/nextnet/backendutil"
|
||||
"git.greysoh.dev/imterah/nextnet/commonbackend"
|
||||
"github.com/charmbracelet/log"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type SSHBackend struct {
|
||||
data SSHBackendData
|
||||
conn ssh.Client
|
||||
clients []*commonbackend.ClientConnection
|
||||
}
|
||||
|
||||
type SSHBackendData struct {
|
||||
Ip string `json:"ip"`
|
||||
Port uint16 `json:"port"`
|
||||
Username string `json:"username"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
ListenOnIPs []string `json:"listenOnIPs"`
|
||||
}
|
||||
|
||||
func (backend *SSHBackend) StartBackend(bytes []byte) (bool, error) {
|
||||
var backendData SSHBackendData
|
||||
|
||||
err := json.Unmarshal(bytes, &backendData) // ?????
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
backend.data = backendData
|
||||
|
||||
if len(backend.data.ListenOnIPs) == 0 {
|
||||
backend.data.ListenOnIPs = []string{"0.0.0.0"}
|
||||
}
|
||||
|
||||
// create signer for privateKey
|
||||
signer, err := ssh.ParsePrivateKey([]byte(backendData.PrivateKey))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
auth := ssh.PublicKeys(signer)
|
||||
|
||||
config := &ssh.ClientConfig{
|
||||
User: backendData.Username,
|
||||
Auth: []ssh.AuthMethod{
|
||||
auth,
|
||||
},
|
||||
}
|
||||
|
||||
conn, err := ssh.Dial("tcp", backendData.Ip+":"+string(backendData.Port), config)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
backend.conn = conn
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (backend *SSHBackend) StopBackend() (bool, error) {
|
||||
err := backend.conn.Close()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (backend *SSHBackend) AddConnection(command *commonbackend.AddConnectionCommand) (bool, error) {
|
||||
for _, ipListener := range backend.data.ListenOnIPs {
|
||||
ip := net.TCPAddr{
|
||||
IP: net.ParseIP(ipListener),
|
||||
Port: int(command.DestPort),
|
||||
}
|
||||
listener, err := backend.conn.ListenTCP(&ip)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
forwardedConn, err := listener.Accept()
|
||||
if err != nil {
|
||||
log.Warnf("failed to accept listener connection: %s", err.Error())
|
||||
continue
|
||||
}
|
||||
sourceConn, err := net.Dial("tcp", command.SourceIP+":"+string(command.SourcePort))
|
||||
if err != nil {
|
||||
log.Warnf("failed to dial source connection: %s", err.Error())
|
||||
continue
|
||||
}
|
||||
sourceBuffer := make([]byte, 65535)
|
||||
forwardedBuffer := make([]byte, 65535)
|
||||
go func() {
|
||||
defer sourceConn.Close()
|
||||
defer forwardedConn.Close()
|
||||
|
||||
for {
|
||||
len, err := forwardedConn.Read(forwardedBuffer)
|
||||
if err != nil {
|
||||
log.Errorf("failed to read from forwarded connection: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = sourceConn.Write(forwardedBuffer[:len])
|
||||
if err != nil {
|
||||
log.Errorf("failed to write to source connection: %s", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
defer sourceConn.Close()
|
||||
defer forwardedConn.Close()
|
||||
|
||||
for {
|
||||
len, err := sourceConn.Read(sourceBuffer)
|
||||
if err != nil && err.Error() != "EOF" && strings.HasSuffix(err.Error(), "use of closed network connection") {
|
||||
log.Errorf("failed to read from source connection: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = forwardedConn.Write(sourceBuffer[:len])
|
||||
if err != nil && err.Error() != "EOF" && strings.HasSuffix(err.Error(), "use of closed network connection") {
|
||||
log.Errorf("failed to write to forwarded connection: %s", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (backend *SSHBackend) RemoveConnection(command *commonbackend.RemoveConnectionCommand) (bool, error) {
|
||||
// FIXME: implement
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (backend *SSHBackend) GetAllConnections() []*commonbackend.ClientConnection {
|
||||
// return []*commonbackend.ClientConnection{}
|
||||
return backend.clients
|
||||
}
|
||||
|
||||
func (backend *SSHBackend) CheckParametersForConnections(clientParameters *commonbackend.CheckClientParameters) *commonbackend.CheckParametersResponse {
|
||||
if clientParameters.Protocol != "tcp" {
|
||||
return &commonbackend.CheckParametersResponse{
|
||||
IsValid: false,
|
||||
Message: "Only TCP is supported",
|
||||
}
|
||||
}
|
||||
|
||||
return &commonbackend.CheckParametersResponse{
|
||||
IsValid: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (backend *SSHBackend) CheckParametersForBackend(arguments []byte) *commonbackend.CheckParametersResponse {
|
||||
var backendData SSHBackendData
|
||||
|
||||
err := json.Unmarshal(arguments, &backendData) // ?????
|
||||
if err != nil {
|
||||
return &commonbackend.CheckParametersResponse{
|
||||
IsValid: false,
|
||||
Message: fmt.Sprintf("could not read json: %s", err.Error()),
|
||||
}
|
||||
}
|
||||
|
||||
return &commonbackend.CheckParametersResponse{
|
||||
IsValid: true,
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// When using logging, you should use charmbracelet/log, because that's what everything else uses in this ecosystem of a project. - imterah
|
||||
logLevel := os.Getenv("NEXTNET_LOG_LEVEL")
|
||||
|
||||
if logLevel != "" {
|
||||
switch logLevel {
|
||||
case "debug":
|
||||
log.SetLevel(log.DebugLevel)
|
||||
|
||||
case "info":
|
||||
log.SetLevel(log.InfoLevel)
|
||||
|
||||
case "warn":
|
||||
log.SetLevel(log.WarnLevel)
|
||||
|
||||
case "error":
|
||||
log.SetLevel(log.ErrorLevel)
|
||||
|
||||
case "fatal":
|
||||
log.SetLevel(log.FatalLevel)
|
||||
}
|
||||
}
|
||||
|
||||
backend := &SSHBackend{}
|
||||
|
||||
application := backendutil.NewHelper(backend)
|
||||
err := application.Start()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("failed execution in application: %s", err.Error())
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue