feat(sshbox): add sshbox to run commands (#161)

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
Ettore Di Giacinto
2025-05-17 23:34:51 +02:00
committed by GitHub
parent a668830a79
commit 4a0d3a7a94
7 changed files with 200 additions and 24 deletions

View File

@@ -12,21 +12,24 @@ import (
"golang.org/x/crypto/ssh"
)
func NewShell(config map[string]string) *ShellAction {
func NewShell(config map[string]string, sshBoxURL string) *ShellAction {
return &ShellAction{
privateKey: config["privateKey"],
user: config["user"],
host: config["host"],
password: config["password"],
customName: config["customName"],
customDescription: config["customDescription"],
sshBoxURL: sshBoxURL,
}
}
type ShellAction struct {
privateKey string
user, host string
customName string
customDescription string
privateKey string
user, host, password string
customName string
customDescription string
sshBoxURL string
}
func (a *ShellAction) Run(ctx context.Context, sharedState *types.AgentSharedState, params types.ActionParams) (types.ActionResult, error) {
@@ -47,7 +50,23 @@ func (a *ShellAction) Run(ctx context.Context, sharedState *types.AgentSharedSta
result.User = a.user
}
output, err := sshCommand(a.privateKey, result.Command, result.User, result.Host)
password := a.password
if a.sshBoxURL != "" && result.Host == "" && result.User == "" && password == "" {
// sshbox url can be root:root@localhost:2222
parts := strings.Split(a.sshBoxURL, "@")
if len(parts) == 2 {
if strings.Contains(parts[0], ":") {
userPass := strings.Split(parts[0], ":")
result.User = userPass[0]
password = userPass[1]
} else {
result.User = parts[0]
}
result.Host = parts[1]
}
}
output, err := sshCommand(a.privateKey, result.Command, result.User, result.Host, password)
if err != nil {
return types.ActionResult{}, err
}
@@ -56,15 +75,15 @@ func (a *ShellAction) Run(ctx context.Context, sharedState *types.AgentSharedSta
}
func (a *ShellAction) Definition() types.ActionDefinition {
name := "shell"
description := "Run a shell command on a remote server."
name := "run_command"
description := "Run a command on a linux environment."
if a.customName != "" {
name = a.customName
}
if a.customDescription != "" {
description = a.customDescription
}
if a.host != "" && a.user != "" {
if (a.host != "" && a.user != "") || a.sshBoxURL != "" {
return types.ActionDefinition{
Name: types.ActionDefinitionName(name),
Description: description,
@@ -105,7 +124,7 @@ func ShellConfigMeta() []config.Field {
Name: "privateKey",
Label: "Private Key",
Type: config.FieldTypeTextarea,
Required: true,
Required: false,
HelpText: "SSH private key for connecting to remote servers",
},
{
@@ -114,6 +133,12 @@ func ShellConfigMeta() []config.Field {
Type: config.FieldTypeText,
HelpText: "Default SSH user for connecting to remote servers",
},
{
Name: "password",
Label: "Default Password",
Type: config.FieldTypeText,
HelpText: "Default SSH password for connecting to remote servers",
},
{
Name: "host",
Label: "Default Host",
@@ -135,19 +160,25 @@ func ShellConfigMeta() []config.Field {
}
}
func sshCommand(privateKey, command, user, host string) (string, error) {
// Create signer from private key string
key, err := ssh.ParsePrivateKey([]byte(privateKey))
if err != nil {
log.Fatalf("failed to parse private key: %v", err)
func sshCommand(privateKey, command, user, host, password string) (string, error) {
authMethods := []ssh.AuthMethod{}
if password != "" {
authMethods = append(authMethods, ssh.Password(password))
}
if privateKey != "" {
// Create signer from private key string
key, err := ssh.ParsePrivateKey([]byte(privateKey))
if err != nil {
log.Fatalf("failed to parse private key: %v", err)
}
authMethods = append(authMethods, ssh.PublicKeys(key))
}
// SSH client configuration
config := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(key),
},
User: user,
Auth: authMethods,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}