๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Back-end

ElastAlert

๋ฐ˜์‘ํ˜•

ElastAlert๋ž€?

  • Elasticsearch์˜ ๋ฐ์ดํ„ฐ์—์„œ ์ž‘์„ฑํ•œ ๊ทœ์น™์— ๋”ฐ๋ผ์„œ ์•Œ๋ฆผ์„ ์ „์†กํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ.
  • Elasticsearch์— ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋กํ•˜๊ณ  ์žˆ๊ณ  ํ•ด๋‹น ๋ฐ์ดํ„ฐ๊ฐ€ ํŠน์ • ํŒจํ„ด(๊ทœ์น™)๊ณผ ์ผ์น˜ํ•  ๋•Œ ์•Œ๋ฆผ์„ ๋ฐ›๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ.

ElastAlert ์ž‘๋™ ๋ฐฉ์‹

๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ ์ฟผ๋ฆฌ

โ†’ ์ •์˜ํ•œ ๊ทœ์น™ ์œ ํ˜•์— ์ผ์น˜ํ•˜๋Š” ์ง€๋ฅผ ๊ฒ€์‚ฌ

โ†’ ์ผ์น˜ํ•œ๋‹ค๋ฉด ์ •์˜ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ alert ๋ฐœ

ElastAlert ๊ทœ์น™ ๊ตฌ์„ฑ

name : ๊ทœ์น™์˜ ๊ณ ์œ ํ•œ ์ด๋ฆ„. ๋™์ผํ•œ ์ด๋ฆ„์ด 2๊ฐœ ์ด์ƒ์ผ ๊ฒฝ์šฐ ElastAlert๊ฐ€ ์‹œ์ž‘๋˜์ง€ ์•Š๋Š”๋‹ค.
index : ์ฟผ๋ฆฌํ•  ๋ฐ์ดํ„ฐ ์†Œ์Šค์˜ ์œ„์น˜. ์™€์ผ๋“œ ์นด๋“œ, ์—ฌ๋Ÿฌ๊ฐœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

index: topbeat-*,packetbeat-*

type : ๊ทœ์น™์˜ ์œ ํ˜•. type์— ๋”ฐ๋ผ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ alert๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

realert : ํ•ด๋‹น ๊ทœ์น™์œผ๋กœ ์ธํ•œ alert๊ฐ€ ๋‹ค์‹œ ๋ณด๋‚ด์งˆ๋•Œ๊นŒ์ง€์˜ ์ตœ์†Œ ์‹œ๊ฐ„

filter : ๋ฐ์ดํ„ฐ์—์„œ ๊ฒฐ๊ณผ๋ฅผ ํ•„ํ„ฐ๋ง ํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ์ค€.

alert : alert ์œ ํ˜• (Slack, AWS SES, AWS SNS, Discord, Email, GoogleChat, HTTP POST ๋“ฑ๋“ฑ)

alert_text : alert ๋ฉ”์„ธ์ง€ ๊ตฌ์„ฑ

alert_text_type : ๋ฉ”์„ธ์ง€์˜ ํƒ€์ž…

alert_text_args : ๋ฉ”์„ธ์ง€์—์„œ ์‚ฌ์šฉํ•  ๋งค๊ฐœ๋ณ€์ˆ˜

  1. Jinja Template (alert_text_jinja)
alert_text_type: alert_text_jinja

alert_text: 
  Alert triggered! *({{num_hits}} Matches!)*
  Something happened with {{username}} ({{email}})
  {{description|truncate}}
  1. ํŒŒ์ด์„  Formatting ๋ฌธ๋ฒ• (alert_text_only)
alert_text: "Something happened with {0} at {1}"
alert_text_type: alert_text_only
alert_text_args: ["username", "@timestamp"]

Filter ์œ ํ˜•

query_string

filter:
- query:
    query_string:
      query: "field: value OR otherfield: otherval

term

filter:
- term:
    name_field: "bob"

terms

- terms:
    fieldX: ["value1", "value2"]

wildcard

filter:
- query:
    wildcard:
      field: "foo*bar"

range

filter:
- range:
    status_code:
      from: 500
      to: 599

Rule Type

any

ํ•„์— ์ผ์น˜ํ•˜๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด์„œ alert ๋ฐœ์ƒ

blacklist

๋ชจ๋‹ˆํ„ฐ๋ง ํ•„๋“œ๋ฅผ ํ™•์ธํ•˜๊ณ  ๋ฐ์ดํ„ฐ๊ฐ€ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ์— ์žˆ์œผ๋ฉด alert ๋ฐœ์ƒ

  • compare_key : ๋ชจ๋‹ˆํ„ฐ๋ง ํ•„๋“œ, ํ•ด๋‹น ํ•„๋“œ๊ฐ€ null์ด๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฌด์‹œ๋œ๋‹ค.
  • blacklist : ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ ๊ฐ’ or ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ ๊ฐ’์„ ํฌํ•จํ•˜๋Š” ํŒŒ์ผ์˜ ๊ฒฝ๋กœ
# (Required)
type: **blacklist**
# (Required)
****compare_key: filed1
# (Required)
blacklist:
    - value1
    - value2
    - "!file /tmp/blacklist1.txt"
    - "!file /tmp/blacklist2.txt"

whitelist

๋ชจ๋‹ˆํ„ฐ๋ง ํ•„๋“œ๋ฅผ ํ™•์ธํ•˜๊ณ  ๋ฐ์ดํ„ฐ๊ฐ€ ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ์— ์—†์œผ๋ฉด alert ๋ฐœ์ƒ

  • compare_key : ๋ชจ๋‹ˆํ„ฐ๋ง ํ•„๋“œ
  • ignore_null : true์ผ ๋•Œ ํ•ด๋‹น ํ•„๋“œ๊ฐ€ null์ด๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฌด์‹œ๋œ๋‹ค.
  • whitelist : ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๊ฐ’ or ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๊ฐ’์„ ํฌํ•จํ•˜๋Š” ํŒŒ์ผ์˜ ๊ฒฝ๋กœ
# (Required)
type: **whitelist** 

ignore_null: true
# (Required)
compare_key: filed1
# (Required)
whitelist:
    - value1
    - value2
    - "!file /tmp/whitelist1.txt"
    - "!file /tmp/whitelist2.txt"

change

๋ชจ๋‹ˆํ„ฐ๋ง ํ•„๋“œ์— ๋Œ€ํ•ด์„œ ๊ฐ’์ด ๋‹ฌ๋ผ์ง€๋ฉด alert ๋ฐœ์ƒ

  • compare_key : ๋ชจ๋‹ˆํ„ฐ๋ง ํ•„๋“œ, ํ•„๋“œ๋ฅผ ์—ฌ๋Ÿฌ๊ฐœ ์ •์˜ ๊ฐ€๋Šฅ
  • ignore_null : ํ•„๋“œ๊ฐ€ ์—†์œผ๋ฉด ๋ฐ์ดํ„ฐ ๋ฌด์‹œ
  • query_key : ํ•ด๋‹น ํ•„๋“œ์— ๊ฐ’์ด ๊ฐ™์€ ๊ฒฝ์šฐ์— compare_key๋ฅผ ๋น„๊ต
  • timeframe : query_key์— ๋Œ€ํ•œ ๋งˆ์ง€๋ง‰ ๊ฒฐ๊ณผ๋ฅผ ์–ผ๋งˆ๋‚˜ ์œ ์ง€์‹œํ‚ฌ์ง€. (Optional)
# (Required)
name: New country login

# (Required)
type: change

# (Required)
index: logstash-*

# (Required)
compare_key: country_name

# (Required)
ignore_null: true

# (Required)
query_key: username

timeframe:
  days: 1

# (Required)
filter:
- query:
    query_string:
      query: "document_type: login"

frequency

ํ•„ํ„ฐ์— ์ผ์น˜ํ•˜๋Š” ์ด๋ฒคํŠธ์˜ ์ˆ˜๊ฐ€ ์ผ์ • ์ˆ˜๋ฅผ ๋„˜๊ธฐ๋ฉด alert ๋ฐœ์ƒ

  • num_events : alert๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ด๋ฒคํŠธ์˜ ์ˆ˜
  • timeframe : num_events์˜ ์ˆ˜๋ฅผ ์ธก์ •ํ•˜๋Š” ์‹œ๊ฐ„
# (Required)
name: Example frequency rule

# (Required)
type: frequency

# (Required)
index: logstash-*

# (Required, frequency specific)
num_events: 50

# (Required, frequency specific)
timeframe:
  hours: 4

# (Required)
filter:
- term:
    some_field: "some_value"

spike

ํŠน์ • ๊ธฐ๊ฐ„์˜ ์ด๋ฒคํŠธ ์ˆ˜๊ฐ€, ํŠน์ • ๋ฐฐ์ˆ˜๋งŒํผ ์ฆ๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ค„์–ด๋“ค์—ˆ์„ ๋•Œ alert ๋ฐœ์ƒ

  • spike_height : alert๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ด๋ฒคํŠธ ๋ฐฐ์ˆ˜
  • spike_type : 'up' or 'down' or 'both'
    up ์€ ์ฆ๊ฐ€ํ–ˆ์„ ๋•Œ
    down์€ ๊ฐ์†Œํ–ˆ์„ ๋•Œ
    both๋Š” ์ฆ๊ฐ€, ๊ฐ์†Œ ํ–ˆ์„ ๋•Œ
  • timeframe : ๋น„๊ตํ•  ๊ธฐ๊ฐ„ ์ •์˜
  • threshold_ref : reference์— ํ•ด๋‹นํ•˜๋Š” ์ตœ์†Œ ์ด๋ฒคํŠธ ์ˆ˜
  • threshold_cur : current์— ํ•ด๋‹นํ•˜๋Š” ์ตœ์†Œ ์ด๋ฒคํŠธ ์ˆ˜
  • field_value : ์ด๋ฒคํŠธ์˜ ์ˆ˜๊ฐ€ ์•„๋‹Œ ํ•ด๋‹น ํ•„๋“œ์˜ ๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ alert๋ฅผ ๋ฐœ์ƒ -> ์˜จ๋„๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•  ๋•Œ ์œ ์šฉ
# (Required)
name: Event spike

# (Required)
type: spike

# (Required)
index: logstash-*

# (Required one of _cur or _ref, spike specific)
threshold_cur: 15
#threshold_ref: 5

# (Required, spike specific)
timeframe:
  hours: 2

# (Required, spike specific)
spike_height: 4

# (Required, spike specific)
spike_type: "up"

# (Required)
filter:
- query:
    query_string:
      query: "field: value"

# 10:00 ~ 11:00 : 5 events (ref: 0, cur: 5)  - Alert X
# 11:00 ~ 12:00 : 5 events (ref: 0, cur: 10) - Alert X
# 12:00 ~ 13:00 : 10 events (ref: 5, cur: 15) - Alert X
# 13:00 ~ 14:00 : 35 events (ref: 10, cur: 45) - Alert O

flatline

์ด๋ฒคํŠธ์˜ ์ด ์ˆ˜๊ฐ€ ์ผ์ • ๊ธฐ๊ฐ„ ๋™์•ˆ ์ง€์ •๋œ ์ž„๊ณ„๊ฐ’ ์•„๋ž˜์— ์žˆ์„ ๋•Œ alert ๋ฐœ์ƒ

  • threshold : alert๊ฐ€ ๋ฐœ์ƒ๋˜์ง€ ์•Š์„ ์ตœ์†Œ ์ด๋ฒคํŠธ ์ˆ˜
  • timeframe : ๋ชจ๋‹ˆํ„ฐ๋ง ๊ธฐ๊ฐ„
# (Required)
name: Event flatline

# (Required)
type: flatline 

# (Required)
index: logstash-*

# (Required)
threshold: 15

# (Required)
timeframe:
  hours: 2

# (Required)
filter:
- query:
    query_string:
      query: "field: value"

new_term

์ƒˆ๋กœ์šด ๊ฐ’์ด ๋“ค์–ด์˜ค๋ฉด alert ๋ฐœ์ƒ

  • fileds : ๋ชจ๋‹ˆํ„ฐ๋ง filed, ์—†์œผ๋ฉด query_key ์‚ฌ์šฉ
# (Required)
name: Event newterm 

# (Required)
type: new_term 

# (Required)
index: logstash-*

fileds: ["field1"]

# (Required)
filter:
- query:
    query_string:
      query: "field: value"

cardinality

ํŠน์ • ํ•„๋“œ์˜ ์œ ๋‹ˆํฌํ•œ ๊ฐ’์˜ ์ˆ˜๊ฐ€ ์ž„๊ณ„๊ฐ’๋ณด๋‹ค ๋†’๊ฑฐ๋‚˜ ๋‚ฎ์„ ๋•Œ alert ๋ฐœ์ƒ

# Alert when the rate of events exceeds a threshold
# (Required)
# Index to search, wildcard supported
index: logstash-*

# (Required)
# Rule name, must be unique
name: Example cardinality rule

# (Required)
type: cardinality

# (Required, cardinality specific)
# Count the number of unique values for this field
cardinality_field: "Hostname"

# (Required, frequency specific)
# Alert when there less than 15 unique hostnames
min_cardinality: 15
max_cardinality: 15

# (Required, frequency specific)
# The cardinality is defined as the number of unique values for the most recent 4 hours
timeframe:
  hours: 4

# (Required)
filter:
- term:
    status: "active"

metric_aggregation

ํŠน์ • ํ•„๋“œ๋“ค์˜ ํ•ฉ, ํ‰๊ท , ์ตœ์†Œ, ์ตœ๋Œ€ ๋“ฑ์„ ๊ณ„์‚ฐํ•˜์—ฌ ๊ธฐ์ค€์— ์ถฉ์กฑํ•˜๋ฉด alert ๋ฐœ์ƒ

# (Required)
name: Metricbeat CPU Spike Rule
# (Required)
type: metric_aggregation
# (Required)
index: metricbeat-*

buffer_time:
  hours: 1

# (Required)
metric_agg_key: system.cpu.user.pct (๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ’์ด์—ฌ์•ผํ•จ.)
# (Required)
metric_agg_type: avg
query_key: beat.hostname

bucket_interval:
  minutes: 5

# (Required)
min_threshold: 0.1
# (Required)
max_threshold: 0.8
# (Required)
filter:
- term:
    metricset.name: cpu

# (Required)
# The alert is use when a match is found
alert:
- "debug"

spike_aggregation

ํŠน์ • ํ•„๋“œ๋“ค์˜ ํ•ฉ, ํ‰๊ท , ์ตœ์†Œ, ์ตœ๋Œ€ ๋“ฑ์„ ๊ณ„์‚ฐํ•œ ๊ฐ’์ด spike_height๋ฐฐ ๋งŒํผ ์ฆ๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ค„์–ด๋“ค์—ˆ์„ ๋•Œ alert ๋ฐœ์ƒ

percentage_match

์ฟผ๋ฆฌ or ํ•„๋“œ์˜ ๊ฐ’์ด ์„ค์ •ํ•œ ๊ธฐ๊ฐ„๋™์•ˆ์˜ ๋น„์œจ์ด ์„ค์ •ํ•œ ๋น„์œจ๋ณด๋‹ค ์ž‘๊ฑฐ๋‚˜, ํด ๋•Œ alert ๋ฐœ์ƒ

# (Required)
name: Example Percentage Match
# (Required)
type: percentage_match
# (95% ์ž‘์œผ๋ฉด alert)
# (Required)
index: logstash-http-request-*

# (Required)
filter:
- term:
   _type: http_request

buffer_time:
  minutes: 5

query_key: Hostname.keyword

# (Required)
match_bucket_filter:
- terms:
    ResponseStatus: [200]

# (Required)
min_percentage: 95
#max_percentage: 60

#bucket_interval:
#  minutes: 1

# (Required)
# The alert is use when a match is found
alert:
- "debug"

์ฐธ๊ณ  ์‚ฌ์ดํŠธ

ElastAlert2 ๊ณต์‹๋ฌธ์„œ: https://elastalert2.readthedocs.io/en/latest/elastalert.html

๋ฐ˜์‘ํ˜•