Skip to content

Adding support to osctrl-cli to look up nodes via osctrl-api #658

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/api/handlers/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,8 @@ func (h *HandlersApi) LookupNodeHandler(w http.ResponseWriter, r *http.Request)
} else {
apiErrorResponse(w, "error getting node", http.StatusInternalServerError, err)
}
return
}
// Serialize and serve JSON
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, _nodeToApiLookupResponse(n))
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, n)
}
18 changes: 0 additions & 18 deletions cmd/api/handlers/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"net/http"

"github.com/jmpsec/osctrl/pkg/logging"
"github.com/jmpsec/osctrl/pkg/nodes"
"github.com/jmpsec/osctrl/pkg/types"
"github.com/jmpsec/osctrl/pkg/utils"
"github.com/rs/zerolog/log"
Expand Down Expand Up @@ -54,20 +53,3 @@ func checkValidPlatform(platforms []string, platform string) bool {
}
return false
}

// Helper to convert a node into a ApiLookupResponse
func _nodeToApiLookupResponse(node nodes.OsqueryNode) types.ApiLookupResponse {
return types.ApiLookupResponse{
UUID: node.UUID,
Platform: node.Platform,
PlatformVersion: node.PlatformVersion,
OsqueryVersion: node.OsqueryVersion,
Hostname: node.Hostname,
Localname: node.Localname,
IPAddress: node.IPAddress,
Username: node.Username,
Environment: node.Environment,
HardwareSerial: node.HardwareSerial,
LastSeen: node.LastSeen.String(),
}
}
23 changes: 12 additions & 11 deletions cmd/cli/api-carve.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package main

import (
"bytes"
"encoding/json"
"fmt"
"strings"
"path"

"github.com/jmpsec/osctrl/pkg/carves"
"github.com/jmpsec/osctrl/pkg/queries"
Expand All @@ -15,7 +16,7 @@ import (
// GetCarveQueries to retrieve carves from osctrl
func (api *OsctrlAPI) GetCarveQueries(target, env string) ([]queries.DistributedQuery, error) {
var qs []queries.DistributedQuery
reqURL := fmt.Sprintf("%s%s%s/%s/queries/%s", api.Configuration.URL, APIPath, APICarves, env, target)
reqURL := path.Join(api.Configuration.URL, APIPath, APICarves, env, "queries", target)
rawCs, err := api.GetGeneric(reqURL, nil)
if err != nil {
return qs, fmt.Errorf("error api request - %w - %s", err, string(rawCs))
Expand All @@ -29,7 +30,7 @@ func (api *OsctrlAPI) GetCarveQueries(target, env string) ([]queries.Distributed
// GetCarves to retrieve carves from osctrl
func (api *OsctrlAPI) GetCarves(env string) ([]carves.CarvedFile, error) {
var cs []carves.CarvedFile
reqURL := fmt.Sprintf("%s%s%s/%s/list", api.Configuration.URL, APIPath, APICarves, env)
reqURL := path.Join(api.Configuration.URL, APIPath, APICarves, env, "list")
rawCs, err := api.GetGeneric(reqURL, nil)
if err != nil {
return cs, fmt.Errorf("error api request - %w - %s", err, string(rawCs))
Expand All @@ -43,7 +44,7 @@ func (api *OsctrlAPI) GetCarves(env string) ([]carves.CarvedFile, error) {
// GetCarve to retrieve one carve from osctrl
func (api *OsctrlAPI) GetCarve(env, name string) (carves.CarvedFile, error) {
var c carves.CarvedFile
reqURL := fmt.Sprintf("%s%s%s/%s/%s", api.Configuration.URL, APIPath, APICarves, env, name)
reqURL := path.Join(api.Configuration.URL, APIPath, APICarves, env, name)
rawC, err := api.GetGeneric(reqURL, nil)
if err != nil {
return c, fmt.Errorf("error api request - %w - %s", err, string(rawC))
Expand All @@ -57,7 +58,7 @@ func (api *OsctrlAPI) GetCarve(env, name string) (carves.CarvedFile, error) {
// DeleteCarve to delete carve from osctrl
func (api *OsctrlAPI) DeleteCarve(env, name string) (types.ApiGenericResponse, error) {
var r types.ApiGenericResponse
reqURL := fmt.Sprintf("%s%s%s/%s/%s/%s", api.Configuration.URL, APIPath, APICarves, env, settings.CarveDelete, name)
reqURL := path.Join(api.Configuration.URL, APIPath, APICarves, env, settings.CarveDelete, name)
rawQ, err := api.PostGeneric(reqURL, nil)
if err != nil {
return r, fmt.Errorf("error api request - %w - %s", err, string(rawQ))
Expand All @@ -71,7 +72,7 @@ func (api *OsctrlAPI) DeleteCarve(env, name string) (types.ApiGenericResponse, e
// ExpireCarve to expire carve from osctrl
func (api *OsctrlAPI) ExpireCarve(env, name string) (types.ApiGenericResponse, error) {
var r types.ApiGenericResponse
reqURL := fmt.Sprintf("%s%s%s/%s/%s/%s", api.Configuration.URL, APIPath, APICarves, env, settings.QueryExpire, name)
reqURL := path.Join(api.Configuration.URL, APIPath, APICarves, env, settings.QueryExpire, name)
rawQ, err := api.PostGeneric(reqURL, nil)
if err != nil {
return r, fmt.Errorf("error api request - %w - %s", err, string(rawQ))
Expand All @@ -85,7 +86,7 @@ func (api *OsctrlAPI) ExpireCarve(env, name string) (types.ApiGenericResponse, e
// CompleteCarve to complete a carve from osctrl
func (api *OsctrlAPI) CompleteCarve(env, name string) (types.ApiGenericResponse, error) {
var r types.ApiGenericResponse
reqURL := fmt.Sprintf("%s%s%s/%s/%s/%s", api.Configuration.URL, APIPath, APICarves, env, settings.CarveComplete, name)
reqURL := path.Join(api.Configuration.URL, APIPath, APICarves, env, settings.CarveComplete, name)
rawQ, err := api.PostGeneric(reqURL, nil)
if err != nil {
return r, fmt.Errorf("error api request - %w - %s", err, string(rawQ))
Expand All @@ -97,19 +98,19 @@ func (api *OsctrlAPI) CompleteCarve(env, name string) (types.ApiGenericResponse,
}

// RunCarve to initiate a carve in osctrl
func (api *OsctrlAPI) RunCarve(env, uuid, path string, exp int) (types.ApiQueriesResponse, error) {
func (api *OsctrlAPI) RunCarve(env, uuid, fPath string, exp int) (types.ApiQueriesResponse, error) {
c := types.ApiDistributedCarveRequest{
UUID: uuid,
Path: path,
Path: fPath,
ExpHours: exp,
}
var r types.ApiQueriesResponse
reqURL := fmt.Sprintf("%s%s%s/%s", api.Configuration.URL, APIPath, APICarves, env)
reqURL := path.Join(api.Configuration.URL, APIPath, APICarves, env)
jsonMessage, err := json.Marshal(c)
if err != nil {
log.Err(err).Msg("error marshaling data")
}
jsonParam := strings.NewReader(string(jsonMessage))
jsonParam := bytes.NewReader(jsonMessage)
rawC, err := api.PostGeneric(reqURL, jsonParam)
if err != nil {
return r, fmt.Errorf("error api request - %w - %s", err, string(rawC))
Expand Down
9 changes: 5 additions & 4 deletions cmd/cli/api-environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"io"
"path"

"github.com/jmpsec/osctrl/pkg/environments"
"github.com/jmpsec/osctrl/pkg/settings"
Expand All @@ -13,7 +14,7 @@ import (
// GetEnvironments to retrieve all environments from osctrl
func (api *OsctrlAPI) GetEnvironments() ([]environments.TLSEnvironment, error) {
var envs []environments.TLSEnvironment
reqURL := fmt.Sprintf("%s%s%s", api.Configuration.URL, APIPath, APIEnvironments)
reqURL := path.Join("%s%s%s", api.Configuration.URL, APIPath, APIEnvironments)
rawEnvs, err := api.GetGeneric(reqURL, nil)
if err != nil {
return envs, fmt.Errorf("error api request - %w - %s", err, string(rawEnvs))
Expand All @@ -27,7 +28,7 @@ func (api *OsctrlAPI) GetEnvironments() ([]environments.TLSEnvironment, error) {
// GetEnvironment to retrieve users from osctrl
func (api *OsctrlAPI) GetEnvironment(identifier string) (environments.TLSEnvironment, error) {
var e environments.TLSEnvironment
reqURL := fmt.Sprintf("%s%s%s/%s", api.Configuration.URL, APIPath, APIEnvironments, identifier)
reqURL := path.Join("%s%s%s/%s", api.Configuration.URL, APIPath, APIEnvironments, identifier)
rawE, err := api.GetGeneric(reqURL, nil)
if err != nil {
return e, fmt.Errorf("error api request - %w - %s", err, string(rawE))
Expand All @@ -41,7 +42,7 @@ func (api *OsctrlAPI) GetEnvironment(identifier string) (environments.TLSEnviron
// GetEnvMap to retrieve a map of environments by ID
func (api *OsctrlAPI) GetEnvMap() (environments.MapEnvByID, error) {
var envMap environments.MapEnvByID
reqURL := fmt.Sprintf("%s%s%s/map/id", api.Configuration.URL, APIPath, APIEnvironments)
reqURL := path.Join(api.Configuration.URL, APIPath, APIEnvironments, "map", "id")
rawE, err := api.GetGeneric(reqURL, nil)
if err != nil {
return envMap, fmt.Errorf("error api request - %w - %s", err, string(rawE))
Expand Down Expand Up @@ -95,7 +96,7 @@ func (api *OsctrlAPI) NotexpireRemove(identifier string) (string, error) {
// ExtendEnrollment to extend in time the enrollment URL of an environment
func (api *OsctrlAPI) ActionEnrollmentRemove(identifier, action, target string, data io.Reader) (string, error) {
var res types.ApiGenericResponse
reqURL := fmt.Sprintf("%s%s%s/%s/%s/%s", api.Configuration.URL, APIPath, APIEnvironments, identifier, target, action)
reqURL := path.Join(api.Configuration.URL, APIPath, APIEnvironments, identifier, target, action)
rawE, err := api.PostGeneric(reqURL, data)
if err != nil {
return "", fmt.Errorf("error api request - %w - %s", err, string(rawE))
Expand Down
7 changes: 4 additions & 3 deletions cmd/cli/api-login.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package main

import (
"bytes"
"encoding/json"
"fmt"
"strings"
"path"

"github.com/jmpsec/osctrl/pkg/types"
)
Expand All @@ -20,8 +21,8 @@ func (api *OsctrlAPI) PostLogin(env, username, password string, expHours int) (t
if err != nil {
return res, fmt.Errorf("error marshaling data %w", err)
}
jsonParam := strings.NewReader(string(jsonMessage))
reqURL := fmt.Sprintf("%s%s%s/%s", api.Configuration.URL, APIPath, APILogin, env)
jsonParam := bytes.NewReader(jsonMessage)
reqURL := path.Join(api.Configuration.URL, APIPath, APILogin, env)
rawRes, err := api.PostGeneric(reqURL, jsonParam)
if err != nil {
return res, fmt.Errorf("error api request - %w - %s", err, string(rawRes))
Expand Down
33 changes: 28 additions & 5 deletions cmd/cli/api-node.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package main

import (
"bytes"
"encoding/json"
"fmt"
"strings"
"path"

"github.com/jmpsec/osctrl/pkg/nodes"
"github.com/jmpsec/osctrl/pkg/types"
Expand All @@ -12,7 +13,7 @@ import (
// GetNodes to retrieve nodes from osctrl
func (api *OsctrlAPI) GetNodes(env, target string) ([]nodes.OsqueryNode, error) {
var nds []nodes.OsqueryNode
reqURL := fmt.Sprintf("%s%s%s/%s/%s", api.Configuration.URL, APIPath, APINodes, env, target)
reqURL := path.Join(api.Configuration.URL, APIPath, APINodes, env, target)
rawNodes, err := api.GetGeneric(reqURL, nil)
if err != nil {
return nds, fmt.Errorf("error api request - %w - %s", err, string(rawNodes))
Expand All @@ -26,7 +27,7 @@ func (api *OsctrlAPI) GetNodes(env, target string) ([]nodes.OsqueryNode, error)
// GetNode to retrieve one node from osctrl
func (api *OsctrlAPI) GetNode(env, identifier string) (nodes.OsqueryNode, error) {
var node nodes.OsqueryNode
reqURL := fmt.Sprintf("%s%s%s/%s/node/%s", api.Configuration.URL, APIPath, APINodes, env, identifier)
reqURL := path.Join(api.Configuration.URL, APIPath, APINodes, env, "node", identifier)
rawNode, err := api.GetGeneric(reqURL, nil)
if err != nil {
return node, fmt.Errorf("error api request - %w - %s", err, string(rawNode))
Expand All @@ -43,12 +44,12 @@ func (api *OsctrlAPI) DeleteNode(env, identifier string) error {
UUID: identifier,
}
var r types.ApiGenericResponse
reqURL := fmt.Sprintf("%s%s%s/%s/delete", api.Configuration.URL, APIPath, APINodes, env)
reqURL := path.Join(api.Configuration.URL, APIPath, APINodes, env, "delete")
jsonMessage, err := json.Marshal(n)
if err != nil {
return fmt.Errorf("error marshaling data - %w", err)
}
jsonParam := strings.NewReader(string(jsonMessage))
jsonParam := bytes.NewReader(jsonMessage)
rawN, err := api.PostGeneric(reqURL, jsonParam)
if err != nil {
return fmt.Errorf("error api request - %w - %s", err, string(rawN))
Expand All @@ -63,3 +64,25 @@ func (api *OsctrlAPI) DeleteNode(env, identifier string) error {
func (api *OsctrlAPI) TagNode(env, identifier, tag string) error {
return nil
}

// LookupNode to look up node from osctrl by identifier (UUID, localname or hostname)
func (api *OsctrlAPI) LookupNode(identifier string) (nodes.OsqueryNode, error) {
var node nodes.OsqueryNode
l := types.ApiLookupRequest{
Identifier: identifier,
}
jsonMessage, err := json.Marshal(l)
if err != nil {
return node, fmt.Errorf("error marshaling data %w", err)
}
jsonParam := bytes.NewReader(jsonMessage)
reqURL := path.Join(api.Configuration.URL, APIPath, APINodes, "lookup")
rawNode, err := api.PostGeneric(reqURL, jsonParam)
if err != nil {
return node, fmt.Errorf("error api request - %w - %s", err, string(rawNode))
}
if err := json.Unmarshal(rawNode, &node); err != nil {
return node, fmt.Errorf("can not parse body - %w", err)
}
return node, nil
}
17 changes: 9 additions & 8 deletions cmd/cli/api-query.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package main

import (
"bytes"
"encoding/json"
"fmt"
"strings"
"path"

"github.com/jmpsec/osctrl/pkg/queries"
"github.com/jmpsec/osctrl/pkg/settings"
Expand All @@ -13,7 +14,7 @@ import (
// GetQueries to retrieve queries from osctrl
func (api *OsctrlAPI) GetQueries(target, env string) ([]queries.DistributedQuery, error) {
var qs []queries.DistributedQuery
reqURL := fmt.Sprintf("%s%s%s/%s/list/%s", api.Configuration.URL, APIPath, APIQueries, env, target)
reqURL := path.Join(api.Configuration.URL, APIPath, APIQueries, env, "list", target)
rawQs, err := api.GetGeneric(reqURL, nil)
if err != nil {
return qs, fmt.Errorf("error api request - %w - %s", err, string(rawQs))
Expand All @@ -27,7 +28,7 @@ func (api *OsctrlAPI) GetQueries(target, env string) ([]queries.DistributedQuery
// GetQuery to retrieve one query from osctrl
func (api *OsctrlAPI) GetQuery(env, name string) (queries.DistributedQuery, error) {
var q queries.DistributedQuery
reqURL := fmt.Sprintf("%s%s%s/%s/%s", api.Configuration.URL, APIPath, APIQueries, env, name)
reqURL := path.Join(api.Configuration.URL, APIPath, APIQueries, env, name)
rawQ, err := api.GetGeneric(reqURL, nil)
if err != nil {
return q, fmt.Errorf("error api request - %w - %s", err, string(rawQ))
Expand All @@ -41,7 +42,7 @@ func (api *OsctrlAPI) GetQuery(env, name string) (queries.DistributedQuery, erro
// DeleteQuery to delete query from osctrl
func (api *OsctrlAPI) DeleteQuery(env, name string) (types.ApiGenericResponse, error) {
var r types.ApiGenericResponse
reqURL := fmt.Sprintf("%s%s%s/%s/%s/%s", api.Configuration.URL, APIPath, APIQueries, env, settings.QueryDelete, name)
reqURL := path.Join(api.Configuration.URL, APIPath, APIQueries, env, settings.QueryDelete, name)
rawQ, err := api.PostGeneric(reqURL, nil)
if err != nil {
return r, fmt.Errorf("error api request - %w - %s", err, string(rawQ))
Expand All @@ -55,7 +56,7 @@ func (api *OsctrlAPI) DeleteQuery(env, name string) (types.ApiGenericResponse, e
// ExpireQuery to expire query from osctrl
func (api *OsctrlAPI) ExpireQuery(env, name string) (types.ApiGenericResponse, error) {
var r types.ApiGenericResponse
reqURL := fmt.Sprintf("%s%s%s/%s/%s/%s", api.Configuration.URL, APIPath, APIQueries, env, settings.QueryExpire, name)
reqURL := path.Join(api.Configuration.URL, APIPath, APIQueries, env, settings.QueryExpire, name)
rawQ, err := api.PostGeneric(reqURL, nil)
if err != nil {
return r, fmt.Errorf("error api request - %w - %s", err, string(rawQ))
Expand All @@ -69,7 +70,7 @@ func (api *OsctrlAPI) ExpireQuery(env, name string) (types.ApiGenericResponse, e
// CompleteQuery to complete a query from osctrl
func (api *OsctrlAPI) CompleteQuery(env, name string) (types.ApiGenericResponse, error) {
var r types.ApiGenericResponse
reqURL := fmt.Sprintf("%s%s%s/%s/%s/%s", api.Configuration.URL, APIPath, APIQueries, env, settings.QueryComplete, name)
reqURL := path.Join(api.Configuration.URL, APIPath, APIQueries, env, settings.QueryComplete, name)
rawQ, err := api.PostGeneric(reqURL, nil)
if err != nil {
return r, fmt.Errorf("error api request - %w - %s", err, string(rawQ))
Expand All @@ -89,13 +90,13 @@ func (api *OsctrlAPI) RunQuery(env, uuid, query string, hidden bool, exp int) (t
ExpHours: exp,
}
var r types.ApiQueriesResponse
reqURL := fmt.Sprintf("%s%s%s/%s", api.Configuration.URL, APIPath, APIQueries, env)
reqURL := path.Join(api.Configuration.URL, APIPath, APIQueries, env)
jsonMessage, err := json.Marshal(q)
if err != nil {
return r, fmt.Errorf("error marshaling data - %w", err)

}
jsonParam := strings.NewReader(string(jsonMessage))
jsonParam := bytes.NewReader(jsonMessage)
rawQ, err := api.PostGeneric(reqURL, jsonParam)
if err != nil {
return r, fmt.Errorf("error api request - %w - %s", err, string(rawQ))
Expand Down
Loading
Loading