fix: metric samples timestamp + ssh-script port/user handling
- InsertMetricSample now includes ts=now() (TimescaleDB hypertable requires it) - ssh-script: pass host and port separately (ssh uses -p flag, not host:port) - ssh-script: use OIKOS_SSH_USER from config/env, default root - Add -o LogLevel=ERROR to suppress SSH warnings polluting JSON output - Use Output() (stdout only) instead of CombinedOutput() - Set OIKOS_SSH_USER=root in scheduler docker-compose service
This commit is contained in:
@@ -84,6 +84,7 @@ services:
|
||||
OIKOS_DEBUG: "true"
|
||||
OIKOS_SCHEDULER_INTERVAL: "30s"
|
||||
OIKOS_SSH_KEY_PATH: /etc/oikos/ssh_key
|
||||
OIKOS_SSH_USER: root
|
||||
volumes:
|
||||
- ${OIKOS_SSH_KEY_PATH:-~/.ssh/id_ed25519}:/etc/oikos/ssh_key:ro
|
||||
cap_add:
|
||||
|
||||
@@ -240,8 +240,8 @@ SELECT * FROM risk_classes ORDER BY name;
|
||||
SELECT * FROM approval_rules ORDER BY entity_type, action;
|
||||
|
||||
-- name: InsertMetricSample :exec
|
||||
INSERT INTO metric_samples (entity_id, metric, value, tags)
|
||||
VALUES ($1, $2, $3, $4);
|
||||
INSERT INTO metric_samples (entity_id, metric, value, tags, ts)
|
||||
VALUES ($1, $2, $3, $4, now());
|
||||
|
||||
-- name: QueryMetrics :many
|
||||
SELECT time_bucket(sqlc.arg('bucket_interval')::interval, ts) AS bucket,
|
||||
|
||||
@@ -560,8 +560,8 @@ func (q *Queries) InsertFeedback(ctx context.Context, arg InsertFeedbackParams)
|
||||
}
|
||||
|
||||
const insertMetricSample = `-- name: InsertMetricSample :exec
|
||||
INSERT INTO metric_samples (entity_id, metric, value, tags)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
INSERT INTO metric_samples (entity_id, metric, value, tags, ts)
|
||||
VALUES ($1, $2, $3, $4, now())
|
||||
`
|
||||
|
||||
type InsertMetricSampleParams struct {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
@@ -567,13 +568,13 @@ func checkSSHScript(ctx context.Context, cd sqlcgen.ListEnabledCheckDefsRow) che
|
||||
defer cancel()
|
||||
|
||||
scriptPath := "/opt/oikos/checks/" + cfg.Script
|
||||
addr := net.JoinHostPort(cfg.Host, strconv.Itoa(cfg.Port))
|
||||
port := strconv.Itoa(cfg.Port)
|
||||
|
||||
output, err := sshExec(ctx, addr, cfg.User, scriptPath, timeout)
|
||||
output, err := sshExec(ctx, cfg.Host, port, cfg.User, scriptPath, timeout)
|
||||
if err != nil {
|
||||
return checkResult{
|
||||
health: "down", signalKind: "ssh-script",
|
||||
evidence: fmt.Sprintf("ssh %s %s: %v", addr, cfg.Script, err),
|
||||
evidence: fmt.Sprintf("ssh %s:%s %s: %v", cfg.Host, strconv.Itoa(cfg.Port), cfg.Script, err),
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
@@ -617,19 +618,31 @@ func allowlistedScript(name string) bool {
|
||||
return scriptNameRe.MatchString(name)
|
||||
}
|
||||
|
||||
func sshExec(ctx context.Context, addr, user, cmd string, timeout time.Duration) ([]byte, error) {
|
||||
func sshExec(ctx context.Context, host, port, user, cmd string, timeout time.Duration) ([]byte, error) {
|
||||
args := []string{
|
||||
"-o", "ConnectTimeout=" + strconv.Itoa(int(timeout.Seconds())),
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-o", "BatchMode=yes",
|
||||
"-o", "UserKnownHostsFile=/dev/null",
|
||||
"-o", "LogLevel=ERROR",
|
||||
}
|
||||
if sshKeyPath != "" {
|
||||
args = append(args, "-i", sshKeyPath)
|
||||
}
|
||||
args = append(args, "-l", user, addr, cmd)
|
||||
if port != "" && port != "22" {
|
||||
args = append(args, "-p", port)
|
||||
}
|
||||
args = append(args, "-l", user, host, cmd)
|
||||
c := exec.CommandContext(ctx, "ssh", args...)
|
||||
return c.Output()
|
||||
out, err := c.Output()
|
||||
if err != nil {
|
||||
var ee *exec.ExitError
|
||||
if errors.As(err, &ee) {
|
||||
return nil, fmt.Errorf("ssh %s: %v (stderr: %s)", host, err, string(ee.Stderr))
|
||||
}
|
||||
return nil, fmt.Errorf("ssh %s: %v", host, err)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user