Skip to content

Commit c85d581

Browse files
committed
fix: align MikroTik filter and rule contracts
1 parent 6c3af7e commit c85d581

4 files changed

Lines changed: 169 additions & 6 deletions

File tree

‎filters/audits/mikrotik.md‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# MikroTik normalization and rule review
2+
3+
Normalize denial/IP/protocol values and align SSH predicates/history fields with surviving filter output.
4+
5+
This draft targets UTMStack `v11`. It contains 1 filter changes
6+
and 1 rule changes for this technology only. Review covered
7+
1 filter configurations and 6 matching shipped rule files.
8+
Unchanged rules are listed in the regression manifest; they are not duplicated in the diff.
9+
10+
## Contract and validation
11+
12+
- Compared exact standard names/types with go-sdk v1.1.31 and the supplied UTMStack dictionaries.
13+
- Checked documented pipeline ordering, rename/move behavior, open vendor log fields,
14+
event-side versus alert-side fields, and surviving fields used by affected rule predicates/history/grouping.
15+
- Strict SDK configuration decoding and actual CEL compilation pass for this scope.
16+
- 3 synthetic normalization cases pass, including SDK Event conversion and any
17+
trigger predicate assertions recorded in the manifest.
18+
- The scoped alerts module tests and `git diff --check` pass with the shared contract runner applied.
19+
20+
The shared alert-contract PR supplies the reusable Go runner for the manifest in
21+
`plugins/alerts/testdata/filter-contracts/mikrotik.json`. Apply that support before running `go test ./...` in `plugins/alerts`.
22+
23+
The model starts from synthetic extraction results. It does not run complex grok,
24+
JSON/KV/XML/CSV extraction, time conversion, dynamic plugins, historical OpenSearch
25+
queries, or the closed EventProcessor. Raw vendor logs and resulting alerts must
26+
still be checked in staging before rollout. No customer false-positive reduction
27+
has been measured and no production rollout is included.
28+
29+
30+
31+
## References
32+
33+
- [SDK schema](https://github.com/threatwinds/go-sdk/blob/v1.1.31/plugins/plugins.proto)
34+
- [Filter steps](https://github.com/threatwinds/go-sdk/wiki/Filter-Steps-Reference)
35+
- [Standard event schema](https://github.com/threatwinds/go-sdk/wiki/Standard-Event-Schema)
36+
- [Rule implementation](https://github.com/threatwinds/go-sdk/wiki/Implementing-Rules)
37+
38+
`afterEvents`, empty noncapturing grok names, supported numeric strings, and custom
39+
`log.*` fields are accepted. Existing textual protocol casing and vendor action names
40+
are preserved unless a concrete consumer mismatch requires correction.

‎filters/mikrotik/mikrotik-fw.yml‎

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,8 @@ pipeline:
210210
function: string
211211
params:
212212
key: actionResult
213-
value: "blocked"
214-
where: 'exists("log.action") && equals("log.action", "drop")'
213+
value: denied
214+
where: exists("log.action") && equals("log.action", "drop")
215215

216216
# Removing unused fields
217217
- delete:
@@ -221,4 +221,69 @@ pipeline:
221221
- log.trash3
222222
- log.trash4
223223
- log.trash5
224-
- log.restData
224+
- log.restData
225+
226+
# Keep addresses in IP fields and retain other source values under log.
227+
- rename:
228+
from:
229+
- origin.ip
230+
to: log.unparsedOriginIp
231+
where: exists("origin.ip") && (!(inCIDR("origin.ip","0.0.0.0/0") || inCIDR("origin.ip","::/0")) || oneOf("origin.ip",["0.0.0.0","::"]))
232+
- rename:
233+
from:
234+
- target.ip
235+
to: log.unparsedTargetIp
236+
where: exists("target.ip") && (!(inCIDR("target.ip","0.0.0.0/0") || inCIDR("target.ip","::/0")) || oneOf("target.ip",["0.0.0.0","::"]))
237+
- add:
238+
function: string
239+
params:
240+
key: protocol
241+
value: ICMP
242+
where: equals("protocol",1)
243+
- add:
244+
function: string
245+
params:
246+
key: protocol
247+
value: TCP
248+
where: equals("protocol",6)
249+
- add:
250+
function: string
251+
params:
252+
key: protocol
253+
value: UDP
254+
where: equals("protocol",17)
255+
- add:
256+
function: string
257+
params:
258+
key: protocol
259+
value: GRE
260+
where: equals("protocol",47)
261+
- add:
262+
function: string
263+
params:
264+
key: protocol
265+
value: ESP
266+
where: equals("protocol",50)
267+
- add:
268+
function: string
269+
params:
270+
key: protocol
271+
value: AH
272+
where: equals("protocol",51)
273+
- add:
274+
function: string
275+
params:
276+
key: protocol
277+
value: ICMPV6
278+
where: equals("protocol",58)
279+
- add:
280+
function: string
281+
params:
282+
key: protocol
283+
value: SCTP
284+
where: equals("protocol",132)
285+
- rename:
286+
from:
287+
- protocol
288+
to: log.ipProtocolNumber
289+
where: exists("protocol") && greaterOrEqual("protocol",0)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"technology": "MikroTik",
3+
"filters": [
4+
"filters/mikrotik/mikrotik-fw.yml"
5+
],
6+
"rules": [
7+
"rules/mikrotik/mikrotik_fw/dns_cache_poisoning.yml",
8+
"rules/mikrotik/mikrotik_fw/mikrotik_dns_redirection.yml",
9+
"rules/mikrotik/mikrotik_fw/mikrotik_socks_proxy.yml",
10+
"rules/mikrotik/mikrotik_fw/mikrotik_user_creation.yml",
11+
"rules/mikrotik/mikrotik_fw/routeros_brute_force_attempts.yml",
12+
"rules/mikrotik/mikrotik_fw/ssh_brute_force_attempts.yml"
13+
],
14+
"fixtures": [
15+
{
16+
"name": "mikrotik_valid_source_ip",
17+
"filter": "mikrotik/mikrotik-fw.yml",
18+
"input": {
19+
"origin": {
20+
"ip": "2001:db8::7"
21+
}
22+
},
23+
"expected": {
24+
"origin.ip": "2001:db8::7"
25+
},
26+
"absent": [],
27+
"rules": {}
28+
},
29+
{
30+
"name": "mikrotik_hostname_not_ip",
31+
"filter": "mikrotik/mikrotik-fw.yml",
32+
"input": {
33+
"origin": {
34+
"ip": "lab-host"
35+
}
36+
},
37+
"expected": {
38+
"log.unparsedOriginIp": "lab-host"
39+
},
40+
"absent": [
41+
"origin.ip"
42+
],
43+
"rules": {}
44+
},
45+
{
46+
"name": "mikrotik_numeric_protocol",
47+
"filter": "mikrotik/mikrotik-fw.yml",
48+
"input": {
49+
"protocol": 6
50+
},
51+
"expected": {
52+
"protocol": "TCP"
53+
},
54+
"absent": [],
55+
"rules": {}
56+
}
57+
]
58+
}

‎rules/mikrotik/mikrotik_fw/ssh_brute_force_attempts.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ description: |
2424
5. Ensure SSH access is restricted to authorized management networks only
2525
6. Review MikroTik firewall rules and consider disabling SSH access from untrusted networks
2626
where: |
27-
equals("target.port", 22) && equals("protocol", "tcp") && exists("origin.ip") &&
27+
equals("target.port", 22) && equalsIgnoreCase("protocol", "tcp") && exists("origin.ip") &&
2828
(contains("log.chain", "input") || contains("log.action", "drop")
29-
|| (contains("log.topics", "system") && contains("log.kvMessage", "ssh")))
29+
|| (contains("log.topics", "system") && contains("raw", "ssh")))
3030
afterEvents:
3131
- indexPattern: v11-log-firewall-mikrotik-*
3232
with:
@@ -38,7 +38,7 @@ afterEvents:
3838
value: '22'
3939
- field: protocol
4040
operator: filter_term
41-
value: 'tcp'
41+
value: '{{.protocol}}'
4242
within: 15m
4343
count: 10
4444
groupBy:

0 commit comments

Comments
 (0)