MySQL & MariaDB Protocol Guard (mysql)
The MySQL Protocol Guard plugin inspects MySQL and MariaDB client/server handshake packets. It monitors the initial server greeting, transparently handles SSL client requests, and checks response packets for error 0xFF with error code 1045 (ER_ACCESS_DENIED_ERROR) to stop credential stuffing attacks in real time.
Capabilities & Threat Defense
| Threat / Attack Vector | Defense Mechanism | Action Taken |
|---|---|---|
| Credential Stuffing & Brute-Force | Intercepts Error Packet 0xFF code 1045 (ER_ACCESS_DENIED_ERROR) | IP automatically banned after max_auth_failures |
| Connection Exhaustion (Too many connections) | Enforces rate limits per minute and burst caps | Connection dropped or throttled before reaching MySQL |
| SSL Handshake Handling | Transparently detects SSL request packets | Seamless TLS encryption forwarding |
Installation
Install the plugin via CLI or declaratively:
# Install using short nametcp-warden plugins install mysql# Or install via Git repository URLtcp-warden plugins install https://github.com/routewarden/plugins/mysqlConfiguration Reference
Add a MySQL guard service to tcp-warden.yaml:
services: mysql_bastion: listen: ":3306" upstream: "127.0.0.1:33060" protocol: "mysql" rate_limit: connections_per_minute: 60 burst: 10 max_auth_failures: 3 ban_after_failures: 3 ban_duration: "2h" plugin_config: max_auth_failures: 3Configuration Options
| Field | Type | Default | Description |
|---|---|---|---|
listen | string | ":3306" | Local proxy listen address and port. |
upstream | string | "127.0.0.1:33060" | Target MySQL or MariaDB server address and port. |
protocol | string | "mysql" | Must be set to "mysql" or "mariadb". |
max_auth_failures | int | 3 | Failed login attempts before the client IP is banned. |
ban_duration | string | "2h" | Duration of the automated IP ban ("30m", "2h", "24h"). |
Network & Deployment Architecture
Because MySQL operates on an unprivileged port (3306), kernel packet redirection (nftables/iptables) is not required. Deploy RouteWarden using either direct reverse proxying or container network isolation:
Option A: Direct Reverse Proxy (Bare-Metal / VM)
Bind RouteWarden to :3306, with MySQL listening on loopback on an alternate port or private subnet IP:
# tcp-warden.yaml — RouteWarden binds to standard port 3306services: mysql_bastion: listen: ":3306" upstream: "127.0.0.1:33060" # Or private backend "10.0.0.20:3306" protocol: "mysql"Option B: Docker Compose Bridge Isolation
In containerized environments, publish port 3306 on RouteWarden while keeping the MySQL container completely internal:
services: routewarden: image: ghcr.io/routewarden/tcp-warden:latest ports: - "3306:3306" - "9091:9091" volumes: - ./tcp-warden.yaml:/etc/routewarden/tcp-warden.yaml:ro depends_on: - db db: image: mariadb:11 environment: MYSQL_ROOT_PASSWORD: securepassword # Do NOT publish host ports; keep internal to bridge networkTesting & Verification
Verify legitimate logins, simulate credential errors, and check the ban list:
# Verify legitimate login through RouteWardenmysql -h 127.0.0.1 -P 3306 -u root -p