diff --git a/README.md b/README.md index 4321e9ee..179923a4 100644 --- a/README.md +++ b/README.md @@ -3031,12 +3031,13 @@ endpoints: password: "password" body: | { - "command": "uptime" + "command": "echo '{\"memory\": {\"used\": 512}}'" } interval: 1m conditions: - "[CONNECTED] == true" - "[STATUS] == 0" + - "[BODY].memory.used > 500" ``` you can also use no authentication to monitor the endpoint by not specifying the username @@ -3059,6 +3060,7 @@ endpoints: The following placeholders are supported for endpoints of type SSH: - `[CONNECTED]` resolves to `true` if the SSH connection was successful, `false` otherwise - `[STATUS]` resolves the exit code of the command executed on the remote server (e.g. `0` for success) +- `[BODY]` resolves to the stdout output of the command executed on the remote server - `[IP]` resolves to the IP address of the server - `[RESPONSE_TIME]` resolves to the time it took to establish the connection and execute the command diff --git a/client/client.go b/client/client.go index 6fd49f95..81df0d2a 100644 --- a/client/client.go +++ b/client/client.go @@ -1,6 +1,7 @@ package client import ( + "bytes" "context" "crypto/tls" "crypto/x509" @@ -301,7 +302,7 @@ func CheckSSHBanner(address string, cfg *Config) (bool, int, error) { } // ExecuteSSHCommand executes a command to an address using the SSH protocol. -func ExecuteSSHCommand(sshClient *ssh.Client, body string, config *Config) (bool, int, error) { +func ExecuteSSHCommand(sshClient *ssh.Client, body string, config *Config) (bool, int, []byte, error) { type Body struct { Command string `json:"command"` } @@ -309,26 +310,30 @@ func ExecuteSSHCommand(sshClient *ssh.Client, body string, config *Config) (bool var b Body body = parseLocalAddressPlaceholder(body, sshClient.Conn.LocalAddr()) if err := json.Unmarshal([]byte(body), &b); err != nil { - return false, 0, err + return false, 0, nil, err } sess, err := sshClient.NewSession() if err != nil { - return false, 0, err + return false, 0, nil, err } + // Capture stdout + var stdout bytes.Buffer + sess.Stdout = &stdout err = sess.Start(b.Command) if err != nil { - return false, 0, err + return false, 0, nil, err } defer sess.Close() err = sess.Wait() + output := stdout.Bytes() if err == nil { - return true, 0, nil + return true, 0, output, nil } var exitErr *ssh.ExitError if ok := errors.As(err, &exitErr); !ok { - return false, 0, err + return false, 0, nil, err } - return true, exitErr.ExitStatus(), nil + return true, exitErr.ExitStatus(), output, nil } // Ping checks if an address can be pinged and returns the round-trip time if the address can be pinged diff --git a/config/endpoint/endpoint.go b/config/endpoint/endpoint.go index d0e2c151..1faf3bf1 100644 --- a/config/endpoint/endpoint.go +++ b/config/endpoint/endpoint.go @@ -514,11 +514,16 @@ func (e *Endpoint) call(result *Result) { result.AddError(err.Error()) return } - result.Success, result.HTTPStatus, err = client.ExecuteSSHCommand(cli, e.getParsedBody(), e.ClientConfig) + var output []byte + result.Success, result.HTTPStatus, output, err = client.ExecuteSSHCommand(cli, e.getParsedBody(), e.ClientConfig) if err != nil { result.AddError(err.Error()) return } + // Only store the output in result.Body if there's a condition that uses the BodyPlaceholder + if e.needsToReadBody() { + result.Body = output + } result.Duration = time.Since(startTime) } else { response, err = client.GetHTTPClient(e.ClientConfig).Do(request)