feat(inputs.win_eventlog): Add state-persistence capabilities (#12790)

This commit is contained in:
Sven Rebhan 2023-03-07 19:53:25 +01:00 committed by GitHub
parent 7b2d48e041
commit 0e1b637414
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 162 additions and 56 deletions

View File

@ -65,6 +65,10 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
</QueryList> </QueryList>
''' '''
## When true, event logs are read from the beginning; otherwise only future events
## will be logged.
# from_beginning = false
## System field names: ## System field names:
## "Source", "EventID", "Version", "Level", "Task", "Opcode", "Keywords", "TimeCreated", ## "Source", "EventID", "Version", "Level", "Task", "Opcode", "Keywords", "TimeCreated",
## "EventRecordID", "ActivityID", "RelatedActivityID", "ProcessID", "ThreadID", "ProcessName", ## "EventRecordID", "ActivityID", "RelatedActivityID", "ProcessID", "ThreadID", "ProcessName",

View File

@ -40,6 +40,10 @@
</QueryList> </QueryList>
''' '''
## When true, event logs are read from the beginning; otherwise only future events
## will be logged.
# from_beginning = false
## System field names: ## System field names:
## "Source", "EventID", "Version", "Level", "Task", "Opcode", "Keywords", "TimeCreated", ## "Source", "EventID", "Version", "Level", "Task", "Opcode", "Keywords", "TimeCreated",
## "EventRecordID", "ActivityID", "RelatedActivityID", "ProcessID", "ThreadID", "ProcessName", ## "EventRecordID", "ActivityID", "RelatedActivityID", "ProcessID", "ThreadID", "ProcessName",

View File

@ -23,7 +23,9 @@ type EvtSubscribeFlag uint32
// EVT_SUBSCRIBE_FLAGS enumeration // EVT_SUBSCRIBE_FLAGS enumeration
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa385588(v=vs.85).aspx // https://msdn.microsoft.com/en-us/library/windows/desktop/aa385588(v=vs.85).aspx
const ( const (
EvtSubscribeToFutureEvents EvtSubscribeFlag = 1 EvtSubscribeToFutureEvents EvtSubscribeFlag = 1
EvtSubscribeStartAtOldestRecord EvtSubscribeFlag = 2
EvtSubscribeStartAfterBookmark EvtSubscribeFlag = 3
) )
// EvtRenderFlag uint32 // EvtRenderFlag uint32
@ -32,9 +34,9 @@ type EvtRenderFlag uint32
// EVT_RENDER_FLAGS enumeration // EVT_RENDER_FLAGS enumeration
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa385563(v=vs.85).aspx // https://msdn.microsoft.com/en-us/library/windows/desktop/aa385563(v=vs.85).aspx
const ( const (
//revive:disable:var-naming
// Render the event as an XML string. For details on the contents of the // Render the event as an XML string. For details on the contents of the
// XML string, see the Event schema. // XML string, see the Event schema.
EvtRenderEventXml EvtRenderFlag = 1 EvtRenderEventXml EvtRenderFlag = 1
//revive:enable:var-naming // Render bookmark
EvtRenderBookmark EvtRenderFlag = 2
) )

View File

@ -29,52 +29,102 @@ var sampleConfig string
// WinEventLog config // WinEventLog config
type WinEventLog struct { type WinEventLog struct {
Locale uint32 `toml:"locale"` Locale uint32 `toml:"locale"`
EventlogName string `toml:"eventlog_name"` EventlogName string `toml:"eventlog_name"`
Query string `toml:"xpath_query"` Query string `toml:"xpath_query"`
ProcessUserData bool `toml:"process_userdata"` FromBeginning bool `toml:"from_beginning"`
ProcessEventData bool `toml:"process_eventdata"` ProcessUserData bool `toml:"process_userdata"`
Separator string `toml:"separator"` ProcessEventData bool `toml:"process_eventdata"`
OnlyFirstLineOfMessage bool `toml:"only_first_line_of_message"` Separator string `toml:"separator"`
TimeStampFromEvent bool `toml:"timestamp_from_event"` OnlyFirstLineOfMessage bool `toml:"only_first_line_of_message"`
EventTags []string `toml:"event_tags"` TimeStampFromEvent bool `toml:"timestamp_from_event"`
EventFields []string `toml:"event_fields"` EventTags []string `toml:"event_tags"`
ExcludeFields []string `toml:"exclude_fields"` EventFields []string `toml:"event_fields"`
ExcludeEmpty []string `toml:"exclude_empty"` ExcludeFields []string `toml:"exclude_fields"`
subscription EvtHandle ExcludeEmpty []string `toml:"exclude_empty"`
buf []byte Log telegraf.Logger `toml:"-"`
Log telegraf.Logger
subscription EvtHandle
subscriptionFlag EvtSubscribeFlag
bookmark EvtHandle
} }
var bufferSize = 1 << 14 const bufferSize = 1 << 14
func (*WinEventLog) SampleConfig() string { func (*WinEventLog) SampleConfig() string {
return sampleConfig return sampleConfig
} }
// Gather Windows Event Log entries func (w *WinEventLog) Init() error {
func (w *WinEventLog) Gather(acc telegraf.Accumulator) error { w.subscriptionFlag = EvtSubscribeToFutureEvents
if w.FromBeginning {
var err error w.subscriptionFlag = EvtSubscribeStartAtOldestRecord
if w.subscription == 0 {
w.subscription, err = w.evtSubscribe(w.EventlogName, w.Query)
if err != nil {
return fmt.Errorf("subscription of Windows Event Log failed: %w", err)
}
} }
bookmark, err := _EvtCreateBookmark(nil)
if err != nil {
return err
}
w.bookmark = bookmark
return nil
}
func (w *WinEventLog) Start(_ telegraf.Accumulator) error {
subscription, err := w.evtSubscribe()
if err != nil {
return fmt.Errorf("subscription of Windows Event Log failed: %w", err)
}
w.subscription = subscription
w.Log.Debug("Subscription handle id:", w.subscription) w.Log.Debug("Subscription handle id:", w.subscription)
loop: return nil
}
func (w *WinEventLog) Stop() {
_ = _EvtClose(w.subscription)
}
func (w *WinEventLog) GetState() interface{} {
bookmarkXML, err := w.renderBookmark(w.bookmark)
if err != nil {
w.Log.Errorf("State-persistence failed, cannot render bookmark: %w", err)
return ""
}
return bookmarkXML
}
func (w *WinEventLog) SetState(state interface{}) error {
bookmarkXML, ok := state.(string)
if !ok {
return fmt.Errorf("invalid type %T for state", state)
}
ptr, err := syscall.UTF16PtrFromString(bookmarkXML)
if err != nil {
return fmt.Errorf("convertion to pointer failed: %w", err)
}
bookmark, err := _EvtCreateBookmark(ptr)
if err != nil {
return fmt.Errorf("creating bookmark failed: %w", err)
}
w.bookmark = bookmark
w.subscriptionFlag = EvtSubscribeStartAfterBookmark
return nil
}
// Gather Windows Event Log entries
func (w *WinEventLog) Gather(acc telegraf.Accumulator) error {
for { for {
events, err := w.fetchEvents(w.subscription) events, err := w.fetchEvents(w.subscription)
if err != nil { if err != nil {
switch { if err == ERROR_NO_MORE_ITEMS {
case err == ERROR_NO_MORE_ITEMS: break
break loop
case err != nil:
w.Log.Errorf("Error getting events: %v", err)
return err
} }
w.Log.Errorf("Error getting events: %v", err)
return err
} }
for _, event := range events { for _, event := range events {
@ -244,27 +294,28 @@ func (w *WinEventLog) shouldExcludeEmptyField(field string, fieldType string, fi
return false return false
} }
func (w *WinEventLog) evtSubscribe(logName, xquery string) (EvtHandle, error) { func (w *WinEventLog) evtSubscribe() (EvtHandle, error) {
var logNamePtr, xqueryPtr *uint16
sigEvent, err := windows.CreateEvent(nil, 0, 0, nil) sigEvent, err := windows.CreateEvent(nil, 0, 0, nil)
if err != nil { if err != nil {
return 0, err return 0, err
} }
defer windows.CloseHandle(sigEvent) defer windows.CloseHandle(sigEvent)
logNamePtr, err = syscall.UTF16PtrFromString(logName) logNamePtr, err := syscall.UTF16PtrFromString(w.EventlogName)
if err != nil { if err != nil {
return 0, err return 0, err
} }
xqueryPtr, err = syscall.UTF16PtrFromString(xquery) xqueryPtr, err := syscall.UTF16PtrFromString(w.Query)
if err != nil { if err != nil {
return 0, err return 0, err
} }
subsHandle, err := _EvtSubscribe(0, uintptr(sigEvent), logNamePtr, xqueryPtr, var bookmark EvtHandle
0, 0, 0, EvtSubscribeToFutureEvents) if w.subscriptionFlag == EvtSubscribeStartAfterBookmark {
bookmark = w.bookmark
}
subsHandle, err := _EvtSubscribe(0, uintptr(sigEvent), logNamePtr, xqueryPtr, bookmark, 0, 0, w.subscriptionFlag)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -299,34 +350,55 @@ func (w *WinEventLog) fetchEvents(subsHandle EvtHandle) ([]Event, error) {
return nil, err return nil, err
} }
var evterr error
for _, eventHandle := range eventHandles { for _, eventHandle := range eventHandles {
if eventHandle != 0 { if eventHandle == 0 {
event, err := w.renderEvent(eventHandle) continue
if err == nil {
events = append(events, event)
}
} }
if event, err := w.renderEvent(eventHandle); err == nil {
events = append(events, event)
}
if err := _EvtUpdateBookmark(w.bookmark, eventHandle); err != nil && evterr == nil {
evterr = err
}
if err := _EvtClose(eventHandle); err != nil && evterr == nil {
evterr = err
}
}
return events, evterr
}
func (w *WinEventLog) renderBookmark(bookmark EvtHandle) (string, error) {
var bufferUsed, propertyCount uint32
buf := make([]byte, bufferSize)
err := _EvtRender(0, bookmark, EvtRenderBookmark, uint32(len(buf)), &buf[0], &bufferUsed, &propertyCount)
if err != nil {
return "", err
} }
for i := 0; i < len(eventHandles); i++ { x, err := DecodeUTF16(buf[:bufferUsed])
err := _EvtClose(eventHandles[i]) if err != nil {
if err != nil { return "", err
return events, err
}
} }
return events, nil if x[len(x)-1] == 0 {
x = x[:len(x)-1]
}
return string(x), err
} }
func (w *WinEventLog) renderEvent(eventHandle EvtHandle) (Event, error) { func (w *WinEventLog) renderEvent(eventHandle EvtHandle) (Event, error) {
var bufferUsed, propertyCount uint32 var bufferUsed, propertyCount uint32
buf := make([]byte, bufferSize)
event := Event{} event := Event{}
err := _EvtRender(0, eventHandle, EvtRenderEventXml, uint32(len(w.buf)), &w.buf[0], &bufferUsed, &propertyCount) err := _EvtRender(0, eventHandle, EvtRenderEventXml, uint32(len(buf)), &buf[0], &bufferUsed, &propertyCount)
if err != nil { if err != nil {
return event, err return event, err
} }
eventXML, err := DecodeUTF16(w.buf[:bufferUsed]) eventXML, err := DecodeUTF16(buf[:bufferUsed])
if err != nil { if err != nil {
return event, err return event, err
} }
@ -483,7 +555,6 @@ func openPublisherMetadata(
func init() { func init() {
inputs.Add("win_eventlog", func() telegraf.Input { inputs.Add("win_eventlog", func() telegraf.Input {
return &WinEventLog{ return &WinEventLog{
buf: make([]byte, bufferSize),
ProcessUserData: true, ProcessUserData: true,
ProcessEventData: true, ProcessEventData: true,
Separator: "_", Separator: "_",

View File

@ -75,6 +75,8 @@ var (
procEvtNext = modwevtapi.NewProc("EvtNext") procEvtNext = modwevtapi.NewProc("EvtNext")
procEvtFormatMessage = modwevtapi.NewProc("EvtFormatMessage") procEvtFormatMessage = modwevtapi.NewProc("EvtFormatMessage")
procEvtOpenPublisherMetadata = modwevtapi.NewProc("EvtOpenPublisherMetadata") procEvtOpenPublisherMetadata = modwevtapi.NewProc("EvtOpenPublisherMetadata")
procEvtCreateBookmark = modwevtapi.NewProc("EvtCreateBookmark")
procEvtUpdateBookmark = modwevtapi.NewProc("EvtUpdateBookmark")
) )
func _EvtSubscribe( func _EvtSubscribe(
@ -231,3 +233,26 @@ func _EvtOpenPublisherMetadata(session EvtHandle, publisherIdentity *uint16, log
} }
return return
} }
func _EvtCreateBookmark(bookmarkXML *uint16) (EvtHandle, error) {
r0, _, e1 := syscall.Syscall(procEvtCreateBookmark.Addr(), 1, uintptr(unsafe.Pointer(bookmarkXML)), 0, 0)
handle := EvtHandle(r0)
if handle != 0 {
return handle, nil
}
if e1 != 0 {
return handle, errnoErr(e1)
}
return handle, syscall.EINVAL
}
func _EvtUpdateBookmark(bookmark, event EvtHandle) error {
r0, _, e1 := syscall.Syscall(procEvtUpdateBookmark.Addr(), 2, uintptr(bookmark), uintptr(event), 0)
if r0 != 0 {
return nil
}
if e1 != 0 {
return errnoErr(e1)
}
return syscall.EINVAL
}