From b6db8e36f67509cbd76d3645c4b8c20fc8aaf6f8 Mon Sep 17 00:00:00 2001 From: Denis Mishankov Date: Fri, 15 Nov 2024 14:29:27 +0300 Subject: [PATCH] add remote file and ssh key --- server/main.go | 2 -- server/sources.go | 44 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/server/main.go b/server/main.go index b0fab68..673f09d 100644 --- a/server/main.go +++ b/server/main.go @@ -45,8 +45,6 @@ var upgrader = websocket.Upgrader{ } func handleLogStream(w http.ResponseWriter, req *http.Request) { - // TODO: may need keepalive messages - sourceName := chi.URLParam(req, "source") window, err := strconv.Atoi(chi.URLParam(req, "window")) if err != nil { diff --git a/server/sources.go b/server/sources.go index 07b546f..09bfc29 100644 --- a/server/sources.go +++ b/server/sources.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "iter" + "os" "os/exec" "strconv" @@ -16,7 +17,6 @@ type Tailer interface { Tail() (iter.Seq[string], error) } -// TODO: delete BaseTailer and make Tail take initial amount arg type BaseTailer struct { initialAmount int } @@ -27,10 +27,26 @@ func NewTailerFromSource(source SourceConfig, initialAmount int) Tailer { return LocalFile{filePath: source.Path, BaseTailer: BaseTailer{initialAmount: initialAmount}} case "local:docker": return LocalDocker{containerId: source.ContainerId, BaseTailer: BaseTailer{initialAmount: initialAmount}} + case "ssh:file": + return Remote{ + host: source.Host, + port: source.Port, + username: source.Username, + password: source.Password, + privateKeyPath: source.PrivateKeyPath, + command: fmt.Sprintf("tail -f -n %v %v", strconv.Itoa(initialAmount), source.Path), + BaseTailer: BaseTailer{initialAmount: initialAmount}, + } case "ssh:docker": - // TODO: create and use NewRemote - // TODO: command may be determined in Tail, because initial amount would be passed to it. But Tail does not know about type - return Remote{host: source.Host, port: source.Port, username: source.Username, password: source.Password, privateKeyPath: source.PrivateKeyPath, command: fmt.Sprintf("docker logs -f -n %v %v", strconv.Itoa(initialAmount), source.ContainerId), BaseTailer: BaseTailer{initialAmount: initialAmount}} + return Remote{ + host: source.Host, + port: source.Port, + username: source.Username, + password: source.Password, + privateKeyPath: source.PrivateKeyPath, + command: fmt.Sprintf("docker logs -f -n %v %v", strconv.Itoa(initialAmount), source.ContainerId), + BaseTailer: BaseTailer{initialAmount: initialAmount}, + } default: return nil } @@ -103,9 +119,27 @@ func (r Remote) hostAndPort() string { func (r Remote) Tail() (iter.Seq[string], error) { logger.Debug("Start of Tail. Command:", r.command) + auth := []ssh.AuthMethod{ssh.Password(r.password)} + + if r.privateKeyPath != "" { + key, err := os.ReadFile(r.privateKeyPath) + if err != nil { + logger.Error("Error reading private key file:", err) + return nil, err + } + + signer, err := ssh.ParsePrivateKey(key) + if err != nil { + logger.Error("Error parsing private key:", err) + return nil, err + } + + auth = []ssh.AuthMethod{ssh.PublicKeys(signer)} + } + sshConfig := &ssh.ClientConfig{ User: r.username, - Auth: []ssh.AuthMethod{ssh.Password(r.password)}, //TODO: implement auth with key + Auth: auth, } sshConfig.HostKeyCallback = ssh.InsecureIgnoreHostKey()