Warpage and fail2ban via vector

A working version to block intruderce into warpgate

compose.yml

services:
warpgate:
container_name: warpgate
image: ghcr.io/warp-tech/warpgate
ports:
- 2222:2222
- 127.0.0.1:8888:8888
volumes:
- ./data:/data:Z
- ./sockets:/var/run
stdin_open: true
tty: true
restart: always
environment:
- WARPGATE__WEB__TRUST_PROXY_HEADERS=true
logging:
driver: json-file
options:
max-size: "50m"
max-file: "3"
vector:
image: timberio/vector:latest-alpine
container_name: vector
restart: unless-stopped
depends_on:
- warpgate
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./vector.yaml:/etc/vector/vector.yaml:ro
- /var/log/warpgate:/var/log/warpgate
command: ["--config", "/etc/vector/vector.yaml"]

vector.yaml

sources:
warpgate:
type: docker_logs
include_containers:
- warpgate

transforms:
to_fail2ban:
type: remap
inputs:
- warpgate
source: |
raw = string!(.message)

# ANSI Escape Codes entfernen (falls vorhanden)
msg = replace(raw, r'\x1b\[[0-9;]*m', "")

# --- 1) HTTP Login failed (401) ---
is_http_fail =
contains(msg, "WARN HTTP:") &&
contains(msg, "/@warpgate/api/auth/login") &&
contains(msg, "status=401") &&
contains(msg, "client_ip")

# --- 2) SSH Auth failed (Credentials/User/Password) ---
# Beispiel von dir:
# "ERROR SSH: Failed to verify credentials ... client_ip=::ffff:222.138.251.223"
is_ssh_fail =
contains(msg, "ERROR SSH:") &&
contains(msg, "Failed to verify credentials") &&
contains(msg, "client_ip")

if !(is_http_fail || is_ssh_fail) {
abort
}

# Zeitstempel robust
ts = format_timestamp!(now(), "%Y-%m-%dT%H:%M:%SZ")

# IP extrahieren
parsed = parse_regex!(msg, r'client_ip\s*=\s*(?P[0-9a-fA-F\.:]+)')
ip = parsed.ip

# IPv4-mapped IPv6 (::ffff:1.2.3.4) normalisieren -> 1.2.3.4
ip = replace(ip, r'^::ffff:', "")

# Ausgabezeile für fail2ban (einheitlich)
if is_http_fail {
.message = ts + " warpgate login failed (http) ip=" + ip
} else {
.message = ts + " warpgate login failed (ssh) ip=" + ip
}

sinks:
fail2ban_file:
type: file
inputs:
- to_fail2ban
path: "/var/log/warpgate/auth.log"
encoding:
codec: text

/etc/fail2ban/filter.d/warpgate.conf

[Definition]
datepattern = ^%%Y-%%m-%%dT%%H:%%M:%%SZ
failregex = ^.*warpgate login failed.* ip=\s*$
ignoreregex =

/etc/fail2ban/jail.d/warpgate.conf

[warpgate]
enabled = true
filter = warpgate
logpath = /var/log/warpgate/auth.log

maxretry = 5
findtime = 300
bantime = 900

banaction = iptables-docker-allports

/etc/fail2ban/action.d/iptables-docker-allports.conf

[Definition]

# IPv4
actionstart = iptables -N f2b- || true
iptables -C DOCKER-USER -j f2b- || iptables -I DOCKER-USER -j f2b-
iptables -A f2b- -j RETURN

actionstop = iptables -D DOCKER-USER -j f2b- || true
iptables -F f2b- || true
iptables -X f2b- || true

actioncheck = iptables -n -L DOCKER-USER | grep -q f2b-

actionban = iptables -I f2b- 1 -s -j DROP

actionunban = iptables -D f2b- -s -j DROP

# IPv6
actionstart += ip6tables -N f2b- || true
ip6tables -C DOCKER-USER -j f2b- || ip6tables -I DOCKER-USER -j f2b-
ip6tables -A f2b- -j RETURN

actionstop += ip6tables -D DOCKER-USER -j f2b- || true
ip6tables -F f2b- || true
ip6tables -X f2b- || true

actioncheck += ip6tables -n -L DOCKER-USER | grep -q f2b-

actionban += ip6tables -I f2b- 1 -s -j DROP

actionunban += ip6tables -D f2b- -s -j DROP

fail2ban-regex /var/log/warpgate/auth.log /etc/fail2ban/filter.d/warpgate.conf
systemctl restart fail2ban
fail2ban-client status warpgate