Skip to content

Commit 1a9d6f2

Browse files
committed
feat: add tests, docs, collection and fix parser for OpenVPN
1 parent 4d5c277 commit 1a9d6f2

9 files changed

Lines changed: 198 additions & 8 deletions

File tree

.tests/openvpn-logs/config.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
parsers:
2+
- crowdsecurity/syslog-logs
3+
- ./parsers/s01-parse/proonoob/openvpn.yaml
4+
scenarios:
5+
- ./scenarios/proonoob/openvpn-bf.yaml
6+
postoverflows:
7+
- ""
8+
log_file: openvpn-logs.log
9+
log_type: syslog
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2026-04-10T12:00:01.000000+02:00 vpn ovpn-server[4892]: TLS Error: tls-crypt unwrapping failed from [AF_INET]1.2.3.4:11111
2+
2026-04-10T12:00:02.000000+02:00 vpn ovpn-server[4892]: TLS Error: tls-crypt unwrapping failed from [AF_INET]1.2.3.4:22222
3+
2026-04-10T12:00:03.000000+02:00 vpn ovpn-server[4892]: TLS Error: tls-crypt unwrapping failed from [AF_INET]1.2.3.4:33333
4+
2026-04-10T12:00:04.000000+02:00 vpn ovpn-server[4892]: TLS Error: tls-crypt unwrapping failed from [AF_INET]5.6.7.8:44444
5+
Apr 5 11:26:07 vpn ovpn-server[380]: TLS Error: tls-crypt unwrapping failed from [AF_INET]9.10.11.12:55555

.tests/openvpn-logs/parser.assert

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
len(results["s00-raw"]["crowdsecurity/syslog-logs"]) == 5
2+
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Success == true
3+
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Parsed["logsource"] == "syslog"
4+
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]1.2.3.4:11111"
5+
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Parsed["pid"] == "4892"
6+
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Parsed["program"] == "ovpn-server"
7+
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Parsed["timestamp8601"] == "2026-04-10T12:00:01.000000+02:00"
8+
basename(results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
9+
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Meta["datasource_type"] == "file"
10+
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Meta["machine"] == "vpn"
11+
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Whitelisted == false
12+
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Success == true
13+
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Parsed["logsource"] == "syslog"
14+
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]1.2.3.4:22222"
15+
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Parsed["pid"] == "4892"
16+
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Parsed["program"] == "ovpn-server"
17+
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Parsed["timestamp8601"] == "2026-04-10T12:00:02.000000+02:00"
18+
basename(results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
19+
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Meta["datasource_type"] == "file"
20+
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Meta["machine"] == "vpn"
21+
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Whitelisted == false
22+
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Success == true
23+
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Parsed["logsource"] == "syslog"
24+
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]1.2.3.4:33333"
25+
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Parsed["pid"] == "4892"
26+
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Parsed["program"] == "ovpn-server"
27+
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Parsed["timestamp8601"] == "2026-04-10T12:00:03.000000+02:00"
28+
basename(results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
29+
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Meta["datasource_type"] == "file"
30+
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Meta["machine"] == "vpn"
31+
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Whitelisted == false
32+
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Success == true
33+
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Parsed["logsource"] == "syslog"
34+
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]5.6.7.8:44444"
35+
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Parsed["pid"] == "4892"
36+
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Parsed["program"] == "ovpn-server"
37+
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Parsed["timestamp8601"] == "2026-04-10T12:00:04.000000+02:00"
38+
basename(results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
39+
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Meta["datasource_type"] == "file"
40+
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Meta["machine"] == "vpn"
41+
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Whitelisted == false
42+
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Success == true
43+
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Parsed["logsource"] == "syslog"
44+
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]9.10.11.12:55555"
45+
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Parsed["pid"] == "380"
46+
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Parsed["program"] == "ovpn-server"
47+
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Parsed["timestamp"] == "Apr 5 11:26:07"
48+
basename(results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
49+
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Meta["datasource_type"] == "file"
50+
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Meta["machine"] == "vpn"
51+
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Whitelisted == false
52+
len(results["s01-parse"]["proonoob/openvpn"]) == 5
53+
results["s01-parse"]["proonoob/openvpn"][0].Success == true
54+
results["s01-parse"]["proonoob/openvpn"][0].Evt.Parsed["logsource"] == "syslog"
55+
results["s01-parse"]["proonoob/openvpn"][0].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]1.2.3.4:11111"
56+
results["s01-parse"]["proonoob/openvpn"][0].Evt.Parsed["pid"] == "4892"
57+
results["s01-parse"]["proonoob/openvpn"][0].Evt.Parsed["program"] == "ovpn-server"
58+
results["s01-parse"]["proonoob/openvpn"][0].Evt.Parsed["source_ip"] == "1.2.3.4"
59+
results["s01-parse"]["proonoob/openvpn"][0].Evt.Parsed["sport"] == "11111"
60+
results["s01-parse"]["proonoob/openvpn"][0].Evt.Parsed["timestamp8601"] == "2026-04-10T12:00:01.000000+02:00"
61+
basename(results["s01-parse"]["proonoob/openvpn"][0].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
62+
results["s01-parse"]["proonoob/openvpn"][0].Evt.Meta["datasource_type"] == "file"
63+
results["s01-parse"]["proonoob/openvpn"][0].Evt.Meta["log_type"] == "auth_failed"
64+
results["s01-parse"]["proonoob/openvpn"][0].Evt.Meta["machine"] == "vpn"
65+
results["s01-parse"]["proonoob/openvpn"][0].Evt.Meta["service"] == "openvpn"
66+
results["s01-parse"]["proonoob/openvpn"][0].Evt.Meta["source_ip"] == "1.2.3.4"
67+
results["s01-parse"]["proonoob/openvpn"][0].Evt.Whitelisted == false
68+
results["s01-parse"]["proonoob/openvpn"][1].Success == true
69+
results["s01-parse"]["proonoob/openvpn"][1].Evt.Parsed["logsource"] == "syslog"
70+
results["s01-parse"]["proonoob/openvpn"][1].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]1.2.3.4:22222"
71+
results["s01-parse"]["proonoob/openvpn"][1].Evt.Parsed["pid"] == "4892"
72+
results["s01-parse"]["proonoob/openvpn"][1].Evt.Parsed["program"] == "ovpn-server"
73+
results["s01-parse"]["proonoob/openvpn"][1].Evt.Parsed["source_ip"] == "1.2.3.4"
74+
results["s01-parse"]["proonoob/openvpn"][1].Evt.Parsed["sport"] == "22222"
75+
results["s01-parse"]["proonoob/openvpn"][1].Evt.Parsed["timestamp8601"] == "2026-04-10T12:00:02.000000+02:00"
76+
basename(results["s01-parse"]["proonoob/openvpn"][1].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
77+
results["s01-parse"]["proonoob/openvpn"][1].Evt.Meta["datasource_type"] == "file"
78+
results["s01-parse"]["proonoob/openvpn"][1].Evt.Meta["log_type"] == "auth_failed"
79+
results["s01-parse"]["proonoob/openvpn"][1].Evt.Meta["machine"] == "vpn"
80+
results["s01-parse"]["proonoob/openvpn"][1].Evt.Meta["service"] == "openvpn"
81+
results["s01-parse"]["proonoob/openvpn"][1].Evt.Meta["source_ip"] == "1.2.3.4"
82+
results["s01-parse"]["proonoob/openvpn"][1].Evt.Whitelisted == false
83+
results["s01-parse"]["proonoob/openvpn"][2].Success == true
84+
results["s01-parse"]["proonoob/openvpn"][2].Evt.Parsed["logsource"] == "syslog"
85+
results["s01-parse"]["proonoob/openvpn"][2].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]1.2.3.4:33333"
86+
results["s01-parse"]["proonoob/openvpn"][2].Evt.Parsed["pid"] == "4892"
87+
results["s01-parse"]["proonoob/openvpn"][2].Evt.Parsed["program"] == "ovpn-server"
88+
results["s01-parse"]["proonoob/openvpn"][2].Evt.Parsed["source_ip"] == "1.2.3.4"
89+
results["s01-parse"]["proonoob/openvpn"][2].Evt.Parsed["sport"] == "33333"
90+
results["s01-parse"]["proonoob/openvpn"][2].Evt.Parsed["timestamp8601"] == "2026-04-10T12:00:03.000000+02:00"
91+
basename(results["s01-parse"]["proonoob/openvpn"][2].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
92+
results["s01-parse"]["proonoob/openvpn"][2].Evt.Meta["datasource_type"] == "file"
93+
results["s01-parse"]["proonoob/openvpn"][2].Evt.Meta["log_type"] == "auth_failed"
94+
results["s01-parse"]["proonoob/openvpn"][2].Evt.Meta["machine"] == "vpn"
95+
results["s01-parse"]["proonoob/openvpn"][2].Evt.Meta["service"] == "openvpn"
96+
results["s01-parse"]["proonoob/openvpn"][2].Evt.Meta["source_ip"] == "1.2.3.4"
97+
results["s01-parse"]["proonoob/openvpn"][2].Evt.Whitelisted == false
98+
results["s01-parse"]["proonoob/openvpn"][3].Success == true
99+
results["s01-parse"]["proonoob/openvpn"][3].Evt.Parsed["logsource"] == "syslog"
100+
results["s01-parse"]["proonoob/openvpn"][3].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]5.6.7.8:44444"
101+
results["s01-parse"]["proonoob/openvpn"][3].Evt.Parsed["pid"] == "4892"
102+
results["s01-parse"]["proonoob/openvpn"][3].Evt.Parsed["program"] == "ovpn-server"
103+
results["s01-parse"]["proonoob/openvpn"][3].Evt.Parsed["source_ip"] == "5.6.7.8"
104+
results["s01-parse"]["proonoob/openvpn"][3].Evt.Parsed["sport"] == "44444"
105+
results["s01-parse"]["proonoob/openvpn"][3].Evt.Parsed["timestamp8601"] == "2026-04-10T12:00:04.000000+02:00"
106+
basename(results["s01-parse"]["proonoob/openvpn"][3].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
107+
results["s01-parse"]["proonoob/openvpn"][3].Evt.Meta["datasource_type"] == "file"
108+
results["s01-parse"]["proonoob/openvpn"][3].Evt.Meta["log_type"] == "auth_failed"
109+
results["s01-parse"]["proonoob/openvpn"][3].Evt.Meta["machine"] == "vpn"
110+
results["s01-parse"]["proonoob/openvpn"][3].Evt.Meta["service"] == "openvpn"
111+
results["s01-parse"]["proonoob/openvpn"][3].Evt.Meta["source_ip"] == "5.6.7.8"
112+
results["s01-parse"]["proonoob/openvpn"][3].Evt.Whitelisted == false
113+
results["s01-parse"]["proonoob/openvpn"][4].Success == true
114+
results["s01-parse"]["proonoob/openvpn"][4].Evt.Parsed["logsource"] == "syslog"
115+
results["s01-parse"]["proonoob/openvpn"][4].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]9.10.11.12:55555"
116+
results["s01-parse"]["proonoob/openvpn"][4].Evt.Parsed["pid"] == "380"
117+
results["s01-parse"]["proonoob/openvpn"][4].Evt.Parsed["program"] == "ovpn-server"
118+
results["s01-parse"]["proonoob/openvpn"][4].Evt.Parsed["source_ip"] == "9.10.11.12"
119+
results["s01-parse"]["proonoob/openvpn"][4].Evt.Parsed["sport"] == "55555"
120+
results["s01-parse"]["proonoob/openvpn"][4].Evt.Parsed["timestamp"] == "Apr 5 11:26:07"
121+
basename(results["s01-parse"]["proonoob/openvpn"][4].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
122+
results["s01-parse"]["proonoob/openvpn"][4].Evt.Meta["datasource_type"] == "file"
123+
results["s01-parse"]["proonoob/openvpn"][4].Evt.Meta["log_type"] == "auth_failed"
124+
results["s01-parse"]["proonoob/openvpn"][4].Evt.Meta["machine"] == "vpn"
125+
results["s01-parse"]["proonoob/openvpn"][4].Evt.Meta["service"] == "openvpn"
126+
results["s01-parse"]["proonoob/openvpn"][4].Evt.Meta["source_ip"] == "9.10.11.12"
127+
results["s01-parse"]["proonoob/openvpn"][4].Evt.Whitelisted == false
128+
len(results["success"][""]) == 0
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
len(results) == 0

collections/proonoob/openvpn.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
name: proonoob/openvpn
2+
description: "OpenVPN parser and bruteforce detection"
3+
author: proonoob
4+
parsers:
5+
- proonoob/openvpn
6+
scenarios:
7+
- proonoob/openvpn-bf
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
## Overview
2+
3+
Parser for OpenVPN server logs. Supports both legacy syslog and modern ISO8601 timestamp formats.
4+
5+
## Configuration
6+
7+
Add to /etc/crowdsec/acquis.d/openvpn.yaml
8+
9+
## Detected Events
10+
11+
- auth_failed: TLS tls-crypt unwrapping failed (scanner/probe without valid key)
12+
- auth_failed: AUTH_FAILED (failed authentication)
13+
- auth_failed: TLS handshake failed
14+
- auth_failed: Certificate VERIFY ERROR
15+
16+
## Log formats supported
17+
18+
Legacy syslog:
19+
Apr 5 11:26:07 vpn ovpn-server[380]: TLS Error: tls-crypt unwrapping failed from [AF_INET]182.200.116.38:38382
20+
21+
Modern ISO8601:
22+
2026-04-10T12:17:48.771346+02:00 vpn ovpn-server[448]: TLS Error: tls-crypt unwrapping failed from [AF_INET]182.200.116.38:38382

parsers/s01-parse/proonoob/openvpn.yaml

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
filter: "evt.Line.Labels.type == 'openvpn'"
1+
filter: "evt.Parsed.program == 'ovpn-server'"
22
onsuccess: next_stage
33
name: proonoob/openvpn
44
description: "Parse OpenVPN logs (supports both syslog and ISO8601 timestamp formats)"
55
pattern_syntax:
6-
OPENVPN_TLS_CRYPT: "%{TIMESTAMP_ISO8601:timestamp} %{HOSTNAME} %{NOTSPACE}: TLS Error: tls-crypt unwrapping failed from \\[AF_INET\\]%{IPV4:source_ip}:%{INT:sport}"
7-
OPENVPN_AUTH_FAILED: "%{TIMESTAMP_ISO8601:timestamp} %{HOSTNAME} %{NOTSPACE}: AUTH: Received control message: AUTH_FAILED.*\\[AF_INET\\]%{IPV4:source_ip}:%{INT:sport}"
8-
OPENVPN_TLS_HANDSHAKE: "%{TIMESTAMP_ISO8601:timestamp} %{HOSTNAME} %{NOTSPACE}: TLS Error: TLS handshake failed"
9-
OPENVPN_VERIFY_ERROR: "%{TIMESTAMP_ISO8601:timestamp} %{HOSTNAME} %{NOTSPACE}: VERIFY ERROR"
10-
OPENVPN_CATCHALL: "%{TIMESTAMP_ISO8601:timestamp} %{GREEDYDATA}"
6+
OPENVPN_TLS_CRYPT: "TLS Error: tls-crypt unwrapping failed from \\[AF_INET\\]%{IPV4:source_ip}:%{INT:sport}"
7+
OPENVPN_AUTH_FAILED: "AUTH: Received control message: AUTH_FAILED.*\\[AF_INET\\]%{IPV4:source_ip}:%{INT:sport}"
8+
OPENVPN_TLS_HANDSHAKE: "TLS Error: TLS handshake failed"
9+
OPENVPN_VERIFY_ERROR: "VERIFY ERROR"
10+
OPENVPN_CATCHALL: "%{GREEDYDATA}"
1111
nodes:
1212
- grok:
1313
name: "OPENVPN_TLS_CRYPT"
@@ -31,5 +31,3 @@ statics:
3131
expression: "evt.Parsed.source_ip"
3232
- meta: log_type
3333
value: auth_failed
34-
- target: evt.StrTime
35-
expression: "evt.Parsed.timestamp"

scenarios/proonoob/openvpn-bf.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
## Overview
2+
3+
Detects IPs performing OpenVPN TLS bruteforce or probing attacks.
4+
Bans IPs that trigger 3 or more TLS errors within 15 minutes.
5+
6+
## Configuration
7+
8+
Requires the proonoob/openvpn parser.
9+
10+
## Behavior
11+
12+
- Trigger: 3 TLS errors
13+
- Time window: 15 minutes
14+
- Remediation: ban

scenarios/proonoob/openvpn-bf.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,9 @@ labels:
1010
service: openvpn
1111
type: bruteforce
1212
remediation: true
13+
confidence: 3
14+
spoofable: 0
15+
behavior: "openvpn:bruteforce"
16+
label: "OpenVPN TLS bruteforce/probing"
17+
classification:
18+
- attack.T1110

0 commit comments

Comments
 (0)