loamok/sf2security-bundle
Loamok SF2SecurityBundle is a Symfony 2 security bundle that integrates authentication and access-control services, helping PHP applications manage users, roles, and protected routes through configurable Symfony security settings.
First, the honest orientation: loamok/sf2security-bundle is a Symfony 2 bundle for wiring the Symfony Security Component to a host-level fail2ban daemon. It is not installable or usable inside a Laravel application. There is no service provider, no facade, no middleware, no config integration — its entry point is a Symfony Bundle class registered in app/AppKernel::registerBundles() and its config lives in app/config/config.yml. If you are a Laravel dev looking at this package, the realistic "getting started" is: do not composer require it. What you can actually harvest day-to-day is the pattern — a stable, parseable authentication-failure log line that a fail2ban filter can match — and re-implement it natively in ~30 lines of Laravel.
Where to look first (reading, not installing): clone the repo and read, in order:
composer.json — confirm the Symfony 2.x constraint and PHP version; this tells you instantly it will not co-exist with Laravel's dependency tree.DependencyInjection/ config — this shows what services it registers (a security event listener/subscriber is the usual suspect).Resources/config/*.xml/.yml — this reveals the actual integration point: it subscribes to security.authentication.failure (Symfony's auth-failure event) and writes a log record.Resources/config/ fail2ban filter sample — the regex and log-line shape are the only reusable artifact.First use case (Laravel-native equivalent): log failed login attempts to a dedicated channel in a format a fail2ban filter can match, then let ops create the jail. Minimal implementation:
// config/logging.php — dedicated channel
'channels' => [
'auth_failures' => [
'driver' => 'daily',
'path' => storage_path('logs/auth-failures.log'),
'days' => 14,
],
],
// app/Listeners/LogFailedLogin.php — listen for Auth\Events\Failed
use Illuminate\Auth\Events\Failed;
use Illuminate\Support\Facades\Log;
class LogFailedLogin
{
public function handle(Failed $event): void
{
Log::channel('auth_failures')->warning(
'AUTH_FAILURE ip={ip} user={user} route={route} ua="{ua}"',
[
'ip' => request()->ip(),
'user' => $event->credentials['email'] ?? 'unknown',
'route' => request()->path(),
'ua' => substr((string) request()->userAgent(), 0, 200),
]
);
}
}
# /etc/fail2ban/filter.d/laravel-auth.conf
[Definition]
failregex = ^.*AUTH_FAILURE ip=<HOST> .*$
ignoreregex =
The whole "leverage the package" story for a Laravel dev is this: use the log-line contract as a design reference, implement the logging natively, and hand ban enforcement to platform/SRE.
Pattern 1 — Port the listener, not the bundle. The bundle's functional core is an event listener that fires on authentication failure. In Laravel, the equivalent event is Illuminate\Auth\Events\Failed (dispatched by the AuthenticatesUsers/LogoutOtherDevices flow in AuthController or by Illuminate\Auth\SessionGuard::attempt() on failure). Register the listener in EventServiceProvider. Bind it with care: Failed also fires on validation-time attempts, so if you're using Fortify/Breeze, ensure you're listening in one place, not overlapping.
Pattern 2 — Structured log format with a stable schema. The reusable idea from the Symfony bundle is a single-line, whitespace-delimited, key=value record — trivially regex-able by fail2ban. Do not log JSON if you want a simple <HOST> match; fail2ban's failregex uses <HOST> for the IP, and mixing JSON escaping with that regex is painful. Keep the IP early in the line, or use the __prefix_line convention in newer fail2ban versions. Include: IP, username/email (hashed if PII-sensitive), route, timestamp, and a short fingerprint of the user agent.
Pattern 3 — Proxy-aware IP resolution. A fail2ban ban is only as good as the IP you log. In Laravel, request()->ip() respects TrustProxies middleware; configure it correctly so X-Forwarded-For is trusted only from your known proxy/LB ranges. Do this before the first production deployment of the jail; getting IP wrong is the #1 cause of either banning the LB or banning the CDN.
Pattern 4 — Throttle first, ban second. In parallel with the log line, apply Laravel's RateLimiter / ThrottleRequests on the login route (RateLimiter::for('login', ...)). This provides immediate, app-level defense with a shared store (Redis in multi-node), while fail2ban provides the firewall-level deterrent. These are complements; do both. Symfony's own login_throttling is what superseded the bundle's approach in Symfony 4+, and its Laravel analogue is RateLimiter.
Pattern 5 — Centralised logs if you're distributed. fail2ban reads host log files. On Kubernetes/ECS/autoscaled hosts, either (a) sidecar your Laravel app with a fail2ban container reading a shared log volume, or (b) forward logs (Vector/Fluent Bit) to a log store and run fail2ban on the collector. Neither the Symfony bundle nor any drop-in solves this — it's an ops pattern.
Pattern 6 — Integrate with laravel.log rotation. Use a dedicated channel (as above) with daily rotation so the fail2ban filter's file path is stable and doesn't need to parse a general-purpose log. Configure postrotate/logrotate to send SIGUSR1 to fail2ban so it picks up rotation. A common failure: fail2ban reads a file handle to an inode that logrotate has renamed, and silently misses events.
psr/*, symfony/*, and possibly block installation entirely depending on your constraint set. Do not try to force it. There is no bridge package and no maintained fork (zero dependents, archived repo, five stars).Auth\Events\Failed credentials are sensitive. The Failed event exposes the attempted credentials array. Log only a hash or partial of the email (hash('sha256', $email) truncated), never the password, never the full credentials payload. Sanitise user agents; attackers control them.<HOST> matching depends on line shape. Fail2ban's filter regex is brittle. Test with fail2ban-regex /path/to/auth-failures.log /etc/fail2ban/filter.d/yourfilter.conf on real log samples before trusting the jail. An unmatched filter silently provides zero protection.WARNING), will lock out legitimate users — often support staff behind shared office IPs. Use maxretry, findtime, and bantime generously; test with maxretry=5, findtime=10m, bantime=1h before relaxing/tightening. Consider an ignoreip list for internal networks, the LB, and your on-call.fail2ban bans the CDN edge and you take down all traffic; if you trust too little, you log the LB IP and ban every user. Configure TrustProxies with explicit CIDR ranges.storage/logs/auth-failures.log actually being written? (2) does the file have the permissions fail2ban needs (fail2ban often runs as root or a dedicated user)? (3) does fail2ban-client status yourjail show the jail active and total failed incrementing? (4) tail -f the log while triggering a failed login.LoginFailed domain event alongside Auth\Events\Failed and let listeners decide (log, increment metrics, trigger a notification for admins). This gives you a single seam if you later move enforcement to a WAF or CrowdSec without changing the login flow.How can I help you explore Laravel packages today?