Skip to content

Conversation

@mrmm
Copy link

@mrmm mrmm commented Dec 27, 2025

Summary

This PR adds fail2ban-like login protection to Warpgate with:

  • IP-based rate limiting - Automatically blocks IPs after configurable failed login attempts
  • Exponential backoff - Each subsequent block has longer duration (30min → 1h → 2h → ... → 24h max)
  • User account lockout - Locks accounts after repeated failures (optional auto-unlock)
  • Cross-protocol protection - Failed attempts across SSH, HTTP, MySQL, PostgreSQL all count together
  • Admin management - View status, unblock IPs, unlock users via Admin UI or API
  • In-memory caching - Fast lookups with periodic database sync

Configuration

login_protection:
  enabled: true
  ip_rate_limit:
    max_attempts: 5
    time_window_minutes: 15
    base_block_duration_minutes: 30
    block_duration_multiplier: 2.0
    max_block_duration_hours: 24
  user_lockout:
    max_attempts: 10
    time_window_minutes: 60
    auto_unlock: false
  retention_days: 30

Admin API Endpoints

  • GET /login-protection/status - Security overview
  • GET /login-protection/blocked-ips - List blocked IPs
  • DELETE /login-protection/blocked-ips/:ip - Unblock an IP
  • GET /login-protection/locked-users - List locked users
  • DELETE /login-protection/locked-users/:username - Unlock a user

Test plan

  • Docker Compose local setup - containers start correctly
  • IP blocking test - after 3 failed attempts, returns {"state":"IpBlocked"}
  • User lockout test - account locks after threshold
  • Integration tests - pytest tests pass
  • Build - cargo build succeeds
  • Admin UI - Login Protection page shows stats and lists

mrmm added 11 commits December 27, 2025 16:58
Add LoginProtectionConfig, IpRateLimitConfig, and UserLockoutConfig
structs with sensible defaults. Add IpBlocked and UserLocked error
variants for authentication flow.
Add FailedLoginAttempt, IpBlock, and UserLockout entities.
Create m00025_login_protection migration with composite indexes
for efficient queries on IP/timestamp and username/timestamp.
Add LoginProtectionService with in-memory caching for fast lookups.
Implement IP blocking with exponential backoff, user lockout,
and background cleanup task for expired records.
- Add bullet point about built-in brute-force protection
- Add Documentation section with links to external docs
- Add comparison table row for brute-force protection
- SSH: Check IP blocks before auth, record failed attempts
- HTTP: Check IP blocks and user lockouts in auth flow
- MySQL: Add login protection checks to session authentication
- PostgreSQL: Add login protection checks to session authentication

All protocols now share the same login protection service for
consistent brute-force protection across the entire gateway.
- GET /login-protection/status - Security status overview
- GET /login-protection/blocked-ips - List blocked IPs
- DELETE /login-protection/blocked-ips/:ip - Unblock an IP
- GET /login-protection/locked-users - List locked users
- DELETE /login-protection/locked-users/:username - Unlock a user

All endpoints require admin authentication.
Updates admin and gateway OpenAPI schemas with new login protection
types and endpoints.
- New LoginProtection.svelte page with:
  - Stats cards (blocked IPs, locked users, failed attempts)
  - Blocked IPs list with unblock action
  - Locked users list with unlock action
- Add navigation link in Config.svelte
- Docker Compose setup with warpgate and SSH target
- Test configuration with aggressive settings for quick testing
- Test scripts for IP blocking and user lockout scenarios
- README with testing instructions
Comprehensive pytest tests covering:
- IP blocking after failed attempts threshold
- Exponential backoff for repeat offenders
- User lockout functionality
- Admin API endpoints (status, unblock, unlock)
- Cross-protocol attempt counting
Adds LoginProtectionConfig, IpRateLimitConfig, and UserLockoutConfig
definitions to the JSON schema.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant