123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419 |
- [role="xpack"]
- [[eql]]
- = EQL search
- ++++
- <titleabbrev>EQL</titleabbrev>
- ++++
- Event Query Language (EQL) is a query language for event-based time series
- data, such as logs, metrics, and traces.
- [discrete]
- [[eql-advantages]]
- === Advantages of EQL
- * *EQL lets you express relationships between events.* +
- Many query languages allow you to match single events. EQL lets you match a
- sequence of events across different event categories and time spans.
- * *EQL has a low learning curve.* +
- <<eql-syntax,EQL syntax>> looks like other common query languages, such as SQL.
- EQL lets you write and read queries intuitively, which makes for quick,
- iterative searching.
- * *EQL is designed for security use cases.* +
- While you can use it for any event-based data, we created EQL for threat
- hunting. EQL not only supports indicator of compromise (IOC) searches but can
- describe activity that goes beyond IOCs.
- [discrete]
- [[eql-required-fields]]
- === Required fields
- With the exception of sample queries, EQL searches require that the searched
- data stream or index contains a _timestamp_ field. By default, EQL uses the
- `@timestamp` field from the {ecs-ref}[Elastic Common Schema (ECS)].
- EQL searches also require an _event category_ field, unless you use the
- <<eql-syntax-match-any-event-category,`any` keyword>> to search for documents
- without an event category field. By default, EQL uses the ECS `event.category`
- field.
- To use a different timestamp or event category field, see
- <<specify-a-timestamp-or-event-category-field>>.
- TIP: While no schema is required to use EQL, we recommend using the
- {ecs-ref}[ECS]. EQL searches are designed to work with core ECS fields by
- default.
- [discrete]
- [[run-an-eql-search]]
- === Run an EQL search
- Use the <<eql-search-api,EQL search API>> to run a <<eql-basic-syntax,basic EQL
- query>>.
- ////
- [source,console]
- ----
- DELETE /_data_stream/*
- DELETE /_index_template/*
- ----
- // TEARDOWN
- ////
- [source,console]
- ----
- GET /my-data-stream/_eql/search
- {
- "query": """
- process where process.name == "regsvr32.exe"
- """
- }
- ----
- // TEST[setup:sec_logs]
- By default, basic EQL queries return the 10 most recent matching events in the
- `hits.events` property. These hits are sorted by timestamp, converted to
- milliseconds since the {wikipedia}/Unix_time[Unix epoch], in ascending order.
- [source,console-result]
- ----
- {
- "is_partial": false,
- "is_running": false,
- "took": 60,
- "timed_out": false,
- "hits": {
- "total": {
- "value": 2,
- "relation": "eq"
- },
- "events": [
- {
- "_index": ".ds-my-data-stream-2099.12.07-000001",
- "_id": "OQmfCaduce8zoHT93o4H",
- "_source": {
- "@timestamp": "2099-12-07T11:07:09.000Z",
- "event": {
- "category": "process",
- "id": "aR3NWVOs",
- "sequence": 4
- },
- "process": {
- "pid": 2012,
- "name": "regsvr32.exe",
- "command_line": "regsvr32.exe /s /u /i:https://...RegSvr32.sct scrobj.dll",
- "executable": "C:\\Windows\\System32\\regsvr32.exe"
- }
- }
- },
- {
- "_index": ".ds-my-data-stream-2099.12.07-000001",
- "_id": "xLkCaj4EujzdNSxfYLbO",
- "_source": {
- "@timestamp": "2099-12-07T11:07:10.000Z",
- "event": {
- "category": "process",
- "id": "GTSmSqgz0U",
- "sequence": 6,
- "type": "termination"
- },
- "process": {
- "pid": 2012,
- "name": "regsvr32.exe",
- "executable": "C:\\Windows\\System32\\regsvr32.exe"
- }
- }
- }
- ]
- }
- }
- ----
- // TESTRESPONSE[s/"took": 60/"took": $body.took/]
- // TESTRESPONSE[s/"_index": ".ds-my-data-stream-2099.12.07-000001"/"_index": $body.hits.events.0._index/]
- // TESTRESPONSE[s/"_id": "OQmfCaduce8zoHT93o4H"/"_id": $body.hits.events.0._id/]
- // TESTRESPONSE[s/"_id": "xLkCaj4EujzdNSxfYLbO"/"_id": $body.hits.events.1._id/]
- Use the `size` parameter to get a smaller or larger set of hits:
- [source,console]
- ----
- GET /my-data-stream/_eql/search
- {
- "query": """
- process where process.name == "regsvr32.exe"
- """,
- "size": 50
- }
- ----
- // TEST[setup:sec_logs]
- [discrete]
- [[eql-search-sequence]]
- === Search for a sequence of events
- Use EQL's <<eql-sequences,sequence syntax>> to search for a series of
- ordered events. List the event items in ascending chronological order,
- with the most recent event listed last:
- [source,console]
- ----
- GET /my-data-stream/_eql/search
- {
- "query": """
- sequence
- [ process where process.name == "regsvr32.exe" ]
- [ file where stringContains(file.name, "scrobj.dll") ]
- """
- }
- ----
- // TEST[setup:sec_logs]
- The response's `hits.sequences` property contains the 10 most recent matching
- sequences.
- [source,console-result]
- ----
- {
- ...
- "hits": {
- "total": ...,
- "sequences": [
- {
- "events": [
- {
- "_index": ".ds-my-data-stream-2099.12.07-000001",
- "_id": "OQmfCaduce8zoHT93o4H",
- "_source": {
- "@timestamp": "2099-12-07T11:07:09.000Z",
- "event": {
- "category": "process",
- "id": "aR3NWVOs",
- "sequence": 4
- },
- "process": {
- "pid": 2012,
- "name": "regsvr32.exe",
- "command_line": "regsvr32.exe /s /u /i:https://...RegSvr32.sct scrobj.dll",
- "executable": "C:\\Windows\\System32\\regsvr32.exe"
- }
- }
- },
- {
- "_index": ".ds-my-data-stream-2099.12.07-000001",
- "_id": "yDwnGIJouOYGBzP0ZE9n",
- "_source": {
- "@timestamp": "2099-12-07T11:07:10.000Z",
- "event": {
- "category": "file",
- "id": "tZ1NWVOs",
- "sequence": 5
- },
- "process": {
- "pid": 2012,
- "name": "regsvr32.exe",
- "executable": "C:\\Windows\\System32\\regsvr32.exe"
- },
- "file": {
- "path": "C:\\Windows\\System32\\scrobj.dll",
- "name": "scrobj.dll"
- }
- }
- }
- ]
- }
- ]
- }
- }
- ----
- // TESTRESPONSE[s/ \.\.\.\n/"is_partial": false, "is_running": false, "took": $body.took, "timed_out": false,/]
- // TESTRESPONSE[s/"total": \.\.\.,/"total": { "value": 1, "relation": "eq" },/]
- // TESTRESPONSE[s/"_index": ".ds-my-data-stream-2099.12.07-000001"/"_index": $body.hits.sequences.0.events.0._index/]
- // TESTRESPONSE[s/"_id": "OQmfCaduce8zoHT93o4H"/"_id": $body.hits.sequences.0.events.0._id/]
- // TESTRESPONSE[s/"_id": "yDwnGIJouOYGBzP0ZE9n"/"_id": $body.hits.sequences.0.events.1._id/]
- Use <<eql-with-maxspan-keywords,`with maxspan`>> to constrain matching sequences
- to a timespan:
- [source,console]
- ----
- GET /my-data-stream/_eql/search
- {
- "query": """
- sequence with maxspan=1h
- [ process where process.name == "regsvr32.exe" ]
- [ file where stringContains(file.name, "scrobj.dll") ]
- """
- }
- ----
- // TEST[setup:sec_logs]
- Use the <<eql-by-keyword,`by` keyword>> to match events that share the
- same field values:
- [source,console]
- ----
- GET /my-data-stream/_eql/search
- {
- "query": """
- sequence with maxspan=1h
- [ process where process.name == "regsvr32.exe" ] by process.pid
- [ file where stringContains(file.name, "scrobj.dll") ] by process.pid
- """
- }
- ----
- // TEST[setup:sec_logs]
- If a field value should be shared across all events, use the `sequence by`
- keyword. The following query is equivalent to the previous one.
- [source,console]
- ----
- GET /my-data-stream/_eql/search
- {
- "query": """
- sequence by process.pid with maxspan=1h
- [ process where process.name == "regsvr32.exe" ]
- [ file where stringContains(file.name, "scrobj.dll") ]
- """
- }
- ----
- // TEST[setup:sec_logs]
- The `hits.sequences.join_keys` property contains the shared field values.
- [source,console-result]
- ----
- {
- ...
- "hits": ...,
- "sequences": [
- {
- "join_keys": [
- 2012
- ],
- "events": ...
- }
- ]
- }
- }
- ----
- // TESTRESPONSE[s/ \.\.\.\n/"is_partial": false, "is_running": false, "took": $body.took, "timed_out": false,/]
- // TESTRESPONSE[s/"hits": \.\.\.,/"hits": { "total": { "value": 1, "relation": "eq" },/]
- // TESTRESPONSE[s/"events": \.\.\./"events": $body.hits.sequences.0.events/]
- Use the <<eql-until-keyword,`until` keyword>> to specify an expiration
- event for sequences. Matching sequences must end before this event.
- [source,console]
- ----
- GET /my-data-stream/_eql/search
- {
- "query": """
- sequence by process.pid with maxspan=1h
- [ process where process.name == "regsvr32.exe" ]
- [ file where stringContains(file.name, "scrobj.dll") ]
- until [ process where event.type == "termination" ]
- """
- }
- ----
- // TEST[setup:sec_logs]
- [discrete]
- [[eql-search-sample]]
- === Sample chronologically unordered events
- Use EQL's <<eql-samples,sample syntax>> to search for events that match one or
- more join keys and a set of filters. Samples are similar to sequences, but do
- not return events in chronological order. In fact, sample queries can run on
- data without a timestamp. Sample queries can be useful to find correlations in
- events that don't always occur in the same sequence, or that occur across long
- time spans.
- .Click to show the sample data used in the examples below
- [%collapsible]
- ====
- [source,console]
- ----
- PUT /my-index-000001
- {
- "mappings": {
- "properties": {
- "ip": {
- "type":"ip"
- },
- "version": {
- "type": "version"
- },
- "missing_keyword": {
- "type": "keyword"
- },
- "@timestamp": {
- "type": "date"
- },
- "type_test": {
- "type": "keyword"
- },
- "@timestamp_pretty": {
- "type": "date",
- "format": "dd-MM-yyyy"
- },
- "event_type": {
- "type": "keyword"
- },
- "event": {
- "properties": {
- "category": {
- "type": "alias",
- "path": "event_type"
- }
- }
- },
- "host": {
- "type": "keyword"
- },
- "os": {
- "type": "keyword"
- },
- "bool": {
- "type": "boolean"
- },
- "uptime" : {
- "type" : "long"
- },
- "port" : {
- "type" : "long"
- }
- }
- }
- }
- PUT /my-index-000002
- {
- "mappings": {
- "properties": {
- "ip": {
- "type":"ip"
- },
- "@timestamp": {
- "type": "date"
- },
- "@timestamp_pretty": {
- "type": "date",
- "format": "yyyy-MM-dd"
- },
- "type_test": {
- "type": "keyword"
- },
- "event_type": {
- "type": "keyword"
- },
- "event": {
- "properties": {
- "category": {
- "type": "alias",
- "path": "event_type"
- }
- }
- },
- "host": {
- "type": "keyword"
- },
- "op_sys": {
- "type": "keyword"
- },
- "bool": {
- "type": "boolean"
- },
- "uptime" : {
- "type" : "long"
- },
- "port" : {
- "type" : "long"
- }
- }
- }
- }
- PUT /my-index-000003
- {
- "mappings": {
- "properties": {
- "host_ip": {
- "type":"ip"
- },
- "@timestamp": {
- "type": "date"
- },
- "date": {
- "type": "date"
- },
- "event_type": {
- "type": "keyword"
- },
- "event": {
- "properties": {
- "category": {
- "type": "alias",
- "path": "event_type"
- }
- }
- },
- "missing_keyword": {
- "type": "keyword"
- },
- "host": {
- "type": "keyword"
- },
- "os": {
- "type": "keyword"
- },
- "bool": {
- "type": "boolean"
- },
- "uptime" : {
- "type" : "long"
- },
- "port" : {
- "type" : "long"
- }
- }
- }
- }
- POST /my-index-000001/_bulk?refresh
- {"index":{"_id":1}}
- {"@timestamp":"1234567891","@timestamp_pretty":"12-12-2022","missing_keyword":"test","type_test":"abc","ip":"10.0.0.1","event_type":"alert","host":"doom","uptime":0,"port":1234,"os":"win10","version":"1.0.0","id":11}
- {"index":{"_id":2}}
- {"@timestamp":"1234567892","@timestamp_pretty":"13-12-2022","event_type":"alert","type_test":"abc","host":"CS","uptime":5,"port":1,"os":"win10","version":"1.2.0","id":12}
- {"index":{"_id":3}}
- {"@timestamp":"1234567893","@timestamp_pretty":"12-12-2022","event_type":"alert","type_test":"abc","host":"farcry","uptime":1,"port":1234,"bool":false,"os":"win10","version":"2.0.0","id":13}
- {"index":{"_id":4}}
- {"@timestamp":"1234567894","@timestamp_pretty":"13-12-2022","event_type":"alert","type_test":"abc","host":"GTA","uptime":3,"port":12,"os":"slack","version":"10.0.0","id":14}
- {"index":{"_id":5}}
- {"@timestamp":"1234567895","@timestamp_pretty":"17-12-2022","event_type":"alert","host":"sniper 3d","uptime":6,"port":1234,"os":"fedora","version":"20.1.0","id":15}
- {"index":{"_id":6}}
- {"@timestamp":"1234568896","@timestamp_pretty":"17-12-2022","event_type":"alert","host":"doom","port":65123,"bool":true,"os":"redhat","version":"20.10.0","id":16}
- {"index":{"_id":7}}
- {"@timestamp":"1234567897","@timestamp_pretty":"17-12-2022","missing_keyword":"yyy","event_type":"failure","host":"doom","uptime":15,"port":1234,"bool":true,"os":"redhat","version":"20.2.0","id":17}
- {"index":{"_id":8}}
- {"@timestamp":"1234567898","@timestamp_pretty":"12-12-2022","missing_keyword":"test","event_type":"success","host":"doom","uptime":16,"port":512,"os":"win10","version":"1.2.3","id":18}
- {"index":{"_id":9}}
- {"@timestamp":"1234567899","@timestamp_pretty":"15-12-2022","missing_keyword":"test","event_type":"success","host":"GTA","port":12,"bool":true,"os":"win10","version":"1.2.3","id":19}
- {"index":{"_id":10}}
- {"@timestamp":"1234567893","missing_keyword":null,"ip":"10.0.0.5","event_type":"alert","host":"farcry","uptime":1,"port":1234,"bool":true,"os":"win10","version":"1.2.3","id":110}
- POST /my-index-000002/_bulk?refresh
- {"index":{"_id":1}}
- {"@timestamp":"1234567991","type_test":"abc","ip":"10.0.0.1","event_type":"alert","host":"doom","uptime":0,"port":1234,"op_sys":"win10","id":21}
- {"index":{"_id":2}}
- {"@timestamp":"1234567992","type_test":"abc","event_type":"alert","host":"CS","uptime":5,"port":1,"op_sys":"win10","id":22}
- {"index":{"_id":3}}
- {"@timestamp":"1234567993","type_test":"abc","@timestamp_pretty":"2022-12-17","event_type":"alert","host":"farcry","uptime":1,"port":1234,"bool":false,"op_sys":"win10","id":23}
- {"index":{"_id":4}}
- {"@timestamp":"1234567994","event_type":"alert","host":"GTA","uptime":3,"port":12,"op_sys":"slack","id":24}
- {"index":{"_id":5}}
- {"@timestamp":"1234567995","event_type":"alert","host":"sniper 3d","uptime":6,"port":1234,"op_sys":"fedora","id":25}
- {"index":{"_id":6}}
- {"@timestamp":"1234568996","@timestamp_pretty":"2022-12-17","ip":"10.0.0.5","event_type":"alert","host":"doom","port":65123,"bool":true,"op_sys":"redhat","id":26}
- {"index":{"_id":7}}
- {"@timestamp":"1234567997","@timestamp_pretty":"2022-12-17","event_type":"failure","host":"doom","uptime":15,"port":1234,"bool":true,"op_sys":"redhat","id":27}
- {"index":{"_id":8}}
- {"@timestamp":"1234567998","ip":"10.0.0.1","event_type":"success","host":"doom","uptime":16,"port":512,"op_sys":"win10","id":28}
- {"index":{"_id":9}}
- {"@timestamp":"1234567999","ip":"10.0.0.1","event_type":"success","host":"GTA","port":12,"bool":false,"op_sys":"win10","id":29}
- POST /my-index-000003/_bulk?refresh
- {"index":{"_id":1}}
- {"@timestamp":"1334567891","host_ip":"10.0.0.1","event_type":"alert","host":"doom","uptime":0,"port":12,"os":"win10","id":31}
- {"index":{"_id":2}}
- {"@timestamp":"1334567892","event_type":"alert","host":"CS","os":"win10","id":32}
- {"index":{"_id":3}}
- {"@timestamp":"1334567893","event_type":"alert","host":"farcry","bool":true,"os":"win10","id":33}
- {"index":{"_id":4}}
- {"@timestamp":"1334567894","event_type":"alert","host":"GTA","os":"slack","bool":true,"id":34}
- {"index":{"_id":5}}
- {"@timestamp":"1234567895","event_type":"alert","host":"sniper 3d","os":"fedora","id":35}
- {"index":{"_id":6}}
- {"@timestamp":"1234578896","host_ip":"10.0.0.1","event_type":"alert","host":"doom","bool":true,"os":"redhat","id":36}
- {"index":{"_id":7}}
- {"@timestamp":"1234567897","event_type":"failure","missing_keyword":"test","host":"doom","bool":true,"os":"redhat","id":37}
- {"index":{"_id":8}}
- {"@timestamp":"1234577898","event_type":"success","host":"doom","os":"win10","id":38,"date":"1671235200000"}
- {"index":{"_id":9}}
- {"@timestamp":"1234577899","host_ip":"10.0.0.5","event_type":"success","host":"GTA","bool":true,"os":"win10","id":39}
- ----
- ====
- A sample query specifies at least one join key, using the <<eql-by-keyword,`by`
- keyword>>, and up to five filters:
- [source,console]
- ----
- GET /my-index*/_eql/search
- {
- "query": """
- sample by host
- [any where uptime > 0]
- [any where port > 100]
- [any where bool == true]
- """
- }
- ----
- // TEST[continued]
- By default, the response’s `hits.sequences` property contains up to 10 samples.
- Each sample has a set of `join_keys` and an array with one matching event for
- each of the filters. Events are returned in the order of the filters they match:
- [source,console-result]
- ----
- {
- ...
- "hits": {
- "total": {
- "value": 2,
- "relation": "eq"
- },
- "sequences": [
- {
- "join_keys": [
- "doom" <1>
- ],
- "events": [
- { <2>
- "_index": "my-index-000001",
- "_id": "7",
- "_source": {
- "@timestamp": "1234567897",
- "@timestamp_pretty": "17-12-2022",
- "missing_keyword": "yyy",
- "event_type": "failure",
- "host": "doom",
- "uptime": 15,
- "port": 1234,
- "bool": true,
- "os": "redhat",
- "version": "20.2.0",
- "id": 17
- }
- },
- { <3>
- "_index": "my-index-000001",
- "_id": "1",
- "_source": {
- "@timestamp": "1234567891",
- "@timestamp_pretty": "12-12-2022",
- "missing_keyword": "test",
- "type_test": "abc",
- "ip": "10.0.0.1",
- "event_type": "alert",
- "host": "doom",
- "uptime": 0,
- "port": 1234,
- "os": "win10",
- "version": "1.0.0",
- "id": 11
- }
- },
- { <4>
- "_index": "my-index-000001",
- "_id": "6",
- "_source": {
- "@timestamp": "1234568896",
- "@timestamp_pretty": "17-12-2022",
- "event_type": "alert",
- "host": "doom",
- "port": 65123,
- "bool": true,
- "os": "redhat",
- "version": "20.10.0",
- "id": 16
- }
- }
- ]
- },
- {
- "join_keys": [
- "farcry" <5>
- ],
- "events": [
- {
- "_index": "my-index-000001",
- "_id": "3",
- "_source": {
- "@timestamp": "1234567893",
- "@timestamp_pretty": "12-12-2022",
- "event_type": "alert",
- "type_test": "abc",
- "host": "farcry",
- "uptime": 1,
- "port": 1234,
- "bool": false,
- "os": "win10",
- "version": "2.0.0",
- "id": 13
- }
- },
- {
- "_index": "my-index-000001",
- "_id": "10",
- "_source": {
- "@timestamp": "1234567893",
- "missing_keyword": null,
- "ip": "10.0.0.5",
- "event_type": "alert",
- "host": "farcry",
- "uptime": 1,
- "port": 1234,
- "bool": true,
- "os": "win10",
- "version": "1.2.3",
- "id": 110
- }
- },
- {
- "_index": "my-index-000003",
- "_id": "3",
- "_source": {
- "@timestamp": "1334567893",
- "event_type": "alert",
- "host": "farcry",
- "bool": true,
- "os": "win10",
- "id": 33
- }
- }
- ]
- }
- ]
- }
- }
- ----
- // TESTRESPONSE[s/ \.\.\.\n/"is_partial": false, "is_running": false, "took": $body.took, "timed_out": false,/]
- <1> The events in the first sample have a value of `doom` for `host`.
- <2> This event matches the first filter.
- <3> This event matches the second filter.
- <4> This event matches the third filter.
- <5> The events in the second sample have a value of `farcry` for `host`.
- You can specify multiple join keys:
- [source,console]
- ----
- GET /my-index*/_eql/search
- {
- "query": """
- sample by host
- [any where uptime > 0] by os
- [any where port > 100] by op_sys
- [any where bool == true] by os
- """
- }
- ----
- // TEST[continued]
- This query will return samples where each of the events shares the same value
- for `os` or `op_sys`, as well as for `host`. For example:
- [source,console-result]
- ----
- {
- ...
- "hits": {
- "total": {
- "value": 2,
- "relation": "eq"
- },
- "sequences": [
- {
- "join_keys": [
- "doom", <1>
- "redhat"
- ],
- "events": [
- {
- "_index": "my-index-000001",
- "_id": "7",
- "_source": {
- "@timestamp": "1234567897",
- "@timestamp_pretty": "17-12-2022",
- "missing_keyword": "yyy",
- "event_type": "failure",
- "host": "doom",
- "uptime": 15,
- "port": 1234,
- "bool": true,
- "os": "redhat",
- "version": "20.2.0",
- "id": 17
- }
- },
- {
- "_index": "my-index-000002",
- "_id": "6",
- "_source": {
- "@timestamp": "1234568996",
- "@timestamp_pretty": "2022-12-17",
- "ip": "10.0.0.5",
- "event_type": "alert",
- "host": "doom",
- "port": 65123,
- "bool": true,
- "op_sys": "redhat",
- "id": 26
- }
- },
- {
- "_index": "my-index-000001",
- "_id": "6",
- "_source": {
- "@timestamp": "1234568896",
- "@timestamp_pretty": "17-12-2022",
- "event_type": "alert",
- "host": "doom",
- "port": 65123,
- "bool": true,
- "os": "redhat",
- "version": "20.10.0",
- "id": 16
- }
- }
- ]
- },
- {
- "join_keys": [
- "farcry",
- "win10"
- ],
- "events": [
- {
- "_index": "my-index-000001",
- "_id": "3",
- "_source": {
- "@timestamp": "1234567893",
- "@timestamp_pretty": "12-12-2022",
- "event_type": "alert",
- "type_test": "abc",
- "host": "farcry",
- "uptime": 1,
- "port": 1234,
- "bool": false,
- "os": "win10",
- "version": "2.0.0",
- "id": 13
- }
- },
- {
- "_index": "my-index-000002",
- "_id": "3",
- "_source": {
- "@timestamp": "1234567993",
- "type_test": "abc",
- "@timestamp_pretty": "2022-12-17",
- "event_type": "alert",
- "host": "farcry",
- "uptime": 1,
- "port": 1234,
- "bool": false,
- "op_sys": "win10",
- "id": 23
- }
- },
- {
- "_index": "my-index-000001",
- "_id": "10",
- "_source": {
- "@timestamp": "1234567893",
- "missing_keyword": null,
- "ip": "10.0.0.5",
- "event_type": "alert",
- "host": "farcry",
- "uptime": 1,
- "port": 1234,
- "bool": true,
- "os": "win10",
- "version": "1.2.3",
- "id": 110
- }
- }
- ]
- }
- ]
- }
- }
- ----
- // TESTRESPONSE[s/ \.\.\.\n/"is_partial": false, "is_running": false, "took": $body.took, "timed_out": false,/]
- <1> The events in this sample have a value of `doom` for `host` and a value of
- `redhat` for `os` or `op_sys`.
- By default, the response of a sample query contains up to 10 samples, with one
- sample per unique set of join keys. Use the `size` parameter to get a smaller or
- larger set of samples. To retrieve more than one sample per set of join keys,
- use the `max_samples_per_key` parameter. Pipes are not supported for sample
- queries.
- [source,console]
- ----
- GET /my-index*/_eql/search
- {
- "max_samples_per_key": 2, <1>
- "size": 20, <2>
- "query": """
- sample
- [any where uptime > 0] by host,os
- [any where port > 100] by host,op_sys
- [any where bool == true] by host,os
- """
- }
- ----
- // TEST[continued]
- <1> Retrieve up to 2 samples per set of join keys.
- <2> Retrieve up to 20 samples in total.
- [discrete]
- [[retrieve-selected-fields]]
- === Retrieve selected fields
- By default, each hit in the search response includes the document `_source`,
- which is the entire JSON object that was provided when indexing the document.
- You can use the <<common-options-response-filtering,`filter_path`>> query
- parameter to filter the API response. For example, the following search returns
- only the timestamp and PID from the `_source` of each matching event.
- [source,console]
- ----
- GET /my-data-stream/_eql/search?filter_path=hits.events._source.@timestamp,hits.events._source.process.pid
- {
- "query": """
- process where process.name == "regsvr32.exe"
- """
- }
- ----
- // TEST[setup:sec_logs]
- The API returns the following response.
- [source,console-result]
- ----
- {
- "hits": {
- "events": [
- {
- "_source": {
- "@timestamp": "2099-12-07T11:07:09.000Z",
- "process": {
- "pid": 2012
- }
- }
- },
- {
- "_source": {
- "@timestamp": "2099-12-07T11:07:10.000Z",
- "process": {
- "pid": 2012
- }
- }
- }
- ]
- }
- }
- ----
- You can also use the `fields` parameter to retrieve and format specific fields
- in the response. This field is identical to the search API's
- <<search-fields,`fields` parameter>>.
- include::{es-repo-dir}/search/search-your-data/retrieve-selected-fields.asciidoc[tag=fields-param-desc]
- The following search request uses the `fields` parameter to retrieve values for
- the `event.type` field, all fields starting with `process.`, and the
- `@timestamp` field. The request also uses the `filter_path` query parameter to
- exclude the `_source` of each hit.
- [source,console]
- ----
- GET /my-data-stream/_eql/search?filter_path=-hits.events._source
- {
- "query": """
- process where process.name == "regsvr32.exe"
- """,
- "fields": [
- "event.type",
- "process.*", <1>
- {
- "field": "@timestamp",
- "format": "epoch_millis" <2>
- }
- ]
- }
- ----
- // TEST[setup:sec_logs]
- include::{es-repo-dir}/search/search-your-data/retrieve-selected-fields.asciidoc[tag=fields-param-callouts]
- The response includes values as a flat list in the `fields` section for each
- hit.
- [source,console-result]
- ----
- {
- ...
- "hits": {
- "total": ...,
- "events": [
- {
- "_index": ".ds-my-data-stream-2099.12.07-000001",
- "_id": "OQmfCaduce8zoHT93o4H",
- "fields": {
- "process.name": [
- "regsvr32.exe"
- ],
- "process.name.keyword": [
- "regsvr32.exe"
- ],
- "@timestamp": [
- "4100324829000"
- ],
- "process.command_line": [
- "regsvr32.exe /s /u /i:https://...RegSvr32.sct scrobj.dll"
- ],
- "process.command_line.keyword": [
- "regsvr32.exe /s /u /i:https://...RegSvr32.sct scrobj.dll"
- ],
- "process.executable.keyword": [
- "C:\\Windows\\System32\\regsvr32.exe"
- ],
- "process.pid": [
- 2012
- ],
- "process.executable": [
- "C:\\Windows\\System32\\regsvr32.exe"
- ]
- }
- },
- ....
- ]
- }
- }
- ----
- // TESTRESPONSE[s/ \.\.\.\n/"is_partial": false, "is_running": false, "took": $body.took, "timed_out": false,/]
- // TESTRESPONSE[s/"total": \.\.\.,/"total": { "value": 2, "relation": "eq" },/]
- // TESTRESPONSE[s/"_index": ".ds-my-data-stream-2099.12.07-000001"/"_index": $body.hits.events.0._index/]
- // TESTRESPONSE[s/"_id": "OQmfCaduce8zoHT93o4H"/"_id": $body.hits.events.0._id/]
- // TESTRESPONSE[s/ \.\.\.\.\n/$body.hits.events.1/]
- [discrete]
- [[eql-use-runtime-fields]]
- === Use runtime fields
- Use the `runtime_mappings` parameter to extract and create <<runtime,runtime
- fields>> during a search. Use the `fields` parameter to include runtime fields
- in the response.
- The following search creates a `day_of_week` runtime field from the `@timestamp`
- and returns it in the response.
- [source,console]
- ----
- GET /my-data-stream/_eql/search?filter_path=-hits.events._source
- {
- "runtime_mappings": {
- "day_of_week": {
- "type": "keyword",
- "script": "emit(doc['@timestamp'].value.dayOfWeekEnum.toString())"
- }
- },
- "query": """
- process where process.name == "regsvr32.exe"
- """,
- "fields": [
- "@timestamp",
- "day_of_week"
- ]
- }
- ----
- // TEST[setup:sec_logs]
- The API returns:
- [source,console-result]
- ----
- {
- ...
- "hits": {
- "total": ...,
- "events": [
- {
- "_index": ".ds-my-data-stream-2099.12.07-000001",
- "_id": "OQmfCaduce8zoHT93o4H",
- "fields": {
- "@timestamp": [
- "2099-12-07T11:07:09.000Z"
- ],
- "day_of_week": [
- "MONDAY"
- ]
- }
- },
- ....
- ]
- }
- }
- ----
- // TESTRESPONSE[s/ \.\.\.\n/"is_partial": false, "is_running": false, "took": $body.took, "timed_out": false,/]
- // TESTRESPONSE[s/"total": \.\.\.,/"total": { "value": 2, "relation": "eq" },/]
- // TESTRESPONSE[s/"_index": ".ds-my-data-stream-2099.12.07-000001"/"_index": $body.hits.events.0._index/]
- // TESTRESPONSE[s/"_id": "OQmfCaduce8zoHT93o4H"/"_id": $body.hits.events.0._id/]
- // TESTRESPONSE[s/ \.\.\.\.\n/$body.hits.events.1/]
- [discrete]
- [[specify-a-timestamp-or-event-category-field]]
- === Specify a timestamp or event category field
- The EQL search API uses the `@timestamp` and `event.category` fields from the
- {ecs-ref}[ECS] by default. To specify different fields, use the
- `timestamp_field` and `event_category_field` parameters:
- [source,console]
- ----
- GET /my-data-stream/_eql/search
- {
- "timestamp_field": "file.accessed",
- "event_category_field": "file.type",
- "query": """
- file where (file.size > 1 and file.type == "file")
- """
- }
- ----
- // TEST[setup:sec_logs]
- The event category field must be mapped as a <<keyword,`keyword`>> family field
- type. The timestamp field should be mapped as a <<date,`date`>> field type.
- <<date_nanos,`date_nanos`>> timestamp fields are not supported. You cannot use a
- <<nested,`nested`>> field or the sub-fields of a `nested` field as the timestamp
- or event category field.
- [discrete]
- [[eql-search-specify-a-sort-tiebreaker]]
- === Specify a sort tiebreaker
- By default, the EQL search API returns matching hits by timestamp. If two or
- more events share the same timestamp, {es} uses a tiebreaker field value to sort
- the events in ascending order. {es} orders events with no
- tiebreaker value after events with a value.
- If you don't specify a tiebreaker field or the events also share the same
- tiebreaker value, {es} considers the events concurrent and may
- not return them in a consistent sort order.
- To specify a tiebreaker field, use the `tiebreaker_field` parameter. If you use
- the {ecs-ref}[ECS], we recommend using `event.sequence` as the tiebreaker field.
- [source,console]
- ----
- GET /my-data-stream/_eql/search
- {
- "tiebreaker_field": "event.sequence",
- "query": """
- process where process.name == "cmd.exe" and stringContains(process.executable, "System32")
- """
- }
- ----
- // TEST[setup:sec_logs]
- [discrete]
- [[eql-search-filter-query-dsl]]
- === Filter using Query DSL
- The `filter` parameter uses <<query-dsl,Query DSL>> to limit the documents on
- which an EQL query runs.
- [source,console]
- ----
- GET /my-data-stream/_eql/search
- {
- "filter": {
- "range": {
- "@timestamp": {
- "gte": "now-1d/d",
- "lt": "now/d"
- }
- }
- },
- "query": """
- file where (file.type == "file" and file.name == "cmd.exe")
- """
- }
- ----
- // TEST[setup:sec_logs]
- [discrete]
- [[eql-search-async]]
- === Run an async EQL search
- By default, EQL search requests are synchronous and wait for complete results
- before returning a response. However, complete results can take longer for
- searches across large data sets or <<data-tiers,frozen>> data.
- To avoid long waits, run an async EQL search. Set `wait_for_completion_timeout`
- to a duration you'd like to wait for synchronous results.
- [source,console]
- ----
- GET /my-data-stream/_eql/search
- {
- "wait_for_completion_timeout": "2s",
- "query": """
- process where process.name == "cmd.exe"
- """
- }
- ----
- // TEST[setup:sec_logs]
- If the request doesn't finish within the timeout period, the search becomes async
- and returns a response that includes:
- * A search ID
- * An `is_partial` value of `true`, indicating the search results are
- incomplete
- * An `is_running` value of `true`, indicating the search is ongoing
- The async search continues to run in the background without blocking other
- requests.
- [source,console-result]
- ----
- {
- "id": "FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=",
- "is_partial": true,
- "is_running": true,
- "took": 2000,
- "timed_out": false,
- "hits": ...
- }
- ----
- // TESTRESPONSE[s/FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=/$body.id/]
- // TESTRESPONSE[s/"is_partial": true/"is_partial": $body.is_partial/]
- // TESTRESPONSE[s/"is_running": true/"is_running": $body.is_running/]
- // TESTRESPONSE[s/"took": 2000/"took": $body.took/]
- // TESTRESPONSE[s/"hits": \.\.\./"hits": $body.hits/]
- To check the progress of an async search, use the <<get-async-eql-search-api,get
- async EQL search API>> with the search ID. Specify how long you'd like for
- complete results in the `wait_for_completion_timeout` parameter.
- [source,console]
- ----
- GET /_eql/search/FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=?wait_for_completion_timeout=2s
- ----
- // TEST[skip: no access to search ID]
- If the response's `is_running` value is `false`, the async search has finished.
- If the `is_partial` value is `false`, the returned search results are
- complete.
- [source,console-result]
- ----
- {
- "id": "FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=",
- "is_partial": false,
- "is_running": false,
- "took": 2000,
- "timed_out": false,
- "hits": ...
- }
- ----
- // TESTRESPONSE[s/FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=/$body.id/]
- // TESTRESPONSE[s/"took": 2000/"took": $body.took/]
- // TESTRESPONSE[s/"hits": \.\.\./"hits": $body.hits/]
- Another more lightweight way to check the progress of an async search is to use
- the <<get-async-eql-status-api,get async EQL status API>> with the search ID.
- [source,console]
- ----
- GET /_eql/search/status/FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=
- ----
- // TEST[skip: no access to search ID]
- [source,console-result]
- ----
- {
- "id": "FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=",
- "is_running": false,
- "is_partial": false,
- "expiration_time_in_millis": 1611690295000,
- "completion_status": 200
- }
- ----
- // TESTRESPONSE[s/FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=/$body.id/]
- // TESTRESPONSE[s/"expiration_time_in_millis": 1611690295000/"expiration_time_in_millis": $body.expiration_time_in_millis/]
- [discrete]
- [[eql-search-store-async-eql-search]]
- === Change the search retention period
- By default, the EQL search API stores async searches for five days. After this
- period, any searches and their results are deleted. Use the `keep_alive`
- parameter to change this retention period:
- [source,console]
- ----
- GET /my-data-stream/_eql/search
- {
- "keep_alive": "2d",
- "wait_for_completion_timeout": "2s",
- "query": """
- process where process.name == "cmd.exe"
- """
- }
- ----
- // TEST[setup:sec_logs]
- You can use the <<get-async-eql-search-api,get async EQL search API>>'s
- `keep_alive` parameter to later change the retention period. The new retention
- period starts after the get request runs.
- [source,console]
- ----
- GET /_eql/search/FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=?keep_alive=5d
- ----
- // TEST[skip: no access to search ID]
- Use the <<delete-async-eql-search-api,delete async EQL search API>> to
- manually delete an async EQL search before the `keep_alive` period ends. If the
- search is still ongoing, {es} cancels the search request.
- [source,console]
- ----
- DELETE /_eql/search/FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=
- ----
- // TEST[skip: no access to search ID]
- [discrete]
- [[eql-search-store-sync-eql-search]]
- === Store synchronous EQL searches
- By default, the EQL search API only stores async searches. To save a synchronous
- search, set `keep_on_completion` to `true`:
- [source,console]
- ----
- GET /my-data-stream/_eql/search
- {
- "keep_on_completion": true,
- "wait_for_completion_timeout": "2s",
- "query": """
- process where process.name == "cmd.exe"
- """
- }
- ----
- // TEST[setup:sec_logs]
- The response includes a search ID. `is_partial` and `is_running` are `false`,
- indicating the EQL search was synchronous and returned complete results.
- [source,console-result]
- ----
- {
- "id": "FjlmbndxNmJjU0RPdExBTGg0elNOOEEaQk9xSjJBQzBRMldZa1VVQ2pPa01YUToxMDY=",
- "is_partial": false,
- "is_running": false,
- "took": 52,
- "timed_out": false,
- "hits": ...
- }
- ----
- // TESTRESPONSE[s/FjlmbndxNmJjU0RPdExBTGg0elNOOEEaQk9xSjJBQzBRMldZa1VVQ2pPa01YUToxMDY=/$body.id/]
- // TESTRESPONSE[s/"took": 52/"took": $body.took/]
- // TESTRESPONSE[s/"hits": \.\.\./"hits": $body.hits/]
- Use the <<get-async-eql-search-api,get async EQL search API>> to get the
- same results later:
- [source,console]
- ----
- GET /_eql/search/FjlmbndxNmJjU0RPdExBTGg0elNOOEEaQk9xSjJBQzBRMldZa1VVQ2pPa01YUToxMDY=
- ----
- // TEST[skip: no access to search ID]
- Saved synchronous searches are still subject to the `keep_alive` parameter's
- retention period. When this period ends, the search and its results are deleted.
- You can also check only the status of the saved synchronous search without
- results by using <<get-async-eql-status-api,get async EQL status API>>.
- You can also manually delete saved synchronous searches using the
- <<delete-async-eql-search-api,delete async EQL search API>>.
- [discrete]
- [[run-eql-search-across-clusters]]
- === Run an EQL search across clusters
- experimental::[]
- The EQL search API supports <<modules-cross-cluster-search,cross-cluster search>>.
- However, the local and <<remote-clusters,remote clusters>>
- must use the same {es} version
- if they have versions prior to 7.17.7 (included) or prior to 8.5.1 (included).
- The following <<cluster-update-settings,cluster update settings>> request
- adds two remote clusters: `cluster_one` and `cluster_two`.
- [source,console]
- ----
- PUT /_cluster/settings
- {
- "persistent": {
- "cluster": {
- "remote": {
- "cluster_one": {
- "seeds": [
- "127.0.0.1:9300"
- ]
- },
- "cluster_two": {
- "seeds": [
- "127.0.0.1:9301"
- ]
- }
- }
- }
- }
- }
- ----
- // TEST[setup:host]
- // TEST[s/127.0.0.1:930\d+/\${transport_host}/]
- To target a data stream or index on a remote cluster, use the
- `<cluster>:<target>` syntax.
- [source,console]
- ----
- GET /cluster_one:my-data-stream,cluster_two:my-data-stream/_eql/search
- {
- "query": """
- process where process.name == "regsvr32.exe"
- """
- }
- ----
- // TEST[continued]
- // TEST[setup:sec_logs]
- // TEST[teardown:data_stream_cleanup]
- [discrete]
- [[eql-circuit-breaker]]
- === EQL circuit breaker settings
- The relevant circuit breaker settings can be found in the <<circuit-breakers-page-eql, Circuit Breakers page>>.
- include::syntax.asciidoc[]
- include::functions.asciidoc[]
- include::pipes.asciidoc[]
- include::detect-threats-with-eql.asciidoc[]
|