Windows Event Collector (WEC)
Synopsis
Creates a Windows Event Collector (WEC) server that accepts event forwarding connections from Windows clients using the WS-Management (WS-Man) protocol. Supports Kerberos/SPNEGO and TLS mutual authentication, subscription-based event filtering, bookmark persistence for resumable collection, SLDC/LZNT1 decompression, and multiple workers for high-throughput scenarios.
Schema
- id: <numeric>
name: <string>
description: <string>
type: wec
tags: <string[]>
pipelines: <pipeline[]>
status: <boolean>
properties:
address: <string>
port: <numeric>
hostname: <string>
reuse: <boolean>
workers: <numeric>
buffer_size: <numeric>
backpressure_policy: <string>
max_active_requests: <numeric>
max_requests_per_socket: <numeric>
use_x_forwarded_for: <boolean>
capture_request_headers: <boolean>
allow_machine_id_mismatch: <boolean>
ip_allowlist_regex: <string>
ip_denylist_regex: <string>
tls:
status: <boolean>
cert_name: <string>
key_name: <string>
passphrase: <string>
min_tls_version: <string>
max_tls_version: <string>
insecure_skip_verify: <boolean>
is_auth_required: <boolean>
client_ca_cert_name: <string>
kerberos_keytabs:
- kerberos_keytab: <string>
kerberos_spn: <string>
subscriptions:
- id: <string>
name: <string>
channel: <string>
query: <string>
content_format: <string>
read_existing_events: <boolean>
send_bookmarks: <boolean>
fresh_on_reconnect: <boolean>
compress: <boolean>
targets: <string[]>
fields:
- name: <string>
value: <string>
batch_size: <numeric>
Configuration
The following fields are used to define the device:
Device
| Field | Required | Default | Description |
|---|---|---|---|
id | Y | - | Unique numeric identifier |
name | Y | - | Device name |
description | N | - | Optional description |
type | Y | - | Must be wec |
tags | N | - | Optional tags |
pipelines | N | - | Optional pre-processor pipelines |
status | N | true | Enable/disable the device |
Connection
| Field | Required | Default | Description |
|---|---|---|---|
address | N | "0.0.0.0" | Listen address |
port | Y | - | Listen port (typically 5985 for HTTP or 5986 for HTTPS) |
hostname | N | listen address | FQDN advertised to clients in subscription endpoint URIs |
If address is 0.0.0.0, set hostname explicitly to a client-resolvable FQDN.
There is no protocol key. tls.status chooses between HTTP and HTTPS: false is HTTP, conventionally on port 5985, and true is HTTPS, conventionally on port 5986. The
TLS
| Field | Required | Default | Description |
|---|---|---|---|
tls.status | N | false | Enable TLS encryption |
tls.cert_name | Y* | cert.pem | TLS certificate |
tls.key_name | Y* | key.pem | TLS private key |
tls.passphrase | N | - | Passphrase for an encrypted private key |
tls.min_tls_version | N | tls1.2 | Minimum accepted TLS version (tls1.0, tls1.1, tls1.2, tls1.3) |
tls.max_tls_version | N | - | Maximum accepted TLS version. When unset, the highest mutually supported version is negotiated. |
tls.insecure_skip_verify | N | false | Has no effect on this device. It applies only where the Director connects to a server, never to the connections it accepts: a client certificate is checked only through is_auth_required and client_ca_cert_name. Changing it restarts the listener and nothing else. |
* = Required when tls.status is true.
tls.min_version is a deprecated alias for tls.min_tls_version, honored only when tls.min_tls_version is unset. Use tls.min_tls_version in new configurations.
Mutual TLS is configured through is_auth_required and client_ca_cert_name, described under Authentication below. This device does not use the tls.client_ca_name and tls.client_auth_required keys read by the other listeners.
TLS material fields (cert_name, key_name, ca_name, client_ca_name) accept any of the following:
- File name — resolved relative to the service root directory. Nested paths such as
certs/prod/server.pemare supported. - Absolute path — honored only if it resolves inside the service root. Any path that escapes the root is refused.
- Inline PEM content — used verbatim when the value contains
-----BEGIN. - Environment variable —
${ENV_VAR}. - Vault reference —
$secret{id=...}or$secret{store=...,ref=...}.
Port 5986 is the standard WEF/HTTPS port. Plain HTTP on port 5985 is not recommended for production.
Authentication
| Field | Required | Default | Description |
|---|---|---|---|
is_auth_required | N | false | Reject unauthenticated requests |
client_ca_cert_name | N | - | CA bundle used to verify client certificates. When unset, the host trust store is used, which is sufficient for domain-joined Windows hosts whose domain CA is already trusted. |
Both keys are read at the top level of properties, not under tls.
When is_auth_required is true, how the collector checks a forwarder's Kerberos ticket depends on whether keytabs are configured:
- With keytabs, the collector checks tickets against them, on any Director. Client certificates are not demanded, because these clients authenticate through SPNEGO in the HTTP
Authorizationheader — requiring a certificate would fail the handshake before Kerberos could negotiate. - Without keytabs, the collector checks tickets with Windows SSPI, which uses the Director's own computer account. SSPI exists only on Windows: on any other Director the collector does not start, and reports
SSPI authentication is only available on Windows; configure keytab-based authentication instead. Over HTTPS the TLS layer also demands a client certificate from every forwarder, checked againstclient_ca_cert_nameor the system trust store, and the handshake fails without one.
The web interface asks this under
Kerberos / SPNEGO
Multiple keytab entries are supported so a single listener can serve clients enrolled in different Kerberos realms.
| Field | Required | Default | Description |
|---|---|---|---|
kerberos_keytabs[].kerberos_keytab | N* | Base64-encoded keytab file content | |
kerberos_keytabs[].kerberos_spn | N* | Service Principal Name, e.g. HTTP/wec.corp.com |
* = Both fields must be provided together per entry. Entries missing either field are skipped with a warning.
Keytab data is provided as base64-encoded bytes so no physical keytab file is required on the host. Each entry in kerberos_keytabs is tried in order; the first entry that successfully authenticates the client wins.
Subscriptions
Each subscription defines a set of events that Windows clients will forward to this collector. Clients receive the list of subscriptions during enumeration and begin forwarding matching events automatically.
| Field | Required | Default | Description |
|---|---|---|---|
subscriptions[].id | Y | Unique subscription identifier (used in endpoint URIs) | |
subscriptions[].name | Y | Human-readable subscription name | |
subscriptions[].channel | Y | Windows event log channel, e.g. Security, System | |
subscriptions[].query | Y | * for every event; a full <QueryList> block, sent as it is; or a bare XPath predicate such as EventID=4624, which the collector wraps as *[System[(…)]]. A complete XPath such as *[System[(Level=1)]] is wrapped a second time and matches nothing. < and > are refused outside a <QueryList>, and a query is capped at 32 KB. | |
subscriptions[].content_format | N | RenderedText | Event content format (RenderedText or Events) |
subscriptions[].read_existing_events | N | false | Read events that existed before the subscription was created |
subscriptions[].send_bookmarks | N | true | Request bookmark payloads from clients for resumable collection |
subscriptions[].fresh_on_reconnect | N | false | When a forwarder reconnects, discard its stored bookmark and start from new events instead of resuming where it left off |
subscriptions[].compress | N | false | Request SLDC-compressed event batches from clients |
subscriptions[].targets | N | - | DNS name patterns restricting which clients receive this subscription (wildcards supported, empty means all clients) |
In the web interface fresh_on_reconnect is read_existing_events is false: with the bookmark discarded on every reconnect, a forwarder told to read existing events would start from the beginning of its log, and send the whole log again each time.
Subscription Fields
Per-subscription fields are injected into every event delivered under that subscription. Values prefixed with __subscription. are resolved from subscription metadata at runtime.
| Field | Required | Default | Description |
|---|---|---|---|
subscriptions[].fields[].name | Y | Field name to inject | |
subscriptions[].fields[].value | Y | Static value or __subscription.<key> reference |
Available __subscription.* references: version, subscriptionName, contentFormat, readExistingEvents, heartbeatInterval, batchTimeout, sendBookmarks, compress, locale.
Advanced Configuration
Performance
| Field | Required | Default | Description |
|---|---|---|---|
reuse | N | true | Enable socket address reuse (SO_REUSEADDR) |
workers | N | One per 8 CPU cores | Number of concurrent event processing workers, never below 1 and reduced further on memory-constrained hosts. Forced to 1 when reuse is false. |
buffer_size | N | 4096 | Async event channel buffer size (number of event batches) |
backpressure_policy | N | "" | Behavior when the event buffer is full: drop, block, or empty (synchronous fallback) |
max_active_requests | N | 256 | Maximum concurrent in-flight HTTP requests (0 = unlimited) |
max_requests_per_socket | N | 0 | Maximum HTTP requests per TCP connection before close (0 = unlimited) |
max_buffer_size | N | 128MB | Ingest buffer admission cap. Also sizes this listener's ingest buffers: 3/8 of this value at start, growing to at most 3x |
max_buffer_size is the ingest-buffer admission cap, and is distinct from any buffer_size field above, which sizes the network read buffer. Left unset it is 128MB, whose buffers take 48 MiB of heap per listener worker at start and grow to at most 384 MiB as bursts need the room — and with reuse: true a device runs one worker per listener. Size strings are binary, and 32MB and 32MiB are exact synonyms.
See Listener Memory Sizing for the arithmetic, the sizing table, and what happens when the cap is exhausted.
IP Filtering
| Field | Required | Default | Description |
|---|---|---|---|
ip_allowlist_regex | N | Regex that client IP must match; non-matching IPs receive 403 | |
ip_denylist_regex | N | Regex that client IP must NOT match; matching IPs receive 403 |
Proxy / Header Handling
| Field | Required | Default | Description |
|---|---|---|---|
use_x_forwarded_for | N | false | Use X-Forwarded-For header to resolve originating client IP |
capture_request_headers | N | false | Attach raw HTTP request headers to each event's client identity |
allow_machine_id_mismatch | N | false | Allow events when the SOAP MachineID does not match the TLS cert CN (logged as a warning when allowed) |
Messages
| Field | Required | Default | Description |
|---|---|---|---|
batch_size | N | 10000 | Number of log records to read per processing batch |
queue.interval is a Director service-level setting configured in vmetric.yml and cannot be overridden per device.
Details
How WEF Works
Windows Event Forwarding (WEF) uses the WS-Management protocol over HTTP or HTTPS. When a Windows client connects, it sends an Enumerate request to discover available subscriptions. For each matching subscription it receives, the client begins forwarding events in batches. Each batch may include a bookmark that records the client's position in the event log, allowing the server to resume collection from the correct offset after a restart or subscription version change.
The server handles four main WS-Man actions: Enumerate (returns available subscriptions), Subscribe (client acknowledges a subscription), Events (client delivers a batch of events), and Heartbeat (client signals it is still connected but has no new events to deliver).
Pointing Forwarders at the Collector
Forwarders find the collector through Group Policy. The web interface shows the value to use as a read-only
Server=http://wec01.corp.example.com:5985/wsman/SubscriptionManager/WEC,Refresh=60
Set it under Computer Configuration > Policies > Administrative Templates > Windows Components > Event Forwarding > Configure target Subscription Manager. The path is case-sensitive, and Refresh is how often forwarders check for changed subscriptions, in seconds.
The hostname in that line is the device's hostname, and it must be a name or address forwarders can reach. The web interface's 0.0.0.0, which is right for address but tells a forwarder nothing.
Subscriptions and Versioning
When a subscription's configuration changes (channel, query, format, etc.), the server automatically bumps its version identifier and invalidates all stored bookmarks for that subscription. Clients detect the version change during their next heartbeat or event delivery and re-subscribe, causing them to replay events from the beginning of the channel or from the point specified by read_existing_events.
Bookmark Persistence
When send_bookmarks is enabled on a subscription, Windows clients include a BookmarkList XML fragment with each event batch. The server validates and stores this bookmark keyed by client identity and subscription ID. On reconnect or version bump, the stored bookmark is sent back to the client in the SubscribeResponse so it can resume forwarding from where it left off.
Compression
When compress is enabled on a subscription, clients compress event batches using the Streaming Lossless Data Compression (SLDC) algorithm (LZNT1 variant). The server automatically detects and decompresses SLDC payloads. A maximum decompressed size of 64 MB is enforced to prevent memory exhaustion.
Client Identity
The server resolves client identity in order of trust: Kerberos principal (highest), TLS certificate CN (verified chain required), SOAP MachineID / X-Machine-Name header (unverified, used for logging only), and remote IP address (fallback). The MachineName field on a client identity is only populated from verified sources (Kerberos or TLS). ReportedMachineName carries the unverified client-supplied value and should not be used for authorization decisions.
MachineID Mismatch
When TLS client certificates are in use, the server compares the hostname portion of the certificate CN against the SOAP MachineID. A mismatch is rejected by default. Set allow_machine_id_mismatch: true to log the mismatch as a warning and allow the request to proceed. Only the short hostname (before the first .) is compared.
Examples
The following are commonly used configuration types.
Basic
A minimal WEC server listening on the standard HTTP port:
Creating a simple WEC server for a Security event subscription... | |
TLS with Kerberos Authentication
Securing the collector with TLS and requiring Kerberos authentication:
Production WEC server with TLS and SPNEGO/Kerberos... | |
Multiple Subscriptions with Filtering
Collecting from multiple channels with XPath queries and target restrictions:
Separate subscriptions per channel with event filtering... | |
High-Volume Processing
Performance tuning for large Windows fleets:
Optimizing for high event throughput with multiple workers and compression... | |
The worker count scales as one worker per 8 CPU cores rather than one per core, and is reduced further on memory-constrained hosts. Setting workers without enabling reuse has no effect: the count is forced to 1, because the workers rely on SO_REUSEPORT to share the listening socket.
Subscription Fields
Injecting metadata into every event for downstream enrichment:
Tagging events with subscription context for easier routing... | |
Multi-Realm Kerberos
Serving clients from multiple Kerberos realms with separate keytabs:
Accepting tickets from two different AD domains... | |
Each keytab entry is tried in order. Ensure SPNs are correctly registered in their respective Active Directory domains.
Pipeline Processing
Applying pre-processing pipelines to normalize events before ingestion:
Parsing and normalizing raw Windows event XML... | |