Skip to content

Commit ac84a75

Browse files
committed
Osquerybeat: Improve handling of osquery.autoload file, allow customizations
Previously the osquery.autoload file was overwritten every time on osquerybeat start and stamped with our extension. After the change we check the content of the file and do not overwrite it on each osquerybeat start. This allows the user to deploy their own extensions if their want and start osquery with that.
1 parent cca59ba commit ac84a75

2 files changed

Lines changed: 185 additions & 2 deletions

File tree

x-pack/osquerybeat/internal/osqd/osqueryd.go

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package osqd
66

77
import (
8+
"bufio"
89
"context"
910
"fmt"
1011
"io"
@@ -263,8 +264,9 @@ func (q *OSQueryD) prepare(ctx context.Context) (func(), error) {
263264

264265
// Write the autoload file
265266
extensionAutoloadPath := q.resolveDataPath(osqueryAutoload)
266-
if err := ioutil.WriteFile(extensionAutoloadPath, []byte(extensionPath), 0644); err != nil {
267-
return nil, errors.Wrap(err, "failed write osquery extension autoload file")
267+
err = prepareAutoloadFile(extensionAutoloadPath, extensionPath, q.log)
268+
if err != nil {
269+
return nil, errors.Wrapf(err, "failed to prepare extensions autoload file")
268270
}
269271

270272
// Write the flagsfile in order to lock down/prevent loading default flags from osquery global locations.
@@ -286,6 +288,60 @@ func (q *OSQueryD) prepare(ctx context.Context) (func(), error) {
286288
return func() {}, nil
287289
}
288290

291+
func prepareAutoloadFile(extensionAutoloadPath, mandatoryExtensionPath string, log *logp.Logger) error {
292+
ok, err := fileutil.FileExists(extensionAutoloadPath)
293+
if err != nil {
294+
return errors.Wrapf(err, "failed to check osquery.autoload file exists")
295+
}
296+
297+
rewrite := false
298+
299+
if ok {
300+
log.Debugf("Extensions autoload file %s exists, verify the first extension is ours", extensionAutoloadPath)
301+
err = verifyAutoloadFile(extensionAutoloadPath, mandatoryExtensionPath)
302+
if err != nil {
303+
log.Debugf("Extensions autoload file %v verification failed, err: %v, create a new one", extensionAutoloadPath, err)
304+
rewrite = true
305+
}
306+
} else {
307+
log.Debugf("Extensions autoload file %s doesn't exists, create a new one", extensionAutoloadPath)
308+
rewrite = true
309+
}
310+
311+
if rewrite {
312+
if err := ioutil.WriteFile(extensionAutoloadPath, []byte(mandatoryExtensionPath), 0644); err != nil {
313+
return errors.Wrap(err, "failed write osquery extension autoload file")
314+
}
315+
}
316+
return nil
317+
}
318+
319+
func verifyAutoloadFile(extensionAutoloadPath, mandatoryExtensionPath string) error {
320+
f, err := os.Open(extensionAutoloadPath)
321+
if err != nil {
322+
return err
323+
}
324+
defer f.Close()
325+
scanner := bufio.NewScanner(f)
326+
for i := 0; scanner.Scan(); i++ {
327+
line := scanner.Text()
328+
if i == 0 {
329+
// Check that the first line is the mandatory extension
330+
if line != mandatoryExtensionPath {
331+
return errors.New("extentsions autoload file is missing mandatory extension in the first line of the file")
332+
}
333+
}
334+
335+
// Check that the line contains the valid path that exists
336+
_, err := os.Stat(line)
337+
if err != nil {
338+
return err
339+
}
340+
}
341+
342+
return scanner.Err()
343+
}
344+
289345
func (q *OSQueryD) prepareBinPath() error {
290346
// If path to osquery was not set use the current executable path
291347
if q.binPath == "" {

x-pack/osquerybeat/internal/osqd/osqueryd_test.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,18 @@
55
package osqd
66

77
import (
8+
"bufio"
9+
"errors"
10+
"io/ioutil"
11+
"os"
12+
"path/filepath"
813
"testing"
914

15+
"github.com/elastic/beats/v7/libbeat/common"
16+
"github.com/elastic/beats/v7/libbeat/logp"
17+
"github.com/elastic/beats/v7/x-pack/osquerybeat/internal/fileutil"
18+
19+
"github.com/gofrs/uuid"
1020
"github.com/google/go-cmp/cmp"
1121
)
1222

@@ -46,3 +56,120 @@ func TestNew(t *testing.T) {
4656
t.Error(diff)
4757
}
4858
}
59+
60+
func TestVerifyAutoloadFileMissing(t *testing.T) {
61+
dir := uuid.Must(uuid.NewV4()).String()
62+
extensionAutoloadPath := filepath.Join(dir, osqueryAutoload)
63+
mandatoryExtensionPath := filepath.Join(dir, extensionName)
64+
err := verifyAutoloadFile(extensionAutoloadPath, mandatoryExtensionPath)
65+
if !errors.Is(err, os.ErrNotExist) {
66+
t.Fatalf("expected error: %v, got: %v", os.ErrNotExist, err)
67+
}
68+
}
69+
70+
// TestPrepareAutoloadFile tests possibly different states of the osquery.autoload file and that it is restored into the workable state
71+
func TestPrepareAutoloadFile(t *testing.T) {
72+
validLogger := logp.NewLogger("osqueryd_test")
73+
74+
// Prepare the directory with extension
75+
dir, err := os.MkdirTemp("", "")
76+
if err != nil {
77+
t.Fatal(err)
78+
}
79+
defer os.RemoveAll(dir)
80+
mandatoryExtensionPath := filepath.Join(dir, extensionName)
81+
82+
// Write fake extension file for testing
83+
err = ioutil.WriteFile(mandatoryExtensionPath, nil, 0644)
84+
if err != nil {
85+
t.Fatal(err)
86+
}
87+
88+
randomContent := func(sz int) []byte {
89+
b, err := common.RandomBytes(sz)
90+
if err != nil {
91+
t.Fatal(err)
92+
}
93+
return b
94+
}
95+
96+
tests := []struct {
97+
Name string
98+
FileContent []byte
99+
}{
100+
{
101+
Name: "Empty file",
102+
FileContent: nil,
103+
},
104+
{
105+
Name: "File with mandatory extension",
106+
FileContent: []byte(mandatoryExtensionPath),
107+
},
108+
{
109+
Name: "Missing mandatory extension, should restore the file",
110+
FileContent: []byte(filepath.Join(dir, "foobar.ext")),
111+
},
112+
{
113+
Name: "User extension path doesn't exists",
114+
FileContent: []byte(mandatoryExtensionPath + "\n" + filepath.Join(dir, "foobar.ext")),
115+
},
116+
{
117+
Name: "Random garbage",
118+
FileContent: randomContent(1234),
119+
},
120+
}
121+
122+
for _, tc := range tests {
123+
t.Run(tc.Name, func(t *testing.T) {
124+
125+
// Setup
126+
dir, err := os.MkdirTemp("", "")
127+
if err != nil {
128+
t.Fatal(err)
129+
}
130+
131+
defer os.RemoveAll(dir)
132+
133+
extensionAutoloadPath := filepath.Join(dir, osqueryAutoload)
134+
135+
err = ioutil.WriteFile(extensionAutoloadPath, tc.FileContent, 0644)
136+
if err != nil {
137+
t.Fatal(err)
138+
}
139+
140+
err = prepareAutoloadFile(extensionAutoloadPath, mandatoryExtensionPath, validLogger)
141+
if err != nil {
142+
t.Fatal(err)
143+
}
144+
145+
// Check the content, should have our mandatory extension and possibly the other extension paths with each extension existing on the disk
146+
f, err := os.Open(extensionAutoloadPath)
147+
if err != nil {
148+
t.Fatal(err)
149+
}
150+
defer f.Close()
151+
scanner := bufio.NewScanner(f)
152+
for i := 0; scanner.Scan(); i++ {
153+
line := scanner.Text()
154+
if i == 0 {
155+
if line != mandatoryExtensionPath {
156+
t.Fatalf("expected the fist line of the file to be: %v , got: %v", mandatoryExtensionPath, line)
157+
}
158+
}
159+
// Check that it is a valid path to the file on the disk
160+
ok, err := fileutil.FileExists(line)
161+
if err != nil {
162+
t.Fatal(err)
163+
}
164+
if !ok {
165+
t.Fatalf("expected to have only valid paths to the extensions files that exists, got: %v", line)
166+
}
167+
}
168+
169+
err = scanner.Err()
170+
if err != nil {
171+
t.Fatal(err)
172+
}
173+
})
174+
}
175+
}

0 commit comments

Comments
 (0)