Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .tests/openvpn-logs/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
parsers:
- crowdsecurity/syslog-logs
- ./parsers/s01-parse/proonoob/openvpn.yaml
scenarios:
- ./scenarios/proonoob/openvpn-bf.yaml
postoverflows:
- ""
log_file: openvpn-logs.log
log_type: syslog
5 changes: 5 additions & 0 deletions .tests/openvpn-logs/openvpn-logs.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
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
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
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
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
Apr 5 11:26:07 vpn ovpn-server[380]: TLS Error: tls-crypt unwrapping failed from [AF_INET]9.10.11.12:55555
128 changes: 128 additions & 0 deletions .tests/openvpn-logs/parser.assert
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
len(results["s00-raw"]["crowdsecurity/syslog-logs"]) == 5
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Success == true
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Parsed["logsource"] == "syslog"
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]1.2.3.4:11111"
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Parsed["pid"] == "4892"
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Parsed["program"] == "ovpn-server"
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Parsed["timestamp8601"] == "2026-04-10T12:00:01.000000+02:00"
basename(results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Meta["datasource_type"] == "file"
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Meta["machine"] == "vpn"
results["s00-raw"]["crowdsecurity/syslog-logs"][0].Evt.Whitelisted == false
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Success == true
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Parsed["logsource"] == "syslog"
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]1.2.3.4:22222"
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Parsed["pid"] == "4892"
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Parsed["program"] == "ovpn-server"
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Parsed["timestamp8601"] == "2026-04-10T12:00:02.000000+02:00"
basename(results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Meta["datasource_type"] == "file"
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Meta["machine"] == "vpn"
results["s00-raw"]["crowdsecurity/syslog-logs"][1].Evt.Whitelisted == false
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Success == true
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Parsed["logsource"] == "syslog"
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]1.2.3.4:33333"
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Parsed["pid"] == "4892"
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Parsed["program"] == "ovpn-server"
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Parsed["timestamp8601"] == "2026-04-10T12:00:03.000000+02:00"
basename(results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Meta["datasource_type"] == "file"
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Meta["machine"] == "vpn"
results["s00-raw"]["crowdsecurity/syslog-logs"][2].Evt.Whitelisted == false
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Success == true
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Parsed["logsource"] == "syslog"
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]5.6.7.8:44444"
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Parsed["pid"] == "4892"
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Parsed["program"] == "ovpn-server"
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Parsed["timestamp8601"] == "2026-04-10T12:00:04.000000+02:00"
basename(results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Meta["datasource_type"] == "file"
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Meta["machine"] == "vpn"
results["s00-raw"]["crowdsecurity/syslog-logs"][3].Evt.Whitelisted == false
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Success == true
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Parsed["logsource"] == "syslog"
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]9.10.11.12:55555"
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Parsed["pid"] == "380"
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Parsed["program"] == "ovpn-server"
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Parsed["timestamp"] == "Apr 5 11:26:07"
basename(results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Meta["datasource_type"] == "file"
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Meta["machine"] == "vpn"
results["s00-raw"]["crowdsecurity/syslog-logs"][4].Evt.Whitelisted == false
len(results["s01-parse"]["proonoob/openvpn"]) == 5
results["s01-parse"]["proonoob/openvpn"][0].Success == true
results["s01-parse"]["proonoob/openvpn"][0].Evt.Parsed["logsource"] == "syslog"
results["s01-parse"]["proonoob/openvpn"][0].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]1.2.3.4:11111"
results["s01-parse"]["proonoob/openvpn"][0].Evt.Parsed["pid"] == "4892"
results["s01-parse"]["proonoob/openvpn"][0].Evt.Parsed["program"] == "ovpn-server"
results["s01-parse"]["proonoob/openvpn"][0].Evt.Parsed["source_ip"] == "1.2.3.4"
results["s01-parse"]["proonoob/openvpn"][0].Evt.Parsed["sport"] == "11111"
results["s01-parse"]["proonoob/openvpn"][0].Evt.Parsed["timestamp8601"] == "2026-04-10T12:00:01.000000+02:00"
basename(results["s01-parse"]["proonoob/openvpn"][0].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
results["s01-parse"]["proonoob/openvpn"][0].Evt.Meta["datasource_type"] == "file"
results["s01-parse"]["proonoob/openvpn"][0].Evt.Meta["log_type"] == "auth_failed"
results["s01-parse"]["proonoob/openvpn"][0].Evt.Meta["machine"] == "vpn"
results["s01-parse"]["proonoob/openvpn"][0].Evt.Meta["service"] == "openvpn"
results["s01-parse"]["proonoob/openvpn"][0].Evt.Meta["source_ip"] == "1.2.3.4"
results["s01-parse"]["proonoob/openvpn"][0].Evt.Whitelisted == false
results["s01-parse"]["proonoob/openvpn"][1].Success == true
results["s01-parse"]["proonoob/openvpn"][1].Evt.Parsed["logsource"] == "syslog"
results["s01-parse"]["proonoob/openvpn"][1].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]1.2.3.4:22222"
results["s01-parse"]["proonoob/openvpn"][1].Evt.Parsed["pid"] == "4892"
results["s01-parse"]["proonoob/openvpn"][1].Evt.Parsed["program"] == "ovpn-server"
results["s01-parse"]["proonoob/openvpn"][1].Evt.Parsed["source_ip"] == "1.2.3.4"
results["s01-parse"]["proonoob/openvpn"][1].Evt.Parsed["sport"] == "22222"
results["s01-parse"]["proonoob/openvpn"][1].Evt.Parsed["timestamp8601"] == "2026-04-10T12:00:02.000000+02:00"
basename(results["s01-parse"]["proonoob/openvpn"][1].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
results["s01-parse"]["proonoob/openvpn"][1].Evt.Meta["datasource_type"] == "file"
results["s01-parse"]["proonoob/openvpn"][1].Evt.Meta["log_type"] == "auth_failed"
results["s01-parse"]["proonoob/openvpn"][1].Evt.Meta["machine"] == "vpn"
results["s01-parse"]["proonoob/openvpn"][1].Evt.Meta["service"] == "openvpn"
results["s01-parse"]["proonoob/openvpn"][1].Evt.Meta["source_ip"] == "1.2.3.4"
results["s01-parse"]["proonoob/openvpn"][1].Evt.Whitelisted == false
results["s01-parse"]["proonoob/openvpn"][2].Success == true
results["s01-parse"]["proonoob/openvpn"][2].Evt.Parsed["logsource"] == "syslog"
results["s01-parse"]["proonoob/openvpn"][2].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]1.2.3.4:33333"
results["s01-parse"]["proonoob/openvpn"][2].Evt.Parsed["pid"] == "4892"
results["s01-parse"]["proonoob/openvpn"][2].Evt.Parsed["program"] == "ovpn-server"
results["s01-parse"]["proonoob/openvpn"][2].Evt.Parsed["source_ip"] == "1.2.3.4"
results["s01-parse"]["proonoob/openvpn"][2].Evt.Parsed["sport"] == "33333"
results["s01-parse"]["proonoob/openvpn"][2].Evt.Parsed["timestamp8601"] == "2026-04-10T12:00:03.000000+02:00"
basename(results["s01-parse"]["proonoob/openvpn"][2].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
results["s01-parse"]["proonoob/openvpn"][2].Evt.Meta["datasource_type"] == "file"
results["s01-parse"]["proonoob/openvpn"][2].Evt.Meta["log_type"] == "auth_failed"
results["s01-parse"]["proonoob/openvpn"][2].Evt.Meta["machine"] == "vpn"
results["s01-parse"]["proonoob/openvpn"][2].Evt.Meta["service"] == "openvpn"
results["s01-parse"]["proonoob/openvpn"][2].Evt.Meta["source_ip"] == "1.2.3.4"
results["s01-parse"]["proonoob/openvpn"][2].Evt.Whitelisted == false
results["s01-parse"]["proonoob/openvpn"][3].Success == true
results["s01-parse"]["proonoob/openvpn"][3].Evt.Parsed["logsource"] == "syslog"
results["s01-parse"]["proonoob/openvpn"][3].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]5.6.7.8:44444"
results["s01-parse"]["proonoob/openvpn"][3].Evt.Parsed["pid"] == "4892"
results["s01-parse"]["proonoob/openvpn"][3].Evt.Parsed["program"] == "ovpn-server"
results["s01-parse"]["proonoob/openvpn"][3].Evt.Parsed["source_ip"] == "5.6.7.8"
results["s01-parse"]["proonoob/openvpn"][3].Evt.Parsed["sport"] == "44444"
results["s01-parse"]["proonoob/openvpn"][3].Evt.Parsed["timestamp8601"] == "2026-04-10T12:00:04.000000+02:00"
basename(results["s01-parse"]["proonoob/openvpn"][3].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
results["s01-parse"]["proonoob/openvpn"][3].Evt.Meta["datasource_type"] == "file"
results["s01-parse"]["proonoob/openvpn"][3].Evt.Meta["log_type"] == "auth_failed"
results["s01-parse"]["proonoob/openvpn"][3].Evt.Meta["machine"] == "vpn"
results["s01-parse"]["proonoob/openvpn"][3].Evt.Meta["service"] == "openvpn"
results["s01-parse"]["proonoob/openvpn"][3].Evt.Meta["source_ip"] == "5.6.7.8"
results["s01-parse"]["proonoob/openvpn"][3].Evt.Whitelisted == false
results["s01-parse"]["proonoob/openvpn"][4].Success == true
results["s01-parse"]["proonoob/openvpn"][4].Evt.Parsed["logsource"] == "syslog"
results["s01-parse"]["proonoob/openvpn"][4].Evt.Parsed["message"] == "TLS Error: tls-crypt unwrapping failed from [AF_INET]9.10.11.12:55555"
results["s01-parse"]["proonoob/openvpn"][4].Evt.Parsed["pid"] == "380"
results["s01-parse"]["proonoob/openvpn"][4].Evt.Parsed["program"] == "ovpn-server"
results["s01-parse"]["proonoob/openvpn"][4].Evt.Parsed["source_ip"] == "9.10.11.12"
results["s01-parse"]["proonoob/openvpn"][4].Evt.Parsed["sport"] == "55555"
results["s01-parse"]["proonoob/openvpn"][4].Evt.Parsed["timestamp"] == "Apr 5 11:26:07"
basename(results["s01-parse"]["proonoob/openvpn"][4].Evt.Meta["datasource_path"]) == "openvpn-logs.log"
results["s01-parse"]["proonoob/openvpn"][4].Evt.Meta["datasource_type"] == "file"
results["s01-parse"]["proonoob/openvpn"][4].Evt.Meta["log_type"] == "auth_failed"
results["s01-parse"]["proonoob/openvpn"][4].Evt.Meta["machine"] == "vpn"
results["s01-parse"]["proonoob/openvpn"][4].Evt.Meta["service"] == "openvpn"
results["s01-parse"]["proonoob/openvpn"][4].Evt.Meta["source_ip"] == "9.10.11.12"
results["s01-parse"]["proonoob/openvpn"][4].Evt.Whitelisted == false
len(results["success"][""]) == 0
1 change: 1 addition & 0 deletions .tests/openvpn-logs/scenario.assert
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
len(results) == 0
7 changes: 7 additions & 0 deletions collections/proonoob/openvpn.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: proonoob/openvpn
description: "OpenVPN parser and bruteforce detection"
author: proonoob
parsers:
- proonoob/openvpn
scenarios:
- proonoob/openvpn-bf
22 changes: 22 additions & 0 deletions parsers/s01-parse/proonoob/openvpn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## Overview

Parser for OpenVPN server logs. Supports both legacy syslog and modern ISO8601 timestamp formats.

## Configuration

Add to /etc/crowdsec/acquis.d/openvpn.yaml

## Detected Events

- auth_failed: TLS tls-crypt unwrapping failed (scanner/probe without valid key)
- auth_failed: AUTH_FAILED (failed authentication)
- auth_failed: TLS handshake failed
- auth_failed: Certificate VERIFY ERROR

## Log formats supported

Legacy syslog:
Apr 5 11:26:07 vpn ovpn-server[380]: TLS Error: tls-crypt unwrapping failed from [AF_INET]182.200.116.38:38382

Modern ISO8601:
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
33 changes: 33 additions & 0 deletions parsers/s01-parse/proonoob/openvpn.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
filter: "evt.Parsed.program == 'ovpn-server'"
onsuccess: next_stage
name: proonoob/openvpn
description: "Parse OpenVPN logs (supports both syslog and ISO8601 timestamp formats)"
pattern_syntax:
OPENVPN_TLS_CRYPT: "TLS Error: tls-crypt unwrapping failed from \\[AF_INET\\]%{IPV4:source_ip}:%{INT:sport}"
OPENVPN_AUTH_FAILED: "AUTH: Received control message: AUTH_FAILED.*\\[AF_INET\\]%{IPV4:source_ip}:%{INT:sport}"
OPENVPN_TLS_HANDSHAKE: "TLS Error: TLS handshake failed"
OPENVPN_VERIFY_ERROR: "VERIFY ERROR"
OPENVPN_CATCHALL: "%{GREEDYDATA}"
nodes:
- grok:
name: "OPENVPN_TLS_CRYPT"
apply_on: message
- grok:
name: "OPENVPN_AUTH_FAILED"
apply_on: message
- grok:
name: "OPENVPN_TLS_HANDSHAKE"
apply_on: message
- grok:
name: "OPENVPN_VERIFY_ERROR"
apply_on: message
- grok:
name: "OPENVPN_CATCHALL"
apply_on: message
statics:
- meta: service
value: openvpn
- meta: source_ip
expression: "evt.Parsed.source_ip"
- meta: log_type
value: auth_failed
14 changes: 14 additions & 0 deletions scenarios/proonoob/openvpn-bf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Overview

Detects IPs performing OpenVPN TLS bruteforce or probing attacks.
Bans IPs that trigger 3 or more TLS errors within 15 minutes.

## Configuration

Requires the proonoob/openvpn parser.

## Behavior

- Trigger: 3 TLS errors
- Time window: 15 minutes
- Remediation: ban
18 changes: 18 additions & 0 deletions scenarios/proonoob/openvpn-bf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
type: leaky
name: proonoob/openvpn-bf
description: "Detect OpenVPN probing and bruteforce attempts via TLS errors"
filter: "evt.Meta.service == 'openvpn' && evt.Meta.log_type == 'auth_failed'"
groupby: "evt.Meta.source_ip"
capacity: 2
leakspeed: "5m"
blackhole: "1m"
labels:
service: openvpn
type: bruteforce
remediation: true
confidence: 3
spoofable: 0
behavior: "openvpn:bruteforce"
label: "OpenVPN TLS bruteforce/probing"
classification:
- attack.T1110