feature: Adds basic backend starting for sshappbackend.
This commit is contained in:
parent
a35602a6f2
commit
ede4d528aa
8 changed files with 434 additions and 23 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -2,7 +2,7 @@
|
||||||
backend/api/api
|
backend/api/api
|
||||||
backend/sshbackend/sshbackend
|
backend/sshbackend/sshbackend
|
||||||
backend/dummybackend/dummybackend
|
backend/dummybackend/dummybackend
|
||||||
backend/sshappbackend/remote-code/bin
|
backend/sshappbackend/local-code/remote-bin
|
||||||
backend/sshappbackend/local-code/sshappbackend
|
backend/sshappbackend/local-code/sshappbackend
|
||||||
backend/externalbackendlauncher/externalbackendlauncher
|
backend/externalbackendlauncher/externalbackendlauncher
|
||||||
frontend/frontend
|
frontend/frontend
|
||||||
|
|
|
@ -21,13 +21,13 @@ if [ ! -d bin ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo " - building for arm64"
|
echo " - building for arm64"
|
||||||
GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -trimpath -o bin/rt-arm64 .
|
GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -trimpath -o ../local-code/remote-bin/rt-arm64 .
|
||||||
echo " - building for arm"
|
echo " - building for arm"
|
||||||
GOOS=linux GOARCH=arm go build -ldflags="-s -w" -trimpath -o bin/rt-arm .
|
GOOS=linux GOARCH=arm go build -ldflags="-s -w" -trimpath -o ../local-code/remote-bin/rt-arm .
|
||||||
echo " - building for amd64"
|
echo " - building for amd64"
|
||||||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -trimpath -o bin/rt-amd64 .
|
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -trimpath -o ../local-code/remote-bin/rt-amd64 .
|
||||||
echo " - building for i386"
|
echo " - building for i386"
|
||||||
GOOS=linux GOARCH=386 go build -ldflags="-s -w" -trimpath -o bin/rt-386 .
|
GOOS=linux GOARCH=386 go build -ldflags="-s -w" -trimpath -o ../local-code/remote-bin/rt-386 .
|
||||||
popd > /dev/null
|
popd > /dev/null
|
||||||
|
|
||||||
pushd sshappbackend/local-code > /dev/null
|
pushd sshappbackend/local-code > /dev/null
|
||||||
|
|
|
@ -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
|
||||||
|
@ -242,13 +235,8 @@ func entrypoint(cCtx *cli.Context) error {
|
||||||
|
|
||||||
log.Debug("entering execution loop (in main goroutine)...")
|
log.Debug("entering execution loop (in main goroutine)...")
|
||||||
|
|
||||||
stdout := WriteLogger{
|
stdout := WriteLogger{}
|
||||||
UseError: false,
|
stderr := WriteLogger{}
|
||||||
}
|
|
||||||
|
|
||||||
stderr := WriteLogger{
|
|
||||||
UseError: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
log.Info("starting process...")
|
log.Info("starting process...")
|
||||||
|
|
8
backend/sshappbackend/local-code/fs.go
Normal file
8
backend/sshappbackend/local-code/fs.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed remote-bin
|
||||||
|
var binFiles embed.FS
|
23
backend/sshappbackend/local-code/logger.go
Normal file
23
backend/sshappbackend/local-code/logger.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WriteLogger struct{}
|
||||||
|
|
||||||
|
func (writer WriteLogger) Write(p []byte) (n int, err error) {
|
||||||
|
logSplit := strings.Split(string(p), "\n")
|
||||||
|
|
||||||
|
for _, line := range logSplit {
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Process: %s", line)
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(p), err
|
||||||
|
}
|
|
@ -1,7 +1,355 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"git.terah.dev/imterah/hermes/backend/backendutil"
|
||||||
|
"git.terah.dev/imterah/hermes/backend/commonbackend"
|
||||||
|
"github.com/charmbracelet/log"
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
"github.com/pkg/sftp"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SSHAppBackendData struct {
|
||||||
|
IP string `json:"ip" validate:"required"`
|
||||||
|
Port uint16 `json:"port" validate:"required"`
|
||||||
|
Username string `json:"username" validate:"required"`
|
||||||
|
PrivateKey string `json:"privateKey" validate:"required"`
|
||||||
|
ListenOnIPs []string `json:"listenOnIPs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SSHAppBackend struct {
|
||||||
|
config *SSHAppBackendData
|
||||||
|
conn *ssh.Client
|
||||||
|
clients []*commonbackend.ProxyClientConnection
|
||||||
|
arrayPropMutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (backend *SSHAppBackend) StartBackend(configBytes []byte) (bool, error) {
|
||||||
|
log.Info("SSHAppBackend is initializing...")
|
||||||
|
var backendData SSHAppBackendData
|
||||||
|
|
||||||
|
if err := json.Unmarshal(configBytes, &backendData); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validator.New().Struct(&backendData); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
backend.config = &backendData
|
||||||
|
|
||||||
|
if len(backend.config.ListenOnIPs) == 0 {
|
||||||
|
backend.config.ListenOnIPs = []string{"0.0.0.0"}
|
||||||
|
}
|
||||||
|
|
||||||
|
signer, err := ssh.ParsePrivateKey([]byte(backendData.PrivateKey))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to initialize: %s", err.Error())
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
auth := ssh.PublicKeys(signer)
|
||||||
|
|
||||||
|
config := &ssh.ClientConfig{
|
||||||
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||||
|
User: backendData.Username,
|
||||||
|
Auth: []ssh.AuthMethod{
|
||||||
|
auth,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", backendData.IP, backendData.Port), config)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to initialize: %s", err.Error())
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
backend.conn = conn
|
||||||
|
|
||||||
|
log.Info("SSHAppBackend has connected successfully.")
|
||||||
|
log.Info("Getting CPU architecture...")
|
||||||
|
|
||||||
|
session, err := backend.conn.NewSession()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to create session: %s", err.Error())
|
||||||
|
conn.Close()
|
||||||
|
backend.conn = nil
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdoutBuf bytes.Buffer
|
||||||
|
session.Stdout = &stdoutBuf
|
||||||
|
|
||||||
|
err = session.Run("uname -m")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to run uname command: %s", err.Error())
|
||||||
|
conn.Close()
|
||||||
|
backend.conn = nil
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuArchBytes := make([]byte, stdoutBuf.Len())
|
||||||
|
stdoutBuf.Read(cpuArchBytes)
|
||||||
|
|
||||||
|
cpuArch := string(cpuArchBytes)
|
||||||
|
cpuArch = cpuArch[:len(cpuArch)-1]
|
||||||
|
|
||||||
|
var backendBinary string
|
||||||
|
|
||||||
|
// Ordered in (subjective) popularity
|
||||||
|
if cpuArch == "x86_64" {
|
||||||
|
backendBinary = "remote-bin/rt-amd64"
|
||||||
|
} else if cpuArch == "aarch64" {
|
||||||
|
backendBinary = "remote-bin/rt-arm64"
|
||||||
|
} else if cpuArch == "arm" {
|
||||||
|
backendBinary = "remote-bin/rt-arm"
|
||||||
|
} else if len(cpuArch) == 4 && string(cpuArch[0]) == "i" && strings.HasSuffix(cpuArch, "86") {
|
||||||
|
backendBinary = "remote-bin/rt-386"
|
||||||
|
} else {
|
||||||
|
log.Warn("Failed to determine executable to use: CPU architecture not compiled/supported currently")
|
||||||
|
conn.Close()
|
||||||
|
backend.conn = nil
|
||||||
|
return false, fmt.Errorf("CPU architecture not compiled/supported currently")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Checking if we need to copy the application...")
|
||||||
|
|
||||||
|
var binary []byte
|
||||||
|
needsToCopyBinary := true
|
||||||
|
|
||||||
|
session, err = backend.conn.NewSession()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to create session: %s", err.Error())
|
||||||
|
conn.Close()
|
||||||
|
backend.conn = nil
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
session.Stdout = &stdoutBuf
|
||||||
|
|
||||||
|
err = session.Start("[ -f /tmp/sshappbackend.runtime ] && md5sum /tmp/sshappbackend.runtime | cut -d \" \" -f 1")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to calculate hash of possibly existing backend: %s", err.Error())
|
||||||
|
conn.Close()
|
||||||
|
backend.conn = nil
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fileExists := stdoutBuf.Len() != 0
|
||||||
|
|
||||||
|
if fileExists {
|
||||||
|
remoteMD5HashStringBuf := make([]byte, stdoutBuf.Len())
|
||||||
|
stdoutBuf.Read(remoteMD5HashStringBuf)
|
||||||
|
|
||||||
|
remoteMD5HashString := string(remoteMD5HashStringBuf)
|
||||||
|
remoteMD5HashString = remoteMD5HashString[:len(remoteMD5HashString)-1]
|
||||||
|
|
||||||
|
remoteMD5Hash, err := hex.DecodeString(remoteMD5HashString)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to decode hex: %s", err.Error())
|
||||||
|
conn.Close()
|
||||||
|
backend.conn = nil
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
binary, err = binFiles.ReadFile(backendBinary)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to read file in the embedded FS: %s", err.Error())
|
||||||
|
conn.Close()
|
||||||
|
backend.conn = nil
|
||||||
|
return false, fmt.Errorf("(embedded FS): %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
localMD5Hash := md5.Sum(binary)
|
||||||
|
|
||||||
|
log.Infof("remote: %s, local: %s", remoteMD5HashString, hex.EncodeToString(localMD5Hash[:]))
|
||||||
|
|
||||||
|
if bytes.Compare(localMD5Hash[:], remoteMD5Hash) == 0 {
|
||||||
|
needsToCopyBinary = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if needsToCopyBinary {
|
||||||
|
log.Info("Copying binary...")
|
||||||
|
sftpInstance, err := sftp.NewClient(conn)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to initialize SFTP: %s", err.Error())
|
||||||
|
conn.Close()
|
||||||
|
backend.conn = nil
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer sftpInstance.Close()
|
||||||
|
|
||||||
|
if len(binary) == 0 {
|
||||||
|
binary, err = binFiles.ReadFile(backendBinary)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to read file in the embedded FS: %s", err.Error())
|
||||||
|
conn.Close()
|
||||||
|
backend.conn = nil
|
||||||
|
return false, fmt.Errorf("(embedded FS): %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var file *sftp.File
|
||||||
|
|
||||||
|
if fileExists {
|
||||||
|
file, err = sftpInstance.Create("/tmp/sshappbackend.runtime")
|
||||||
|
} else {
|
||||||
|
file, err = sftpInstance.OpenFile("/tmp/sshappbackend.runtime", os.O_WRONLY)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to create file: %s", err.Error())
|
||||||
|
conn.Close()
|
||||||
|
backend.conn = nil
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = file.Write(binary)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to write file: %s", err.Error())
|
||||||
|
conn.Close()
|
||||||
|
backend.conn = nil
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = file.Chmod(775)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to change permissions on file: %s", err.Error())
|
||||||
|
conn.Close()
|
||||||
|
backend.conn = nil
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Done copying file.")
|
||||||
|
} else {
|
||||||
|
log.Info("Skipping copying as there's a copy on disk already.")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Starting process...")
|
||||||
|
|
||||||
|
session, err = backend.conn.NewSession()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to create session: %s", err.Error())
|
||||||
|
conn.Close()
|
||||||
|
backend.conn = nil
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
session.Stdout = WriteLogger{}
|
||||||
|
session.Stderr = WriteLogger{}
|
||||||
|
|
||||||
|
go session.Run("/tmp/sshappbackend.runtime")
|
||||||
|
log.Info("SSHAppBackend has initialized successfully.")
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (backend *SSHAppBackend) StopBackend() (bool, error) {
|
||||||
|
err := backend.conn.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (backend *SSHAppBackend) GetBackendStatus() (bool, error) {
|
||||||
|
return backend.conn != nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (backend *SSHAppBackend) StartProxy(command *commonbackend.AddProxy) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (backend *SSHAppBackend) StopProxy(command *commonbackend.RemoveProxy) (bool, error) {
|
||||||
|
return false, fmt.Errorf("could not find the proxy")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (backend *SSHAppBackend) GetAllClientConnections() []*commonbackend.ProxyClientConnection {
|
||||||
|
return backend.clients
|
||||||
|
}
|
||||||
|
|
||||||
|
func (backend *SSHAppBackend) CheckParametersForConnections(clientParameters *commonbackend.CheckClientParameters) *commonbackend.CheckParametersResponse {
|
||||||
|
return &commonbackend.CheckParametersResponse{
|
||||||
|
IsValid: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (backend *SSHAppBackend) CheckParametersForBackend(arguments []byte) *commonbackend.CheckParametersResponse {
|
||||||
|
var backendData SSHAppBackendData
|
||||||
|
|
||||||
|
if err := json.Unmarshal(arguments, &backendData); err != nil {
|
||||||
|
return &commonbackend.CheckParametersResponse{
|
||||||
|
IsValid: false,
|
||||||
|
Message: fmt.Sprintf("could not read json: %s", err.Error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validator.New().Struct(&backendData); err != nil {
|
||||||
|
return &commonbackend.CheckParametersResponse{
|
||||||
|
IsValid: false,
|
||||||
|
Message: fmt.Sprintf("failed validation of parameters: %s", err.Error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &commonbackend.CheckParametersResponse{
|
||||||
|
IsValid: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Println("lokkuh code")
|
logLevel := os.Getenv("HERMES_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 := &SSHAppBackend{}
|
||||||
|
|
||||||
|
application := backendutil.NewHelper(backend)
|
||||||
|
err := application.Start()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed execution in application: %s", err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -38,6 +38,7 @@ require (
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||||
|
github.com/kr/fs v0.1.0 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
|
@ -49,6 +50,7 @@ require (
|
||||||
github.com/muesli/reflow v0.3.0 // indirect
|
github.com/muesli/reflow v0.3.0 // indirect
|
||||||
github.com/muesli/termenv v0.15.2 // indirect
|
github.com/muesli/termenv v0.15.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||||
|
github.com/pkg/sftp v1.13.7 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
|
42
go.sum
42
go.sum
|
@ -60,6 +60,8 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02
|
||||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
|
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||||
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
@ -86,6 +88,8 @@ github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo
|
||||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||||
|
github.com/pkg/sftp v1.13.7 h1:uv+I3nNJvlKZIQGSr8JVQLNHFU9YhhNpvC14Y6KgmSM=
|
||||||
|
github.com/pkg/sftp v1.13.7/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
@ -114,23 +118,61 @@ github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
|
||||||
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
|
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
|
||||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
|
||||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg=
|
golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg=
|
||||||
golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
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/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
|
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ=
|
google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ=
|
||||||
google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue