# ------------------------- # Sources # ------------------------- # Syslog Forwarder (from syslog-ng -> Vector TCP 9000, JSON) [sources.syslog_forward] type = "socket" address = "0.0.0.0:9000" mode = "tcp" decoding.codec = "json" # ------------------------- # Transforms # ------------------------- # Route syslog by MESSAGE prefix/content [transforms.route_syslog] type = "route" inputs = ["syslog_forward"] route.edr = 'starts_with(to_string(.MESSAGE) ?? "", "EDR{") || contains(to_string(.MESSAGE) ?? "", "\"syslog_type\":\"EDR_") || starts_with(to_string(.PROGRAM) ?? "", "EDR{") || contains(to_string(.PROGRAM) ?? "", "\"syslog_type\":\"EDR_")' route.epp = '((to_string(.PROGRAM) ?? "") == "EPP" || contains(to_string(.MESSAGE) ?? "", "\"syslog_type\"")) && !starts_with(to_string(.MESSAGE) ?? "", "EDR{") && !contains(to_string(.MESSAGE) ?? "", "\"syslog_type\":\"EDR_") && !starts_with(to_string(.PROGRAM) ?? "", "EDR{") && !contains(to_string(.PROGRAM) ?? "", "\"syslog_type\":\"EDR_")' route.mds = 'starts_with(to_string(.MESSAGE) ?? "", "LEEF:") && contains(to_string(.MESSAGE) ?? "", "|MDS|")' # Unmatched syslog fallback [transforms.parse_invalid] type = "remap" inputs = ["route_syslog._unmatched"] source = ''' .service = "unknown_syslog" .env = "local" .parse_status = "invalid" if exists(.HOST) { .ingest_host = to_string!(.HOST) } if exists(.PROGRAM) { .program = to_string!(.PROGRAM) } if exists(.MESSAGE) { .message = to_string!(.MESSAGE) } if !exists(.message) { .message = "unmatched syslog" } # Always use ingest time for Loki .timestamp = now() # Ensure label fields exist if !exists(.syslog_type) { .syslog_type = "unknown" } if !exists(.level) { .level = "info" } # Clean noisy wrapper fields del(.MESSAGE) del(.HOST) del(.PROGRAM) del(.FACILITY) del(.HOST_FROM) del(.LEGACY_MSGHDR) del(.MSGFORMAT) del(.PRIORITY) del(.SOURCE) del(.SOURCEIP) del(.TAGS) del(.TRANSPORT) del(.ISODATE) ''' # AhnLab EPP syslog parser + normalization [transforms.parse_epp] type = "remap" inputs = ["route_syslog.epp"] source = ''' .service = "ahnlab_epp" .env = "local" .parse_status = "ok" parse_failed = false # ---- syslog-ng wrapper 기본값 정규화 ---- if exists(.HOST) { .ingest_host = to_string!(.HOST) } if exists(.PROGRAM) { .program = to_string!(.PROGRAM) } if exists(.MESSAGE) { .event = { "original": to_string!(.MESSAGE) } } # ---- MESSAGE 안의 JSON 파싱 (EPP: {..} 또는 {..}) ---- epp_json = null if exists(.MESSAGE) { msg = to_string!(.MESSAGE) # 1) JSON 그대로 시도 epp_json, err = parse_json(msg) # 2) 실패하면 EPP: {json} 형태에서 JSON만 추출 if err != null { m, err2 = parse_regex(msg, r'(?s)(?P\{.*\})') if err2 == null && exists(m.json) { epp_json, err = parse_json(m.json) } } } if epp_json != null && is_object(epp_json) { .epp = epp_json # ---- 핵심 필드 매핑 ---- if exists(.epp.syslog_type) { .syslog_type = to_string!(.epp.syslog_type) } if exists(.epp.syslog_data) { sd = .epp.syslog_data if exists(sd.computer_name) { .host = to_string!(sd.computer_name) } if exists(sd.ip) { .src_ip = to_string!(sd.ip) } if exists(sd.node_hash_type) { .node_hash_type = to_string!(sd.node_hash_type) } if exists(sd.hash_value) { .hash_value = to_string!(sd.hash_value) } if exists(sd.reset_data) { .reset_data = to_string!(sd.reset_data) } if exists(sd.level) { lvl = downcase(to_string!(sd.level)) if lvl == "high" { .risk = "High" } if lvl == "medium" { .risk = "Medium" } if lvl == "low" { .risk = "Low" } if lvl == "normal" { .risk = "Normal" } if lvl == "information" { .risk = "Information" } if lvl == "suspicious" { .risk = "Suspicious" } } if exists(sd.log_string_id) { .event_id = to_string!(sd.log_string_id) .message = to_string!(sd.log_string_id) } if exists(sd.sw) && is_array(sd.sw) { .sw_count = length!(sd.sw) } if exists(sd.log_string_args) { if is_array(sd.log_string_args) { .log_args = sd.log_string_args } else { args_raw = to_string(sd.log_string_args) ?? "" if args_raw != "" { args, err = parse_json(args_raw) if err == null && is_array(args) { .log_args = args } else { .log_args_raw = args_raw } } } if exists(.event_id) { .message = to_string!(.event_id) } } if exists(sd.detail_string_id) && (sd.detail_string_id != "") { .detail_id = to_string!(sd.detail_string_id) } if exists(sd.detail_string_args) { detail_raw = to_string!(sd.detail_string_args) detail_args, err = parse_json(detail_raw) if err == null && is_array(detail_args) { .detail_args = detail_args } else if detail_raw != "" { .detail_args_raw = detail_raw } } if exists(sd.login_id) { .login_id = to_string!(sd.login_id) } if exists(sd.user_name) && (sd.user_name != "") { .user_name = to_string!(sd.user_name) } if exists(sd.type) { .event_type = to_string!(sd.type) } if exists(sd.task_id) { .task_id = to_string!(sd.task_id) } if exists(sd.instance_id) { .instance_id = to_string!(sd.instance_id) } if exists(sd.task_type) { .task_type = to_string!(sd.task_type) } if exists(sd.task_status) { .task_status = to_string!(sd.task_status) } if exists(sd.task_error) { .task_error = to_string!(sd.task_error) } if exists(sd.product_id) { .product_id = to_string!(sd.product_id) } if exists(sd.node_id) { .node_id = to_int(sd.node_id) ?? null } if exists(sd.function) { .function = to_string!(sd.function) } if exists(sd.data_uuid) { .data_uuid = to_string!(sd.data_uuid) } if exists(sd.mac) { .mac = to_string!(sd.mac) } if exists(sd.log_type) { .log_type = to_string!(sd.log_type) } if exists(sd.rule_type) { .rule_type = to_int(sd.rule_type) ?? null } if exists(sd.scan_type) { .scan_type = to_string!(sd.scan_type) } if exists(sd.status) { .status = to_string!(sd.status) } if exists(sd.engine_version) { .engine_version = to_string!(sd.engine_version) } if exists(sd.name) && (sd.name != "") { .detect_name = to_string!(sd.name) if !exists(.message) || .message == to_string!(.syslog_type) { .message = to_string!(sd.name) } } if exists(sd.path) { .file_path = to_string!(sd.path) } if exists(sd.file_size) && (to_string!(sd.file_size) != "") { .file_size = to_int(sd.file_size) ?? null } if exists(sd.file_hash) { fh = to_string!(sd.file_hash) if length(fh) == 32 { .md5_hash = fh } if length(fh) == 64 { .sha256_hash = fh } if length(fh) != 32 && length(fh) != 64 { .file_hash = fh } } if exists(sd.sha256_hash) { .sha256_hash = to_string!(sd.sha256_hash) } # timestamp: client_time object contains a single timestamp value # Keep event time separately; use ingest time for Loki to avoid rejections. if exists(sd.client_time) && is_object(sd.client_time) { keys = keys!(sd.client_time) if length(keys) > 0 { key = keys[0] ts = to_string!(get!(sd.client_time, [key])) parsed_ts = parse_timestamp(ts, "%+") ?? null if parsed_ts != null { .event_time = parsed_ts } } } } if exists(.epp.group_id) { .group_id = to_int(.epp.group_id) ?? null } } if epp_json == null { parse_failed = true } # ---- 최종 안전장치 ---- if !exists(.message) { if exists(.syslog_type) { .message = to_string!(.syslog_type) } else if exists(.MESSAGE) { .message = to_string!(.MESSAGE) } } # Ensure label fields exist if !exists(.syslog_type) { .syslog_type = "EPP_EVENT" } if !exists(.level) { .level = "info" } # event_time fallback: ISODATE if !exists(.event_time) && exists(.ISODATE) { parsed_isodate = parse_timestamp(to_string!(.ISODATE), "%+") ?? null if parsed_isodate != null { .event_time = parsed_isodate } } # Always use ingest time for Loki .timestamp = now() if parse_failed { .parse_status = "partial" } # ---- 노이즈/중복 필드 정리 ---- if exists(.user_name) && (is_null(.user_name) || .user_name == "") { del(.user_name) } if !exists(.src_ip) && exists(.SOURCEIP) { .src_ip = to_string!(.SOURCEIP) } if !exists(.host) && exists(.HOST) { .host = to_string!(.HOST) } if exists(.epp) { del(.epp) } del(.MESSAGE) del(.HOST) del(.PROGRAM) del(.FACILITY) del(.HOST_FROM) del(.LEGACY_MSGHDR) del(.MSGFORMAT) del(.PRIORITY) del(.SOURCE) del(.SOURCEIP) del(.TAGS) del(.TRANSPORT) del(.ISODATE) ''' # AhnLab EDR syslog parser + normalization [transforms.parse_edr] type = "remap" inputs = ["route_syslog.edr"] source = ''' .service = "ahnlab_edr" .env = "local" .parse_status = "ok" parse_failed = false # ---- syslog-ng wrapper 기본값 정규화 ---- if exists(.HOST) { .ingest_host = to_string!(.HOST) } if exists(.PROGRAM) { .program = to_string!(.PROGRAM) } if exists(.program) && starts_with(to_string(.program) ?? "", "EDR{") { .program = "EDR" } # ---- MESSAGE 안의 JSON 파싱 (EDR{...} 또는 {...}) ---- edr_json = null msg = null raw_msg = null if exists(.MESSAGE) { msg = to_string!(.MESSAGE) } if (msg == null || msg == "") && exists(.PROGRAM) { msg = to_string!(.PROGRAM) } if msg != null && msg != "" { raw_msg = msg msg = to_string(msg) # 일부 로그는 PROGRAM에 접두어가 붙고 MESSAGE가 나머지인 형태 if exists(.PROGRAM) && starts_with(to_string(.PROGRAM) ?? "", "EDR{") && exists(.MESSAGE) && starts_with(to_string(.MESSAGE) ?? "", "{") { prog = to_string!(.PROGRAM) msg_part = to_string!(.MESSAGE) if ends_with(prog, "\"syslog_data\"") { msg = prog + ":" + msg_part } else { msg = prog + msg_part } } # "EDR{...}" 형태면 prefix 제거 if starts_with(msg, "EDR{") { msg = replace(msg, "^EDR", "") } .event = { "original": raw_msg } # 1) JSON 그대로 시도 tmp = parse_json(msg) ?? null if is_object(tmp) { edr_json = tmp } else if is_string(tmp) { inner = to_string!(tmp) tmp2 = parse_json(inner) ?? null if is_object(tmp2) { edr_json = tmp2 } else { escaped = replace(inner, "\\", "\\\\") tmp3 = parse_json(escaped) ?? null if is_object(tmp3) { edr_json = tmp3 } } } # 2) 실패하면 {json} 형태에서 JSON만 추출 if edr_json == null { m, err2 = parse_regex(msg, r'(?s)(?P\{.*\})') if err2 == null && exists(m.json) { sub = to_string(m.json) tmp4 = parse_json(sub) ?? null if is_object(tmp4) { edr_json = tmp4 } else { escaped = replace(sub, "\\", "\\\\") tmp5 = parse_json(escaped) ?? null if is_object(tmp5) { edr_json = tmp5 } } } } } if edr_json != null && is_object(edr_json) { .edr = edr_json if exists(.edr.syslog_type) { .syslog_type = to_string!(.edr.syslog_type) } if exists(.edr.version) { .version = to_string!(.edr.version) } if exists(.edr.group_id) { .group_id = to_int(.edr.group_id) ?? null } if exists(.edr.syslog_data) { sd = .edr.syslog_data if exists(sd.computer_name) { .host = to_string!(sd.computer_name) } if exists(sd.ip) { .src_ip = to_string!(sd.ip) } if exists(sd.login_id) { .login_id = to_string!(sd.login_id) } if exists(sd.mac) { .mac = to_string!(sd.mac) } if exists(sd.node_id) { .node_id = to_int(sd.node_id) ?? null } if exists(sd.data_uuid) { .data_uuid = to_string!(sd.data_uuid) } if !exists(.group_id) && exists(sd.group_id) { .group_id = to_int(sd.group_id) ?? null } if exists(sd.detect_type) { .detect_type = to_string!(sd.detect_type) } if exists(sd.virus_fullname) { .virus_fullname = to_string!(sd.virus_fullname) } if exists(sd.target_data_type) { .target_data_type = to_string!(sd.target_data_type) } if exists(sd.behavior_category) { .behavior_category = to_string!(sd.behavior_category) } if exists(sd.alert_type) { .alert_type = to_int(sd.alert_type) ?? null } if exists(sd.rule_type) { .rule_type = to_int(sd.rule_type) ?? null } if exists(sd.rule_severity) { .rule_severity = to_int(sd.rule_severity) ?? null } if exists(sd.severity) { sev_raw = to_string!(sd.severity) .severity = sev_raw sev = upcase(sev_raw) # Map EDR severity to High/Medium/Low -> risk .risk = "Unknown" if sev == "L" || sev == "LOW" { .risk = "Low" } if sev == "M" || sev == "MEDIUM" { .risk = "Medium" } if sev == "H" || sev == "HIGH" { .risk = "High" } } if exists(sd.tactic) && is_array(sd.tactic) { .tactic = sd.tactic } if exists(sd.technique) && is_array(sd.technique) { .technique = sd.technique } if exists(sd.feature_tags) && is_array(sd.feature_tags) { .feature_tags = sd.feature_tags } if exists(sd.rule_seq) { .rule_seq = to_int(sd.rule_seq) ?? null } if exists(sd.action_id) { .action_id = to_int(sd.action_id) ?? null } if exists(sd.action_result) { .action_result = to_int(sd.action_result) ?? null } if exists(sd.action_category) { .action_category = to_int(sd.action_category) ?? null } if exists(sd.obj_id) { .obj_id = to_string!(sd.obj_id) } if exists(sd.current_name) { .process_name = to_string!(sd.current_name) } if exists(sd.current_path) { .process_path = to_string!(sd.current_path) } if exists(sd.target_data) && is_object(sd.target_data) { td = sd.target_data if exists(td.command_line) { .command_line = to_string!(td.command_line) } if exists(td.file_path) { .file_path = to_string!(td.file_path) } if exists(td.sha256_hash) { .sha256_hash = to_string!(td.sha256_hash) } if exists(td.md5_hash) { .md5_hash = to_string!(td.md5_hash) } if exists(td.pid) { .pid = to_int(td.pid) ?? null } } if exists(sd.clean_datas) && is_array(sd.clean_datas) { .clean_count = length!(sd.clean_datas) } if exists(sd.behaviors) && is_array(sd.behaviors) { .behavior_count = length!(sd.behaviors) first = sd.behaviors[0] if is_object(first) { if exists(first.rule_seq) { .behavior_rule_seq = to_int(first.rule_seq) ?? null } if exists(first.rule_severity) { .behavior_rule_severity = to_int(first.rule_severity) ?? null } if exists(first.virus_fullname) { .behavior_virus_fullname = to_string!(first.virus_fullname) } } } # event time (prefer create_time, fallback time) if exists(sd.create_time) { parsed = parse_timestamp(to_string!(sd.create_time), "%+") ?? null if parsed != null { .event_time = parsed } } else if exists(sd.time) { parsed = parse_timestamp(to_string!(sd.time), "%+") ?? null if parsed != null { .event_time = parsed } } } } if edr_json == null { parse_failed = true } if !exists(.message) { if exists(.syslog_type) { .message = to_string!(.syslog_type) } else { .message = "EDR_EVENT" } } # Ensure label fields exist if !exists(.syslog_type) { .syslog_type = "EDR_EVENT" } if !exists(.level) { .level = "info" } # Always use ingest time for Loki .timestamp = now() if parse_failed { .parse_status = "partial" } # ---- 노이즈/중복 필드 정리 ---- if exists(.edr) { del(.edr) } if exists(.user_name) && (is_null(.user_name) || .user_name == "") { del(.user_name) } if !exists(.src_ip) && exists(.SOURCEIP) { .src_ip = to_string!(.SOURCEIP) } if !exists(.host) && exists(.HOST) { .host = to_string!(.HOST) } del(.MESSAGE) del(.HOST) del(.PROGRAM) del(.FACILITY) del(.HOST_FROM) del(.LEGACY_MSGHDR) del(.MSGFORMAT) del(.PRIORITY) del(.SOURCE) del(.SOURCEIP) del(.TAGS) del(.TRANSPORT) del(.ISODATE) ''' # AhnLab MDS (LEEF) syslog parser + normalization [transforms.parse_mds] type = "remap" inputs = ["route_syslog.mds"] source = ''' .service = "ahnlab_mds" .env = "local" .parse_status = "ok" parse_failed = false # ---- syslog-ng wrapper 기본값 정규화 ---- if exists(.HOST) { .ingest_host = to_string!(.HOST) } if exists(.PROGRAM) { .program = to_string!(.PROGRAM) } if exists(.MESSAGE) { .event = { "original": to_string!(.MESSAGE) } } msg = null kv = {} if exists(.MESSAGE) { msg = to_string!(.MESSAGE) } if msg != null && msg != "" { # LEEF header: LEEF:1.0|Vendor|Product|Version|EventID|key=val\t... m, err = parse_regex(msg, r'^LEEF:(?P[^|]+)\|(?P[^|]*)\|(?P[^|]*)\|(?P[^|]*)\|(?P[^|]*)\|(?P.*)$') if err == null { .leef_version = to_string(m.leef_version) .vendor = to_string(m.vendor) .product = to_string(m.product) if exists(m.mds_version) { .version = to_string(m.mds_version) } if exists(m.event_id) && (m.event_id != "") { .leef_event_id = to_string(m.event_id) } kv_str = to_string(m.kv) # 일부 로그는 KV 구간에 '|'가 섞여 들어옴 -> 탭으로 정규화 kv_str = replace(kv_str, "|", "\t") kv, err2 = parse_key_value(kv_str, field_delimiter: "\t", key_value_delimiter: "=") if err2 == null && is_object(kv) { .mds = kv # ---- 핵심 필드 매핑 ---- if exists(kv.syslog_type) { .syslog_type = to_string!(kv.syslog_type) } if !exists(.syslog_type) && exists(.leef_event_id) { .syslog_type = to_string!(.leef_event_id) } if exists(kv.log_type) { .log_type = to_string!(kv.log_type) } if exists(kv.cat) { .event_type = to_string!(kv.cat) } if exists(kv.sev) { sev_str = to_string!(kv.sev) .severity = sev_str # detection-level mapping -> risk (only when log_type=detection, or if missing treat as detection) if !exists(.log_type) || .log_type == "detection" { .risk = "Unknown" if match(sev_str, r'^\d+$') { sev_int = to_int!(sev_str) .risk = "Normal" if sev_int == 1 { .risk = "Low" } if sev_int == 2 { .risk = "Information" } if sev_int == 3 { .risk = "Suspicious" } if sev_int >= 4 { .risk = "Medium" } if sev_int >= 7 { .risk = "High" } } } } if exists(kv.proto) { .protocol = to_string!(kv.proto) } if exists(kv.act) { .action = to_string!(kv.act) } if exists(kv.event_time) { ts_raw = to_string!(kv.event_time) parsed = parse_timestamp(ts_raw, "%+") ?? parse_timestamp(ts_raw, "%b %d %Y %H:%M:%S") ?? parse_timestamp(ts_raw, "%b %e %Y %H:%M:%S") ?? null if parsed != null { .event_time = parsed } } if !exists(.event_time) && exists(kv.detect_time) { ts_raw = to_string!(kv.detect_time) parsed = parse_timestamp(ts_raw, "%+") ?? parse_timestamp(ts_raw, "%b %d %Y %H:%M:%S") ?? parse_timestamp(ts_raw, "%b %e %Y %H:%M:%S") ?? null if parsed != null { .event_time = parsed } } if !exists(.event_time) && exists(kv.devTime) { ts_raw = to_string!(kv.devTime) parsed = parse_timestamp(ts_raw, "%+") ?? parse_timestamp(ts_raw, "%b %d %Y %H:%M:%S") ?? parse_timestamp(ts_raw, "%b %e %Y %H:%M:%S") ?? null if parsed != null { .event_time = parsed } } if exists(kv.attacker) { .attacker = to_string!(kv.attacker) } if exists(kv.target) { .target = to_string!(kv.target) } if exists(kv.detect_name) { .detect_name = to_string!(kv.detect_name) } if exists(kv.detect_object) { .detect_object = to_string!(kv.detect_object) } if exists(kv.device_id) { .device_id = to_string!(kv.device_id) } if exists(kv.event_id) { .event_id = to_string!(kv.event_id) } if exists(kv.msg) && (to_string!(kv.msg) != "") { .message = to_string!(kv.msg) } if exists(kv.obj_name) { .obj_name = to_string!(kv.obj_name) } if exists(kv.md5) { .md5_hash = to_string!(kv.md5) } if exists(kv.file_type) { .file_type = to_string!(kv.file_type) } if exists(kv.file_size) && (to_string!(kv.file_size) != "") { .file_size = to_int!(kv.file_size) } if exists(kv.src) { .src_ip = to_string!(kv.src) } if exists(kv.source_ip) && !exists(.src_ip) { .src_ip = to_string!(kv.source_ip) } if exists(kv.srcPort) && (to_string!(kv.srcPort) != "") { .src_port = to_int!(kv.srcPort) } if exists(kv.dst) { .dst_ip = to_string!(kv.dst) } if exists(kv.dstPort) && (to_string!(kv.dstPort) != "") { .dst_port = to_int!(kv.dstPort) } if exists(kv.session) { .session = to_string!(kv.session) } if exists(kv.session_info) { .session_info = to_string!(kv.session_info) } } else { parse_failed = true } } else { parse_failed = true } } else { parse_failed = true } if !exists(.message) { if exists(.event_type) { .message = to_string!(.event_type) } else { .message = "MDS_EVENT" } } # Ensure label fields exist if !exists(.syslog_type) { .syslog_type = "MDS_EVENT" } if !exists(.level) { .level = "info" } # Always use ingest time for Loki .timestamp = now() if parse_failed { .parse_status = "partial" } # ---- 노이즈/중복 필드 정리 ---- if exists(.mds) { del(.mds) } if !exists(.src_ip) && exists(.SOURCEIP) { .src_ip = to_string!(.SOURCEIP) } if !exists(.host) && exists(.HOST) { .host = to_string!(.HOST) } del(.MESSAGE) del(.HOST) del(.PROGRAM) del(.FACILITY) del(.HOST_FROM) del(.LEGACY_MSGHDR) del(.MSGFORMAT) del(.PRIORITY) del(.SOURCE) del(.SOURCEIP) del(.TAGS) del(.TRANSPORT) del(.ISODATE) ''' # ------------------------- # Sinks # ------------------------- [transforms.ensure_labels] type = "remap" inputs = ["parse_epp", "parse_edr", "parse_mds", "parse_invalid"] source = ''' if !exists(.syslog_type) { .syslog_type = "unknown" } .level = "info" if !exists(.risk) { .risk = "unknown" } if !exists(.parse_status) { .parse_status = "ok" } ''' [transforms.risk_alert_events] type = "filter" inputs = ["ensure_labels"] condition = "(.service == \"ahnlab_epp\" || .service == \"ahnlab_mds\") && match(to_string(.risk) ?? \"\", r'^(Low|Medium|High)$')" [transforms.risk_alert_payload] type = "remap" inputs = ["risk_alert_events"] source = ''' service = to_string(.service) ?? "unknown" risk = to_string(.risk) ?? "unknown" msg = to_string(.message) ?? "risk event" host = to_string(.host) ?? "" src_ip = to_string(.src_ip) ?? "" event_time = to_string(.event_time) ?? "" received = format_timestamp!(now(), "%+") . = { "service": service, "risk": risk, "message": msg, "received": received } if host != "" { .host = host } if src_ip != "" { .src_ip = src_ip } if event_time != "" { .event_time = event_time } ''' [sinks.loki_sink] type = "loki" inputs = ["ensure_labels"] endpoint = "http://loki:3100" encoding.codec = "json" # Low-cardinality labels only labels.service = "{{ service }}" labels.env = "{{ env }}" labels.level = "{{ level }}" labels.syslog_type = "{{ syslog_type }}" labels.parse_status = "{{ parse_status }}" labels.risk = "{{ risk }}" remove_label_fields = true [sinks.api_teams_webhook] type = "http" inputs = ["risk_alert_payload"] uri = "http://api:3001/webhooks/teams" method = "post" encoding.codec = "json" batch.max_events = 1 batch.timeout_secs = 1 request.headers.X-Webhook-Token = "${WEBHOOK_TOKEN}" # --- Vector internal logs (debug) --- [sources.vector_internal] type = "internal_logs" [sinks.vector_internal_console] type = "console" inputs = ["vector_internal"] encoding.codec = "json" [sinks.debug_syslog_in] type = "console" inputs = ["syslog_forward"] encoding.codec = "json"