ssh backend (mostly)

This commit is contained in:
valerie 2024-12-01 23:14:33 -05:00
parent aa16549667
commit 79389d37b8
Signed by: valnyx
GPG key ID: CC10324DD962CB7E
3 changed files with 219 additions and 1 deletions

View file

@ -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
)

View file

@ -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=

View 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())
}
}