Skip to content

Commit 6cef3f6

Browse files
authored
Extension server (#1)
Basic implementation of the extension server, and example table plugin.
1 parent 9f45fcf commit 6cef3f6

File tree

9 files changed

+358
-10
lines changed

9 files changed

+358
-10
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
example_query
1+
# Example binaries
2+
example_*
23

4+
# Glide vendor directory
35
vendor/
46

57
## From https://github.com/github/gitignore/blob/master/Go.gitignore ##

Makefile

+8-2
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@ gen: ./osquery.thrift
77
rm -rf gen/osquery/extension-remote gen/osquery/extension_manager-remote
88
gofmt -w ./gen
99

10-
examples: example_query
10+
examples: example_query example_call example_table
1111

1212
example_query: examples/query/*.go
13-
go build -o example_query ./examples/query/main.go
13+
go build -o example_query ./examples/query/*.go
14+
15+
example_call: examples/call/*.go
16+
go build -o example_call ./examples/call/*.go
17+
18+
example_table: examples/table/*.go
19+
go build -o example_table ./examples/table/*.go
1420

1521
.PHONY: all

client/client.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ import (
1212

1313
// ExtensionManagerClient is a wrapper for the osquery Thrift extensions API.
1414
type ExtensionManagerClient struct {
15-
client *osquery.ExtensionManagerClient
15+
client osquery.ExtensionManager
1616
transport thrift.TTransport
1717
}
1818

1919
// NewClient creates a new client communicating to osquery over the socket at
20-
// the provided path. If resolving the address or the connection to the socket
20+
// the provided path. If resolving the address or connecting to the socket
2121
// fails, this function will error.
2222
func NewClient(sockPath string, timeout time.Duration) (*ExtensionManagerClient, error) {
2323
addr, err := net.ResolveUnixAddr("unix", sockPath)
@@ -49,11 +49,21 @@ func (c *ExtensionManagerClient) Ping() (*osquery.ExtensionStatus, error) {
4949
return c.client.Ping()
5050
}
5151

52+
// Call requests a call to an extension (or core) registry plugin.
53+
func (c *ExtensionManagerClient) Call(registry, item string, request osquery.ExtensionPluginRequest) (*osquery.ExtensionResponse, error) {
54+
return c.client.Call(registry, item, request)
55+
}
56+
5257
// Extensions requests the list of active registered extensions.
5358
func (c *ExtensionManagerClient) Extensions() (osquery.InternalExtensionList, error) {
5459
return c.client.Extensions()
5560
}
5661

62+
// RegisterExtension registers the extension plugins with the osquery process.
63+
func (c *ExtensionManagerClient) RegisterExtension(info *osquery.InternalExtensionInfo, registry osquery.ExtensionRegistry) (*osquery.ExtensionStatus, error) {
64+
return c.client.RegisterExtension(info, registry)
65+
}
66+
5767
// Options requests the list of bootstrap or configuration options.
5868
func (c *ExtensionManagerClient) Options() (osquery.InternalOptionList, error) {
5969
return c.client.Options()

examples/call/main.go

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"time"
7+
8+
"github.com/kolide/osquery-golang/client"
9+
)
10+
11+
func main() {
12+
if len(os.Args) != 3 {
13+
fmt.Printf(`Usage: %s SOCKET_PATH TABLE_NAME
14+
15+
Retrieves the columns for the given table by making a call to the plugin.
16+
`, os.Args[0])
17+
os.Exit(1)
18+
}
19+
20+
client, err := client.NewClient(os.Args[1], 10*time.Second)
21+
if err != nil {
22+
fmt.Println("Error creating Thrift client: " + err.Error())
23+
os.Exit(1)
24+
}
25+
defer client.Close()
26+
27+
resp, err := client.Call("table", os.Args[2], map[string]string{"action": "columns"})
28+
if err != nil {
29+
fmt.Println("Error communicating with osqueryd: " + err.Error())
30+
os.Exit(1)
31+
}
32+
if resp.Status.Code != 0 {
33+
fmt.Println("osqueryd returned error: " + resp.Status.Message)
34+
os.Exit(1)
35+
}
36+
37+
fmt.Printf("Got results:\n%#v\n", resp.Response)
38+
}

examples/query/main.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ import (
1010

1111
func main() {
1212
if len(os.Args) != 3 {
13-
fmt.Printf("Usage: %s SOCKET_PATH QUERY\n", os.Args[0])
13+
fmt.Printf(`Usage: %s SOCKET_PATH QUERY\n
14+
15+
Requests osqueryd to run the provided query and prints the results.
16+
`, os.Args[0])
1417
os.Exit(1)
1518
}
1619

examples/table/main.go

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"os/signal"
8+
"syscall"
9+
"time"
10+
11+
"github.com/kolide/osquery-golang/gen/osquery"
12+
"github.com/kolide/osquery-golang/server"
13+
)
14+
15+
func main() {
16+
if len(os.Args) != 2 {
17+
fmt.Printf(`Usage: %s SOCKET_PATH\n
18+
19+
Registers an example table extension.
20+
`, os.Args[0])
21+
os.Exit(1)
22+
}
23+
24+
serv, err := server.NewExtensionManagerServer("foobar", os.Args[1], 1*time.Second)
25+
if err != nil {
26+
fmt.Printf("Error creating extension: %v\n", err)
27+
os.Exit(1)
28+
}
29+
serv.RegisterPlugin(&FooTable{})
30+
31+
// Shut down server when process killed so that we don't leave the unix
32+
// domain socket file on the filesystem.
33+
sig := make(chan os.Signal, 1)
34+
signal.Notify(sig, os.Interrupt, os.Kill, syscall.SIGTERM)
35+
go func() {
36+
<-sig
37+
fmt.Println("Stopping extension server.")
38+
err := serv.Shutdown()
39+
if err != nil {
40+
fmt.Println("Error shutting down server: " + err.Error())
41+
os.Exit(1)
42+
}
43+
os.Exit(0)
44+
}()
45+
46+
fmt.Println("Starting extension server...")
47+
err = serv.Start()
48+
if err != nil {
49+
fmt.Println("Error starting server: " + err.Error())
50+
os.Exit(1)
51+
}
52+
}
53+
54+
type FooTable struct{}
55+
56+
func (f *FooTable) Name() string {
57+
return "FooTable"
58+
}
59+
60+
func (f *FooTable) RegistryName() string {
61+
return "table"
62+
}
63+
64+
func (f *FooTable) Routes() osquery.ExtensionPluginResponse {
65+
return []map[string]string{
66+
{"id": "column", "name": "foo", "type": "TEXT", "op": "0"},
67+
{"id": "column", "name": "bar", "type": "TEXT", "op": "0"},
68+
}
69+
}
70+
71+
func (f *FooTable) Ping() osquery.ExtensionStatus {
72+
return osquery.ExtensionStatus{Code: 0, Message: "OK"}
73+
}
74+
75+
func (f *FooTable) Call(ctx context.Context, request osquery.ExtensionPluginRequest) osquery.ExtensionResponse {
76+
switch request["action"] {
77+
case "generate":
78+
return osquery.ExtensionResponse{
79+
Status: &osquery.ExtensionStatus{Code: 0, Message: "OK"},
80+
Response: osquery.ExtensionPluginResponse{
81+
{"foo": "hello", "bar": "world"},
82+
{"foo": "some", "bar": "thing"},
83+
},
84+
}
85+
default:
86+
return osquery.ExtensionResponse{
87+
Status: &osquery.ExtensionStatus{Code: 0, Message: "OK"},
88+
Response: f.Routes(),
89+
}
90+
}
91+
}
92+
93+
func (f *FooTable) Shutdown() {
94+
}

glide.lock

+4-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

glide.yaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package: github.com/kolide/osquery-golang
22
import:
33
- package: git.apache.org/thrift.git
4-
version: ^0.10.0
4+
repo: [email protected]:kolide/thrift.git
5+
version: server_socket_from_addr
56
subpackages:
67
- lib/go/thrift
78
- package: github.com/pkg/errors

0 commit comments

Comments
 (0)