diff --git a/go.mod b/go.mod index 3d252975ea..68cfe90513 100644 --- a/go.mod +++ b/go.mod @@ -156,6 +156,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.4.0 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.7.0 // indirect github.com/aws/smithy-go v1.8.0 // indirect + github.com/beevik/ntp v0.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect github.com/c-bata/go-prompt v0.2.3 // indirect diff --git a/go.sum b/go.sum index db79838fce..10717a9c15 100644 --- a/go.sum +++ b/go.sum @@ -257,6 +257,8 @@ github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= +github.com/beevik/ntp v0.3.0 h1:xzVrPrE4ziasFXgBVBZJDP0Wg/KpMwk2KHJ4Ba8GrDw= +github.com/beevik/ntp v0.3.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= diff --git a/orbit/changes/9239-cis-2.3.2.2 b/orbit/changes/9239-cis-2.3.2.2 new file mode 100644 index 0000000000..01c341d215 --- /dev/null +++ b/orbit/changes/9239-cis-2.3.2.2 @@ -0,0 +1 @@ +* Add table implementation `sntp_request` to query NTP servers. \ No newline at end of file diff --git a/orbit/cmd/orbit/orbit.go b/orbit/cmd/orbit/orbit.go index 806f0906e2..60320432c4 100644 --- a/orbit/cmd/orbit/orbit.go +++ b/orbit/cmd/orbit/orbit.go @@ -710,6 +710,7 @@ func main() { desktopChannel: c.String("desktop-channel"), trw: trw, }), + table.WithExtension(sntpRequest{}), ) if c.Bool("fleet-desktop") { diff --git a/orbit/cmd/orbit/sntp_request.go b/orbit/cmd/orbit/sntp_request.go new file mode 100644 index 0000000000..96f3eb79ae --- /dev/null +++ b/orbit/cmd/orbit/sntp_request.go @@ -0,0 +1,62 @@ +package main + +import ( + "context" + "errors" + "strconv" + "time" + + "github.com/beevik/ntp" + orbit_table "github.com/fleetdm/fleet/v4/orbit/pkg/table" + "github.com/osquery/osquery-go/plugin/table" +) + +// sntpRequest allows querying SNTP servers to get the timestamp +// and clock offset from a NTP server (in millisecond precision). +type sntpRequest struct{} + +var _ orbit_table.Extension = sntpRequest{} + +// Name partially implements orbit_table.Extension. +func (t sntpRequest) Name() string { + return "sntp_request" +} + +// Columns partially implements orbit_table.Extension. +func (t sntpRequest) Columns() []table.ColumnDefinition { + return []table.ColumnDefinition{ + table.TextColumn("server"), + + table.BigIntColumn("timestamp_ms"), + table.BigIntColumn("clock_offset_ms"), + } +} + +// GenerateFunc partially implements orbit_table.Extension. +func (t sntpRequest) GenerateFunc(_ context.Context, queryContext table.QueryContext) ([]map[string]string, error) { + server := "" + if constraints, ok := queryContext.Constraints["server"]; ok { + for _, constraint := range constraints.Constraints { + if constraint.Operator == table.OperatorEquals { + server = constraint.Expression + } + } + } + if server == "" { + return nil, errors.New("missing SNTP server column constraint; e.g. WHERE server = 'my.sntp.server'") + } + + options := ntp.QueryOptions{ + Timeout: 30 * time.Second, + } + response, err := ntp.QueryWithOptions(server, options) + if err != nil { + return nil, err + } + return []map[string]string{{ + "server": server, + + "timestamp_ms": strconv.FormatInt(response.Time.UnixMilli(), 10), + "clock_offset_ms": strconv.FormatInt(response.ClockOffset.Milliseconds(), 10), + }}, nil +}