diff --git a/.tests/openvpn-logs/config.yaml b/.tests/openvpn-logs/config.yaml new file mode 100644 index 00000000000..9c80466ea80 --- /dev/null +++ b/.tests/openvpn-logs/config.yaml @@ -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 diff --git a/.tests/openvpn-logs/openvpn-logs.log b/.tests/openvpn-logs/openvpn-logs.log new file mode 100644 index 00000000000..56a3a0c971f --- /dev/null +++ b/.tests/openvpn-logs/openvpn-logs.log @@ -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 diff --git a/.tests/openvpn-logs/parser.assert b/.tests/openvpn-logs/parser.assert new file mode 100644 index 00000000000..a13e9380dfe --- /dev/null +++ b/.tests/openvpn-logs/parser.assert @@ -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 diff --git a/.tests/openvpn-logs/scenario.assert b/.tests/openvpn-logs/scenario.assert new file mode 100644 index 00000000000..03455618a29 --- /dev/null +++ b/.tests/openvpn-logs/scenario.assert @@ -0,0 +1 @@ +len(results) == 0 diff --git a/collections/proonoob/openvpn.yaml b/collections/proonoob/openvpn.yaml new file mode 100644 index 00000000000..b85276a27ab --- /dev/null +++ b/collections/proonoob/openvpn.yaml @@ -0,0 +1,7 @@ +name: proonoob/openvpn +description: "OpenVPN parser and bruteforce detection" +author: proonoob +parsers: + - proonoob/openvpn +scenarios: + - proonoob/openvpn-bf diff --git a/parsers/s01-parse/proonoob/openvpn.md b/parsers/s01-parse/proonoob/openvpn.md new file mode 100644 index 00000000000..f7a62afb071 --- /dev/null +++ b/parsers/s01-parse/proonoob/openvpn.md @@ -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 diff --git a/parsers/s01-parse/proonoob/openvpn.yaml b/parsers/s01-parse/proonoob/openvpn.yaml new file mode 100644 index 00000000000..74f24018569 --- /dev/null +++ b/parsers/s01-parse/proonoob/openvpn.yaml @@ -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 diff --git a/scenarios/proonoob/openvpn-bf.md b/scenarios/proonoob/openvpn-bf.md new file mode 100644 index 00000000000..d93e47816c3 --- /dev/null +++ b/scenarios/proonoob/openvpn-bf.md @@ -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 diff --git a/scenarios/proonoob/openvpn-bf.yaml b/scenarios/proonoob/openvpn-bf.yaml new file mode 100644 index 00000000000..3174fc4771a --- /dev/null +++ b/scenarios/proonoob/openvpn-bf.yaml @@ -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