From c7759a6fee442d08620fd0461b78fc50a751fcd1 Mon Sep 17 00:00:00 2001 From: Panos Koutsovasilis Date: Mon, 22 Apr 2024 18:18:43 +0300 Subject: [PATCH] fix(auditbeat/fim/fsnotify): remove time window where a child file operation in of a directory can be lost --- CHANGELOG.next.asciidoc | 1 + .../file_integrity/monitor/monitor_test.go | 7 ++- .../file_integrity/monitor/recursive.go | 51 ++++++++++++++----- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index cecdeefd8752..21b6dec1d64e 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -64,6 +64,7 @@ https://github.com/elastic/beats/compare/v8.8.1\...main[Check the HEAD diff] - Change cache processor documentation from `write_period` to `write_interval`. {pull}38561[38561] - Fix cache processor expiries heap cleanup on partial file writes. {pull}38561[38561] - Fix cache processor expiries infinite growth when large a large TTL is used and recurring keys are cached. {pull}38561[38561] +- Prevent scenario of losing children-related file events in a directory for recursive fsnotify backend of auditbeat file integrity module {pull}0[0] *Auditbeat* diff --git a/auditbeat/module/file_integrity/monitor/monitor_test.go b/auditbeat/module/file_integrity/monitor/monitor_test.go index 5828f348d23b..2f66d6469b26 100644 --- a/auditbeat/module/file_integrity/monitor/monitor_test.go +++ b/auditbeat/module/file_integrity/monitor/monitor_test.go @@ -144,6 +144,11 @@ func TestRecursiveSubdirPermissions(t *testing.T) { t.Skip("Skipping permissions test on Windows") } + if os.Getuid() == 0 { + t.Skip("skipping as root can access every file and thus this unittest will fail") + return + } + // Create dir to be watched dir, err := os.MkdirTemp("", "monitor") @@ -202,7 +207,7 @@ func TestRecursiveSubdirPermissions(t *testing.T) { for { // No event is received ev, err := readTimeout(t, watcher) - if err == errReadTimeout { + if errors.Is(err, errReadTimeout) { break } assertNoError(t, err) diff --git a/auditbeat/module/file_integrity/monitor/recursive.go b/auditbeat/module/file_integrity/monitor/recursive.go index 7a0768d6fcbd..d5acdf364e29 100644 --- a/auditbeat/module/file_integrity/monitor/recursive.go +++ b/auditbeat/module/file_integrity/monitor/recursive.go @@ -84,37 +84,60 @@ func (watcher *recursiveWatcher) ErrorChannel() <-chan error { return watcher.inner.Errors } +func (watcher *recursiveWatcher) watchFile(path string, info os.FileInfo) error { + var err error + if info == nil { + info, err = os.Lstat(path) + if err != nil { + return err + } + } + + if info.IsDir() { + if err = watcher.tree.AddDir(path); err == nil { + if err = watcher.inner.Add(path); err != nil { + return fmt.Errorf("failed adding watcher to '%s': %w", path, err) + } + } + } else { + err = watcher.tree.AddFile(path) + } + return err +} + func (watcher *recursiveWatcher) addRecursive(path string) error { if watcher.isExcludedPath(path) { return nil } + if err := watcher.watchFile(path, nil); err != nil { + return fmt.Errorf("failed adding watcher to '%s': %w", path, err) + } + var errs multierror.Errors - err := filepath.Walk(path, func(path string, info os.FileInfo, fnErr error) error { - if watcher.isExcludedPath(path) { + err := filepath.Walk(path, func(walkPath string, info os.FileInfo, fnErr error) error { + if walkPath == path { + return nil + } + + if watcher.isExcludedPath(walkPath) { return nil } if fnErr != nil { - errs = append(errs, fmt.Errorf("error walking path '%s': %w", path, fnErr)) + errs = append(errs, fmt.Errorf("error walking path '%s': %w", walkPath, fnErr)) // If FileInfo is not nil, the directory entry can be processed // even if there was some error if info == nil { return nil } } - var err error - if info.IsDir() { - if err = watcher.tree.AddDir(path); err == nil { - if err = watcher.inner.Add(path); err != nil { - errs = append(errs, fmt.Errorf("failed adding watcher to '%s': %w", path, err)) - return nil - } - } - } else { - err = watcher.tree.AddFile(path) + + if err := watcher.watchFile(walkPath, info); err != nil { + errs = append(errs, fmt.Errorf("failed adding watcher to '%s': %w", walkPath, err)) } - return err + + return nil }) watcher.log.Debugw("Added recursive watch", "path", path)