chore: Mostly ports SSHBackend.

This commit is contained in:
Tera << 8 2025-01-12 11:35:26 -05:00
parent 0d92d27427
commit 2c6b5eb5b9
Signed by: imterah
GPG key ID: 8FA7DD57BA6CEA37
5 changed files with 98 additions and 62 deletions

View file

@ -229,6 +229,7 @@ func (runtime *Runtime) goRoutineHandler() error {
} }
} }
case *commonbackend.ProxyStatusRequest: case *commonbackend.ProxyStatusRequest:
command.Message()
err := handleCommand("proxyStatusRequest", command, sock, messageData.Channel) err := handleCommand("proxyStatusRequest", command, sock, messageData.Channel)
if err != nil { if err != nil {

View file

@ -1,6 +1,9 @@
package backendutil package backendutil
import "git.terah.dev/imterah/hermes/backend/commonbackend" import (
"capnproto.org/go/capnp/v3"
"git.terah.dev/imterah/hermes/backend/commonbackend"
)
type BackendInterface interface { type BackendInterface interface {
StartBackend(arguments []byte) (bool, error) StartBackend(arguments []byte) (bool, error)
@ -8,7 +11,15 @@ type BackendInterface interface {
GetBackendStatus() (bool, error) GetBackendStatus() (bool, error)
StartProxy(command *commonbackend.AddProxy) (bool, error) StartProxy(command *commonbackend.AddProxy) (bool, error)
StopProxy(command *commonbackend.RemoveProxy) (bool, error) StopProxy(command *commonbackend.RemoveProxy) (bool, error)
GetAllClientConnections() []*commonbackend.ProxyClientConnection GetAllClientConnections(seg *capnp.Segment) []*commonbackend.Connection
CheckParametersForConnections(clientParameters *commonbackend.CheckClientParameters) *commonbackend.CheckParametersResponse CheckParametersForConnections(clientParameters *commonbackend.CheckClientParameters) *CheckParametersResponse
CheckParametersForBackend(arguments []byte) *commonbackend.CheckParametersResponse CheckParametersForBackend(arguments []byte) *CheckParametersResponse
}
// Sent as a response to either CheckClientParameters or CheckBackendParameters
type CheckParametersResponse struct {
Type string // Will be 'checkParametersResponse' always
InResponseTo string // Will be either 'checkClientParameters' or 'checkServerParameters'
IsValid bool // If true, valid, and if false, invalid
Message string // String message from the client (ex. failed to unmarshal JSON: x is not defined)
} }

View file

@ -31,23 +31,23 @@ func (backend *DummyBackend) StopProxy(command *commonbackend.RemoveProxy) (bool
return true, nil return true, nil
} }
func (backend *DummyBackend) GetAllClientConnections() []*commonbackend.ProxyClientConnection { func (backend *DummyBackend) GetAllClientConnections() []*commonbackend.Connection {
return []*commonbackend.ProxyClientConnection{} return []*commonbackend.Connection{}
} }
func (backend *DummyBackend) CheckParametersForConnections(clientParameters *commonbackend.CheckClientParameters) *commonbackend.CheckParametersResponse { func (backend *DummyBackend) CheckParametersForConnections(clientParameters *commonbackend.CheckClientParameters) *backendutil.CheckParametersResponse {
// You don't have to specify Type and InReplyTo. Those will be handled for you. // You don't have to specify Type and InReplyTo. Those will be handled for you.
// Message is optional. // Message is optional.
return &commonbackend.CheckParametersResponse{ return &backendutil.CheckParametersResponse{
IsValid: true, IsValid: true,
Message: "Valid!", Message: "Valid!",
} }
} }
func (backend *DummyBackend) CheckParametersForBackend(arguments []byte) *commonbackend.CheckParametersResponse { func (backend *DummyBackend) CheckParametersForBackend(arguments []byte) *backendutil.CheckParametersResponse {
// You don't have to specify Type and InReplyTo. Those will be handled for you. // You don't have to specify Type and InReplyTo. Those will be handled for you.
// Message is optional. // Message is optional.
return &commonbackend.CheckParametersResponse{ return &backendutil.CheckParametersResponse{
IsValid: true, IsValid: true,
Message: "Valid!", Message: "Valid!",
} }

View file

@ -21,11 +21,8 @@ type ProxyInstance struct {
Protocol string `json:"protocol"` Protocol string `json:"protocol"`
} }
type WriteLogger struct { type WriteLogger struct{}
UseError bool
}
// TODO: deprecate UseError switching
func (writer WriteLogger) Write(p []byte) (n int, err error) { func (writer WriteLogger) Write(p []byte) (n int, err error) {
logSplit := strings.Split(string(p), "\n") logSplit := strings.Split(string(p), "\n")
@ -34,11 +31,7 @@ func (writer WriteLogger) Write(p []byte) (n int, err error) {
continue continue
} }
if writer.UseError { log.Infof("application: %s", line)
log.Errorf("application: %s", line)
} else {
log.Infof("application: %s", line)
}
} }
return len(p), err return len(p), err
@ -157,7 +150,7 @@ func entrypoint(cCtx *cli.Context) error {
if !command.IsRunning { if !command.IsRunning {
var status string var status string
if command.StatusCode == commonbackend.StatusSuccess { if command.StatusCode() == commonbackend.StatusCode_success {
status = "Success" status = "Success"
} else { } else {
status = "Failure" status = "Failure"
@ -236,19 +229,14 @@ func entrypoint(cCtx *cli.Context) error {
log.Debug("entering infinite keepalive loop...") log.Debug("entering infinite keepalive loop...")
for { for {
time.Sleep(1 * time.Second)
} }
} }
}() }()
log.Debug("entering execution loop (in main goroutine)...") log.Debug("entering execution loop (in main goroutine)...")
stdout := WriteLogger{ logger := WriteLogger{}
UseError: false,
}
stderr := WriteLogger{
UseError: true,
}
for { for {
log.Info("starting process...") log.Info("starting process...")
@ -257,9 +245,7 @@ func entrypoint(cCtx *cli.Context) error {
cmd := exec.Command(executablePath) cmd := exec.Command(executablePath)
cmd.Env = append(cmd.Env, fmt.Sprintf("HERMES_API_SOCK=%s", sockPath), fmt.Sprintf("HERMES_LOG_LEVEL=%s", logLevel)) cmd.Env = append(cmd.Env, fmt.Sprintf("HERMES_API_SOCK=%s", sockPath), fmt.Sprintf("HERMES_LOG_LEVEL=%s", logLevel))
cmd.Stdout = stdout cmd.Stdout, cmd.Stderr = logger, logger
cmd.Stderr = stderr
err := cmd.Run() err := cmd.Run()
if err != nil { if err != nil {

View file

@ -22,14 +22,14 @@ type SSHListener struct {
SourceIP string SourceIP string
SourcePort uint16 SourcePort uint16
DestPort uint16 DestPort uint16
Protocol string // Will be either 'tcp' or 'udp' Protocol commonbackend.Protocol
Listeners []net.Listener Listeners []net.Listener
} }
type SSHBackend struct { type SSHBackend struct {
config *SSHBackendData config *SSHBackendData
conn *ssh.Client conn *ssh.Client
clients []*commonbackend.ProxyClientConnection clients []*commonbackend.Connection
proxies []*SSHListener proxies []*SSHListener
arrayPropMutex sync.Mutex arrayPropMutex sync.Mutex
} }
@ -105,18 +105,31 @@ func (backend *SSHBackend) GetBackendStatus() (bool, error) {
} }
func (backend *SSHBackend) StartProxy(command *commonbackend.AddProxy) (bool, error) { func (backend *SSHBackend) StartProxy(command *commonbackend.AddProxy) (bool, error) {
sourceIPBytes, err := command.SourceIP()
if err != nil {
return false, err
}
// YOLO.
sourceIP, err := commonbackend.IPBytesToIPSafe(sourceIPBytes)
if err != nil {
return false, err
}
listenerObject := &SSHListener{ listenerObject := &SSHListener{
SourceIP: command.SourceIP, SourceIP: sourceIP.String(),
SourcePort: command.SourcePort, SourcePort: command.SourcePort(),
DestPort: command.DestPort, DestPort: command.DestPort(),
Protocol: command.Protocol, Protocol: command.Protocol(),
Listeners: []net.Listener{}, Listeners: []net.Listener{},
} }
for _, ipListener := range backend.config.ListenOnIPs { for _, ipListener := range backend.config.ListenOnIPs {
ip := net.TCPAddr{ ip := net.TCPAddr{
IP: net.ParseIP(ipListener), IP: net.ParseIP(ipListener),
Port: int(command.DestPort), Port: int(command.DestPort()),
} }
listener, err := backend.conn.ListenTCP(&ip) listener, err := backend.conn.ListenTCP(&ip)
@ -158,7 +171,7 @@ func (backend *SSHBackend) StartProxy(command *commonbackend.AddProxy) (bool, er
} }
clientIPAndPort := forwardedConn.RemoteAddr().String() clientIPAndPort := forwardedConn.RemoteAddr().String()
clientIP := clientIPAndPort[:strings.LastIndex(clientIPAndPort, ":")] clientIPString := clientIPAndPort[:strings.LastIndex(clientIPAndPort, ":")]
clientPort, err := strconv.Atoi(clientIPAndPort[strings.LastIndex(clientIPAndPort, ":")+1:]) clientPort, err := strconv.Atoi(clientIPAndPort[strings.LastIndex(clientIPAndPort, ":")+1:])
if err != nil { if err != nil {
@ -166,17 +179,21 @@ func (backend *SSHBackend) StartProxy(command *commonbackend.AddProxy) (bool, er
continue continue
} }
advertisedConn := &commonbackend.ProxyClientConnection{ clientIP, err := commonbackend.IPStringToIPBytes(clientIPString)
SourceIP: command.SourceIP,
SourcePort: command.SourcePort,
DestPort: command.DestPort,
ClientIP: clientIP,
ClientPort: uint16(clientPort),
// FIXME (imterah): shouldn't protocol be in here? if err != nil {
// Protocol: command.Protocol, log.Warnf("failed to parse client IP: %s", err.Error())
continue
} }
advertisedConn := &commonbackend.Connection{}
advertisedConn.SetSourceIP(sourceIPBytes)
advertisedConn.SetSourcePort(command.SourcePort())
advertisedConn.SetDestPort(command.DestPort())
advertisedConn.SetClientIP(clientIP)
advertisedConn.SetClientPort(uint16(clientPort))
backend.arrayPropMutex.Lock() backend.arrayPropMutex.Lock()
backend.clients = append(backend.clients, advertisedConn) backend.clients = append(backend.clients, advertisedConn)
backend.arrayPropMutex.Unlock() backend.arrayPropMutex.Unlock()
@ -278,7 +295,20 @@ func (backend *SSHBackend) StopProxy(command *commonbackend.RemoveProxy) (bool,
backend.arrayPropMutex.Lock() backend.arrayPropMutex.Lock()
for proxyIndex, proxy := range backend.proxies { for proxyIndex, proxy := range backend.proxies {
if command.SourceIP == proxy.SourceIP && command.SourcePort == proxy.SourcePort && command.DestPort == proxy.DestPort && command.Protocol == proxy.Protocol { sourceIPBytes, err := command.SourceIP()
if err != nil {
log.Warnf("failed to get source IP: %s", err.Error())
continue
}
sourceIP, err := commonbackend.IPBytesToIPSafe(sourceIPBytes)
if err != nil {
log.Warnf("failed to get source IP: %s", err.Error())
}
if sourceIP.String() == proxy.SourceIP && command.SourcePort() == proxy.SourcePort && command.DestPort() == proxy.DestPort && command.Protocol() == proxy.Protocol {
for _, listener := range proxy.Listeners { for _, listener := range proxy.Listeners {
err := listener.Close() err := listener.Close()
@ -299,44 +329,44 @@ func (backend *SSHBackend) StopProxy(command *commonbackend.RemoveProxy) (bool,
return false, fmt.Errorf("could not find the proxy") return false, fmt.Errorf("could not find the proxy")
} }
func (backend *SSHBackend) GetAllClientConnections() []*commonbackend.ProxyClientConnection { func (backend *SSHBackend) GetAllClientConnections() []*commonbackend.Connection {
defer backend.arrayPropMutex.Unlock() defer backend.arrayPropMutex.Unlock()
backend.arrayPropMutex.Lock() backend.arrayPropMutex.Lock()
return backend.clients return backend.clients
} }
func (backend *SSHBackend) CheckParametersForConnections(clientParameters *commonbackend.CheckClientParameters) *commonbackend.CheckParametersResponse { func (backend *SSHBackend) CheckParametersForConnections(clientParameters *commonbackend.CheckClientParameters) *backendutil.CheckParametersResponse {
if clientParameters.Protocol != "tcp" { if clientParameters.Protocol() != commonbackend.Protocol_tcp {
return &commonbackend.CheckParametersResponse{ return &backendutil.CheckParametersResponse{
IsValid: false, IsValid: false,
Message: "Only TCP is supported for SSH", Message: "Only TCP is supported for SSH",
} }
} }
return &commonbackend.CheckParametersResponse{ return &backendutil.CheckParametersResponse{
IsValid: true, IsValid: true,
} }
} }
func (backend *SSHBackend) CheckParametersForBackend(arguments []byte) *commonbackend.CheckParametersResponse { func (backend *SSHBackend) CheckParametersForBackend(arguments []byte) *backendutil.CheckParametersResponse {
var backendData SSHBackendData var backendData SSHBackendData
if err := json.Unmarshal(arguments, &backendData); err != nil { if err := json.Unmarshal(arguments, &backendData); err != nil {
return &commonbackend.CheckParametersResponse{ return &backendutil.CheckParametersResponse{
IsValid: false, IsValid: false,
Message: fmt.Sprintf("could not read json: %s", err.Error()), Message: fmt.Sprintf("could not read json: %s", err.Error()),
} }
} }
if err := validator.New().Struct(&backendData); err != nil { if err := validator.New().Struct(&backendData); err != nil {
return &commonbackend.CheckParametersResponse{ return &backendutil.CheckParametersResponse{
IsValid: false, IsValid: false,
Message: fmt.Sprintf("failed validation of parameters: %s", err.Error()), Message: fmt.Sprintf("failed validation of parameters: %s", err.Error()),
} }
} }
return &commonbackend.CheckParametersResponse{ return &backendutil.CheckParametersResponse{
IsValid: true, IsValid: true,
} }
} }
@ -388,12 +418,20 @@ func (backend *SSHBackend) backendDisconnectHandler() {
log.Info("SSHBackend has reconnected successfully. Attempting to set up proxies again...") log.Info("SSHBackend has reconnected successfully. Attempting to set up proxies again...")
for _, proxy := range backend.proxies { for _, proxy := range backend.proxies {
ok, err := backend.StartProxy(&commonbackend.AddProxy{ sourceIP, err := commonbackend.IPStringToIPBytes(proxy.SourceIP)
SourceIP: proxy.SourceIP,
SourcePort: proxy.SourcePort, if err != nil {
DestPort: proxy.DestPort, log.Warnf("Failed to parse IP address: %s", err.Error())
Protocol: proxy.Protocol, }
})
proxyStartCommand := &commonbackend.AddProxy{}
proxyStartCommand.SetSourceIP(sourceIP)
proxyStartCommand.SetSourcePort(proxy.SourcePort)
proxyStartCommand.SetDestPort(proxy.DestPort)
proxyStartCommand.SetProtocol(proxy.Protocol)
ok, err := backend.StartProxy(proxyStartCommand)
if err != nil { if err != nil {
log.Errorf("Failed to set up proxy: %s", err.Error()) log.Errorf("Failed to set up proxy: %s", err.Error())