diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 7c29c6cd406..6366eeacd64 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -96,6 +96,9 @@ https://github.com/elastic/beats/compare/v8.8.1\...main[Check the HEAD diff] - Set field types to correctly match ECS in sessionmd processor {issue}38955[38955] {pull}38994[38994] - Keep process info on exited processes, to avoid failing to enrich events in sessionmd processor {pull}39173[39173] +- Prevent scenario of losing children-related file events in a directory for recursive fsnotify backend of auditbeat file integrity module {pull}39133[39133] + + *Filebeat* - [Gcs Input] - Added missing locks for safe concurrency {pull}34914[34914] diff --git a/auditbeat/module/file_integrity/monitor/monitor_test.go b/auditbeat/module/file_integrity/monitor/monitor_test.go index 5828f348d23..2f66d6469b2 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 7a0768d6fcb..31f2b538370 100644 --- a/auditbeat/module/file_integrity/monitor/recursive.go +++ b/auditbeat/module/file_integrity/monitor/recursive.go @@ -84,37 +84,63 @@ 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 { + return err + } + + if err = watcher.inner.Add(path); err != nil { + return err + } + + return nil + } + + return watcher.tree.AddFile(path) +} + 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)