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_DEBUG: "true"
|
||||||
OIKOS_SCHEDULER_INTERVAL: "30s"
|
OIKOS_SCHEDULER_INTERVAL: "30s"
|
||||||
OIKOS_SSH_KEY_PATH: /etc/oikos/ssh_key
|
OIKOS_SSH_KEY_PATH: /etc/oikos/ssh_key
|
||||||
|
OIKOS_SSH_USER: root
|
||||||
volumes:
|
volumes:
|
||||||
- ${OIKOS_SSH_KEY_PATH:-~/.ssh/id_ed25519}:/etc/oikos/ssh_key:ro
|
- ${OIKOS_SSH_KEY_PATH:-~/.ssh/id_ed25519}:/etc/oikos/ssh_key:ro
|
||||||
cap_add:
|
cap_add:
|
||||||
|
|||||||
@@ -240,8 +240,8 @@ SELECT * FROM risk_classes ORDER BY name;
|
|||||||
SELECT * FROM approval_rules ORDER BY entity_type, action;
|
SELECT * FROM approval_rules ORDER BY entity_type, action;
|
||||||
|
|
||||||
-- name: InsertMetricSample :exec
|
-- name: InsertMetricSample :exec
|
||||||
INSERT INTO metric_samples (entity_id, metric, value, tags)
|
INSERT INTO metric_samples (entity_id, metric, value, tags, ts)
|
||||||
VALUES ($1, $2, $3, $4);
|
VALUES ($1, $2, $3, $4, now());
|
||||||
|
|
||||||
-- name: QueryMetrics :many
|
-- name: QueryMetrics :many
|
||||||
SELECT time_bucket(sqlc.arg('bucket_interval')::interval, ts) AS bucket,
|
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
|
const insertMetricSample = `-- name: InsertMetricSample :exec
|
||||||
INSERT INTO metric_samples (entity_id, metric, value, tags)
|
INSERT INTO metric_samples (entity_id, metric, value, tags, ts)
|
||||||
VALUES ($1, $2, $3, $4)
|
VALUES ($1, $2, $3, $4, now())
|
||||||
`
|
`
|
||||||
|
|
||||||
type InsertMetricSampleParams struct {
|
type InsertMetricSampleParams struct {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
@@ -567,13 +568,13 @@ func checkSSHScript(ctx context.Context, cd sqlcgen.ListEnabledCheckDefsRow) che
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
scriptPath := "/opt/oikos/checks/" + cfg.Script
|
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 {
|
if err != nil {
|
||||||
return checkResult{
|
return checkResult{
|
||||||
health: "down", signalKind: "ssh-script",
|
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,
|
err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -617,19 +618,31 @@ func allowlistedScript(name string) bool {
|
|||||||
return scriptNameRe.MatchString(name)
|
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{
|
args := []string{
|
||||||
"-o", "ConnectTimeout=" + strconv.Itoa(int(timeout.Seconds())),
|
"-o", "ConnectTimeout=" + strconv.Itoa(int(timeout.Seconds())),
|
||||||
"-o", "StrictHostKeyChecking=no",
|
"-o", "StrictHostKeyChecking=no",
|
||||||
"-o", "BatchMode=yes",
|
"-o", "BatchMode=yes",
|
||||||
"-o", "UserKnownHostsFile=/dev/null",
|
"-o", "UserKnownHostsFile=/dev/null",
|
||||||
|
"-o", "LogLevel=ERROR",
|
||||||
}
|
}
|
||||||
if sshKeyPath != "" {
|
if sshKeyPath != "" {
|
||||||
args = append(args, "-i", 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...)
|
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